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