View Javadoc
1   package com.atlassian.plugin.osgi.spring;
2   
3   import com.atlassian.plugin.PluginException;
4   import com.atlassian.plugin.module.ContainerAccessor;
5   
6   import java.lang.reflect.InvocationTargetException;
7   import java.lang.reflect.Method;
8   import java.util.Collection;
9   import java.util.Map;
10  
11  import static com.google.common.base.Preconditions.checkState;
12  import static com.google.common.base.Predicates.isNull;
13  import static com.google.common.collect.Iterables.any;
14  import static com.google.common.collect.Lists.newArrayList;
15  
16  /**
17   * Manages spring context access, including autowiring.
18   *
19   * @since 2.2.0
20   */
21  public class DefaultSpringContainerAccessor implements ContainerAccessor {
22      private final Object nativeBeanFactory;
23      private final Method nativeCreateBeanMethod;
24      private final Method nativeAutowireBeanMethod;
25      private final Method nativeGetBeanMethod;
26      private final Method nativeGetBeansOfTypeMethod;
27  
28      /**
29       * The autowire strategy to use when creating and wiring a bean
30       */
31      public enum AutowireStrategy {
32          AUTOWIRE_NO,
33          /**
34           * Performs setter-based injection by name
35           */
36          AUTOWIRE_BY_NAME,
37  
38          /**
39           * Performs setter-based injection by type
40           */
41          AUTOWIRE_BY_TYPE,
42  
43          /**
44           * Performs construction-based injection by type
45           */
46          AUTOWIRE_BY_CONSTRUCTOR,
47  
48          /**
49           * Autodetects appropriate injection by first seeing if any no-arg constructors exist.  If not, performs constructor
50           * injection, and if so, autowires by type then name
51           */
52          AUTOWIRE_AUTODETECT
53      }
54  
55      public DefaultSpringContainerAccessor(final Object applicationContext) {
56          Object beanFactory = null;
57          try {
58              final Method m = applicationContext.getClass().getMethod("getAutowireCapableBeanFactory");
59              beanFactory = m.invoke(applicationContext);
60          } catch (final NoSuchMethodException e) {
61              // Should never happen
62              throw new PluginException("Cannot find createBean method on registered bean factory: " + beanFactory, e);
63          } catch (final IllegalAccessException e) {
64              // Should never happen
65              throw new PluginException("Cannot access createBean method", e);
66          } catch (final InvocationTargetException e) {
67              handleSpringMethodInvocationError(e);
68          }
69  
70          nativeBeanFactory = beanFactory;
71          try {
72              nativeCreateBeanMethod = beanFactory.getClass().getMethod("createBean", Class.class, int.class, boolean.class);
73              nativeAutowireBeanMethod = beanFactory.getClass().getMethod("autowireBeanProperties", Object.class, int.class, boolean.class);
74              nativeGetBeanMethod = beanFactory.getClass().getMethod("getBean", String.class);
75              nativeGetBeansOfTypeMethod = beanFactory.getClass().getMethod("getBeansOfType", Class.class);
76  
77              checkState(!any(newArrayList(nativeGetBeansOfTypeMethod, nativeAutowireBeanMethod, nativeCreateBeanMethod, nativeGetBeanMethod), isNull()));
78          } catch (final NoSuchMethodException e) {
79              // Should never happen
80              throw new PluginException("Cannot find one or more methods on registered bean factory: " + nativeBeanFactory, e);
81          }
82      }
83  
84      private void handleSpringMethodInvocationError(final InvocationTargetException e) {
85          if (e.getCause() instanceof Error) {
86              throw (Error) e.getCause();
87          } else if (e.getCause() instanceof RuntimeException) {
88              throw (RuntimeException) e.getCause();
89          } else {
90              // Should never happen as Spring methods only throw runtime exceptions
91              throw new PluginException("Unable to invoke createBean", e.getCause());
92          }
93      }
94  
95      public <T> T createBean(final Class<T> clazz) {
96          try {
97              return clazz.cast(nativeCreateBeanMethod.invoke(nativeBeanFactory, clazz, AutowireStrategy.AUTOWIRE_AUTODETECT.ordinal(), false));
98          } catch (final IllegalAccessException e) {
99              // Should never happen
100             throw new PluginException("Unable to access createBean method", e);
101         } catch (final InvocationTargetException e) {
102             handleSpringMethodInvocationError(e);
103             return null;
104         }
105     }
106 
107     @Override
108     public <T> T injectBean(T bean) {
109         try {
110             nativeAutowireBeanMethod.invoke(nativeBeanFactory, bean, AutowireStrategy.AUTOWIRE_AUTODETECT.ordinal(), false);
111         } catch (final IllegalAccessException e) {
112             // Should never happen
113             throw new PluginException("Unable to access createBean method", e);
114         } catch (final InvocationTargetException e) {
115             handleSpringMethodInvocationError(e);
116         }
117         return bean;
118     }
119 
120     public <T> Collection<T> getBeansOfType(Class<T> interfaceClass) {
121         try {
122             Map<String, T> beans = (Map<String, T>) nativeGetBeansOfTypeMethod.invoke(nativeBeanFactory, interfaceClass);
123             return beans.values();
124         } catch (final IllegalAccessException e) {
125             // Should never happen
126             throw new PluginException("Unable to access getBeansOfType method", e);
127         } catch (final InvocationTargetException e) {
128             handleSpringMethodInvocationError(e);
129             return null;
130         }
131     }
132 
133     @Override
134     public <T> T getBean(final String id) {
135         try {
136             return (T) nativeGetBeanMethod.invoke(nativeBeanFactory, id);
137         } catch (final IllegalAccessException e) {
138             // Should never happen
139             throw new PluginException("Unable to access getBean method", e);
140         } catch (final InvocationTargetException e) {
141             handleSpringMethodInvocationError(e);
142             return null;
143         }
144     }
145 }