1   package com.atlassian.plugin.osgi.factory;
2   
3   import org.osgi.util.tracker.ServiceTrackerCustomizer;
4   import org.osgi.framework.ServiceReference;
5   import org.osgi.framework.Bundle;
6   import org.dom4j.Element;
7   import org.apache.commons.logging.Log;
8   import org.apache.commons.logging.LogFactory;
9   import org.apache.commons.lang.Validate;
10  import com.atlassian.plugin.osgi.external.ListableModuleDescriptorFactory;
11  import com.atlassian.plugin.descriptors.UnrecognisedModuleDescriptor;
12  import com.atlassian.plugin.ModuleDescriptor;
13  
14  import java.util.List;
15  import java.util.ArrayList;
16  
17  /**
18   * Service tracker that tracks {@link com.atlassian.plugin.osgi.external.ListableModuleDescriptorFactory} instances and handles transforming
19   * {@link com.atlassian.plugin.descriptors.UnrecognisedModuleDescriptor}} instances into modules if the new factory supports them.  Updates to factories
20   * and removal are also handled.
21   *
22   * @since 2.1.2
23   */
24  class UnrecognizedModuleDescriptorServiceTrackerCustomizer implements ServiceTrackerCustomizer
25  {
26      private static final Log log = LogFactory.getLog(UnrecognizedModuleDescriptorServiceTrackerCustomizer.class);
27  
28      private final Bundle bundle;
29      private final OsgiPlugin plugin;
30  
31      public UnrecognizedModuleDescriptorServiceTrackerCustomizer(OsgiPlugin plugin)
32      {
33          Validate.notNull(plugin);
34          this.bundle = plugin.getBundle();
35          Validate.notNull(bundle);
36          this.plugin = plugin;
37      }
38      /**
39       * Turns any {@link com.atlassian.plugin.descriptors.UnrecognisedModuleDescriptor} modules that can be handled by the new factory into real
40       * modules
41       */
42      public Object addingService(final ServiceReference serviceReference)
43      {
44          final ListableModuleDescriptorFactory factory = (ListableModuleDescriptorFactory) bundle.getBundleContext().getService(serviceReference);
45          boolean usedFactory = false;
46          for (final UnrecognisedModuleDescriptor unrecognised : getModuleDescriptorsByDescriptorClass(UnrecognisedModuleDescriptor.class))
47          {
48              final Element source = plugin.getModuleElements().get(unrecognised.getKey());
49              if ((source != null) && factory.hasModuleDescriptor(source.getName()))
50              {
51                  usedFactory = true;
52                  try
53                  {
54                      final ModuleDescriptor<?> descriptor = factory.getModuleDescriptor(source.getName());
55                      descriptor.init(unrecognised.getPlugin(), source);
56                      plugin.addModuleDescriptor(descriptor);
57                      if (log.isInfoEnabled())
58                      {
59                          log.info("Turned plugin module " + descriptor.getCompleteKey() + " into module " + descriptor);
60                      }
61                  }
62                  catch (final Exception e)
63                  {
64                      log.error("Unable to transform " + unrecognised.getCompleteKey() + " into actual plugin module using factory " + factory, e);
65                      unrecognised.setErrorText(e.getMessage());
66                  }
67              }
68          }
69          if (usedFactory)
70          {
71              return factory;
72          }
73          else
74          {
75              // The docs seem to indicate returning null is enough to untrack a service, but the source code and tests
76              // show otherwise.
77              bundle.getBundleContext().ungetService(serviceReference);
78              return null;
79          }
80      }
81  
82      /**
83       * Updates any local module descriptors that were created from the modified factory
84       */
85      public void modifiedService(final ServiceReference serviceReference, final Object o)
86      {
87          removedService(serviceReference, o);
88          addingService(serviceReference);
89      }
90  
91      /**
92       * Reverts any current module descriptors that were provided from the factory being removed into {@link
93       * UnrecognisedModuleDescriptor} instances.
94       */
95      public void removedService(final ServiceReference serviceReference, final Object o)
96      {
97          final ListableModuleDescriptorFactory factory = (ListableModuleDescriptorFactory) o;
98          for (final Class<ModuleDescriptor<?>> moduleDescriptorClass : factory.getModuleDescriptorClasses())
99          {
100             for (final ModuleDescriptor<?> descriptor : getModuleDescriptorsByDescriptorClass(moduleDescriptorClass))
101             {
102                 final UnrecognisedModuleDescriptor unrecognisedModuleDescriptor = new UnrecognisedModuleDescriptor();
103                 final Element source = plugin.getModuleElements().get(descriptor.getKey());
104                 if (source != null)
105                 {
106                     unrecognisedModuleDescriptor.init(plugin, source);
107                     unrecognisedModuleDescriptor.setErrorText(UnrecognisedModuleDescriptorFallbackFactory.DESCRIPTOR_TEXT);
108                     plugin.addModuleDescriptor(unrecognisedModuleDescriptor);
109                     if (log.isInfoEnabled())
110                     {
111                         log.info("Removed plugin module " + unrecognisedModuleDescriptor.getCompleteKey() + " as its factory was uninstalled");
112                     }
113                 }
114             }
115         }
116     }
117 
118     /**
119      *
120      * @param descriptor
121      * @param <T>
122      * @return
123      */
124     <T extends ModuleDescriptor<?>> List<T> getModuleDescriptorsByDescriptorClass(final Class<T> descriptor)
125     {
126         final List<T> result = new ArrayList<T>();
127 
128         for (final ModuleDescriptor<?> moduleDescriptor : plugin.getModuleDescriptors())
129         {
130             if (moduleDescriptor.getClass()
131                     .isAssignableFrom(descriptor))
132             {
133                 result.add(descriptor.cast(moduleDescriptor));
134             }
135         }
136         return result;
137     }
138 }