001 /*
002 * Copyright 2006-2011 The Kuali Foundation
003 *
004 * Licensed under the Educational Community License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 * http://www.opensource.org/licenses/ecl2.php
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016
017 package org.kuali.rice.ksb.messaging.config;
018
019 import java.util.ArrayList;
020 import java.util.Arrays;
021 import java.util.Collection;
022 import java.util.Collections;
023 import java.util.LinkedList;
024 import java.util.List;
025
026 import javax.sql.DataSource;
027
028 import org.apache.commons.httpclient.contrib.ssl.EasySSLProtocolSocketFactory;
029 import org.apache.commons.httpclient.protocol.Protocol;
030 import org.apache.commons.httpclient.protocol.ProtocolSocketFactory;
031 import org.kuali.rice.core.api.config.CoreConfigHelper;
032 import org.kuali.rice.core.api.config.module.RunMode;
033 import org.kuali.rice.core.api.config.property.Config;
034 import org.kuali.rice.core.api.config.property.ConfigContext;
035 import org.kuali.rice.core.api.exception.RiceRuntimeException;
036 import org.kuali.rice.core.api.lifecycle.BaseLifecycle;
037 import org.kuali.rice.core.api.lifecycle.Lifecycle;
038 import org.kuali.rice.core.api.resourceloader.ResourceLoader;
039 import org.kuali.rice.core.framework.persistence.jpa.OrmUtils;
040 import org.kuali.rice.core.impl.config.module.ModuleConfigurer;
041 import org.kuali.rice.core.impl.lifecycle.ServiceDelegatingLifecycle;
042 import org.kuali.rice.core.util.ClassLoaderUtils;
043 import org.kuali.rice.core.util.RiceConstants;
044 import org.kuali.rice.ksb.api.KsbApiConstants;
045 import org.kuali.rice.ksb.api.KsbApiServiceLocator;
046 import org.kuali.rice.ksb.api.bus.ServiceDefinition;
047 import org.kuali.rice.ksb.messaging.AlternateEndpoint;
048 import org.kuali.rice.ksb.messaging.AlternateEndpointLocation;
049 import org.kuali.rice.ksb.messaging.MessageFetcher;
050 import org.kuali.rice.ksb.messaging.resourceloader.KSBResourceLoaderFactory;
051 import org.kuali.rice.ksb.messaging.serviceconnectors.HttpInvokerConnector;
052 import org.kuali.rice.ksb.service.KSBServiceLocator;
053 import org.kuali.rice.ksb.util.KSBConstants;
054 import org.quartz.Scheduler;
055 import org.springframework.transaction.PlatformTransactionManager;
056
057
058 /**
059 * Used to configure the embedded workflow. This could be used to configure
060 * embedded workflow programmatically but mostly this is a base class by which
061 * to hang specific configuration behavior off of through subclassing
062 *
063 * @author Kuali Rice Team (rice.collab@kuali.org)
064 *
065 */
066 public class KSBConfigurer extends ModuleConfigurer {
067
068 private static final String SERVICE_BUS_CLIENT_SPRING = "classpath:org/kuali/rice/ksb/config/KsbServiceBusClientSpringBeans.xml";
069 private static final String MESSAGE_CLIENT_SPRING = "classpath:org/kuali/rice/ksb/config/KsbMessageClientSpringBeans.xml";
070 private static final String OJB_MESSAGE_CLIENT_SPRING = "classpath:org/kuali/rice/ksb/config/KsbOjbMessageClientSpringBeans.xml";
071 private static final String BAM_SPRING = "classpath:org/kuali/rice/ksb/config/KsbBamSpringBeans.xml";
072 private static final String OJB_BAM_SPRING = "classpath:org/kuali/rice/ksb/config/KsbOjbBamSpringBeans.xml";
073 private static final String MODULE_SPRING = "classpath:org/kuali/rice/ksb/config/KsbModuleConfigurationSpringBeans.xml";
074 private static final String REGISTRY_SERVER_SPRING = "classpath:org/kuali/rice/ksb/config/KsbRegistryServerSpringBeans.xml";
075 private static final String OJB_REGISTRY_SPRING = "classpath:org/kuali/rice/ksb/config/KsbOjbRegistrySpringBeans.xml";
076 private static final String WEB_SPRING = "classpath:org/kuali/rice/ksb/config/KsbWebSpringBeans.xml";
077
078 private List<ServiceDefinition> services = new ArrayList<ServiceDefinition>();
079
080 private List<AlternateEndpointLocation> alternateEndpointLocations = new ArrayList<AlternateEndpointLocation>();
081
082 private List<AlternateEndpoint> alternateEndpoints = new ArrayList<AlternateEndpoint>();
083
084 private DataSource registryDataSource;
085
086 private DataSource messageDataSource;
087
088 private DataSource nonTransactionalMessageDataSource;
089
090 private DataSource bamDataSource;
091
092 private Scheduler exceptionMessagingScheduler;
093
094 private PlatformTransactionManager platformTransactionManager;
095
096 private List<Lifecycle> internalLifecycles;
097
098 public KSBConfigurer() {
099 super(KsbApiConstants.KSB_MODULE_NAME);
100 setValidRunModes(Arrays.asList(RunMode.REMOTE, RunMode.LOCAL));
101 this.internalLifecycles = new ArrayList<Lifecycle>();
102 }
103
104 @Override
105 public void addAdditonalToConfig() {
106 configureDataSource();
107 configureScheduler();
108 configurePlatformTransactionManager();
109 configureAlternateEndpoints();
110 }
111
112 @Override
113 public List<String> getPrimarySpringFiles(){
114 final List<String> springFileLocations = new ArrayList<String>();
115
116 boolean isJpa = OrmUtils.isJpaEnabled("rice.ksb");
117 if (isJpa) {
118 // TODO redo this once we're back to JPA
119 // springFileLocations.add("classpath:org/kuali/rice/ksb/config/KSBJPASpringBeans.xml");
120 throw new UnsupportedOperationException("JPA not currently supported for KSB");
121 }
122
123 springFileLocations.add(SERVICE_BUS_CLIENT_SPRING);
124
125 if (isMessagePersistenceEnabled()) {
126 springFileLocations.add(MESSAGE_CLIENT_SPRING);
127 springFileLocations.add(OJB_MESSAGE_CLIENT_SPRING);
128 }
129
130 if (isBamEnabled()) {
131 springFileLocations.add(BAM_SPRING);
132 springFileLocations.add(OJB_BAM_SPRING);
133 }
134
135 if (getRunMode().equals( RunMode.LOCAL )) {
136 // TODO hack 'cause KSB used KNS - this needs to be fixed!!!
137 springFileLocations.add("classpath:org/kuali/rice/krad/config/KRADSpringBeans.xml");
138 springFileLocations.add("classpath:org/kuali/rice/kns/config/KNSSpringBeans.xml");
139 springFileLocations.add(REGISTRY_SERVER_SPRING);
140 springFileLocations.add(OJB_REGISTRY_SPRING);
141 if (ConfigContext.getCurrentContextConfig().getBooleanProperty(KSBConstants.Config.LOAD_KRAD_MODULE_CONFIGURATION, false)) {
142 springFileLocations.add(MODULE_SPRING);
143 springFileLocations.add(WEB_SPRING);
144 }
145 }
146
147 return springFileLocations;
148 }
149
150 /**
151 * Returns true - KSB UI should always be included.
152 *
153 * @see org.kuali.rice.core.ConfigContext.getCurrentContextConfig().ModuleConfigurer#shouldRenderWebInterface()
154 */
155 @Override
156 public boolean shouldRenderWebInterface() {
157 return true;
158 }
159
160 @Override
161 public Collection<ResourceLoader> getResourceLoadersToRegister() throws Exception{
162 ResourceLoader ksbRemoteResourceLoader = KSBResourceLoaderFactory.createRootKSBRemoteResourceLoader();
163 ksbRemoteResourceLoader.start();
164 return Collections.singletonList(ksbRemoteResourceLoader);
165 }
166
167 @Override
168 public List<Lifecycle> loadLifecycles() throws Exception {
169 List<Lifecycle> lifecycles = new LinkedList<Lifecycle>();
170 // this validation of our service list needs to happen after we've
171 // loaded our configs so it's a lifecycle
172 lifecycles.add(new BaseLifecycle() {
173
174 @Override
175 public void start() throws Exception {
176 // first check if we want to allow self-signed certificates for SSL communication
177 if (Boolean.valueOf(ConfigContext.getCurrentContextConfig().getProperty(KSBConstants.Config.KSB_ALLOW_SELF_SIGNED_SSL)).booleanValue()) {
178 Protocol.registerProtocol("https", new Protocol("https",
179 (ProtocolSocketFactory) new EasySSLProtocolSocketFactory(), 443));
180 }
181 super.start();
182 }
183 });
184 lifecycles.add(new ServiceDelegatingLifecycle(KSBConstants.ServiceNames.BUS_ADMIN_SERVICE));
185 return lifecycles;
186 }
187
188
189
190 @Override
191 public void doAdditonalConfigurerValidations() {
192 for (final ServiceDefinition serviceDef : KSBConfigurer.this.services) {
193 serviceDef.validate();
194 }
195 }
196
197 @Override
198 public void doAdditionalContextStartedLogic() {
199 ServicePublisher servicePublisher = new ServicePublisher(getServices());
200 Lifecycle serviceBus = new ServiceDelegatingLifecycle(KsbApiServiceLocator.SERVICE_BUS);
201 Lifecycle threadPool = new ServiceDelegatingLifecycle(KSBConstants.ServiceNames.THREAD_POOL_SERVICE);
202 Lifecycle scheduledThreadPool = new ServiceDelegatingLifecycle(KSBConstants.ServiceNames.SCHEDULED_THREAD_POOL_SERVICE);
203
204 try {
205 servicePublisher.start();
206 internalLifecycles.add(servicePublisher);
207 serviceBus.start();
208 internalLifecycles.add(serviceBus);
209 threadPool.start();
210 internalLifecycles.add(threadPool);
211 scheduledThreadPool.start();
212 internalLifecycles.add(scheduledThreadPool);
213 } catch (Exception e) {
214 if (e instanceof RuntimeException) {
215 throw (RuntimeException)e;
216 }
217 throw new RiceRuntimeException("Failed to initialize KSB on context startup");
218 }
219
220 requeueMessages();
221 }
222
223 @Override
224 protected void doAdditionalModuleStopLogic() throws Exception {
225 for (int index = internalLifecycles.size() - 1; index >= 0; index--) {
226 try {
227 internalLifecycles.get(index).stop();
228 } catch (Exception e) {
229 LOG.error("Failed to properly execute shutdown logic.", e);
230 }
231 }
232 }
233
234 /**
235 * Used to refresh the service registry after the Application Context is initialized. This way any services that were exported on startup
236 * will be available in the service registry once startup is complete.
237 */
238 private void requeueMessages() {
239 LOG.info("Refreshing Service Registry to export services to the bus.");
240 KsbApiServiceLocator.getServiceBus().synchronize();
241
242 //automatically requeue documents sitting with status of 'R'
243 MessageFetcher messageFetcher = new MessageFetcher((Integer) null);
244 KSBServiceLocator.getThreadPool().execute(messageFetcher);
245 }
246
247 protected boolean isMessagePersistenceEnabled() {
248 return ConfigContext.getCurrentContextConfig().getBooleanProperty(KSBConstants.Config.MESSAGE_PERSISTENCE, true);
249 }
250
251 protected boolean isBamEnabled() {
252 return ConfigContext.getCurrentContextConfig().getBooleanProperty(Config.BAM_ENABLED, false);
253 }
254
255 protected void configureScheduler() {
256 if (this.getExceptionMessagingScheduler() != null) {
257 LOG.info("Configuring injected exception messaging Scheduler");
258 ConfigContext.getCurrentContextConfig().putObject(KSBConstants.Config.INJECTED_EXCEPTION_MESSAGE_SCHEDULER_KEY, this.getExceptionMessagingScheduler());
259 }
260 }
261
262 protected void configureDataSource() {
263 if (isMessagePersistenceEnabled()) {
264 if (getMessageDataSource() != null) {
265 ConfigContext.getCurrentContextConfig().putObject(KSBConstants.Config.KSB_MESSAGE_DATASOURCE, getMessageDataSource());
266 }
267 if (getNonTransactionalMessageDataSource() != null) {
268 ConfigContext.getCurrentContextConfig().putObject(KSBConstants.Config.KSB_MESSAGE_NON_TRANSACTIONAL_DATASOURCE, getNonTransactionalMessageDataSource());
269 }
270 }
271 if (getRunMode().equals(RunMode.LOCAL)) {
272 if (getRegistryDataSource() != null) {
273 ConfigContext.getCurrentContextConfig().putObject(KSBConstants.Config.KSB_REGISTRY_DATASOURCE, getRegistryDataSource());
274 }
275 }
276 if (isBamEnabled()) {
277 if (getBamDataSource() != null) {
278 ConfigContext.getCurrentContextConfig().putObject(KSBConstants.Config.KSB_BAM_DATASOURCE, getBamDataSource());
279 }
280 }
281 }
282
283 protected void configurePlatformTransactionManager() {
284 if (getPlatformTransactionManager() == null) {
285 return;
286 }
287 ConfigContext.getCurrentContextConfig().putObject(RiceConstants.SPRING_TRANSACTION_MANAGER, getPlatformTransactionManager());
288 }
289
290 protected void configureAlternateEndpoints() {
291 ConfigContext.getCurrentContextConfig().putObject(KSBConstants.Config.KSB_ALTERNATE_ENDPOINT_LOCATIONS, getAlternateEndpointLocations());
292 ConfigContext.getCurrentContextConfig().putObject(KSBConstants.Config.KSB_ALTERNATE_ENDPOINTS, getAlternateEndpoints());
293 }
294
295 @Override
296 public void doAdditionalContextStoppedLogic() {
297 try {
298 HttpInvokerConnector.shutdownIdleConnectionTimeout();
299 } catch (Exception e) {
300 LOG.error("Failed to shutdown idle connection timeout evictor thread.", e);
301 }
302 cleanUpConfiguration();
303 }
304
305 /**
306 * Because our configuration is global, shutting down Rice does not get rid of objects stored there. For that reason
307 * we need to manually clean these up. This is most important in the case of the service bus because the configuration
308 * is used to store services to be exported. If we don't clean this up then a shutdown/startup within the same
309 * class loading context causes the service list to be doubled and results in "multiple endpoint" error messages.
310 *
311 */
312 protected void cleanUpConfiguration() {
313 ConfigContext.getCurrentContextConfig().removeObject(KSBConstants.Config.KSB_ALTERNATE_ENDPOINTS);
314 }
315
316 public List<ServiceDefinition> getServices() {
317 return this.services;
318 }
319
320 public void setServices(List<ServiceDefinition> javaServices) {
321 this.services = javaServices;
322 }
323
324 public DataSource getMessageDataSource() {
325 return this.messageDataSource;
326 }
327
328 public void setMessageDataSource(DataSource messageDataSource) {
329 this.messageDataSource = messageDataSource;
330 }
331
332 public DataSource getNonTransactionalMessageDataSource() {
333 return this.nonTransactionalMessageDataSource;
334 }
335
336 public void setNonTransactionalMessageDataSource(DataSource nonTransactionalMessageDataSource) {
337 this.nonTransactionalMessageDataSource = nonTransactionalMessageDataSource;
338 }
339
340 public DataSource getRegistryDataSource() {
341 return this.registryDataSource;
342 }
343
344 public void setRegistryDataSource(DataSource registryDataSource) {
345 this.registryDataSource = registryDataSource;
346 }
347
348 public DataSource getBamDataSource() {
349 return this.bamDataSource;
350 }
351
352 public void setBamDataSource(DataSource bamDataSource) {
353 this.bamDataSource = bamDataSource;
354 }
355
356 public Scheduler getExceptionMessagingScheduler() {
357 return this.exceptionMessagingScheduler;
358 }
359
360 public void setExceptionMessagingScheduler(Scheduler exceptionMessagingScheduler) {
361 this.exceptionMessagingScheduler = exceptionMessagingScheduler;
362 }
363
364 public PlatformTransactionManager getPlatformTransactionManager() {
365 return platformTransactionManager;
366 }
367
368 public void setPlatformTransactionManager(PlatformTransactionManager springTransactionManager) {
369 this.platformTransactionManager = springTransactionManager;
370 }
371
372 public List<AlternateEndpointLocation> getAlternateEndpointLocations() {
373 return this.alternateEndpointLocations;
374 }
375
376 public void setAlternateEndpointLocations(List<AlternateEndpointLocation> alternateEndpointLocations) {
377 this.alternateEndpointLocations = alternateEndpointLocations;
378 }
379
380 public List<AlternateEndpoint> getAlternateEndpoints() {
381 return this.alternateEndpoints;
382 }
383
384 public void setAlternateEndpoints(List<AlternateEndpoint> alternateEndpoints) {
385 this.alternateEndpoints = alternateEndpoints;
386 }
387
388 private final class ServicePublisher extends BaseLifecycle {
389
390 private final List<ServiceDefinition> serviceDefinitions;
391
392 ServicePublisher(List<ServiceDefinition> serviceDefinitions) {
393 this.serviceDefinitions = serviceDefinitions;
394 }
395
396 @Override
397 public void start() throws Exception {
398 if (serviceDefinitions != null && !serviceDefinitions.isEmpty()) {
399 LOG.debug("Configuring " + serviceDefinitions.size() + " services for application id " + CoreConfigHelper.getApplicationId() + " using config for classloader " + ClassLoaderUtils.getDefaultClassLoader());
400 KsbApiServiceLocator.getServiceBus().publishServices(serviceDefinitions, true);
401 super.start();
402 }
403 }
404
405 }
406
407 }