View Javadoc

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