001: /*
002: * Copyright (c) 2002-2006 by OpenSymphony
003: * All rights reserved.
004: */
005: package com.opensymphony.xwork.spring;
006:
007: import com.opensymphony.xwork.ObjectFactory;
008: import org.apache.commons.logging.Log;
009: import org.apache.commons.logging.LogFactory;
010: import org.springframework.beans.BeansException;
011: import org.springframework.beans.factory.NoSuchBeanDefinitionException;
012: import org.springframework.beans.factory.UnsatisfiedDependencyException;
013: import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
014: import org.springframework.context.ApplicationContext;
015: import org.springframework.context.ApplicationContextAware;
016: import org.springframework.context.ConfigurableApplicationContext;
017:
018: import java.util.HashMap;
019: import java.util.Map;
020:
021: /**
022: * Simple implementation of the ObjectFactory that makes use of Spring's application context if one has been configured,
023: * before falling back on the default mechanism of instantiating a new class using the class name. <p/> In order to use
024: * this class in your application, you will need to instantiate a copy of this class and set it as XWork's ObjectFactory
025: * before the xwork.xml file is parsed. In a servlet environment, this could be done using a ServletContextListener.
026: *
027: * @author Simon Stewart (sms@lateral.net)
028: */
029: public class SpringObjectFactory extends ObjectFactory implements
030: ApplicationContextAware {
031: private static final Log log = LogFactory
032: .getLog(SpringObjectFactory.class);
033:
034: protected ApplicationContext appContext;
035: protected AutowireCapableBeanFactory autoWiringFactory;
036: protected int autowireStrategy = AutowireCapableBeanFactory.AUTOWIRE_BY_NAME;
037: private Map classes = new HashMap();
038: private boolean useClassCache = true;
039:
040: /**
041: * Set the Spring ApplicationContext that should be used to look beans up with.
042: *
043: * @param appContext The Spring ApplicationContext that should be used to look beans up with.
044: */
045: public void setApplicationContext(ApplicationContext appContext)
046: throws BeansException {
047: this .appContext = appContext;
048: autoWiringFactory = findAutoWiringBeanFactory(this .appContext);
049: }
050:
051: /**
052: * Sets the autowiring strategy
053: *
054: * @param autowireStrategy
055: */
056: public void setAutowireStrategy(int autowireStrategy) {
057: switch (autowireStrategy) {
058: case AutowireCapableBeanFactory.AUTOWIRE_AUTODETECT:
059: log.info("Setting autowire strategy to autodetect");
060: this .autowireStrategy = autowireStrategy;
061: break;
062: case AutowireCapableBeanFactory.AUTOWIRE_BY_NAME:
063: log.info("Setting autowire strategy to name");
064: this .autowireStrategy = autowireStrategy;
065: break;
066: case AutowireCapableBeanFactory.AUTOWIRE_BY_TYPE:
067: log.info("Setting autowire strategy to type");
068: this .autowireStrategy = autowireStrategy;
069: break;
070: case AutowireCapableBeanFactory.AUTOWIRE_CONSTRUCTOR:
071: log.info("Setting autowire strategy to constructor");
072: this .autowireStrategy = autowireStrategy;
073: break;
074: default:
075: throw new IllegalStateException("Invalid autowire type set");
076: }
077: }
078:
079: public int getAutowireStrategy() {
080: return autowireStrategy;
081: }
082:
083: /**
084: * If the given context is assignable to AutowireCapbleBeanFactory or contains a parent or a factory that is, then
085: * set the autoWiringFactory appropriately.
086: *
087: * @param context
088: */
089: protected AutowireCapableBeanFactory findAutoWiringBeanFactory(
090: ApplicationContext context) {
091: if (context instanceof AutowireCapableBeanFactory) {
092: // Check the context
093: return (AutowireCapableBeanFactory) context;
094: } else if (context instanceof ConfigurableApplicationContext) {
095: // Try and grab the beanFactory
096: return ((ConfigurableApplicationContext) context)
097: .getBeanFactory();
098: } else if (context.getParent() != null) {
099: // And if all else fails, try again with the parent context
100: return findAutoWiringBeanFactory(context.getParent());
101: }
102: return null;
103: }
104:
105: /**
106: * Looks up beans using Spring's application context before falling back to the method defined in the {@link
107: * ObjectFactory}.
108: *
109: * @param beanName The name of the bean to look up in the application context
110: * @param extraContext
111: * @return A bean from Spring or the result of calling the overridden
112: * method.
113: * @throws Exception
114: */
115: public Object buildBean(String beanName, Map extraContext)
116: throws Exception {
117: try {
118: return appContext.getBean(beanName);
119: } catch (NoSuchBeanDefinitionException e) {
120: Class beanClazz = getClassInstance(beanName);
121: return buildBean(beanClazz, extraContext);
122: }
123: }
124:
125: /**
126: * @param clazz
127: * @param extraContext
128: * @throws Exception
129: */
130: public Object buildBean(Class clazz, Map extraContext)
131: throws Exception {
132: Object bean;
133:
134: try {
135: bean = autoWiringFactory.autowire(clazz,
136: AutowireCapableBeanFactory.AUTOWIRE_CONSTRUCTOR,
137: false);
138: } catch (UnsatisfiedDependencyException e) {
139: // Fall back
140: bean = super .buildBean(clazz, extraContext);
141: }
142:
143: bean = autoWiringFactory
144: .applyBeanPostProcessorsBeforeInitialization(bean, bean
145: .getClass().getName());
146: // We don't need to call the init-method since one won't be registered.
147: bean = autoWiringFactory
148: .applyBeanPostProcessorsAfterInitialization(bean, bean
149: .getClass().getName());
150:
151: return autoWireBean(bean, autoWiringFactory);
152: }
153:
154: public Object autoWireBean(Object bean) {
155: return autoWireBean(bean, autoWiringFactory);
156: }
157:
158: /**
159: * @param bean
160: * @param autoWiringFactory
161: */
162: public Object autoWireBean(Object bean,
163: AutowireCapableBeanFactory autoWiringFactory) {
164: if (autoWiringFactory != null) {
165: autoWiringFactory.autowireBeanProperties(bean,
166: autowireStrategy, false);
167: }
168: if (bean instanceof ApplicationContextAware) {
169: ((ApplicationContextAware) bean)
170: .setApplicationContext(appContext);
171: }
172:
173: return bean;
174: }
175:
176: public Class getClassInstance(String className)
177: throws ClassNotFoundException {
178: Class clazz = null;
179: if (useClassCache) {
180: // this cache of classes is needed because Spring sucks at dealing with situations where the
181: // class instance changes (such as WebWork's QuickStart)
182: clazz = (Class) classes.get(className);
183: }
184:
185: if (clazz == null) {
186: if (appContext.containsBean(className)) {
187: clazz = appContext.getBean(className).getClass();
188: } else {
189: clazz = super .getClassInstance(className);
190: }
191:
192: if (useClassCache) {
193: classes.put(className, clazz);
194: }
195: }
196:
197: return clazz;
198: }
199:
200: /**
201: * This method sets the ObjectFactory used by XWork to this object. It's best used as the "init-method" of a Spring
202: * bean definition in order to hook Spring and XWork together properly (as an alternative to the
203: * com.opensymphony.webwork.spring.lifecycle.SpringObjectFactoryListener)
204: */
205: public void initObjectFactory() {
206: ObjectFactory.setObjectFactory(this );
207: }
208:
209: /**
210: * Allows for ObjectFactory implementations that support
211: * Actions without no-arg constructors.
212: *
213: * @return false
214: */
215: public boolean isNoArgConstructorRequired() {
216: return false;
217: }
218:
219: public void setUseClassCache(boolean useClassCache) {
220: this.useClassCache = useClassCache;
221: }
222: }
|