View Javadoc

1   package com.atlassian.plugin.refimpl;
2   
3   import java.lang.reflect.Constructor;
4   import java.lang.reflect.InvocationHandler;
5   import java.lang.reflect.InvocationTargetException;
6   import java.lang.reflect.Method;
7   import java.lang.reflect.Proxy;
8   import java.util.ArrayList;
9   import java.util.Collection;
10  import java.util.Comparator;
11  import java.util.List;
12  import java.util.Map;
13  import java.util.Set;
14  import java.util.TreeSet;
15  import java.util.concurrent.ConcurrentHashMap;
16  
17  import org.osgi.util.tracker.ServiceTracker;
18  
19  import com.atlassian.plugin.hostcontainer.HostContainer;
20  import com.atlassian.plugin.osgi.container.OsgiContainerManager;
21  
22  /**
23   * Similar to the SimpleConstructorHostContainer, except it also scans the osgi container for registered components.
24   */
25  class RefimplHostContainer implements HostContainer
26  {
27      private final Map<Class<?>, Object> context;
28      private final OsgiContainerManager osgiContainer;
29  
30      public RefimplHostContainer(Map<Class<?>, Object> context, OsgiContainerManager osgiContainer)
31      {
32          this.context = new ConcurrentHashMap<Class<?>, Object>(context);
33          context.put(HostContainer.class, this);
34          
35          this.osgiContainer = osgiContainer;
36      }
37  
38      /**
39       * Creates a class instance, performing dependency injection using the initialised context map
40       *
41       * @param moduleClass The target object class
42       * @return The instance
43       * @throws IllegalArgumentException Wraps any exceptions thrown during the constructor call
44       */
45      public <T> T create(final Class<T> moduleClass) throws IllegalArgumentException
46      {
47          for (final Constructor<T> constructor : findConstructorsLargestFirst(moduleClass))
48          {
49              final List<Object> params = new ArrayList<Object>();
50              for (final Class<?> paramType : constructor.getParameterTypes())
51              {
52                  final ServiceTracker tracker = osgiContainer.getServiceTracker(paramType.getName());
53                  if (tracker == null)
54                  {
55                      continue;
56                  }
57                  tracker.open();
58                  Object serviceProxy = Proxy.newProxyInstance(paramType.getClassLoader(), new Class<?>[] { paramType }, new InvocationHandler() 
59                  {
60                      public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
61                      {
62                          return method.invoke(tracker.getService(), args);
63                      }
64                  });
65                  params.add(serviceProxy);
66              }
67              if (constructor.getParameterTypes().length != params.size())
68              {
69                  continue;
70              }
71  
72              try
73              {
74                  return constructor.newInstance(params.toArray());
75              }
76              catch (final InstantiationException e)
77              {
78                  throw new IllegalArgumentException(e);
79              }
80              catch (final IllegalAccessException e)
81              {
82                  throw new IllegalArgumentException(e);
83              }
84              catch (final InvocationTargetException e)
85              {
86                  throw new IllegalArgumentException(e);
87              }
88          }
89  
90          throw new IllegalArgumentException("Unable to match any constructor for class " + moduleClass);
91      }
92  
93      /**
94       * Gets a class instance of out of the context map
95       *
96       * @param moduleClass The object class
97       * @return The object instance.  May be null.
98       */
99      @SuppressWarnings("unchecked")
100     public <T> T getInstance(final Class<T> moduleClass)
101     {
102         return (T) context.get(moduleClass);
103     }
104 
105     @SuppressWarnings("unchecked")
106     private <T> Collection<Constructor<T>> findConstructorsLargestFirst(final Class<T> moduleClass)
107     {
108         final Set<Constructor<T>> constructors = new TreeSet<Constructor<T>>(new Comparator<Constructor<T>>()
109         {
110             public int compare(final Constructor<T> first, final Constructor<T> second)
111             {
112                 // @TODO this only sorts via largest, and therefore it also causes any of the same length to get dropped from the set, see TreeSet for more details
113                 return Integer.valueOf(second.getParameterTypes().length).compareTo(first.getParameterTypes().length);
114             }
115         });
116         for (final Constructor<?> constructor : moduleClass.getConstructors())
117         {
118             constructors.add((Constructor<T>) constructor);
119         }
120         return constructors;
121     }
122 }