1   package com.atlassian.plugin.osgi.factory;
2   
3   import com.atlassian.plugin.*;
4   import com.atlassian.plugin.hostcontainer.DefaultHostContainer;
5   import com.atlassian.plugin.event.PluginEventManager;
6   import com.atlassian.plugin.descriptors.ChainModuleDescriptorFactory;
7   import com.atlassian.plugin.factories.PluginFactory;
8   import com.atlassian.plugin.impl.UnloadablePlugin;
9   import com.atlassian.plugin.loaders.classloading.DeploymentUnit;
10  import com.atlassian.plugin.osgi.container.OsgiContainerManager;
11  import com.atlassian.plugin.osgi.container.OsgiPersistentCache;
12  import com.atlassian.plugin.osgi.factory.transform.DefaultPluginTransformer;
13  import com.atlassian.plugin.osgi.factory.transform.PluginTransformationException;
14  import com.atlassian.plugin.osgi.factory.transform.PluginTransformer;
15  import com.atlassian.plugin.osgi.factory.transform.model.SystemExports;
16  import com.atlassian.plugin.osgi.factory.descriptor.ComponentModuleDescriptor;
17  import com.atlassian.plugin.osgi.factory.descriptor.ModuleTypeModuleDescriptor;
18  import com.atlassian.plugin.osgi.factory.descriptor.ComponentImportModuleDescriptor;
19  import com.atlassian.plugin.parsers.DescriptorParser;
20  import com.atlassian.plugin.parsers.DescriptorParserFactory;
21  import org.apache.commons.io.IOUtils;
22  import org.apache.commons.lang.Validate;
23  import org.apache.commons.logging.Log;
24  import org.apache.commons.logging.LogFactory;
25  import org.osgi.util.tracker.ServiceTracker;
26  import org.osgi.framework.Constants;
27  import org.osgi.framework.Version;
28  
29  import java.io.File;
30  import java.io.InputStream;
31  import java.util.*;
32  
33  /**
34   * Plugin loader that starts an OSGi container and loads plugins into it, wrapped as OSGi bundles.
35   */
36  public class OsgiPluginFactory implements PluginFactory
37  {
38      private static final Log log = LogFactory.getLog(OsgiPluginFactory.class);
39  
40      private final OsgiContainerManager osgi;
41      private final String pluginDescriptorFileName;
42      private final DescriptorParserFactory descriptorParserFactory;
43      private final PluginEventManager pluginEventManager;
44      private final Set<String> applicationKeys;
45      private final OsgiPersistentCache persistentCache;
46      private final ModuleDescriptorFactory transformedDescriptorFactory = new DefaultModuleDescriptorFactory(new DefaultHostContainer())
47      {{
48              addModuleDescriptor("component", ComponentModuleDescriptor.class);
49              addModuleDescriptor("component-import", ComponentImportModuleDescriptor.class);
50              addModuleDescriptor("module-type", ModuleTypeModuleDescriptor.class);
51          }};
52  
53      private volatile PluginTransformer pluginTransformer;
54  
55      private ServiceTracker moduleDescriptorFactoryTracker;
56  
57      public OsgiPluginFactory(String pluginDescriptorFileName, String applicationKey, OsgiPersistentCache persistentCache, OsgiContainerManager osgi, PluginEventManager pluginEventManager)
58      {
59          this(pluginDescriptorFileName, new HashSet<String>(Arrays.asList(applicationKey)), persistentCache, osgi, pluginEventManager);
60      }
61  
62      public OsgiPluginFactory(String pluginDescriptorFileName, Set<String> applicationKeys, OsgiPersistentCache persistentCache, OsgiContainerManager osgi, PluginEventManager pluginEventManager)
63      {
64          Validate.notNull(pluginDescriptorFileName, "Plugin descriptor is required");
65          Validate.notNull(osgi, "The OSGi container is required");
66          Validate.notNull(applicationKeys, "The application keys are required");
67          Validate.notNull(persistentCache, "The osgi persistent cache is required");
68          Validate.notNull(persistentCache, "The plugin event manager is required");
69  
70          this.osgi = osgi;
71          this.pluginDescriptorFileName = pluginDescriptorFileName;
72          this.descriptorParserFactory = new OsgiPluginXmlDescriptorParserFactory();
73          this.pluginEventManager = pluginEventManager;
74          this.applicationKeys = applicationKeys;
75          this.persistentCache = persistentCache;
76      }
77  
78      private PluginTransformer getPluginTransformer()
79      {
80          if (pluginTransformer == null)
81          {
82              String exportString = (String) osgi.getBundles()[0].getHeaders()
83                      .get(Constants.EXPORT_PACKAGE);
84              SystemExports exports = new SystemExports(exportString);
85              pluginTransformer = new DefaultPluginTransformer(persistentCache, exports, applicationKeys, pluginDescriptorFileName);
86          }
87          return pluginTransformer;
88      }
89  
90      public String canCreate(PluginArtifact pluginArtifact) throws PluginParseException
91      {
92          Validate.notNull(pluginArtifact, "The plugin artifact is required");
93  
94          String pluginKey = null;
95          InputStream descriptorStream = null;
96          try
97          {
98              descriptorStream = pluginArtifact.getResourceAsStream(pluginDescriptorFileName);
99  
100             if (descriptorStream != null)
101             {
102                 final DescriptorParser descriptorParser = descriptorParserFactory.getInstance(descriptorStream, applicationKeys.toArray(new String[applicationKeys.size()]));
103                 if (descriptorParser.getPluginsVersion() == 2)
104                 {
105                     pluginKey = descriptorParser.getKey();
106                 }
107             }
108         }
109         finally
110         {
111             IOUtils.closeQuietly(descriptorStream);
112         }
113         return pluginKey;
114     }
115 
116     /**
117      * @deprecated Since 2.2.0, use {@link #create(PluginArtifact,ModuleDescriptorFactory)} instead
118      */
119     public Plugin create(DeploymentUnit deploymentUnit, ModuleDescriptorFactory moduleDescriptorFactory) throws PluginParseException
120     {
121         Validate.notNull(deploymentUnit, "The deployment unit is required");
122         return create(new JarPluginArtifact(deploymentUnit.getPath()), moduleDescriptorFactory);
123     }
124 
125     /**
126      * Deploys the plugin artifact
127      *
128      * @param pluginArtifact          the plugin artifact to deploy
129      * @param moduleDescriptorFactory The factory for plugin modules
130      * @return The instantiated and populated plugin
131      * @throws PluginParseException If the descriptor cannot be parsed
132      * @since 2.2.0
133      */
134     public Plugin create(PluginArtifact pluginArtifact, ModuleDescriptorFactory moduleDescriptorFactory) throws PluginParseException
135     {
136         Validate.notNull(pluginArtifact, "The plugin deployment unit is required");
137         Validate.notNull(moduleDescriptorFactory, "The module descriptor factory is required");
138 
139         Plugin plugin = null;
140         InputStream pluginDescriptor = null;
141         try
142         {
143             pluginDescriptor = pluginArtifact.getResourceAsStream(pluginDescriptorFileName);
144             if (pluginDescriptor == null)
145             {
146                 throw new PluginParseException("No descriptor found in classloader for : " + pluginArtifact);
147             }
148 
149             ModuleDescriptorFactory combinedFactory = getChainedModuleDescriptorFactory(moduleDescriptorFactory);
150             DescriptorParser parser = descriptorParserFactory.getInstance(pluginDescriptor, applicationKeys.toArray(new String[applicationKeys.size()]));
151 
152             Plugin osgiPlugin = new OsgiPlugin(parser.getKey(), osgi, createOsgiPluginJar(pluginArtifact), pluginEventManager);
153 
154             // Temporarily configure plugin until it can be properly installed
155             plugin = parser.configurePlugin(combinedFactory, osgiPlugin);
156         }
157         catch (PluginTransformationException ex)
158         {
159             return reportUnloadablePlugin(pluginArtifact.toFile(), ex);
160         }
161         finally
162         {
163             IOUtils.closeQuietly(pluginDescriptor);
164         }
165         return plugin;
166     }
167 
168     /**
169      * Get a chained module descriptor factory that includes any dynamically available descriptor factories
170      *
171      * @param originalFactory The factory provided by the host application
172      * @return The composite factory
173      */
174     private ModuleDescriptorFactory getChainedModuleDescriptorFactory(ModuleDescriptorFactory originalFactory)
175     {
176         // we really don't want two of these
177         synchronized (this)
178         {
179             if (moduleDescriptorFactoryTracker == null)
180             {
181                 moduleDescriptorFactoryTracker = osgi.getServiceTracker(ModuleDescriptorFactory.class.getName());
182             }
183         }
184 
185         // Really shouldn't be null, but could be in tests since we can't mock a service tracker :(
186         if (moduleDescriptorFactoryTracker != null)
187         {
188             List<ModuleDescriptorFactory> factories = new ArrayList<ModuleDescriptorFactory>();
189 
190             factories.add(transformedDescriptorFactory);
191             factories.add(originalFactory);
192             Object[] serviceObjs = moduleDescriptorFactoryTracker.getServices();
193 
194             // Add all the dynamic module descriptor factories registered as osgi services
195             if (serviceObjs != null)
196             {
197                 for (Object fac : serviceObjs)
198                 {
199                     factories.add((ModuleDescriptorFactory) fac);
200                 }
201             }
202 
203             // Catch all unknown descriptors as unrecognised
204             factories.add(new UnrecognisedModuleDescriptorFallbackFactory());
205 
206             return new ChainModuleDescriptorFactory(factories.toArray(new ModuleDescriptorFactory[]{}));
207         }
208         else
209         {
210             return originalFactory;
211         }
212 
213 
214     }
215 
216     private PluginArtifact createOsgiPluginJar(PluginArtifact pluginArtifact)
217     {
218         File transformedFile = getPluginTransformer().transform(pluginArtifact, osgi.getHostComponentRegistrations());
219         return new JarPluginArtifact(transformedFile);
220     }
221 
222     private Plugin reportUnloadablePlugin(File file, Exception e)
223     {
224         log.error("Unable to load plugin: " + file, e);
225 
226         UnloadablePlugin plugin = new UnloadablePlugin();
227         plugin.setErrorText("Unable to load plugin: " + e.getMessage());
228         return plugin;
229     }
230 }