View Javadoc

1   package com.atlassian.plugin.osgi.spring;
2   
3   import com.atlassian.plugin.AutowireCapablePlugin;
4   import com.atlassian.plugin.PluginException;
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 SpringContainerAccessor
22  {
23      private final Object nativeBeanFactory;
24      private final Method nativeCreateBeanMethod;
25      private final Method nativeAutowireBeanMethod;
26      private final Method nativeGetBeanMethod;
27      private final Method nativeGetBeansOfTypeMethod;
28  
29      /**
30       * The autowire strategy to use when creating and wiring a bean
31       */
32      private enum AutowireStrategy
33      {
34          AUTOWIRE_NO,
35          /** Performs setter-based injection by name */
36          AUTOWIRE_BY_NAME,
37  
38          /** Performs setter-based injection by type */
39          AUTOWIRE_BY_TYPE,
40  
41          /** Performs construction-based injection by type */
42          AUTOWIRE_BY_CONSTRUCTOR,
43  
44          /**
45           * Autodetects appropriate injection by first seeing if any no-arg constructors exist.  If not, performs constructor
46           * injection, and if so, autowires by type then name
47           */
48          AUTOWIRE_AUTODETECT
49      }
50  
51      public DefaultSpringContainerAccessor(final Object applicationContext)
52      {
53          Object beanFactory = null;
54          try
55          {
56              final Method m = applicationContext.getClass().getMethod("getAutowireCapableBeanFactory");
57              beanFactory = m.invoke(applicationContext);
58          }
59          catch (final NoSuchMethodException e)
60          {
61              // Should never happen
62              throw new PluginException("Cannot find createBean method on registered bean factory: " + beanFactory, e);
63          }
64          catch (final IllegalAccessException e)
65          {
66              // Should never happen
67              throw new PluginException("Cannot access createBean method", e);
68          }
69          catch (final InvocationTargetException e)
70          {
71              handleSpringMethodInvocationError(e);
72          }
73  
74          nativeBeanFactory = beanFactory;
75          try
76          {
77              nativeCreateBeanMethod = beanFactory.getClass().getMethod("createBean", Class.class, int.class, boolean.class);
78              nativeAutowireBeanMethod = beanFactory.getClass().getMethod("autowireBeanProperties", Object.class, int.class, boolean.class);
79              nativeGetBeanMethod = beanFactory.getClass().getMethod("getBean", String.class);
80              nativeGetBeansOfTypeMethod = beanFactory.getClass().getMethod("getBeansOfType", Class.class);
81  
82              checkState(!any(newArrayList(nativeGetBeansOfTypeMethod, nativeAutowireBeanMethod, nativeCreateBeanMethod, nativeGetBeanMethod), isNull()));
83          }
84          catch (final NoSuchMethodException e)
85          {
86              // Should never happen
87              throw new PluginException("Cannot find one or more methods on registered bean factory: " + nativeBeanFactory, e);
88          }
89      }
90  
91      private void handleSpringMethodInvocationError(final InvocationTargetException e)
92      {
93          if (e.getCause() instanceof Error)
94          {
95              throw (Error) e.getCause();
96          }
97          else if (e.getCause() instanceof RuntimeException)
98          {
99              throw (RuntimeException) e.getCause();
100         }
101         else
102         {
103             // Should never happen as Spring methods only throw runtime exceptions
104             throw new PluginException("Unable to invoke createBean", e.getCause());
105         }
106     }
107 
108     public <T> T createBean(final Class<T> clazz)
109     {
110         try
111         {
112             return clazz.cast(nativeCreateBeanMethod.invoke(nativeBeanFactory, clazz, AutowireStrategy.AUTOWIRE_AUTODETECT.ordinal(), false));
113         }
114         catch (final IllegalAccessException e)
115         {
116             // Should never happen
117             throw new PluginException("Unable to access createBean method", e);
118         }
119         catch (final InvocationTargetException e)
120         {
121             handleSpringMethodInvocationError(e);
122             return null;
123         }
124     }
125 
126     @Override
127     public <T> T injectBean(T bean)
128     {
129         autowireBean(bean, AutowireCapablePlugin.AutowireStrategy.AUTOWIRE_AUTODETECT);
130         return bean;
131     }
132 
133     public <T> Collection<T> getBeansOfType(Class<T> interfaceClass) {
134         try
135         {
136             Map<String, T> beans = (Map<String, T>) nativeGetBeansOfTypeMethod.invoke(nativeBeanFactory, interfaceClass);
137             return beans.values();
138         }
139         catch (final IllegalAccessException e)
140         {
141             // Should never happen
142             throw new PluginException("Unable to access getBeansOfType method", e);
143         }
144         catch (final InvocationTargetException e)
145         {
146             handleSpringMethodInvocationError(e);
147             return null;
148         }
149     }
150 
151     public void autowireBean(final Object instance, AutowireCapablePlugin.AutowireStrategy autowireStrategy)
152     {
153         try
154         {
155             nativeAutowireBeanMethod.invoke(nativeBeanFactory, instance, autowireStrategy.ordinal(), false);
156         }
157         catch (final IllegalAccessException e)
158         {
159             // Should never happen
160             throw new PluginException("Unable to access createBean method", e);
161         }
162         catch (final InvocationTargetException e)
163         {
164             handleSpringMethodInvocationError(e);
165         }
166     }
167 
168     public Object getBean(String id)
169     {
170         try
171         {
172             return nativeGetBeanMethod.invoke(nativeBeanFactory, id);
173         }
174         catch (final IllegalAccessException e)
175         {
176             // Should never happen
177             throw new PluginException("Unable to access getBean method", e);
178         }
179         catch (final InvocationTargetException e)
180         {
181             handleSpringMethodInvocationError(e);
182             return null;
183         }
184     }
185 }