View Javadoc
1   package com.atlassian.plugin.osgi.factory;
2   
3   import com.atlassian.plugin.DefaultModuleDescriptorFactory;
4   import com.atlassian.plugin.ModuleDescriptor;
5   import com.atlassian.plugin.ModuleDescriptorFactory;
6   import com.atlassian.plugin.descriptors.ChainModuleDescriptorFactory;
7   import com.atlassian.plugin.hostcontainer.DefaultHostContainer;
8   import com.atlassian.plugin.osgi.external.ListableModuleDescriptorFactory;
9   import com.atlassian.plugin.osgi.factory.descriptor.ComponentImportModuleDescriptor;
10  import com.atlassian.plugin.osgi.factory.descriptor.ComponentModuleDescriptor;
11  import com.atlassian.plugin.osgi.factory.descriptor.ModuleTypeModuleDescriptor;
12  import org.osgi.util.tracker.ServiceTracker;
13  
14  import java.util.ArrayList;
15  import java.util.Collection;
16  import java.util.List;
17  
18  /**
19   * Builds a {@link ModuleDescriptorFactory} suitable for building {@link ModuleDescriptor} instances in an OSGi context
20   * for {@link OsgiPlugin} instances.
21   * The result will be able to handle, in the following order:
22   * <ul>
23   * <li>OSGi-specific module descriptors like component, component-import, and module-type</li>
24   * <li>Product-provided module descriptors</li>
25   * <li>Any module descriptor factories exposed as OSGi services</li>
26   * <li>Unknown module descriptors</li>
27   * </ul>
28   *
29   * @since 2.7.0
30   */
31  public class OsgiChainedModuleDescriptorFactoryCreator {
32      private final ServiceTrackerFactory serviceTrackerFactory;
33      private final ModuleDescriptorFactory transformedDescriptorFactory = new DefaultModuleDescriptorFactory(new DefaultHostContainer()) {{
34          addModuleDescriptor("component", ComponentModuleDescriptor.class);
35          addModuleDescriptor("component-import", ComponentImportModuleDescriptor.class);
36          addModuleDescriptor("module-type", ModuleTypeModuleDescriptor.class);
37      }};
38  
39      private volatile ServiceTracker moduleDescriptorFactoryTracker;
40  
41      public interface ServiceTrackerFactory {
42  
43          ServiceTracker create(String className);
44      }
45  
46      public interface ResourceLocator {
47          boolean doesResourceExist(String name);
48      }
49  
50      public OsgiChainedModuleDescriptorFactoryCreator(ServiceTrackerFactory serviceTrackerFactory) {
51          this.serviceTrackerFactory = serviceTrackerFactory;
52      }
53  
54      public ModuleDescriptorFactory create(ResourceLocator resourceLocator, ModuleDescriptorFactory originalModuleDescriptorFactory) {
55          // we really don't want two of these
56          synchronized (this) {
57              if (moduleDescriptorFactoryTracker == null) {
58                  moduleDescriptorFactoryTracker = serviceTrackerFactory.create(ModuleDescriptorFactory.class.getName());
59              }
60          }
61  
62          List<ModuleDescriptorFactory> factories = new ArrayList<>();
63  
64          factories.add(transformedDescriptorFactory);
65          factories.add(originalModuleDescriptorFactory);
66          Object[] serviceObjs = moduleDescriptorFactoryTracker.getServices();
67  
68          Collection<ModuleDescriptorFactory> wrappedListable = new ArrayList<>();
69  
70          // Add all the dynamic module descriptor factories registered as osgi services
71          if (serviceObjs != null) {
72              for (Object fac : serviceObjs) {
73                  ModuleDescriptorFactory dynFactory = (ModuleDescriptorFactory) fac;
74  
75                  // don't add Listable descriptor factories (module-type users) as the module type could get disabled
76                  // between here and when the plugin is actually looking for listable factories
77                  if (!(dynFactory instanceof ListableModuleDescriptorFactory)) {
78                      factories.add((ModuleDescriptorFactory) fac);
79                  } else {
80                      for (Class<? extends ModuleDescriptor> descriptor : ((ListableModuleDescriptorFactory) dynFactory).getModuleDescriptorClasses()) {
81                          // This will only work for classes not in inner jars and breaks on first non-match
82                          if (!resourceLocator.doesResourceExist(descriptor.getName().replace('.', '/') + ".class")) {
83                              wrappedListable.add(new UnavailableModuleDescriptorRequiringRestartFallbackFactory((ModuleDescriptorFactory) fac));
84                              break;
85                          }
86                      }
87                  }
88              }
89          }
90  
91          factories.add(new ChainModuleDescriptorFactory(wrappedListable));
92  
93          // Catch all unknown descriptors as unrecognised
94          factories.add(new UnrecognisedModuleDescriptorFallbackFactory());
95  
96          return new ChainModuleDescriptorFactory(factories);
97      }
98  }