View Javadoc
1   package com.atlassian.plugin.factories;
2   
3   import com.atlassian.plugin.ModuleDescriptor;
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.classloader.PluginClassLoader;
9   import com.atlassian.plugin.impl.DefaultDynamicPlugin;
10  import com.atlassian.plugin.parsers.DescriptorParser;
11  import com.atlassian.plugin.parsers.XmlDescriptorParserFactory;
12  import com.google.common.collect.ImmutableSet;
13  import org.apache.commons.io.IOUtils;
14  import org.apache.commons.lang3.StringUtils;
15  import org.dom4j.Element;
16  
17  import java.io.File;
18  import java.io.InputStream;
19  import java.util.function.Predicate;
20  
21  import static com.google.common.base.Preconditions.checkNotNull;
22  import static com.google.common.base.Preconditions.checkState;
23  
24  /**
25   * Deploys version 1.0 plugins into the legacy custom classloader structure that gives each plugin its own classloader.
26   *
27   * @since 2.0.0
28   */
29  public final class LegacyDynamicPluginFactory extends AbstractPluginFactory {
30  
31      private static final Predicate<Integer> IS_PLUGIN_1 = input -> input != null && input <= Plugin.VERSION_1;
32  
33      private final String pluginDescriptorFileName;
34      private final File tempDirectory;
35  
36      public LegacyDynamicPluginFactory(String pluginDescriptorFileName) {
37          this(pluginDescriptorFileName, new File(System.getProperty("java.io.tmpdir")), new XmlDescriptorParserFactory());
38      }
39  
40      public LegacyDynamicPluginFactory(String pluginDescriptorFileName, File tempDirectory) {
41          this(pluginDescriptorFileName, tempDirectory, new XmlDescriptorParserFactory());
42      }
43  
44      public LegacyDynamicPluginFactory(String pluginDescriptorFileName, File tempDirectory, XmlDescriptorParserFactory xmlDescriptorParserFactory) {
45          super(xmlDescriptorParserFactory, ImmutableSet.of());
46          this.tempDirectory = checkNotNull(tempDirectory);
47          this.pluginDescriptorFileName = checkNotNull(pluginDescriptorFileName);
48          checkState(StringUtils.isNotBlank(pluginDescriptorFileName), "Plugin descriptor name cannot be null or blank");
49      }
50  
51      @Override
52      protected InputStream getDescriptorInputStream(PluginArtifact pluginArtifact) {
53          return pluginArtifact.getResourceAsStream(pluginDescriptorFileName);
54      }
55  
56      @Override
57      protected Predicate<Integer> isValidPluginsVersion() {
58          return IS_PLUGIN_1;
59      }
60  
61      /**
62       * Deploys the plugin artifact
63       *
64       * @param pluginArtifact          the plugin artifact to deploy
65       * @param moduleDescriptorFactory The factory for plugin modules
66       * @return The instantiated and populated plugin
67       * @throws PluginParseException If the descriptor cannot be parsed
68       * @since 2.2.0
69       */
70      public Plugin create(PluginArtifact pluginArtifact, ModuleDescriptorFactory moduleDescriptorFactory) throws PluginParseException {
71          checkNotNull(pluginArtifact, "The deployment unit must not be null");
72          checkNotNull(moduleDescriptorFactory, "The module descriptor factory must not be null");
73  
74          final File file = pluginArtifact.toFile();
75          Plugin plugin = null;
76          InputStream pluginDescriptor = null;
77          PluginClassLoader loader = null;
78          try {
79              pluginDescriptor = pluginArtifact.getResourceAsStream(pluginDescriptorFileName);
80              if (pluginDescriptor == null) {
81                  throw new PluginParseException("No descriptor found in classloader for : " + file);
82              }
83  
84              // The plugin we get back may not be the same (in the case of an UnloadablePlugin), so add what gets returned, rather than the original
85              DescriptorParser parser = descriptorParserFactory.getInstance(pluginDescriptor, ImmutableSet.of());
86              loader = new PluginClassLoader(file, Thread.currentThread().getContextClassLoader(), tempDirectory);
87              plugin = parser.configurePlugin(moduleDescriptorFactory, createPlugin(pluginArtifact, loader));
88          }
89          // Under normal conditions, the deployer would be closed when the plugins are undeployed. However,
90          // these are not normal conditions, so we need to make sure that we close them explicitly.
91          catch (PluginParseException e) {
92              if (loader != null) loader.close();
93              throw e;
94          } catch (RuntimeException e) {
95              if (loader != null) loader.close();
96              throw new PluginParseException(e);
97          } catch (Error e) {
98              if (loader != null) loader.close();
99              throw e;
100         } finally {
101             IOUtils.closeQuietly(pluginDescriptor);
102         }
103         return plugin;
104     }
105 
106     @Override
107     public ModuleDescriptor<?> createModule(final Plugin plugin, final Element module, final ModuleDescriptorFactory moduleDescriptorFactory) {
108         if (plugin instanceof DefaultDynamicPlugin) {
109             InputStream pluginDescriptor = null;
110             try {
111                 final PluginArtifact pluginArtifact = plugin.getPluginArtifact();
112                 pluginDescriptor = pluginArtifact.getResourceAsStream(pluginDescriptorFileName);
113                 final DescriptorParser parser = descriptorParserFactory.getInstance(pluginDescriptor, ImmutableSet.of());
114 
115                 return parser.addModule(moduleDescriptorFactory, plugin, module);
116             } finally {
117                 IOUtils.closeQuietly(pluginDescriptor);
118             }
119         }
120         return null;
121     }
122 
123     /**
124      * Creates the plugin. Override to use a different Plugin class
125      *
126      * @param pluginArtifact The plugin artifact
127      * @param loader         The plugin loader
128      * @return The plugin instance
129      * @since 2.2.0
130      */
131     protected Plugin createPlugin(PluginArtifact pluginArtifact, PluginClassLoader loader) {
132         return new DefaultDynamicPlugin(pluginArtifact, loader);
133     }
134 }