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  
28  /**
29   * Plugin loader that starts an OSGi container and loads plugins into it, wrapped as OSGi bundles.
30   */
31  public class OsgiPluginFactory implements PluginFactory
32  {
33      private static final Log log = LogFactory.getLog(OsgiPluginFactory.class);
34  
35      private final OsgiContainerManager osgi;
36      private final PluginTransformer pluginTransformer;
37      private final String pluginDescriptorFileName;
38      private final DescriptorParserFactory descriptorParserFactory;
39  
40      private ServiceTracker moduleDescriptorFactoryTracker;
41  
42      public OsgiPluginFactory(String pluginDescriptorFileName, OsgiContainerManager osgi)
43      {
44          Validate.notNull(pluginDescriptorFileName, "Plugin descriptor is required");
45          Validate.notNull(osgi, "The OSGi container is required");
46  
47          pluginTransformer = new DefaultPluginTransformer();
48          this.osgi = osgi;
49          this.pluginDescriptorFileName = pluginDescriptorFileName;
50          this.descriptorParserFactory = new ComponentFilteringXmlDescriptorParserFactory();
51      }
52  
53      public String canCreate(PluginArtifact pluginArtifact) throws PluginParseException {
54          Validate.notNull(pluginArtifact, "The plugin artifact is required");
55  
56          String pluginKey = null;
57          InputStream descriptorStream = null;
58          try
59          {
60              descriptorStream = pluginArtifact.getResourceAsStream(pluginDescriptorFileName);
61  
62              if (descriptorStream != null)
63              {
64                  final DescriptorParser descriptorParser = descriptorParserFactory.getInstance(descriptorStream);
65                  if (descriptorParser.getPluginsVersion() == 2)
66                      pluginKey = descriptorParser.getKey();
67              }
68          } 
69          finally
70          {
71              IOUtils.closeQuietly(descriptorStream);
72          }
73          return pluginKey;
74      }
75  
76      public Plugin create(DeploymentUnit deploymentUnit, ModuleDescriptorFactory moduleDescriptorFactory) throws PluginParseException {
77          Validate.notNull(deploymentUnit, "The plugin deployment unit is required");
78          Validate.notNull(moduleDescriptorFactory, "The module descriptor factory is required");
79  
80          Plugin plugin = null;
81          InputStream pluginDescriptor = null;
82          PluginClassLoader loader = new PluginClassLoader(deploymentUnit.getPath());
83  
84          if (loader.getResource(pluginDescriptorFileName) == null)
85              throw new PluginParseException("No descriptor found in classloader for : " + deploymentUnit);
86  
87          try
88          {
89              Plugin osgiPlugin = createOsgiPlugin(deploymentUnit.getPath());
90              ModuleDescriptorFactory combinedFactory = getChainedModuleDescriptorFactory(moduleDescriptorFactory);
91              pluginDescriptor = loader.getResourceAsStream(pluginDescriptorFileName);
92              // 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
93              DescriptorParser parser = descriptorParserFactory.getInstance(pluginDescriptor);
94              plugin = parser.configurePlugin(combinedFactory, osgiPlugin);
95          }
96          finally
97          {
98              IOUtils.closeQuietly(pluginDescriptor);
99          }
100         return plugin;
101     }
102 
103     /**
104      * Get a chained module descriptor factory that includes any dynamically available descriptor factories
105      *
106      * @param originalFactory The factory provided by the host application
107      * @return The composite factory
108      */
109     private ModuleDescriptorFactory getChainedModuleDescriptorFactory(ModuleDescriptorFactory originalFactory)
110     {
111         // we really don't want two of these
112         synchronized(this)
113         {
114             if (moduleDescriptorFactoryTracker == null)
115                 moduleDescriptorFactoryTracker = osgi.getServiceTracker(ModuleDescriptorFactory.class.getName());
116         }
117 
118         // Really shouldn't be null, but could be in tests since we can't mock a service tracker :(
119         if (moduleDescriptorFactoryTracker != null)
120         {
121             Object[] serviceObjs = moduleDescriptorFactoryTracker.getServices();
122             ModuleDescriptorFactory[] dynamicFactories;
123             if (serviceObjs != null && serviceObjs.length > 0)
124             {
125                 dynamicFactories = new ModuleDescriptorFactory[serviceObjs.length + 1];
126                 System.arraycopy(serviceObjs, 0, dynamicFactories, 1, serviceObjs.length);
127             }
128             else
129             {
130                 dynamicFactories = new ModuleDescriptorFactory[1];
131             }
132             dynamicFactories[0] = originalFactory;
133 
134             return new ChainModuleDescriptorFactory(dynamicFactories);
135         }
136         else
137             return originalFactory;
138 
139 
140     }
141 
142     private Plugin createOsgiPlugin(File file)
143     {
144         try
145         {
146             File transformedFile = pluginTransformer.transform(file, osgi.getHostComponentRegistrations());
147             return new OsgiPlugin(osgi.installBundle(transformedFile));
148         } catch (OsgiContainerException e)
149         {
150             return reportUnloadablePlugin(file, e);
151         } catch (PluginTransformationException ex)
152         {
153             return reportUnloadablePlugin(file, ex);
154         }
155     }
156 
157     private Plugin reportUnloadablePlugin(File file, Exception e)
158     {
159         log.error("Unable to load plugin: "+file, e);
160 
161         UnloadablePlugin plugin = new UnloadablePlugin();
162         plugin.setErrorText("Unable to load plugin: "+e.getMessage());
163         return plugin;
164     }
165 }