001: /*
002: * Copyright 2007 The Kuali Foundation.
003: *
004: * Licensed under the Educational Community License, Version 1.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/ecl1.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: package org.kuali.kfs.context;
017:
018: import java.util.ArrayList;
019: import java.util.Collection;
020: import java.util.Collections;
021: import java.util.HashMap;
022: import java.util.HashSet;
023: import java.util.Iterator;
024: import java.util.List;
025: import java.util.Map;
026: import java.util.Set;
027:
028: import org.apache.commons.lang.StringUtils;
029: import org.apache.log4j.Logger;
030: import org.kuali.core.service.KualiConfigurationService;
031: import org.kuali.core.util.cache.MethodCacheInterceptor;
032: import org.kuali.kfs.KFSConstants;
033: import org.kuali.kfs.service.SchedulerService;
034: import org.kuali.kfs.util.MemoryMonitor;
035: import org.kuali.rice.KNSServiceLocator;
036: import org.quartz.Scheduler;
037: import org.quartz.SchedulerException;
038: import org.springframework.beans.factory.NoSuchBeanDefinitionException;
039: import org.springframework.context.ConfigurableApplicationContext;
040: import org.springframework.context.support.ClassPathXmlApplicationContext;
041:
042: import uk.ltd.getahead.dwr.create.SpringCreator;
043:
044: public class SpringContext {
045: private static final Logger LOG = Logger
046: .getLogger(SpringContext.class);
047: private static final String APPLICATION_CONTEXT_DEFINITION = "SpringBeans.xml";
048: private static final String STANDALONE_RICE_DATASOURCE_CONTEXT_DEFINITION = "SpringStandaloneRiceDataSourceBeans.xml";
049: private static final String DATASOURCE_CONTEXT_DEFINITION = "SpringDataSourceBeans.xml";
050: private static final String SPRING_SOURCE_FILES_KEY = "spring.source.files";
051: private static final String SPRING_TEST_FILES_KEY = "spring.test.files";
052: private static final String SPRING_PLUGIN_FILES_KEY = "spring.plugin.files";
053: private static final String MEMORY_MONITOR_THRESHOLD_KEY = "memory.monitor.threshold";
054: private static ConfigurableApplicationContext applicationContext;
055: private static Set<Class> SINGLETON_TYPES = new HashSet<Class>();
056: private static Set<String> SINGLETON_NAMES = new HashSet<String>();
057: private static Map<Class, Object> SINGLETON_BEANS_BY_TYPE_CACHE = Collections
058: .synchronizedMap(new HashMap<Class, Object>());
059: private static Map<String, Object> SINGLETON_BEANS_BY_NAME_CACHE = Collections
060: .synchronizedMap(new HashMap<String, Object>());
061: private static Map<Class, Map> SINGLETON_BEANS_OF_TYPE_CACHE = Collections
062: .synchronizedMap(new HashMap<Class, Map>());
063:
064: /**
065: * Use this method to retrieve a spring bean when one of the following is the case. Pass in the type of the service interface,
066: * NOT the service implementation. 1. there is only one bean of the specified type in our spring context 2. there is only one
067: * bean of the specified type in our spring context, but you want the one whose bean id is the same as type.getSimpleName() with
068: * the exception of the first letter being lower case in the former and upper case in the latter, For example, there are two
069: * beans of type DateTimeService in our context – dateTimeService and testDateTimeService. To retrieve the former, you should
070: * specific DateTimeService.class as the type. To retrieve the latter, you should specify ConfigurableDateService.class as the
071: * type. Unless you are writing a unit test and need to down cast to an implementation, you do not need to cast the result of
072: * this method.
073: *
074: * @param <T>
075: * @param type
076: * @return an object that has been defined as a bean in our spring context and is of the specified type
077: */
078: public static <T> T getBean(Class<T> type) {
079: verifyProperInitialization();
080: T bean = null;
081: if (SINGLETON_BEANS_BY_TYPE_CACHE.containsKey(type)) {
082: bean = (T) SINGLETON_BEANS_BY_TYPE_CACHE.get(type);
083: } else {
084: try {
085: Collection<T> beansOfType = getBeansOfType(type)
086: .values();
087: if (beansOfType.size() > 1) {
088: bean = getBean(type, type.getSimpleName()
089: .substring(0, 1).toLowerCase()
090: + type.getSimpleName().substring(1));
091: } else {
092: bean = beansOfType.iterator().next();
093: }
094: } catch (NoSuchBeanDefinitionException nsbde) {
095: LOG.info("Could not find bean of type "
096: + type.getName() + " - checking KNS context");
097: try {
098: bean = KNSServiceLocator.getBean(type);
099: } catch (Exception e) {
100: LOG.error(e);
101: throw new NoSuchBeanDefinitionException(
102: "No beans of this type in the in KFS or KNS application contexts: "
103: + type.getName());
104: }
105: }
106: if (SINGLETON_TYPES.contains(type)
107: || hasSingletonSuperType(type)) {
108: SINGLETON_TYPES.add(type);
109: SINGLETON_BEANS_BY_TYPE_CACHE.put(type, bean);
110: }
111: }
112: return bean;
113: }
114:
115: /**
116: * Use this method to retrieve all beans of a give type in our spring context. Pass in the type of the service interface, NOT
117: * the service implementation.
118: *
119: * @param <T>
120: * @param type
121: * @return a map of the spring bean ids / beans that are of the specified type
122: */
123: @SuppressWarnings("unchecked")
124: public static <T> Map<String, T> getBeansOfType(Class<T> type) {
125: verifyProperInitialization();
126: Map<String, T> beansOfType = null;
127: if (SINGLETON_BEANS_OF_TYPE_CACHE.containsKey(type)) {
128: beansOfType = SINGLETON_BEANS_OF_TYPE_CACHE.get(type);
129: } else {
130: beansOfType = KNSServiceLocator.getBeansOfType(type);
131: beansOfType.putAll(new HashMap(applicationContext
132: .getBeansOfType(type)));
133: if (SINGLETON_TYPES.contains(type)
134: || hasSingletonSuperType(type)) {
135: SINGLETON_TYPES.add(type);
136: SINGLETON_BEANS_OF_TYPE_CACHE.put(type, beansOfType);
137: }
138: }
139: return beansOfType;
140: }
141:
142: @SuppressWarnings("unchecked")
143: private static <T> T getBean(Class<T> type, String name) {
144: verifyProperInitialization();
145: T bean = null;
146: if (SINGLETON_BEANS_BY_NAME_CACHE.containsKey(name)) {
147: bean = (T) SINGLETON_BEANS_BY_NAME_CACHE.get(name);
148: } else {
149: try {
150: bean = (T) applicationContext.getBean(name);
151: } catch (NoSuchBeanDefinitionException nsbde) {
152: LOG.info("Could not find bean named " + name
153: + " - checking KNS context");
154: try {
155: bean = KNSServiceLocator.getBean(type, name);
156: } catch (Exception e) {
157: LOG.error(e);
158: throw new NoSuchBeanDefinitionException(
159: name,
160: new StringBuffer(
161: "No bean of this type and name in the in KFS or KNS application contexts: ")
162: .append(type.getName())
163: .append(", ").append(name)
164: .toString());
165: }
166: }
167: if (SINGLETON_NAMES.contains(type)) {
168: SINGLETON_BEANS_BY_NAME_CACHE.put(name, bean);
169: }
170: }
171: return bean;
172: }
173:
174: private static boolean hasSingletonSuperType(Class type) {
175: for (Class singletonType : SINGLETON_TYPES) {
176: if (singletonType.isAssignableFrom(type)) {
177: return true;
178: }
179: }
180: return false;
181: }
182:
183: public static List<MethodCacheInterceptor> getMethodCacheInterceptors() {
184: List<MethodCacheInterceptor> methodCacheInterceptors = new ArrayList();
185: methodCacheInterceptors
186: .add(getBean(MethodCacheInterceptor.class));
187: methodCacheInterceptors.add(KNSServiceLocator
188: .getBean(MethodCacheInterceptor.class));
189: return methodCacheInterceptors;
190: }
191:
192: protected static Object getBean(String beanName) {
193: return getBean(Object.class, beanName);
194: }
195:
196: protected static String[] getBeanNames() {
197: verifyProperInitialization();
198: return applicationContext.getBeanDefinitionNames();
199: }
200:
201: protected static void initializeApplicationContext() {
202: initializeApplicationContext(
203: getSpringConfigurationFiles(new String[] { SPRING_SOURCE_FILES_KEY }),
204: true);
205: }
206:
207: protected static void initializeTestApplicationContext() {
208: initializeApplicationContext(
209: getSpringConfigurationFiles(new String[] {
210: SPRING_SOURCE_FILES_KEY, SPRING_TEST_FILES_KEY }),
211: false);
212: }
213:
214: protected static void initializePluginApplicationContext() {
215: initializeApplicationContext(
216: getSpringConfigurationFiles(new String[] {
217: SPRING_SOURCE_FILES_KEY,
218: SPRING_PLUGIN_FILES_KEY }), false);
219: }
220:
221: protected static void close() {
222: applicationContext.close();
223: }
224:
225: private static void verifyProperInitialization() {
226: if (applicationContext == null) {
227: throw new RuntimeException(
228: "Spring not initialized properly. Initialization has begun and the application context is null."
229: + "Probably spring loaded bean is trying to use KNSServiceLocator before the application context is initialized.");
230: }
231: }
232:
233: private static String[] getSpringConfigurationFiles(
234: String[] propertyNames) {
235: List<String> springConfigurationFiles = new ArrayList<String>();
236: springConfigurationFiles.add(APPLICATION_CONTEXT_DEFINITION);
237: if (Boolean.valueOf(PropertyLoadingFactoryBean
238: .getBaseProperty(KFSConstants.USE_STANDALONE_WORKFLOW))) {
239: LOG
240: .info("Initializing Spring Context to point to a Standalone Rice installation.");
241: springConfigurationFiles
242: .add(STANDALONE_RICE_DATASOURCE_CONTEXT_DEFINITION);
243: } else {
244: springConfigurationFiles.add(DATASOURCE_CONTEXT_DEFINITION);
245: }
246: for (int i = 0; i < propertyNames.length; i++) {
247: springConfigurationFiles.addAll(PropertyLoadingFactoryBean
248: .getBaseListProperty(propertyNames[i]));
249: }
250: for (Iterator iterator = springConfigurationFiles.iterator(); iterator
251: .hasNext();) {
252: String fileName = (String) iterator.next();
253: if (StringUtils.isEmpty(fileName)) {
254: iterator.remove();
255: }
256: }
257: return springConfigurationFiles.toArray(new String[] {});
258: }
259:
260: private static void initializeApplicationContext(
261: String[] springFiles, boolean initializeSchedule) {
262: applicationContext = new ClassPathXmlApplicationContext(
263: springFiles);
264: if (Double.valueOf((getBean(KualiConfigurationService.class))
265: .getPropertyString(MEMORY_MONITOR_THRESHOLD_KEY)) > 0) {
266: MemoryMonitor
267: .setPercentageUsageThreshold(Double
268: .valueOf((getBean(KualiConfigurationService.class))
269: .getPropertyString(MEMORY_MONITOR_THRESHOLD_KEY)));
270: MemoryMonitor memoryMonitor = new MemoryMonitor(
271: APPLICATION_CONTEXT_DEFINITION);
272: memoryMonitor.addListener(new MemoryMonitor.Listener() {
273: org.apache.log4j.Logger LOG = org.apache.log4j.Logger
274: .getLogger(MemoryMonitor.class);
275:
276: public void memoryUsageLow(String springContextId,
277: Map<String, String> memoryUsageStatistics,
278: String deadlockedThreadIds) {
279: StringBuffer logStatement = new StringBuffer(
280: springContextId).append("\n\tMemory Usage");
281: for (String memoryType : memoryUsageStatistics
282: .keySet()) {
283: logStatement.append("\n\t\t").append(
284: memoryType.toUpperCase()).append(": ")
285: .append(
286: memoryUsageStatistics
287: .get(memoryType));
288: }
289: logStatement.append("\n\tLocked Thread Ids: ")
290: .append(deadlockedThreadIds).append(
291: "\n\tThread Stacks");
292: for (Map.Entry<Thread, StackTraceElement[]> threadStackTrace : Thread
293: .getAllStackTraces().entrySet()) {
294: logStatement.append("\n\t\tThread: name=")
295: .append(
296: threadStackTrace.getKey()
297: .getName()).append(
298: ", id=").append(
299: threadStackTrace.getKey()
300: .getId()).append(
301: ", priority=").append(
302: threadStackTrace.getKey()
303: .getPriority()).append(
304: ", state=").append(
305: threadStackTrace.getKey()
306: .getState());
307: for (StackTraceElement stackTraceElement : threadStackTrace
308: .getValue()) {
309: logStatement.append("\n\t\t\t"
310: + stackTraceElement);
311: }
312: }
313: LOG.info(logStatement);
314: MemoryMonitor.setPercentageUsageThreshold(0.95);
315: }
316: });
317: }
318: if (getBean(KualiConfigurationService.class)
319: .getPropertyAsBoolean("use.quartz.scheduling")) {
320: try {
321: if (initializeSchedule) {
322: LOG.info("Attempting to initialize the scheduler");
323: (getBean(SchedulerService.class)).initialize();
324: }
325: LOG.info("Starting the scheduler");
326: (getBean(Scheduler.class)).start();
327: } catch (NoSuchBeanDefinitionException e) {
328: LOG
329: .info("Not initializing the scheduler because there is no scheduler bean");
330: } catch (SchedulerException e) {
331: LOG
332: .error(
333: "Caught exception while starting the scheduler",
334: e);
335: }
336: }
337: SpringCreator.setOverrideBeanFactory(applicationContext
338: .getBeanFactory());
339: Collections.addAll(SINGLETON_NAMES, applicationContext
340: .getBeanFactory().getSingletonNames());
341: for (String singletonName : SINGLETON_NAMES) {
342: SINGLETON_TYPES.add(applicationContext.getBeanFactory()
343: .getType(singletonName));
344: }
345: SINGLETON_NAMES.addAll(KNSServiceLocator.getSingletonNames());
346: SINGLETON_TYPES.addAll(KNSServiceLocator.getSingletonTypes());
347: }
348: }
|