View Javadoc

1   package com.atlassian.plugin.osgi.hostcomponents.impl;
2   
3   import com.atlassian.plugin.hostcontainer.HostContainer;
4   import com.atlassian.plugin.osgi.hostcomponents.ComponentRegistrar;
5   import com.atlassian.plugin.osgi.hostcomponents.ContextClassLoaderStrategy;
6   import com.atlassian.plugin.osgi.hostcomponents.HostComponentRegistration;
7   import com.atlassian.plugin.osgi.hostcomponents.InstanceBuilder;
8   import com.atlassian.plugin.osgi.hostcomponents.PropertyBuilder;
9   
10  import org.osgi.framework.BundleContext;
11  import org.osgi.framework.ServiceRegistration;
12  import org.slf4j.Logger;
13  import org.slf4j.LoggerFactory;
14  
15  import java.lang.reflect.InvocationHandler;
16  import java.lang.reflect.InvocationTargetException;
17  import java.lang.reflect.Method;
18  import java.lang.reflect.Proxy;
19  import java.util.ArrayList;
20  import java.util.Arrays;
21  import java.util.Collections;
22  import java.util.List;
23  import java.util.concurrent.CopyOnWriteArrayList;
24  
25  /**
26   * Default component registrar that also can write registered host components into the OSGi service registry.
27   */
28  public class DefaultComponentRegistrar implements ComponentRegistrar
29  {
30      private static final Logger log = LoggerFactory.getLogger(DefaultComponentRegistrar.class);
31  
32      private final List<HostComponentRegistration> registry = new CopyOnWriteArrayList<HostComponentRegistration>();
33  
34      public InstanceBuilder register(final Class<?>... mainInterfaces)
35      {
36          final Registration reg = new Registration(mainInterfaces);
37          registry.add(reg);
38          return new DefaultInstanceBuilder(reg);
39      }
40  
41      public List<ServiceRegistration> writeRegistry(final BundleContext ctx)
42      {
43          final ArrayList<ServiceRegistration> services = new ArrayList<ServiceRegistration>();
44  
45          for (final HostComponentRegistration reg : new ArrayList<HostComponentRegistration>(registry))
46          {
47              if (Arrays.asList(reg.getMainInterfaceClasses()).contains(HostContainer.class))
48              {
49                  log.warn("Cannot register a HostContainer as a host component, skipping");
50                  registry.remove(reg);
51                  continue;
52              }
53  
54              final String[] names = reg.getMainInterfaces();
55  
56              reg.getProperties().put(HOST_COMPONENT_FLAG, Boolean.TRUE.toString());
57  
58              // If no bean name specified, generate one that will be consistent across restarts
59              final String beanName = reg.getProperties().get(PropertyBuilder.BEAN_NAME);
60              if (beanName == null)
61              {
62                  String genKey = String.valueOf(Arrays.asList(reg.getMainInterfaces()).hashCode());
63                  reg.getProperties().put(PropertyBuilder.BEAN_NAME, "hostComponent-"+genKey);
64              }
65  
66              if (log.isDebugEnabled())
67              {
68                  log.debug("Registering: " + Arrays.asList(names) + " instance " + reg.getInstance() + "with properties: " + reg.getProperties());
69              }
70  
71              if (names.length == 0)
72              {
73                  log.warn("Host component " + beanName + " of instance " + reg.getInstance() + " has no interfaces");
74              }
75  
76              Object service = reg.getInstance();
77              if (!ContextClassLoaderStrategy.USE_PLUGIN.name().equals(reg.getProperties().get(PropertyBuilder.CONTEXT_CLASS_LOADER_STRATEGY)))
78              {
79                  service = wrapService(reg.getMainInterfaceClasses(), reg.getInstance());
80              }
81  
82              final ServiceRegistration sreg = ctx.registerService(names, service, reg.getProperties());
83              if (sreg != null)
84              {
85                  services.add(sreg);
86              }
87          }
88          return Collections.unmodifiableList(services);
89      }
90  
91      public List<HostComponentRegistration> getRegistry()
92      {
93          return Collections.unmodifiableList(registry);
94      }
95  
96      /**
97       * Wraps the service in a dynamic proxy that ensures all methods are executed with the object class's class loader
98       * as the context class loader
99       *
100      * @param interfaces The interfaces to proxy
101      * @param service    The instance to proxy
102      * @return A proxy that wraps the service
103      */
104     protected Object wrapService(final Class<?>[] interfaces, final Object service)
105     {
106         return Proxy.newProxyInstance(getClass().getClassLoader(), interfaces, new ContextClassLoaderSettingInvocationHandler(service));
107     }
108 
109     /**
110      * InvocationHandler for a dynamic proxy that ensures all methods are executed with the
111      * object class's class loader as the context class loader.
112      */
113     private static class ContextClassLoaderSettingInvocationHandler implements InvocationHandler
114     {
115         private final Object service;
116         
117         private final ClassLoader serviceClassLoader;
118 
119         ContextClassLoaderSettingInvocationHandler(final Object service)
120         {
121             this.service = service;
122             // It is safe to cache here as service is final & getClass() along with
123             // getClassLoader() always return same value
124             this.serviceClassLoader = service.getClass().getClassLoader();
125         }
126 
127         public Object invoke(final Object o, final Method method, final Object[] objects) throws Throwable
128         {
129             final Thread thread = Thread.currentThread();
130             final ClassLoader ccl = thread.getContextClassLoader();
131             try
132             {
133                 thread.setContextClassLoader(serviceClassLoader);
134                 return method.invoke(service, objects);
135             }
136             catch (final InvocationTargetException e)
137             {
138                 throw e.getTargetException();
139             }
140             finally
141             {
142                 thread.setContextClassLoader(ccl);
143             }
144         }
145     }
146 }