1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package org.kuali.ole.sys.context;
17
18 import java.io.File;
19 import java.io.IOException;
20 import java.text.DateFormat;
21 import java.text.SimpleDateFormat;
22 import java.util.ArrayList;
23 import java.util.Collection;
24 import java.util.Collections;
25 import java.util.Comparator;
26 import java.util.Date;
27 import java.util.HashMap;
28 import java.util.HashSet;
29 import java.util.List;
30 import java.util.Map;
31 import java.util.Set;
32
33 import javax.xml.namespace.QName;
34
35 import org.apache.commons.io.FileUtils;
36 import org.apache.commons.lang.StringUtils;
37 import org.apache.commons.lang.math.NumberUtils;
38 import org.apache.log4j.Logger;
39 import org.kuali.ole.batch.service.OLEBatchSchedulerService;
40 import org.kuali.ole.sys.MemoryMonitor;
41 import org.kuali.ole.sys.OLEConstants;
42 import org.kuali.ole.sys.batch.Step;
43 import org.kuali.ole.sys.batch.service.SchedulerService;
44 import org.kuali.rice.core.api.config.property.ConfigurationService;
45 import org.kuali.rice.core.api.resourceloader.GlobalResourceLoader;
46 import org.kuali.rice.core.framework.resourceloader.SpringResourceLoader;
47 import org.kuali.rice.coreservice.api.CoreServiceApiServiceLocator;
48 import org.kuali.rice.coreservice.api.component.Component;
49 import org.kuali.rice.krad.service.*;
50 import org.quartz.Scheduler;
51 import org.quartz.SchedulerException;
52 import org.springframework.aop.support.AopUtils;
53 import org.springframework.beans.factory.NoSuchBeanDefinitionException;
54 import org.springframework.context.ConfigurableApplicationContext;
55
56 @SuppressWarnings("deprecation")
57 public class SpringContext {
58 private static final Logger LOG = Logger.getLogger(SpringContext.class);
59 protected static final String MEMORY_MONITOR_THRESHOLD_KEY = "memory.monitor.threshold";
60 protected static final String USE_QUARTZ_SCHEDULING_KEY = "use.quartz.scheduling";
61 protected static final String OLE_BATCH_STEP_COMPONENT_SET_ID = "STEP:OLE";
62 protected static ConfigurableApplicationContext applicationContext;
63 protected static Set<Class<? extends Object>> SINGLETON_TYPES = new HashSet<Class<? extends Object>>();
64 protected static Map<Class<? extends Object>, Object> SINGLETON_BEANS_BY_TYPE_CACHE = new HashMap<Class<? extends Object>, Object>();
65 protected static Map<String, Object> SINGLETON_BEANS_BY_NAME_CACHE = new HashMap<String, Object>();
66 @SuppressWarnings("rawtypes")
67 protected static Map<Class<? extends Object>, Map> SINGLETON_BEANS_OF_TYPE_CACHE = new HashMap<Class<? extends Object>, Map>();
68 protected static Thread processWatchThread = null;
69 protected static MemoryMonitor memoryMonitor;
70
71
72
73
74 public static Object getService(String serviceName) {
75 return GlobalResourceLoader.getService(serviceName);
76 }
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92 public static <T> T getBean(Class<T> type) {
93 verifyProperInitialization();
94 T bean = null;
95 if (SINGLETON_BEANS_BY_TYPE_CACHE.containsKey(type)) {
96 bean = (T) SINGLETON_BEANS_BY_TYPE_CACHE.get(type);
97 }
98 else {
99 if ( LOG.isDebugEnabled() ) {
100 LOG.debug("Bean not already in cache: " + type + " - calling getBeansOfType() ");
101 }
102 Collection<T> beansOfType = getBeansOfType(type).values();
103 if ( !beansOfType.isEmpty() ) {
104 if (beansOfType.size() > 1) {
105 bean = getBean(type, StringUtils.uncapitalize(type.getSimpleName()) );
106 }
107 else {
108 bean = beansOfType.iterator().next();
109 }
110 }
111 else {
112 try {
113 bean = getBean(type, StringUtils.uncapitalize(type.getSimpleName()) );
114 }
115 catch (Exception ex) {
116
117 }
118 if ( bean == null ) {
119
120 if ( LOG.isDebugEnabled() ) {
121 LOG.debug("Bean not found in local context: " + type.getName() + " - calling GRL");
122 }
123 Object remoteServiceBean = getService( StringUtils.uncapitalize(type.getSimpleName()) );
124 if ( remoteServiceBean != null ) {
125 if ( type.isAssignableFrom( remoteServiceBean.getClass() ) ) {
126 bean = (T)remoteServiceBean;
127 }
128 }
129 }
130 }
131 if ( bean != null ) {
132 synchronized( SINGLETON_TYPES ) {
133 if (SINGLETON_TYPES.contains(type) || hasSingletonSuperType(type,SINGLETON_TYPES)) {
134 SINGLETON_TYPES.add(type);
135 SINGLETON_BEANS_BY_TYPE_CACHE.put(type, bean);
136 }
137 }
138 }
139 else {
140 throw new RuntimeException( "Request for non-existent bean. Unable to find in local context or on the GRL: " + type.getName() );
141 }
142 }
143 return bean;
144 }
145
146
147
148
149
150
151
152
153
154 public static <T> Map<String, T> getBeansOfType(Class<T> type) {
155 verifyProperInitialization();
156 Map<String, T> beansOfType = null;
157 if (SINGLETON_BEANS_OF_TYPE_CACHE.containsKey(type)) {
158 beansOfType = SINGLETON_BEANS_OF_TYPE_CACHE.get(type);
159 }
160 else {
161 if ( LOG.isDebugEnabled() ) {
162 LOG.debug("Bean not already in \"OF_TYPE\" cache: " + type + " - calling getBeansOfType() on Spring context");
163 }
164 boolean allOfTypeAreSingleton = true;
165 beansOfType = applicationContext.getBeansOfType(type);
166 for ( String key : beansOfType.keySet() ) {
167 if ( !applicationContext.isSingleton(key) ) {
168 allOfTypeAreSingleton = false;
169 }
170 }
171 if ( allOfTypeAreSingleton ) {
172 synchronized( SINGLETON_TYPES ) {
173 SINGLETON_TYPES.add(type);
174 SINGLETON_BEANS_OF_TYPE_CACHE.put(type, beansOfType);
175 }
176 }
177 }
178 return beansOfType;
179 }
180
181 public static <T> T getBean(Class<T> type, String name) {
182 T bean = null;
183 if (SINGLETON_BEANS_BY_NAME_CACHE.containsKey(name)) {
184 bean = (T) SINGLETON_BEANS_BY_NAME_CACHE.get(name);
185 }
186 else {
187 try {
188 bean = (T) applicationContext.getBean(name);
189 if ( applicationContext.isSingleton(name) ) {
190 synchronized( SINGLETON_BEANS_BY_NAME_CACHE ) {
191 SINGLETON_BEANS_BY_NAME_CACHE.put(name, bean);
192 }
193 }
194 }
195 catch (NoSuchBeanDefinitionException nsbde) {
196 if ( LOG.isDebugEnabled() ) {
197 LOG.debug("Bean with name and type not found in local context: " + name + "/" + type.getName() + " - calling GRL");
198 }
199 Object remoteServiceBean = getService( name );
200 if ( remoteServiceBean != null ) {
201 if ( type.isAssignableFrom( AopUtils.getTargetClass(remoteServiceBean) ) ) {
202 bean = (T)remoteServiceBean;
203
204 synchronized( SINGLETON_BEANS_BY_NAME_CACHE ) {
205 SINGLETON_BEANS_BY_NAME_CACHE.put(name, bean);
206 }
207 }
208 }
209 if (bean == null) {
210 throw new RuntimeException("No bean of this type and name exist in the application context or from the GRL: " + type.getName() + ", " + name);
211 }
212 }
213 }
214 return bean;
215 }
216
217 private static boolean hasSingletonSuperType(Class<? extends Object> type, Set<Class<? extends Object>> knownSingletonTypes ) {
218 for (Class<? extends Object> singletonType : knownSingletonTypes) {
219 if (singletonType.isAssignableFrom(type)) {
220 return true;
221 }
222 }
223 return false;
224 }
225
226 public static Object getBean(String beanName) {
227 return getBean(Object.class, beanName);
228 }
229
230 protected static String[] getBeanNames() {
231 verifyProperInitialization();
232 return applicationContext.getBeanDefinitionNames();
233 }
234
235 protected static void close() {
236 if ( processWatchThread != null ) {
237 LOG.info("Shutting down the ProcessWatch thread" );
238 if ( processWatchThread.isAlive() ) {
239 processWatchThread.stop();
240 }
241 processWatchThread = null;
242 }
243
244 try {
245 if ( isInitialized() && getBean(Scheduler.class) != null ) {
246 if ( getBean(Scheduler.class).isStarted() ) {
247 LOG.info( "Shutting Down scheduler" );
248 getBean(Scheduler.class).shutdown();
249 }
250 }
251 } catch (SchedulerException ex) {
252 LOG.error( "Exception while shutting down the scheduler", ex );
253 }
254 LOG.info( "Stopping the MemoryMonitor thread" );
255 if ( memoryMonitor != null ) {
256 memoryMonitor.stop();
257 }
258 }
259
260 public static boolean isInitialized() {
261 return applicationContext != null;
262 }
263
264 private static void verifyProperInitialization() {
265 if (!isInitialized()) {
266 LOG.fatal( "*****************************************************************" );
267 LOG.fatal( "*****************************************************************" );
268 LOG.fatal( "*****************************************************************" );
269 LOG.fatal( "*****************************************************************" );
270 LOG.fatal( "*****************************************************************" );
271 LOG.fatal( "Spring not initialized properly. Initialization has begun and the application context is null. Probably spring loaded bean is trying to use SpringContext.getBean() before the application context is initialized.", new IllegalStateException() );
272 LOG.fatal( "*****************************************************************" );
273 LOG.fatal( "*****************************************************************" );
274 LOG.fatal( "*****************************************************************" );
275 LOG.fatal( "*****************************************************************" );
276 LOG.fatal( "*****************************************************************" );
277 throw new IllegalStateException("Spring not initialized properly. Initialization has begun and the application context is null. Probably spring loaded bean is trying to use SpringContext.getBean() before the application context is initialized.");
278 }
279 }
280
281 static void initMemoryMonitor() {
282 ConfigurationService configurationService = GlobalResourceLoader.getService("kualiConfigurationService");
283 if ( NumberUtils.isNumber(configurationService.getPropertyValueAsString(MEMORY_MONITOR_THRESHOLD_KEY))) {
284 if (Double.valueOf(configurationService.getPropertyValueAsString(MEMORY_MONITOR_THRESHOLD_KEY)) > 0) {
285 LOG.info( "Starting up MemoryMonitor thread" );
286 MemoryMonitor.setPercentageUsageThreshold(Double.valueOf(configurationService.getPropertyValueAsString(MEMORY_MONITOR_THRESHOLD_KEY)));
287 memoryMonitor = new MemoryMonitor("OLE Memory Monitor: Over " + configurationService.getPropertyValueAsString(MEMORY_MONITOR_THRESHOLD_KEY) + "% Memory Used");
288 memoryMonitor.addListener(new MemoryMonitor.Listener() {
289 org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(MemoryMonitor.class);
290
291 @Override
292 public void memoryUsageLow(String springContextId, Map<String, String> memoryUsageStatistics, String deadlockedThreadIds) {
293 StringBuilder logStatement = new StringBuilder(springContextId).append("\n\tMemory Usage");
294 for (String memoryType : memoryUsageStatistics.keySet()) {
295 logStatement.append("\n\t\t").append(memoryType.toUpperCase()).append(": ").append(memoryUsageStatistics.get(memoryType));
296 }
297 logStatement.append("\n\tLocked Thread Ids: ").append(deadlockedThreadIds).append("\n\tThread Stacks");
298 for (Map.Entry<Thread, StackTraceElement[]> threadStackTrace : Thread.getAllStackTraces().entrySet()) {
299 logStatement.append("\n\t\tThread: name=").append(threadStackTrace.getKey().getName()).append(", id=").append(threadStackTrace.getKey().getId()).append(", priority=").append(threadStackTrace.getKey().getPriority()).append(", state=").append(threadStackTrace.getKey().getState());
300 for (StackTraceElement stackTraceElement : threadStackTrace.getValue()) {
301 logStatement.append("\n\t\t\t").append(stackTraceElement);
302 }
303 }
304 LOG.warn(logStatement);
305 MemoryMonitor.setPercentageUsageThreshold(0.95);
306 }
307 });
308 }
309 } else {
310 LOG.warn( MEMORY_MONITOR_THRESHOLD_KEY + " is not a number: " + configurationService.getPropertyValueAsString(MEMORY_MONITOR_THRESHOLD_KEY) );
311 }
312 }
313
314 static void initMonitoringThread() {
315 ConfigurationService configurationService = GlobalResourceLoader.getService("kualiConfigurationService");
316 if ( configurationService.getPropertyValueAsBoolean("periodic.thread.dump") ) {
317 final long sleepPeriod = Long.parseLong(configurationService.getPropertyValueAsString("periodic.thread.dump.seconds") ) * 1000;
318 final File logDir = new File(configurationService.getPropertyValueAsString("logs.directory") );
319 final File monitoringLogDir = new File( logDir, "monitoring" );
320 if ( !monitoringLogDir.exists() ) {
321 monitoringLogDir.mkdir();
322 }
323 if ( LOG.isInfoEnabled() ) {
324 LOG.info( "Starting the Periodic Thread Dump thread - dumping every " + (sleepPeriod/1000) + " seconds");
325 LOG.info( "Periodic Thread Dump Logs: " + monitoringLogDir.getAbsolutePath() );
326 }
327 final DateFormat df = new SimpleDateFormat( "yyyyMMdd" );
328 final DateFormat tf = new SimpleDateFormat( "HH-mm-ss" );
329 Runnable processWatch = new Runnable() {
330 @Override
331 public void run() {
332 while ( true ) {
333 Date now = new Date();
334 File todaysLogDir = new File( monitoringLogDir, df.format(now) );
335 if ( !todaysLogDir.exists() ) {
336 todaysLogDir.mkdir();
337 }
338 File logFile = new File( todaysLogDir, "process-"+tf.format(now)+".log" );
339 try {
340 StringBuilder logStatement = new StringBuilder(10240);
341 logStatement.append("Threads Running at: " ).append( now ).append( "\n\n\n" );
342 Map<Thread,StackTraceElement[]> threads = Thread.getAllStackTraces();
343 List<Thread> sortedThreads = new ArrayList<Thread>( threads.keySet() );
344 Collections.sort( sortedThreads, new Comparator<Thread>() {
345 @Override
346 public int compare(Thread o1, Thread o2) {
347 return o1.getName().compareTo( o2.getName() );
348 }
349 });
350 for ( Thread t : sortedThreads ) {
351 logStatement.append("\tThread: name=").append(t.getName()).append(", id=").append(t.getId()).append(", priority=").append(t.getPriority()).append(", state=").append(t.getState());
352 logStatement.append('\n');
353 for (StackTraceElement stackTraceElement : threads.get(t) ) {
354 logStatement.append("\t\t" + stackTraceElement).append( '\n' );
355 }
356 logStatement.append('\n');
357 }
358 FileUtils.writeStringToFile(logFile, logStatement.toString(), "UTF-8");
359 }
360 catch (IOException ex) {
361 LOG.error( "Unable to write the ProcessWatch output file: " + logFile.getAbsolutePath(), ex );
362 }
363 try {
364 Thread.sleep( sleepPeriod );
365 }
366 catch (InterruptedException ex) {
367 LOG.error( "woken up during sleep of the ProcessWatch thread", ex );
368 }
369 }
370 }
371 };
372 processWatchThread = new Thread( processWatch, "ProcessWatch thread" );
373 processWatchThread.setDaemon(true);
374 processWatchThread.start();
375 }
376 }
377
378 static void initScheduler() {
379 ConfigurationService configurationService = GlobalResourceLoader.getService("kualiConfigurationService");
380 if (configurationService.getPropertyValueAsBoolean(USE_QUARTZ_SCHEDULING_KEY)) {
381 try {
382 LOG.info("Attempting to initialize the SchedulerService");
383 getBean(SchedulerService.class).initialize();
384 getBean(OLEBatchSchedulerService.class).initialize();
385 LOG.info("Starting the Quartz scheduler");
386 getBean(Scheduler.class).start();
387 } catch (NoSuchBeanDefinitionException e) {
388 LOG.warn("Not initializing the scheduler because there is no scheduler bean");
389 } catch ( Exception ex ) {
390 LOG.error("Caught Exception while starting the scheduler", ex);
391 }
392 }
393 }
394
395 public static void registerSingletonBean(String beanId, Object bean) {
396 applicationContext.getBeanFactory().registerSingleton(beanId, bean);
397
398 SINGLETON_BEANS_BY_NAME_CACHE.clear();
399 SINGLETON_BEANS_BY_TYPE_CACHE.clear();
400 SINGLETON_BEANS_OF_TYPE_CACHE.clear();
401 }
402
403 public static void finishInitializationAfterRiceStartup() {
404 if ( LOG.isTraceEnabled() ) {
405 GlobalResourceLoader.logAllContents();
406 }
407 DataDictionaryComponentPublisherService dataDictionaryComponentPublisherService= GlobalResourceLoader.getService("dataDictionaryComponentPublisherService");
408 SpringResourceLoader mainKfsSpringResourceLoader = (SpringResourceLoader)GlobalResourceLoader.getResourceLoader( new QName("OLE", "OLE_RICE_SPRING_RESOURCE_LOADER_NAME") );
409 SpringContext.applicationContext = mainKfsSpringResourceLoader.getContext();
410
411
412 dataDictionaryComponentPublisherService.publishAllComponents();
413
414
415
416 publishBatchStepComponents();
417 }
418
419 public static void publishBatchStepComponents() {
420 Map<String,Step> steps = SpringContext.getBeansOfType(Step.class);
421 List<Component> stepComponents = new ArrayList<Component>( steps.size() );
422 for ( Step step : steps.values() ) {
423 Step unproxiedStep = (Step) ProxyUtils.getTargetIfProxied(step);
424 String namespaceCode = OLEConstants.CoreModuleNamespaces.OLE;
425 if ( LOG.isDebugEnabled() ) {
426 LOG.debug( "Building component for step: " + unproxiedStep.getName() + "(" + unproxiedStep.getClass() + ")" );
427 }
428 ModuleService moduleService = SpringContext.getBean(KualiModuleService.class).getResponsibleModuleService(unproxiedStep.getClass());
429 if ( moduleService != null ) {
430 namespaceCode = moduleService.getModuleConfiguration().getNamespaceCode();
431 }
432 Component.Builder component = Component.Builder.create(namespaceCode, unproxiedStep.getClass().getSimpleName(), unproxiedStep.getClass().getSimpleName());
433 component.setComponentSetId(OLE_BATCH_STEP_COMPONENT_SET_ID);
434 component.setActive(true);
435 stepComponents.add(component.build());
436 }
437
438 CoreServiceApiServiceLocator.getComponentService().publishDerivedComponents(OLE_BATCH_STEP_COMPONENT_SET_ID, stepComponents);
439 }
440
441 public static ConfigurableApplicationContext getApplicationContext() {
442 return applicationContext;
443 }
444
445 public static void setApplicationContext(ConfigurableApplicationContext applicationContext) {
446 SpringContext.applicationContext = applicationContext;
447 }
448 }