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