View Javadoc
1   package com.atlassian.plugin.osgi.factory;
2   
3   import com.atlassian.plugin.ModuleDescriptor;
4   import com.atlassian.plugin.PluginState;
5   import com.atlassian.plugin.descriptors.UnrecognisedModuleDescriptor;
6   import com.atlassian.plugin.event.PluginEventManager;
7   import com.atlassian.plugin.event.events.PluginModuleAvailableEvent;
8   import com.atlassian.plugin.event.events.PluginModuleUnavailableEvent;
9   import com.atlassian.plugin.osgi.external.ListableModuleDescriptorFactory;
10  import org.dom4j.Element;
11  import org.osgi.framework.Bundle;
12  import org.osgi.framework.ServiceReference;
13  import org.osgi.util.tracker.ServiceTrackerCustomizer;
14  import org.slf4j.Logger;
15  import org.slf4j.LoggerFactory;
16  
17  import java.util.ArrayList;
18  import java.util.List;
19  
20  import static com.google.common.base.Preconditions.checkNotNull;
21  
22  /**
23   * Service tracker that tracks {@link com.atlassian.plugin.osgi.external.ListableModuleDescriptorFactory} instances and handles transforming
24   * {@link com.atlassian.plugin.descriptors.UnrecognisedModuleDescriptor}} instances into modules if the new factory supports them.  Updates to factories
25   * and removal are also handled.
26   *
27   * @since 2.1.2
28   */
29  class UnrecognizedModuleDescriptorServiceTrackerCustomizer implements ServiceTrackerCustomizer {
30      private static final Logger log = LoggerFactory.getLogger(UnrecognizedModuleDescriptorServiceTrackerCustomizer.class);
31  
32      private final Bundle bundle;
33      private final OsgiPlugin plugin;
34      private final PluginEventManager pluginEventManager;
35  
36      public UnrecognizedModuleDescriptorServiceTrackerCustomizer(OsgiPlugin plugin, PluginEventManager pluginEventManager) {
37          this.plugin = checkNotNull(plugin);
38          this.bundle = checkNotNull(plugin.getBundle());
39          this.pluginEventManager = checkNotNull(pluginEventManager);
40      }
41  
42      /**
43       * Turns any {@link com.atlassian.plugin.descriptors.UnrecognisedModuleDescriptor} modules that can be handled by the new factory into real
44       * modules
45       */
46      public Object addingService(final ServiceReference serviceReference) {
47          final ListableModuleDescriptorFactory factory = (ListableModuleDescriptorFactory) bundle.getBundleContext().getService(serviceReference);
48  
49          // Only register the factory if it is or should be being used by this plugin.  We still care if they are currently
50          // in use because we need to change them to unrecognized descriptors if the factory goes away.
51          if (canFactoryResolveUnrecognizedDescriptor(factory) || isFactoryInUse(factory)) {
52              return factory;
53          } else {
54              // The docs seem to indicate returning null is enough to untrack a service, but the source code and tests
55              // show otherwise.
56              bundle.getBundleContext().ungetService(serviceReference);
57              return null;
58          }
59      }
60  
61      /**
62       * See if the descriptor factory can resolve any unrecognized descriptors for this plugin, and if so, resolve them
63       *
64       * @param factory The new module descriptor factory
65       * @return True if any were resolved, false otherwise
66       */
67      private boolean canFactoryResolveUnrecognizedDescriptor(ListableModuleDescriptorFactory factory) {
68          boolean usedFactory = false;
69          for (final UnrecognisedModuleDescriptor unrecognised : getModuleDescriptorsByDescriptorClass(UnrecognisedModuleDescriptor.class)) {
70              final Element source = plugin.getModuleElements().get(unrecognised.getKey());
71              if ((source != null) && factory.hasModuleDescriptor(source.getName())) {
72                  usedFactory = true;
73                  try {
74                      final ModuleDescriptor<?> descriptor = factory.getModuleDescriptor(source.getName());
75                      descriptor.init(unrecognised.getPlugin(), source);
76                      plugin.addModuleDescriptor(descriptor);
77                      log.info("Turned unrecognized plugin module {} into module {}", descriptor.getCompleteKey(), descriptor);
78                      pluginEventManager.broadcast(new PluginModuleAvailableEvent(descriptor));
79                  } catch (final Exception e) {
80                      log.error("Unable to transform {} into actual plugin module using factory {}", new Object[]{unrecognised.getCompleteKey(), factory, e});
81                      unrecognised.setErrorText(e.getMessage());
82                  }
83              }
84          }
85          return usedFactory;
86      }
87  
88      /**
89       * Determine if the module descriptor factory is being used by any of the recognized descriptors.
90       *
91       * @param factory The new descriptor factory
92       * @return True if in use, false otherwise
93       */
94      private boolean isFactoryInUse(ListableModuleDescriptorFactory factory) {
95          for (ModuleDescriptor<?> descriptor : plugin.getModuleDescriptors()) {
96              for (Class<? extends ModuleDescriptor> descriptorClass : factory.getModuleDescriptorClasses()) {
97                  if (descriptorClass == descriptor.getClass()) {
98                      return true;
99                  }
100             }
101         }
102         return false;
103     }
104 
105     public void modifiedService(final ServiceReference serviceReference, final Object o) {
106         // do nothing as it is only modifying the properties, which we largely ignore
107     }
108 
109     /**
110      * Reverts any current module descriptors that were provided from the factory being removed into {@link
111      * UnrecognisedModuleDescriptor} instances.
112      */
113     public void removedService(final ServiceReference serviceReference, final Object o) {
114         final ListableModuleDescriptorFactory factory = (ListableModuleDescriptorFactory) o;
115         for (final Class<? extends ModuleDescriptor> moduleDescriptorClass : factory.getModuleDescriptorClasses()) {
116             for (final ModuleDescriptor<?> descriptor : getModuleDescriptorsByDescriptorClass(moduleDescriptorClass)) {
117                 if (plugin.getPluginState() == PluginState.ENABLED) {
118                     pluginEventManager.broadcast(new PluginModuleUnavailableEvent(descriptor));
119                     log.info("Removed plugin module {} as its factory was uninstalled", descriptor.getCompleteKey());
120                 }
121                 plugin.clearModuleDescriptor(descriptor.getKey());
122 
123                 if (plugin.isFrameworkShuttingDown()) {
124                     continue;
125                 }
126                 final UnrecognisedModuleDescriptor unrecognisedModuleDescriptor = new UnrecognisedModuleDescriptor();
127                 final Element source = plugin.getModuleElements().get(descriptor.getKey());
128                 if (source != null) {
129                     unrecognisedModuleDescriptor.init(plugin, source);
130                     unrecognisedModuleDescriptor.setErrorText(UnrecognisedModuleDescriptorFallbackFactory.DESCRIPTOR_TEXT);
131                     plugin.addModuleDescriptor(unrecognisedModuleDescriptor);
132 
133                     if (plugin.getPluginState() == PluginState.ENABLED) {
134                         pluginEventManager.broadcast(new PluginModuleAvailableEvent(unrecognisedModuleDescriptor));
135                     }
136                 }
137             }
138         }
139     }
140 
141     /**
142      * @param descriptor
143      * @param <T>
144      * @return
145      */
146     <T extends ModuleDescriptor<?>> List<T> getModuleDescriptorsByDescriptorClass(final Class<T> descriptor) {
147         final List<T> result = new ArrayList<T>();
148 
149         for (final ModuleDescriptor<?> moduleDescriptor : plugin.getModuleDescriptors()) {
150             if (descriptor.isAssignableFrom(moduleDescriptor.getClass())) {
151                 result.add(descriptor.cast(moduleDescriptor));
152             }
153         }
154         return result;
155     }
156 }