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