View Javadoc

1   package com.atlassian.plugin.osgi.factory;
2   
3   import com.atlassian.plugin.ModuleDescriptorFactory;
4   import com.atlassian.plugin.Plugin;
5   import com.atlassian.plugin.PluginArtifact;
6   import com.atlassian.plugin.PluginParseException;
7   import com.atlassian.plugin.classloader.PluginClassLoader;
8   import com.atlassian.plugin.descriptors.ChainModuleDescriptorFactory;
9   import com.atlassian.plugin.factories.PluginFactory;
10  import com.atlassian.plugin.impl.UnloadablePlugin;
11  import com.atlassian.plugin.loaders.classloading.DeploymentUnit;
12  import com.atlassian.plugin.osgi.container.OsgiContainerException;
13  import com.atlassian.plugin.osgi.container.OsgiContainerManager;
14  import com.atlassian.plugin.osgi.factory.transform.DefaultPluginTransformer;
15  import com.atlassian.plugin.osgi.factory.transform.PluginTransformationException;
16  import com.atlassian.plugin.osgi.factory.transform.PluginTransformer;
17  import com.atlassian.plugin.parsers.DescriptorParser;
18  import com.atlassian.plugin.parsers.DescriptorParserFactory;
19  import org.apache.commons.io.IOUtils;
20  import org.apache.commons.lang.Validate;
21  import org.apache.commons.logging.Log;
22  import org.apache.commons.logging.LogFactory;
23  import org.osgi.util.tracker.ServiceTracker;
24  
25  import java.io.File;
26  import java.io.InputStream;
27  import java.util.List;
28  import java.util.ArrayList;
29  
30  /**
31   * Plugin loader that starts an OSGi container and loads plugins into it, wrapped as OSGi bundles.
32   */
33  public class OsgiPluginFactory implements PluginFactory
34  {
35      private static final Log log = LogFactory.getLog(OsgiPluginFactory.class);
36  
37      private final OsgiContainerManager osgi;
38      private final PluginTransformer pluginTransformer;
39      private final String pluginDescriptorFileName;
40      private final DescriptorParserFactory descriptorParserFactory;
41  
42      private ServiceTracker moduleDescriptorFactoryTracker;
43  
44      public OsgiPluginFactory(String pluginDescriptorFileName, OsgiContainerManager osgi)
45      {
46          Validate.notNull(pluginDescriptorFileName, "Plugin descriptor is required");
47          Validate.notNull(osgi, "The OSGi container is required");
48  
49          pluginTransformer = new DefaultPluginTransformer();
50          this.osgi = osgi;
51          this.pluginDescriptorFileName = pluginDescriptorFileName;
52          this.descriptorParserFactory = new OsgiPluginXmlDescriptorParserFactory();
53      }
54  
55      public String canCreate(PluginArtifact pluginArtifact) throws PluginParseException {
56          Validate.notNull(pluginArtifact, "The plugin artifact is required");
57  
58          String pluginKey = null;
59          InputStream descriptorStream = null;
60          try
61          {
62              descriptorStream = pluginArtifact.getResourceAsStream(pluginDescriptorFileName);
63  
64              if (descriptorStream != null)
65              {
66                  final DescriptorParser descriptorParser = descriptorParserFactory.getInstance(descriptorStream);
67                  if (descriptorParser.getPluginsVersion() == 2)
68                      pluginKey = descriptorParser.getKey();
69              }
70          } 
71          finally
72          {
73              IOUtils.closeQuietly(descriptorStream);
74          }
75          return pluginKey;
76      }
77  
78      public Plugin create(DeploymentUnit deploymentUnit, ModuleDescriptorFactory moduleDescriptorFactory) throws PluginParseException {
79          Validate.notNull(deploymentUnit, "The plugin deployment unit is required");
80          Validate.notNull(moduleDescriptorFactory, "The module descriptor factory is required");
81  
82          Plugin plugin = null;
83          InputStream pluginDescriptor = null;
84          PluginClassLoader loader = new PluginClassLoader(deploymentUnit.getPath());
85  
86          if (loader.getResource(pluginDescriptorFileName) == null)
87              throw new PluginParseException("No descriptor found in classloader for : " + deploymentUnit);
88  
89          try
90          {
91              Plugin osgiPlugin = createOsgiPlugin(deploymentUnit.getPath());
92              ModuleDescriptorFactory combinedFactory = getChainedModuleDescriptorFactory(moduleDescriptorFactory);
93              pluginDescriptor = loader.getResourceAsStream(pluginDescriptorFileName);
94              // The plugin we get back may not be the same (in the case of an UnloadablePlugin), so add what gets returned, rather than the original
95              DescriptorParser parser = descriptorParserFactory.getInstance(pluginDescriptor);
96              plugin = parser.configurePlugin(combinedFactory, osgiPlugin);
97          }
98          finally
99          {
100             IOUtils.closeQuietly(pluginDescriptor);
101         }
102         return plugin;
103     }
104 
105     /**
106      * Get a chained module descriptor factory that includes any dynamically available descriptor factories
107      *
108      * @param originalFactory The factory provided by the host application
109      * @return The composite factory
110      */
111     private ModuleDescriptorFactory getChainedModuleDescriptorFactory(ModuleDescriptorFactory originalFactory)
112     {
113         // we really don't want two of these
114         synchronized(this)
115         {
116             if (moduleDescriptorFactoryTracker == null)
117                 moduleDescriptorFactoryTracker = osgi.getServiceTracker(ModuleDescriptorFactory.class.getName());
118         }
119 
120         // Really shouldn't be null, but could be in tests since we can't mock a service tracker :(
121         if (moduleDescriptorFactoryTracker != null)
122         {
123             List<ModuleDescriptorFactory> factories = new ArrayList<ModuleDescriptorFactory>();
124             Object[] serviceObjs = moduleDescriptorFactoryTracker.getServices();
125 
126             // Add all the dynamic module descriptor factories registered as osgi services
127             if (serviceObjs != null)
128             {
129                 for (Object fac : serviceObjs) factories.add((ModuleDescriptorFactory) fac);
130             }
131 
132             // Put the application factory first
133             factories.add(0, originalFactory);
134 
135             // Catch all unknown descriptors as deferred
136             factories.add(new UnrecognisedModuleDescriptorFallbackFactory());
137 
138             return new ChainModuleDescriptorFactory(factories.toArray(new ModuleDescriptorFactory[]{}));
139         }
140         else
141             return originalFactory;
142 
143 
144     }
145 
146     private Plugin createOsgiPlugin(File file)
147     {
148         try
149         {
150             File transformedFile = pluginTransformer.transform(file, osgi.getHostComponentRegistrations());
151             return new OsgiPlugin(osgi.installBundle(transformedFile));
152         } catch (OsgiContainerException e)
153         {
154             return reportUnloadablePlugin(file, e);
155         } catch (PluginTransformationException ex)
156         {
157             return reportUnloadablePlugin(file, ex);
158         }
159     }
160 
161     private Plugin reportUnloadablePlugin(File file, Exception e)
162     {
163         log.error("Unable to load plugin: "+file, e);
164 
165         UnloadablePlugin plugin = new UnloadablePlugin();
166         plugin.setErrorText("Unable to load plugin: "+e.getMessage());
167         return plugin;
168     }
169 }