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