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 com.atlassian.plugin.util.ContextClassLoaderSettingInvocationHandler;
11  import com.google.common.base.Function;
12  import com.google.common.collect.Maps;
13  import org.osgi.framework.Bundle;
14  import org.osgi.framework.BundleContext;
15  import org.osgi.framework.ServiceFactory;
16  import org.osgi.framework.ServiceRegistration;
17  import org.slf4j.Logger;
18  import org.slf4j.LoggerFactory;
19  
20  import java.lang.reflect.InvocationHandler;
21  import java.lang.reflect.Method;
22  import java.lang.reflect.Proxy;
23  import java.util.ArrayList;
24  import java.util.Arrays;
25  import java.util.Collections;
26  import java.util.List;
27  import java.util.Map;
28  import java.util.concurrent.CopyOnWriteArrayList;
29  
30  /**
31   * Default component registrar that also can write registered host components into the OSGi service registry.
32   */
33  public class DefaultComponentRegistrar implements ComponentRegistrar
34  {
35      private static final Logger log = LoggerFactory.getLogger(DefaultComponentRegistrar.class);
36  
37      private final List<HostComponentRegistration> registry = new CopyOnWriteArrayList<HostComponentRegistration>();
38  
39      public InstanceBuilder register(final Class<?>... mainInterfaces)
40      {
41          final Registration reg = new Registration(mainInterfaces);
42          registry.add(reg);
43          return new DefaultInstanceBuilder(reg);
44      }
45  
46      public List<ServiceRegistration> writeRegistry(final BundleContext ctx)
47      {
48          final ArrayList<ServiceRegistration> services = new ArrayList<ServiceRegistration>();
49  
50          for (final HostComponentRegistration reg : new ArrayList<HostComponentRegistration>(registry))
51          {
52              if (Arrays.asList(reg.getMainInterfaceClasses()).contains(HostContainer.class))
53              {
54                  log.warn("Cannot register a HostContainer as a host component, skipping");
55                  registry.remove(reg);
56                  continue;
57              }
58  
59              final String[] names = reg.getMainInterfaces();
60  
61              reg.getProperties().put(HOST_COMPONENT_FLAG, Boolean.TRUE.toString());
62  
63              // If no bean name specified, generate one that will be consistent across restarts
64              final String beanName = reg.getProperties().get(PropertyBuilder.BEAN_NAME);
65              if (beanName == null)
66              {
67                  String genKey = String.valueOf(Arrays.asList(reg.getMainInterfaces()).hashCode());
68                  reg.getProperties().put(PropertyBuilder.BEAN_NAME, "hostComponent-"+genKey);
69              }
70  
71              if (log.isDebugEnabled())
72              {
73                  log.debug("Registering: " + Arrays.asList(names) + " instance " + reg.getInstance() + "with properties: " + reg.getProperties());
74              }
75  
76              if (names.length == 0)
77              {
78                  log.warn("Host component " + beanName + " of instance " + reg.getInstance() + " has no interfaces");
79              }
80  
81              Object service = reg.getInstance();
82  
83              if (!ContextClassLoaderStrategy.USE_PLUGIN.name().equals(reg.getProperties().get(PropertyBuilder.CONTEXT_CLASS_LOADER_STRATEGY)))
84              {
85                  service = createContextClassLoaderSettingProxy(reg.getMainInterfaceClasses(), service);
86              }
87  
88              if (Boolean.parseBoolean(reg.getProperties().get(PropertyBuilder.TRACK_BUNDLE)))
89              {
90                  service = createTrackBundleProxy(reg.getMainInterfaceClasses(), service);
91              }
92  
93              final ServiceRegistration sreg = ctx.registerService(names, service, reg.getProperties());
94              if (sreg != null)
95              {
96                  services.add(sreg);
97              }
98          }
99          return Collections.unmodifiableList(services);
100     }
101 
102     public List<HostComponentRegistration> getRegistry()
103     {
104         return Collections.unmodifiableList(registry);
105     }
106 
107     /**
108      * Wraps the service in a dynamic proxy that ensures all methods are executed with the object class's class loader
109      * as the context class loader
110      *
111      * @param interfaces The interfaces to proxy
112      * @param service    The instance to proxy
113      * @return A proxy that wraps the service
114      */
115     private Object createContextClassLoaderSettingProxy(final Class<?>[] interfaces, final Object service)
116     {
117         final Function<Object, Object> transformer = new Function<Object, Object>()
118         {
119             public Object apply(final Object service)
120             {
121                 return Proxy.newProxyInstance(DefaultComponentRegistrar.class.getClassLoader(), interfaces,
122                         new ContextClassLoaderSettingInvocationHandler(service));
123             }
124         };
125 
126         if (!(service instanceof ServiceFactory))
127         {
128             return transformer.apply(service);
129         }
130         return new TransformingServiceFactory((ServiceFactory) service)
131         {
132             @Override
133             protected Object transform(Bundle bundle, ServiceRegistration registration, Object service)
134             {
135                 return transformer.apply(service);
136             }
137         };
138 
139     }
140 
141     private ServiceFactory createTrackBundleProxy(final Class<?>[] interfaces, final Object service)
142     {
143         final ServiceFactory delegate = service instanceof ServiceFactory ?
144                 (ServiceFactory) service :
145                 new InstanceServiceFactory(service);
146 
147         return new TransformingServiceFactory(delegate)
148         {
149             @Override
150             protected Object transform(final Bundle bundle, final ServiceRegistration registration, final Object service)
151             {
152                 return Proxy.newProxyInstance(DefaultComponentRegistrar.class.getClassLoader(), interfaces,
153                         new BundleTrackingInvocationHandler(bundle, service));
154             }
155         };
156     }
157 
158     private static class BundleTrackingInvocationHandler implements InvocationHandler
159     {
160 
161         private final Bundle bundle;
162         private final Object service;
163 
164         private BundleTrackingInvocationHandler(Bundle bundle, Object service)
165         {
166             this.bundle = bundle;
167             this.service = service;
168         }
169 
170         @Override
171         public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
172         {
173             Bundle original  = CallingBundleStore.get();
174             try
175             {
176                 CallingBundleStore.set(bundle);
177                 return method.invoke(service, args);
178             }
179             finally
180             {
181                 CallingBundleStore.set(original);
182             }
183         }
184     }
185 
186     private static class InstanceServiceFactory implements ServiceFactory
187     {
188 
189         private final Object service;
190 
191         private InstanceServiceFactory(final Object service)
192         {
193             this.service = service;
194         }
195 
196         @Override
197         public Object getService(final Bundle bundle, final ServiceRegistration registration)
198         {
199             return service;
200         }
201 
202         @Override
203         public void ungetService(final Bundle bundle, final ServiceRegistration registration, final Object service)
204         {
205             // no-op
206         }
207     }
208 
209     private static abstract class TransformingServiceFactory implements ServiceFactory
210     {
211 
212         private final ServiceFactory delegate;
213         private final Map<Long, Object> bundleIdToOriginalService;
214 
215         private TransformingServiceFactory(ServiceFactory delegate)
216         {
217             this.delegate = delegate;
218             this.bundleIdToOriginalService = Maps.newConcurrentMap();
219         }
220 
221 
222         @Override
223         public final Object getService(final Bundle bundle, final ServiceRegistration registration)
224         {
225             final Object service = delegate.getService(bundle, registration);
226             final Object transformed = transform(bundle, registration, service);
227             // Store the original service after it has been transformed
228             // in case the transformer throws an exception
229             bundleIdToOriginalService.put(bundle.getBundleId(), service);
230             return transformed;
231         }
232 
233         @Override
234         public final void ungetService(final Bundle bundle, final ServiceRegistration registration, final Object transformed)
235         {
236             final Object service = bundleIdToOriginalService.remove(bundle.getBundleId());
237             if (service != null)
238             {
239                 delegate.ungetService(bundle, registration, service);
240             }
241         }
242 
243         protected abstract Object transform(Bundle bundle, ServiceRegistration registration, Object service);
244 
245     }
246 
247 }