View Javadoc

1   package siouxsie.mvc.spring;
2   
3   import java.util.HashMap;
4   import java.util.Map;
5   
6   import org.springframework.beans.BeansException;
7   import org.springframework.beans.factory.NoSuchBeanDefinitionException;
8   import org.springframework.beans.factory.UnsatisfiedDependencyException;
9   import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
10  import org.springframework.context.ApplicationContext;
11  import org.springframework.context.ApplicationContextAware;
12  import org.springframework.context.ConfigurableApplicationContext;
13  
14  import com.opensymphony.xwork2.ObjectFactory;
15  import com.opensymphony.xwork2.util.logging.Logger;
16  import com.opensymphony.xwork2.util.logging.LoggerFactory;
17  
18  
19  /**
20   * static Spring object factory.
21   * The application context must be assigned statically 
22   * before the factory is used.
23   * Static init is nasty but allows easy sharing of
24   * application contexts.
25   * If the MVC app is standalone, prefer the XW spring object factory
26   * and set the XML files path via the XW container. 
27   * Code mainly taken from XW 2.1.  
28   * @author Arnaud Cogoluegnes
29   * @version $Id: SpringObjectFactory.java 119 2008-02-24 18:16:32Z acogo $
30   */
31  @SuppressWarnings("unchecked")
32  public class SpringObjectFactory extends ObjectFactory {
33  	
34      /**
35  	 * 
36  	 */
37  	private static final long serialVersionUID = 8061000508318029222L;
38  
39  	private static final Logger LOG = LoggerFactory.getLogger(SpringObjectFactory.class);
40  
41      protected static ApplicationContext APP_CONTEXT;
42      protected static AutowireCapableBeanFactory autoWiringFactory;
43      protected int autowireStrategy = AutowireCapableBeanFactory.AUTOWIRE_BY_NAME;
44      private Map classes = new HashMap();
45      private boolean useClassCache = true;
46      
47      /**
48       * Set the Spring ApplicationContext that should be used to look beans up with.
49       *
50       * @param appContext The Spring ApplicationContext that should be used to look beans up with.
51       */
52      public static void setApplicationContext(ApplicationContext appContext)
53              throws BeansException {
54      	APP_CONTEXT = appContext;
55          autoWiringFactory = findAutoWiringBeanFactory(appContext);
56      }
57  
58      /**
59       * Sets the autowiring strategy
60       *
61       * @param autowireStrategy
62       */
63      public void setAutowireStrategy(int autowireStrategy) {
64          switch (autowireStrategy) {
65              case AutowireCapableBeanFactory.AUTOWIRE_AUTODETECT:
66                  LOG.info("Setting autowire strategy to autodetect");
67                  this.autowireStrategy = autowireStrategy;
68                  break;
69              case AutowireCapableBeanFactory.AUTOWIRE_BY_NAME:
70                  LOG.info("Setting autowire strategy to name");
71                  this.autowireStrategy = autowireStrategy;
72                  break;
73              case AutowireCapableBeanFactory.AUTOWIRE_BY_TYPE:
74                  LOG.info("Setting autowire strategy to type");
75                  this.autowireStrategy = autowireStrategy;
76                  break;
77              case AutowireCapableBeanFactory.AUTOWIRE_CONSTRUCTOR:
78                  LOG.info("Setting autowire strategy to constructor");
79                  this.autowireStrategy = autowireStrategy;
80                  break;
81              default:
82                  throw new IllegalStateException("Invalid autowire type set");
83          }
84      }
85  
86      public int getAutowireStrategy() {
87          return autowireStrategy;
88      }
89  
90  
91      /**
92       * If the given context is assignable to AutowireCapbleBeanFactory or contains a parent or a factory that is, then
93       * set the autoWiringFactory appropriately.
94       *
95       * @param context
96       */
97      protected static AutowireCapableBeanFactory findAutoWiringBeanFactory(ApplicationContext context) {
98          if (context instanceof AutowireCapableBeanFactory) {
99              // Check the context
100             return (AutowireCapableBeanFactory) context;
101         } else if (context instanceof ConfigurableApplicationContext) {
102             // Try and grab the beanFactory
103             return ((ConfigurableApplicationContext) context).getBeanFactory();
104         } else if (context.getParent() != null) {
105             // And if all else fails, try again with the parent context
106             return findAutoWiringBeanFactory(context.getParent());
107         }
108         return null;
109     }
110 
111     /**
112      * Looks up beans using Spring's application context before falling back to the method defined in the {@link
113      * ObjectFactory}.
114      *
115      * @param beanName     The name of the bean to look up in the application context
116      * @param extraContext
117      * @return A bean from Spring or the result of calling the overridden
118      *         method.
119      * @throws Exception
120      */
121     public Object buildBean(String beanName, Map extraContext, boolean injectInternal) throws Exception {
122         Object o = null;
123         try {
124             o = APP_CONTEXT.getBean(beanName);
125         } catch (NoSuchBeanDefinitionException e) {
126             Class beanClazz = getClassInstance(beanName);
127             o = buildBean(beanClazz, extraContext);
128         }
129         if (injectInternal) {
130             injectInternalBeans(o);
131         }
132         return o;
133     }
134 
135     /**
136      * @param clazz
137      * @param extraContext
138      * @throws Exception
139      */
140     public Object buildBean(Class clazz, Map extraContext) throws Exception {
141         Object bean;
142 
143         try {
144             bean = autoWiringFactory.autowire(clazz, AutowireCapableBeanFactory.AUTOWIRE_CONSTRUCTOR, false);
145         } catch (UnsatisfiedDependencyException e) {
146             // Fall back
147             bean = super.buildBean(clazz, extraContext);
148         }
149 
150         bean = autoWiringFactory.applyBeanPostProcessorsBeforeInitialization(bean, bean.getClass().getName());
151         // We don't need to call the init-method since one won't be registered.
152         bean = autoWiringFactory.applyBeanPostProcessorsAfterInitialization(bean, bean.getClass().getName());
153         return autoWireBean(bean, autoWiringFactory);
154     }
155 
156     public Object autoWireBean(Object bean) {
157         return autoWireBean(bean, autoWiringFactory);
158     }
159 
160     /**
161      * @param bean
162      * @param autoWiringFactory
163      */
164     public Object autoWireBean(Object bean, AutowireCapableBeanFactory autoWiringFactory) {
165         if (autoWiringFactory != null) {
166             autoWiringFactory.autowireBeanProperties(bean,
167                     autowireStrategy, false);
168         }
169         if (bean instanceof ApplicationContextAware) {
170             ((ApplicationContextAware) bean).setApplicationContext(APP_CONTEXT);
171         }
172         
173         injectInternalBeans(bean);
174 
175         return bean;
176     }
177 
178     public Class getClassInstance(String className) throws ClassNotFoundException {
179         Class clazz = null;
180         if (useClassCache) {
181             synchronized(classes) {
182                 // this cache of classes is needed because Spring sucks at dealing with situations where the
183                 // class instance changes 
184                 clazz = (Class) classes.get(className);
185             }
186         }
187 
188         if (clazz == null) {
189             if (APP_CONTEXT.containsBean(className)) {
190                 clazz = APP_CONTEXT.getBean(className).getClass();
191             } else {
192                 clazz = super.getClassInstance(className);
193             }
194 
195             if (useClassCache) {
196                 synchronized(classes) {
197                     classes.put(className, clazz);
198                 }
199             }
200         }
201 
202         return clazz;
203     }
204 
205     /**
206      * Allows for ObjectFactory implementations that support
207      * Actions without no-arg constructors.
208      *
209      * @return false
210      */
211     public boolean isNoArgConstructorRequired() {
212         return false;
213     }
214 
215     /**
216      *  Enable / disable caching of classes loaded by Spring.
217      *  
218      * @param useClassCache
219      */
220     public void setUseClassCache(boolean useClassCache) {
221         this.useClassCache = useClassCache;
222     }
223 	 
224 	
225 }