View Javadoc

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