View Javadoc

1   package com.atlassian.plugin.osgi.factory;
2   
3   import com.atlassian.plugin.Application;
4   import com.atlassian.plugin.ModuleDescriptorFactory;
5   import com.atlassian.plugin.Plugin;
6   import com.atlassian.plugin.PluginArtifact;
7   import com.atlassian.plugin.PluginParseException;
8   import com.atlassian.plugin.event.PluginEventManager;
9   import com.atlassian.plugin.factories.AbstractPluginFactory;
10  import com.atlassian.plugin.impl.UnloadablePlugin;
11  import com.atlassian.plugin.osgi.container.OsgiContainerManager;
12  import com.atlassian.plugin.osgi.factory.transform.PluginTransformationException;
13  import com.atlassian.plugin.parsers.DescriptorParser;
14  import com.google.common.base.Predicate;
15  import org.apache.commons.io.IOUtils;
16  import org.apache.commons.lang.Validate;
17  import org.osgi.util.tracker.ServiceTracker;
18  
19  import java.io.File;
20  import java.io.InputStream;
21  import java.util.Set;
22  
23  import static com.google.common.base.Preconditions.checkNotNull;
24  
25  /**
26   * Plugin loader that starts an OSGi container and loads plugins into it, wrapped as OSGi bundles.  Supports
27   * <ul>
28   * <li>Dynamic loading of module descriptors via OSGi services</li>
29   * <li>Delayed enabling until the plugin container is active</li>
30   * <li>XML or Jar manifest configuration</li>
31   * </ul>
32   */
33  public final class RemotablePluginFactory extends AbstractPluginFactory
34  {
35      private final OsgiContainerManager osgi;
36      private final String pluginDescriptorFileName;
37      private final PluginEventManager pluginEventManager;
38  
39      private final OsgiChainedModuleDescriptorFactoryCreator osgiChainedModuleDescriptorFactoryCreator;
40  
41      /**
42       * Constructor for implementations that want to override the DefaultPluginTransformer with a custom implementation
43       */
44      public RemotablePluginFactory(String pluginDescriptorFileName, Set<Application> applications, final OsgiContainerManager osgi, PluginEventManager pluginEventManager)
45      {
46          super(new OsgiPluginXmlDescriptorParserFactory(), applications);
47          this.pluginDescriptorFileName = checkNotNull(pluginDescriptorFileName, "Plugin descriptor is required");
48          this.osgi = checkNotNull(osgi, "The OSGi container is required");
49          this.pluginEventManager = checkNotNull(pluginEventManager, "The plugin event manager is required");
50          this.osgiChainedModuleDescriptorFactoryCreator = new OsgiChainedModuleDescriptorFactoryCreator(new OsgiChainedModuleDescriptorFactoryCreator.ServiceTrackerFactory()
51          {
52              public ServiceTracker create(String className)
53              {
54                  return osgi.getServiceTracker(className);
55              }
56          });
57      }
58  
59  
60      @Override
61      protected InputStream getDescriptorInputStream(PluginArtifact pluginArtifact)
62      {
63          return pluginArtifact.getResourceAsStream(pluginDescriptorFileName);
64      }
65  
66      @Override
67      protected Predicate<Integer> isValidPluginsVersion()
68      {
69          return new Predicate<Integer>()
70          {
71  
72              @Override
73              public boolean apply(Integer version)
74              {
75                  return version == 3;
76              }
77          };
78      }
79  
80      /**
81       * Deploys the plugin artifact.  The artifact will only undergo transformation if a plugin descriptor can be found
82       * and the "Atlassian-Plugin-Key" value is not already defined in the manifest.
83       *
84       * @param pluginArtifact the plugin artifact to deploy
85       * @param moduleDescriptorFactory The factory for plugin modules
86       * @return The instantiated and populated plugin
87       * @throws com.atlassian.plugin.PluginParseException If the descriptor cannot be parsed
88       * @throws IllegalArgumentException If the plugin descriptor isn't found, and the plugin key and bundle version aren't
89       * specified in the manifest
90       * @since 2.2.0
91       */
92      public Plugin create(PluginArtifact pluginArtifact, ModuleDescriptorFactory moduleDescriptorFactory) throws PluginParseException
93      {
94          Validate.notNull(pluginArtifact, "The plugin deployment unit is required");
95          Validate.notNull(moduleDescriptorFactory, "The module descriptor factory is required");
96  
97          Plugin plugin = null;
98          InputStream pluginDescriptor = null;
99          try
100         {
101             pluginDescriptor = pluginArtifact.getResourceAsStream(pluginDescriptorFileName);
102             if (pluginDescriptor != null)
103             {
104                 final ModuleDescriptorFactory combinedFactory = getChainedModuleDescriptorFactory(moduleDescriptorFactory, pluginArtifact);
105                 final DescriptorParser parser = descriptorParserFactory.getInstance(pluginDescriptor, applications.toArray(new Application[applications.size()]));
106 
107                 final String pluginKey = parser.getKey();
108                 final Plugin osgiPlugin = new OsgiPlugin(pluginKey, osgi, pluginArtifact, pluginArtifact, pluginEventManager);
109 
110                 // Temporarily configure plugin until it can be properly installed
111                 plugin = parser.configurePlugin(combinedFactory, osgiPlugin);
112             }
113             else
114             {
115                 throw new PluginParseException("Attempt to create Remotable plugin without a plugin descriptor!");
116             }
117         }
118         catch (PluginTransformationException ex)
119         {
120             return reportUnloadablePlugin(pluginArtifact.toFile(), ex);
121         }
122         finally
123         {
124             IOUtils.closeQuietly(pluginDescriptor);
125         }
126         return plugin;
127     }
128 
129     /**
130      * Get a chained module descriptor factory that includes any dynamically available descriptor factories
131      *
132      * @param originalFactory The factory provided by the host application
133      * @param pluginArtifact
134      * @return The composite factory
135      */
136     private ModuleDescriptorFactory getChainedModuleDescriptorFactory(ModuleDescriptorFactory originalFactory, final PluginArtifact pluginArtifact)
137     {
138         return osgiChainedModuleDescriptorFactoryCreator.create(new OsgiChainedModuleDescriptorFactoryCreator.ResourceLocator()
139         {
140             public boolean doesResourceExist(String name)
141             {
142                 return pluginArtifact.doesResourceExist(name);
143             }
144         }, originalFactory);
145     }
146 
147     private Plugin reportUnloadablePlugin(File file, Exception e)
148     {
149         log.error("Unable to load plugin: " + file, e);
150 
151         UnloadablePlugin plugin = new UnloadablePlugin();
152         plugin.setErrorText("Unable to load plugin: " + e.getMessage());
153         return plugin;
154     }
155 }