View Javadoc

1   package com.atlassian.plugin.osgi.factory;
2   
3   import com.atlassian.plugin.*;
4   import com.atlassian.plugin.event.PluginEventManager;
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      private final PluginEventManager pluginEventManager;
41  
42      private ServiceTracker moduleDescriptorFactoryTracker;
43  
44      public OsgiPluginFactory(String pluginDescriptorFileName, OsgiContainerManager osgi, PluginEventManager pluginEventManager)
45      {
46          Validate.notNull(pluginDescriptorFileName, "Plugin descriptor is required");
47          Validate.notNull(osgi, "The OSGi container is required");
48  
49          pluginTransformer = new DefaultPluginTransformer(pluginDescriptorFileName);
50          this.osgi = osgi;
51          this.pluginDescriptorFileName = pluginDescriptorFileName;
52          this.descriptorParserFactory = new OsgiPluginXmlDescriptorParserFactory();
53          this.pluginEventManager = pluginEventManager;
54      }
55  
56      public String canCreate(PluginArtifact pluginArtifact) throws PluginParseException {
57          Validate.notNull(pluginArtifact, "The plugin artifact is required");
58  
59          String pluginKey = null;
60          InputStream descriptorStream = null;
61          try
62          {
63              descriptorStream = pluginArtifact.getResourceAsStream(pluginDescriptorFileName);
64  
65              if (descriptorStream != null)
66              {
67                  final DescriptorParser descriptorParser = descriptorParserFactory.getInstance(descriptorStream);
68                  if (descriptorParser.getPluginsVersion() == 2)
69                      pluginKey = descriptorParser.getKey();
70              }
71          } 
72          finally
73          {
74              IOUtils.closeQuietly(descriptorStream);
75          }
76          return pluginKey;
77      }
78  
79      /**
80       * @deprecated Since 2.2.0, use {@link #create(PluginArtifact,ModuleDescriptorFactory)} instead
81       */
82      public Plugin create(DeploymentUnit deploymentUnit, ModuleDescriptorFactory moduleDescriptorFactory) throws PluginParseException
83      {
84          return create(new JarPluginArtifact(deploymentUnit.getPath()), moduleDescriptorFactory);
85      }
86  
87      /**
88       * Deploys the plugin artifact
89       * @param pluginArtifact the plugin artifact to deploy
90       * @param moduleDescriptorFactory The factory for plugin modules
91       * @return The instantiated and populated plugin
92       * @throws PluginParseException If the descriptor cannot be parsed
93       * @since 2.2.0
94       */
95      public Plugin create(PluginArtifact pluginArtifact, ModuleDescriptorFactory moduleDescriptorFactory) throws PluginParseException
96      {
97          Validate.notNull(pluginArtifact, "The plugin deployment unit is required");
98          Validate.notNull(moduleDescriptorFactory, "The module descriptor factory is required");
99  
100         Plugin plugin = null;
101         InputStream pluginDescriptor = null;
102         try
103         {
104             pluginDescriptor = pluginArtifact.getResourceAsStream(pluginDescriptorFileName);
105             if (pluginDescriptor == null)
106                 throw new PluginParseException("No descriptor found in classloader for : " + pluginArtifact);
107 
108             ModuleDescriptorFactory combinedFactory = getChainedModuleDescriptorFactory(moduleDescriptorFactory);
109             // 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
110             DescriptorParser parser = descriptorParserFactory.getInstance(pluginDescriptor);
111 
112             Bundle existingBundle = findBundle(parser.getKey(), parser.getPluginInformation().getVersion(), pluginArtifact.toFile());
113             Plugin osgiPlugin;
114             if (existingBundle != null)
115             {
116                 osgiPlugin = new OsgiPlugin(existingBundle, pluginEventManager);
117                 log.info("OSGi bundle "+parser.getKey()+" found already installed.");
118             }
119             else
120             {
121                 osgiPlugin = createOsgiPlugin(pluginArtifact);
122             }
123                 
124             plugin = parser.configurePlugin(combinedFactory, osgiPlugin);
125         }
126         finally
127         {
128             IOUtils.closeQuietly(pluginDescriptor);
129         }
130         return plugin;
131     }
132 
133     /**
134      * Get a chained module descriptor factory that includes any dynamically available descriptor factories
135      *
136      * @param originalFactory The factory provided by the host application
137      * @return The composite factory
138      */
139     private ModuleDescriptorFactory getChainedModuleDescriptorFactory(ModuleDescriptorFactory originalFactory)
140     {
141         // we really don't want two of these
142         synchronized(this)
143         {
144             if (moduleDescriptorFactoryTracker == null)
145                 moduleDescriptorFactoryTracker = osgi.getServiceTracker(ModuleDescriptorFactory.class.getName());
146         }
147 
148         // Really shouldn't be null, but could be in tests since we can't mock a service tracker :(
149         if (moduleDescriptorFactoryTracker != null)
150         {
151             List<ModuleDescriptorFactory> factories = new ArrayList<ModuleDescriptorFactory>();
152             Object[] serviceObjs = moduleDescriptorFactoryTracker.getServices();
153 
154             // Add all the dynamic module descriptor factories registered as osgi services
155             if (serviceObjs != null)
156             {
157                 for (Object fac : serviceObjs) factories.add((ModuleDescriptorFactory) fac);
158             }
159 
160             // Put the application factory first
161             factories.add(0, originalFactory);
162 
163             // Catch all unknown descriptors as unrecognised
164             factories.add(new UnrecognisedModuleDescriptorFallbackFactory());
165 
166             return new ChainModuleDescriptorFactory(factories.toArray(new ModuleDescriptorFactory[]{}));
167         }
168         else
169             return originalFactory;
170 
171 
172     }
173 
174     private Plugin createOsgiPlugin(PluginArtifact pluginArtifact)
175     {
176         try
177         {
178             File transformedFile = pluginTransformer.transform(pluginArtifact, osgi.getHostComponentRegistrations());
179             return new OsgiPlugin(osgi.installBundle(transformedFile), pluginEventManager);
180         } catch (OsgiContainerException e)
181         {
182             return reportUnloadablePlugin(pluginArtifact.toFile(), e);
183         } catch (PluginTransformationException ex)
184         {
185             return reportUnloadablePlugin(pluginArtifact.toFile(), ex);
186         }
187     }
188 
189     private Bundle findBundle(String key, String version, File pluginFile)
190     {
191         for (Bundle bundle : osgi.getBundles())
192         {
193             if (key.equals(bundle.getSymbolicName()) && version.equals(bundle.getHeaders().get(Constants.BUNDLE_VERSION))
194                     && pluginFile.lastModified() < bundle.getLastModified())
195             {
196                 return bundle;
197             }
198         }
199         return null;
200     }
201 
202     private Plugin reportUnloadablePlugin(File file, Exception e)
203     {
204         log.error("Unable to load plugin: "+file, e);
205 
206         UnloadablePlugin plugin = new UnloadablePlugin();
207         plugin.setErrorText("Unable to load plugin: "+e.getMessage());
208         return plugin;
209     }
210 }