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 com.google.common.collect.Ranges;
16  import org.apache.commons.io.IOUtils;
17  import org.osgi.util.tracker.ServiceTracker;
18  import org.slf4j.Logger;
19  import org.slf4j.LoggerFactory;
20  
21  import java.io.File;
22  import java.io.InputStream;
23  import java.util.Set;
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 final OsgiContainerManager osgi;
42      private final String pluginDescriptorFileName;
43      private final PluginEventManager pluginEventManager;
44  
45      private final OsgiChainedModuleDescriptorFactoryCreator osgiChainedModuleDescriptorFactoryCreator;
46  
47      /**
48       * Constructor for implementations that want to override the DefaultPluginTransformer with a custom implementation
49       */
50      public RemotablePluginFactory(String pluginDescriptorFileName, Set<Application> applications, final OsgiContainerManager osgi, PluginEventManager pluginEventManager)
51      {
52          super(new OsgiPluginXmlDescriptorParserFactory(), applications);
53          this.pluginDescriptorFileName = checkNotNull(pluginDescriptorFileName, "Plugin descriptor is required");
54          this.osgi = checkNotNull(osgi, "The OSGi container is required");
55          this.pluginEventManager = checkNotNull(pluginEventManager, "The plugin event manager is required");
56          this.osgiChainedModuleDescriptorFactoryCreator = new OsgiChainedModuleDescriptorFactoryCreator(new OsgiChainedModuleDescriptorFactoryCreator.ServiceTrackerFactory()
57          {
58              public ServiceTracker create(String className)
59              {
60                  return osgi.getServiceTracker(className);
61              }
62          });
63      }
64  
65      @Override
66      protected InputStream getDescriptorInputStream(PluginArtifact pluginArtifact)
67      {
68          return pluginArtifact.getResourceAsStream(pluginDescriptorFileName);
69      }
70  
71      @Override
72      protected Predicate<Integer> isValidPluginsVersion()
73      {
74          return Ranges.singleton(Plugin.VERSION_3);
75      }
76  
77      /**
78       * Creates the plugin
79       *
80       * @param pluginArtifact the plugin artifact to deploy
81       * @param moduleDescriptorFactory The factory for plugin modules
82       * @return The instantiated and populated plugin
83       * @throws com.atlassian.plugin.PluginParseException If the descriptor cannot be parsed
84       * @throws IllegalArgumentException If the plugin descriptor isn't found, and the plugin key and bundle version aren't
85       * specified in the manifest
86       */
87      public Plugin create(PluginArtifact pluginArtifact, ModuleDescriptorFactory moduleDescriptorFactory) throws PluginParseException
88      {
89          checkNotNull(pluginArtifact, "The plugin deployment unit is required");
90          checkNotNull(moduleDescriptorFactory, "The module descriptor factory is required");
91  
92          Plugin plugin = null;
93          InputStream pluginDescriptor = null;
94          try
95          {
96              pluginDescriptor = pluginArtifact.getResourceAsStream(pluginDescriptorFileName);
97              if (pluginDescriptor != null)
98              {
99                  final ModuleDescriptorFactory combinedFactory = getChainedModuleDescriptorFactory(moduleDescriptorFactory, pluginArtifact);
100                 final DescriptorParser parser = descriptorParserFactory.getInstance(pluginDescriptor, applications);
101 
102                 final String pluginKey = parser.getKey();
103                 final Plugin osgiPlugin = new OsgiPlugin(pluginKey, osgi, pluginArtifact, pluginArtifact, pluginEventManager);
104 
105                 // Temporarily configure plugin until it can be properly installed
106                 plugin = parser.configurePlugin(combinedFactory, osgiPlugin);
107             }
108             else
109             {
110                 throw new PluginParseException("Attempt to create Remotable plugin without a plugin descriptor!");
111             }
112         }
113         catch (PluginTransformationException ex)
114         {
115             return reportUnloadablePlugin(pluginArtifact.toFile(), ex);
116         }
117         finally
118         {
119             IOUtils.closeQuietly(pluginDescriptor);
120         }
121         return plugin;
122     }
123 
124     /**
125      * Get a chained module descriptor factory that includes any dynamically available descriptor factories
126      *
127      * @param originalFactory The factory provided by the host application
128      * @param pluginArtifact
129      * @return The composite factory
130      */
131     private ModuleDescriptorFactory getChainedModuleDescriptorFactory(ModuleDescriptorFactory originalFactory, final PluginArtifact pluginArtifact)
132     {
133         return osgiChainedModuleDescriptorFactoryCreator.create(new OsgiChainedModuleDescriptorFactoryCreator.ResourceLocator()
134         {
135             public boolean doesResourceExist(String name)
136             {
137                 return pluginArtifact.doesResourceExist(name);
138             }
139         }, originalFactory);
140     }
141 
142     private Plugin reportUnloadablePlugin(File file, Exception e)
143     {
144         log.error("Unable to load plugin: " + file, e);
145 
146         UnloadablePlugin plugin = new UnloadablePlugin();
147         plugin.setErrorText("Unable to load plugin: " + e.getMessage());
148         return plugin;
149     }
150 }