001: /*
002: * Copyright 2005-2007 The Kuali Foundation.
003: *
004: *
005: * Licensed under the Educational Community License, Version 1.0 (the "License");
006: * you may not use this file except in compliance with the License.
007: * You may obtain a copy of the License at
008: *
009: * http://www.opensource.org/licenses/ecl1.php
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017: package org.kuali.workflow.config;
018:
019: import java.util.ArrayList;
020: import java.util.HashMap;
021: import java.util.Iterator;
022: import java.util.LinkedList;
023: import java.util.List;
024: import java.util.Map;
025:
026: import javax.sql.DataSource;
027: import javax.xml.namespace.QName;
028:
029: import org.apache.commons.beanutils.ConstructorUtils;
030: import org.apache.commons.lang.StringUtils;
031: import org.kuali.bus.services.KSBServiceLocator;
032: import org.kuali.rice.config.Config;
033: import org.kuali.rice.config.ConfigurationException;
034: import org.kuali.rice.config.ModuleConfigurer;
035: import org.kuali.rice.config.SimpleConfig;
036: import org.kuali.rice.config.logging.Log4jLifeCycle;
037: import org.kuali.rice.core.Core;
038: import org.kuali.rice.lifecycle.Lifecycle;
039: import org.kuali.rice.resourceloader.BaseResourceLoader;
040: import org.kuali.rice.resourceloader.GlobalResourceLoader;
041: import org.kuali.rice.resourceloader.ResourceLoader;
042: import org.kuali.rice.resourceloader.ServiceLocator;
043: import org.kuali.rice.resourceloader.SimpleServiceLocator;
044: import org.kuali.workflow.ojb.OjbConfigurer;
045:
046: import edu.iu.uis.eden.EdenConstants;
047: import edu.iu.uis.eden.KEWServiceLocator;
048: import edu.iu.uis.eden.clientapp.ServiceHolder;
049: import edu.iu.uis.eden.exception.WorkflowRuntimeException;
050: import edu.iu.uis.eden.messaging.JavaServiceDefinition;
051: import edu.iu.uis.eden.messaging.ServiceDefinition;
052: import edu.iu.uis.eden.plugin.PluginRegistry;
053: import edu.iu.uis.eden.util.ClassLoaderUtils;
054: import edu.iu.uis.eden.util.Utilities;
055:
056: /**
057: * Configures the KEW Rice module. KEW module initiation proceeds as follows:
058: *
059: * <ol>
060: * <li>Parse and load configuration for:</li>
061: * <ul>
062: * <li>Client Protocol</li>
063: * <li>The KSB</li>
064: * <li>Webservices</li>
065: * <li>JMX</li>
066: * </ul>
067: * </li>
068: * <li>Configure and startup KEW for "Thin Client" mode OR</li>
069: * <li>Configure and startup KEW for "Embedded Mode"</li>
070: * </ol>
071: *
072: * @author ewestfal
073: */
074: public class KEWConfigurer extends ModuleConfigurer {
075:
076: private static final org.apache.log4j.Logger LOG = org.apache.log4j.Logger
077: .getLogger(KEWConfigurer.class);
078:
079: public static final String KEW_DATASOURCE_OBJ = "org.kuali.workflow.datasource";
080: public static final String KEW_DATASOURCE_JNDI = "org.kuali.workflow.datasource.jndi.location";
081:
082: private static final String DEFAULT_JMX_PROTOCOL = "hessian+sig";
083: private static final String DEFAULT_JMX_SERVICE_URL = "service:jmx:"
084: + DEFAULT_JMX_PROTOCOL + "://localhost:8080/remoting/jmx";
085:
086: private String clientProtocol;
087:
088: private List<ServiceHolder> overrideServices;
089: private PluginRegistry pluginRegistry;
090: private DataSource dataSource;
091: private String dataSourceJndiName;
092:
093: //bus stuff
094: private List<ServiceDefinition> services = new ArrayList<ServiceDefinition>();
095:
096: private String serviceServletUrl;
097:
098: // webservice related parameters
099: private String keystoreAlias;
100: private String keystorePassword;
101: private String keystoreFile;
102: private String webservicesUrl;
103: private String webserviceRetry;
104:
105: // jmx parameters
106: private String jmxProtocol;
107: private String jmxServiceUrl;
108: private Map mBeans = new HashMap();
109:
110: private boolean useDefaultUserService = false;
111: private boolean useDefaultWorkgroupService = false;
112: private boolean runEmbeddedServer = false;
113:
114: @Override
115: protected List<Lifecycle> loadLifecycles() throws Exception {
116: List<Lifecycle> lifecycles = new LinkedList<Lifecycle>();
117: if (EdenConstants.WEBSERVICE_CLIENT_PROTOCOL.equals(Core
118: .getCurrentContextConfig().getClientProtocol())) {
119: lifecycles.add(createThinClientLifecycle());
120: } else {
121: if (isStandaloneServer()) {
122: lifecycles.add(new Log4jLifeCycle());
123: }
124: lifecycles.add(new OjbConfigurer());
125: lifecycles.add(createTempEmbeddedLifeCycle());
126: }
127: return lifecycles;
128: }
129:
130: protected boolean isStandaloneServer() {
131: return new Boolean(Core.getCurrentContextConfig().getProperty(
132: "kew.standalone.server")).booleanValue();
133: }
134:
135: @Override
136: public void start() throws Exception {
137: super .start();
138: registerOptionalDefaultServices(Core.getCurrentContextConfig());
139: configureInjectedOverrideServices(GlobalResourceLoader
140: .getResourceLoader());
141: }
142:
143: @Override
144: public void stop() throws Exception {
145: super .stop();
146: }
147:
148: /**
149: * TODO Because a lot of our lifecycles live behind the embedded plugin and the KEWConfigurer does not, this is a simple
150: * measure to load these without having to deal with the removal of the embedded plugin right away.
151: */
152: protected Lifecycle createTempEmbeddedLifeCycle() throws Exception {
153: return (Lifecycle) ConstructorUtils.invokeConstructor(Class
154: .forName("edu.iu.uis.eden.core.TempEmbeddedLifeCycle"),
155: null);
156: }
157:
158: protected Lifecycle createThinClientLifecycle() throws Exception {
159: return (Lifecycle) ConstructorUtils
160: .invokeConstructor(
161: Class
162: .forName("org.kuali.workflow.config.ThinClientLifecycle"),
163: null);
164: }
165:
166: public Config loadConfig(Config parentConfig) throws Exception {
167: LOG.info("Starting configuration of KEW for message entity "
168: + getMessageEntity(parentConfig));
169: Config currentConfig = parseConfig(parentConfig);
170: configureClientProtocol(currentConfig);
171: configureDataSource(currentConfig);
172: configureBus(currentConfig);
173: configureKeystore(currentConfig);
174: configureWebservices(currentConfig);
175: configureManagement(currentConfig);
176: return currentConfig;
177: }
178:
179: protected Config parseConfig(Config parentConfig) throws Exception {
180: List<String> defaultConfigLocations = new ArrayList<String>();
181: defaultConfigLocations
182: .add(EdenConstants.DEFAULT_GLOBAL_CONFIG_LOCATION);
183: defaultConfigLocations
184: .add(EdenConstants.DEFAULT_APPLICATION_CONFIG_LOCATION);
185: Config kewConfig = new SimpleConfig(defaultConfigLocations,
186: parentConfig.getProperties());
187: kewConfig.parseConfig();
188: mergeDefaultsIntoParentConfig(parentConfig, kewConfig);
189: return parentConfig;
190: }
191:
192: /**
193: * Merges any default configuration into the parent config. If a property appears in both
194: * places, precedence is given to the parentConfig. This allows for our defaults to not
195: * override any property which has already been defined.
196: */
197: protected void mergeDefaultsIntoParentConfig(Config parentConfig,
198: Config defaultConfig) {
199: for (Object keyObj : defaultConfig.getProperties().keySet()) {
200: String key = (String) keyObj;
201: if (!parentConfig.getProperties().containsKey(key)) {
202: parentConfig.getProperties().put(key,
203: defaultConfig.getProperty(key));
204: }
205: }
206: }
207:
208: protected String getMessageEntity(Config config) {
209: if (StringUtils.isBlank(config.getMessageEntity())) {
210: throw new ConfigurationException(
211: "The 'message.entity' property was not properly configured.");
212: }
213: return config.getMessageEntity();
214: }
215:
216: protected void configureClientProtocol(Config config) {
217: if (StringUtils.isBlank(clientProtocol)) {
218: clientProtocol = config.getClientProtocol();
219: if (clientProtocol == null) {
220: clientProtocol = EdenConstants.WEBSERVICE_CLIENT_PROTOCOL;
221: }
222: }
223: // from a client, LOCAL protocol is equivalent to EMBEDDED
224: // TODO this was messing up the tests were LOCAL was actually being used
225: /*if (EdenConstants.LOCAL_CLIENT_PROTOCOL.equals(clientProtocol)) {
226: clientProtocol = EdenConstants.EMBEDDED_CLIENT_PROTOCOL;
227: }*/
228: if (!EdenConstants.CLIENT_PROTOCOLS.contains(clientProtocol)) {
229: throw new ConfigurationException(
230: "Invalid client protocol specified '"
231: + clientProtocol + "'.");
232: }
233: config.getProperties().put(Config.CLIENT_PROTOCOL,
234: clientProtocol);
235: }
236:
237: protected void configureDataSource(Config config) {
238: if (getDataSource() != null) {
239: config.getObjects()
240: .put(KEW_DATASOURCE_OBJ, getDataSource());
241: } else if (!StringUtils.isBlank(getDataSourceJndiName())) {
242: config.getProperties().put(KEW_DATASOURCE_JNDI,
243: getDataSourceJndiName());
244: }
245: }
246:
247: @SuppressWarnings("unchecked")
248: protected void configureBus(Config config) throws Exception {
249: if (getServiceServletUrl() != null) {
250: Core.getCurrentContextConfig().overrideProperty(
251: Config.SERVICE_SERVLET_URL, getServiceServletUrl());
252: }
253: LOG.debug("Configuring services for Message Entity "
254: + getMessageEntity(config)
255: + " using config for classloader "
256: + ClassLoaderUtils.getDefaultClassLoader());
257: configureServiceList(config, Config.BUS_DEPLOYED_SERVICES,
258: getServices());
259: }
260:
261: protected void configureKeystore(Config config) {
262: if (!Utilities.isEmpty(keystoreAlias)) {
263: config.getProperties().put(Config.KEYSTORE_ALIAS,
264: keystoreAlias);
265: }
266: if (!Utilities.isEmpty(keystorePassword)) {
267: config.getProperties().put(Config.KEYSTORE_PASSWORD,
268: keystorePassword);
269: }
270: if (!Utilities.isEmpty(keystoreFile)) {
271: config.getProperties().put(Config.KEYSTORE_FILE,
272: keystoreFile);
273: }
274: if (Utilities
275: .isEmpty(config
276: .getProperty(EdenConstants.SIMPLE_DOCUMENT_ACTIONS_SECURITY))) {
277: config.getProperties().put(
278: EdenConstants.SIMPLE_DOCUMENT_ACTIONS_SECURITY,
279: "true");
280: }
281: }
282:
283: protected void configureWebservices(Config config) {
284: if (!Utilities.isEmpty(webservicesUrl)) {
285: config.getProperties().put(
286: Config.BASE_WEB_SERVICE_URL_WORKFLOW_CLIENT_FILE,
287: webservicesUrl);
288: }
289: if (!Utilities.isEmpty(webserviceRetry)) {
290: config.getProperties().put(
291: Config.WEB_SERVICE_CONNECT_RETRY, webserviceRetry);
292: }
293: }
294:
295: protected void configureManagement(Config config) {
296: // configure JMX Protocol
297: String jmxProtocol = getJmxProtocol();
298: if (Utilities.isEmpty(jmxProtocol)) {
299: jmxProtocol = DEFAULT_JMX_PROTOCOL;
300: }
301: setJmxProtocol(jmxProtocol);
302: config.getProperties().put(Config.JMX_PROTOCOL, jmxProtocol);
303:
304: // configure JMX Service Url
305: String jmxServiceUrl = getJmxServiceUrl();
306: if (Utilities.isEmpty(jmxServiceUrl)) {
307: String serviceServletUrl = getServiceServletUrl();
308: if (Utilities.isEmpty(serviceServletUrl)) {
309: jmxServiceUrl = DEFAULT_JMX_SERVICE_URL;
310: LOG
311: .warn("Could not determine the JMX Service URL. Defaulting to "
312: + jmxServiceUrl);
313: } else {
314: // derive the jmx url from the service servlet url
315: int protocolIndex = serviceServletUrl.indexOf("/");
316: if (protocolIndex < 0) {
317: throw new WorkflowRuntimeException(
318: "Failed to derive jmx url from servlet url: "
319: + serviceServletUrl);
320: }
321: jmxServiceUrl = "service:jmx:" + getJmxProtocol() + ":"
322: + serviceServletUrl.substring(protocolIndex);
323: if (!jmxServiceUrl.endsWith("/")) {
324: jmxServiceUrl += "/";
325: }
326: jmxServiceUrl += "jmx";
327: }
328: }
329: setJmxServiceUrl(jmxServiceUrl);
330: config.getProperties().put(Config.JMX_SERVICE_URL,
331: jmxServiceUrl);
332:
333: // configure mBeans
334: if (getMBeans() != null && !getMBeans().isEmpty()) {
335: config.getObjects().put(Config.M_BEANS, getMBeans());
336: }
337: }
338:
339: private void configureServiceList(Config config, String key,
340: List<ServiceDefinition> services) throws Exception {
341: LOG.debug("Configuring services for Message Entity "
342: + getMessageEntity(config)
343: + " using config for classloader "
344: + ClassLoaderUtils.getDefaultClassLoader());
345: List<ServiceDefinition> serviceDefinitions = (List<ServiceDefinition>) config
346: .getObject(key);
347: if (serviceDefinitions == null) {
348: config.getObjects().put(key, services);
349: } else if (services != null) {
350: LOG
351: .debug("Services already exist. Adding additional services");
352: serviceDefinitions.addAll(services);
353: }
354:
355: String serviceServletUrl = Core.getCurrentContextConfig()
356: .getProperty(Config.SERVICE_SERVLET_URL);
357: // if it's empty, then we want to be able to inherit it from the parent configuration
358: if (!StringUtils.isEmpty(serviceServletUrl)) {
359: config.getObjects().put(Config.SERVICE_SERVLET_URL,
360: serviceServletUrl);
361: }
362: for (Iterator iter = services.iterator(); iter.hasNext();) {
363: ServiceDefinition serviceDef = (ServiceDefinition) iter
364: .next();
365: serviceDef.validate();
366: }
367: }
368:
369: protected void registerOptionalDefaultServices(Config config)
370: throws Exception {
371: LOG
372: .debug("Checking for optional default workgroup and user service to load from the embedded plugin");
373: if (isUseDefaultUserService()) {
374: registerEmbeddedDefaultUserService();
375: }
376: if (isUseDefaultWorkgroupService()) {
377: registerEmbeddedDefaultWorkgroupService();
378: }
379: }
380:
381: protected void registerEmbeddedDefaultUserService()
382: throws Exception {
383: JavaServiceDefinition serviceDef = new JavaServiceDefinition();
384: serviceDef.setServiceName(new QName(
385: KEWServiceLocator.USER_SERVICE));
386: serviceDef
387: .setService(KEWServiceLocator
388: .getBean(KEWServiceLocator.OPTIONAL_EMBEDDED_USER_SERVICE));
389: serviceDef.validate();
390: KSBServiceLocator.getServiceDeployer().registerService(
391: serviceDef, false);
392: }
393:
394: protected void registerEmbeddedDefaultWorkgroupService()
395: throws Exception {
396: JavaServiceDefinition serviceDef = new JavaServiceDefinition();
397: serviceDef.setServiceName(new QName(
398: KEWServiceLocator.WORKGROUP_SRV));
399: serviceDef
400: .setService(KEWServiceLocator
401: .getBean(KEWServiceLocator.OPTIONAL_EMBEDDED_WORKGROUP_SERVICE));
402: serviceDef.validate();
403: KSBServiceLocator.getServiceDeployer().registerService(
404: serviceDef, false);
405: }
406:
407: protected void configureInjectedOverrideServices(
408: ResourceLoader embeddedClientServiceRL) {
409: if (this .getOverrideServices() == null) {
410: return;
411: }
412: // ResourceLoader embeddedClientServiceRL = GlobalResourceLoader.getResourceLoader(new QName(ResourceLoader.EMBEDDED_CLIENT_APP_RESOURCE_LOADER));
413: //can be null if they've injected they're own RL that doesn't follow our name
414: if (embeddedClientServiceRL == null) {
415: return;
416: }
417: if (!(embeddedClientServiceRL instanceof BaseResourceLoader)) {
418: LOG
419: .info("Client application has injected its own implmentation of ResourceLoader to override workflow servcies");
420: return;
421: }
422: ServiceLocator sl = ((BaseResourceLoader) embeddedClientServiceRL)
423: .getServiceLocator();
424: if (!(sl instanceof SimpleServiceLocator)) {
425: LOG
426: .info("Client application has used its own implementation of ServiceLocator to override workflow services");
427: return;
428: }
429: for (ServiceHolder serviceHolder : this .getOverrideServices()) {
430: if (LOG.isDebugEnabled()) {
431: LOG.debug("Loading override service "
432: + serviceHolder.getServiceName() + " "
433: + serviceHolder.getService());
434: }
435: ((SimpleServiceLocator) sl).addService(serviceHolder
436: .getServiceName(), serviceHolder.getService());
437: }
438: }
439:
440: protected void addServiceDefinitionsToRegistry(
441: List<ServiceDefinition> serviceDefinition) {
442:
443: }
444:
445: public String getClientProtocol() {
446: return clientProtocol;
447: }
448:
449: public void setClientProtocol(String clientProtocol) {
450: this .clientProtocol = clientProtocol;
451: }
452:
453: public String getJmxProtocol() {
454: return jmxProtocol;
455: }
456:
457: public void setJmxProtocol(String jmxProtocol) {
458: this .jmxProtocol = jmxProtocol;
459: }
460:
461: public String getJmxServiceUrl() {
462: return jmxServiceUrl;
463: }
464:
465: public void setJmxServiceUrl(String jmxServiceUrl) {
466: this .jmxServiceUrl = jmxServiceUrl;
467: }
468:
469: public String getKeystoreAlias() {
470: return keystoreAlias;
471: }
472:
473: public void setKeystoreAlias(String keystoreAlias) {
474: this .keystoreAlias = keystoreAlias;
475: }
476:
477: public String getKeystoreFile() {
478: return keystoreFile;
479: }
480:
481: public void setKeystoreFile(String keystoreFile) {
482: this .keystoreFile = keystoreFile;
483: }
484:
485: public String getKeystorePassword() {
486: return keystorePassword;
487: }
488:
489: public void setKeystorePassword(String keystorePassword) {
490: this .keystorePassword = keystorePassword;
491: }
492:
493: public Map getMBeans() {
494: return mBeans;
495: }
496:
497: public void setMBeans(Map beans) {
498: mBeans = beans;
499: }
500:
501: public List<ServiceHolder> getOverrideServices() {
502: return overrideServices;
503: }
504:
505: public void setOverrideServices(List<ServiceHolder> overrideServices) {
506: this .overrideServices = overrideServices;
507: }
508:
509: public PluginRegistry getPluginRegistry() {
510: return pluginRegistry;
511: }
512:
513: public void setPluginRegistry(PluginRegistry pluginRegistry) {
514: this .pluginRegistry = pluginRegistry;
515: }
516:
517: public boolean isRunEmbeddedServer() {
518: return runEmbeddedServer;
519: }
520:
521: public void setRunEmbeddedServer(boolean runEmbeddedServer) {
522: this .runEmbeddedServer = runEmbeddedServer;
523: }
524:
525: public List<ServiceDefinition> getServices() {
526: return services;
527: }
528:
529: public void setServices(List<ServiceDefinition> services) {
530: this .services = services;
531: }
532:
533: public String getServiceServletUrl() {
534: return serviceServletUrl;
535: }
536:
537: public void setServiceServletUrl(String serviceServletUrl) {
538: this .serviceServletUrl = serviceServletUrl;
539: }
540:
541: public boolean isUseDefaultUserService() {
542: return useDefaultUserService;
543: }
544:
545: public void setUseDefaultUserService(boolean useDefaultUserService) {
546: this .useDefaultUserService = useDefaultUserService;
547: }
548:
549: public boolean isUseDefaultWorkgroupService() {
550: return useDefaultWorkgroupService;
551: }
552:
553: public void setUseDefaultWorkgroupService(
554: boolean useDefaultWorkgroupService) {
555: this .useDefaultWorkgroupService = useDefaultWorkgroupService;
556: }
557:
558: public String getWebserviceRetry() {
559: return webserviceRetry;
560: }
561:
562: public void setWebserviceRetry(String webserviceRetry) {
563: this .webserviceRetry = webserviceRetry;
564: }
565:
566: public String getWebservicesUrl() {
567: return webservicesUrl;
568: }
569:
570: public void setWebservicesUrl(String webservicesUrl) {
571: this .webservicesUrl = webservicesUrl;
572: }
573:
574: public DataSource getDataSource() {
575: return dataSource;
576: }
577:
578: public void setDataSource(DataSource dataSource) {
579: this .dataSource = dataSource;
580: }
581:
582: public String getDataSourceJndiName() {
583: return dataSourceJndiName;
584: }
585:
586: public void setDataSourceJndiName(String jndiDatasourceLocation) {
587: this.dataSourceJndiName = jndiDatasourceLocation;
588: }
589:
590: }
|