View Javadoc
1   package com.atlassian.plugin.factories;
2   
3   import com.atlassian.plugin.Application;
4   import com.atlassian.plugin.JarPluginArtifact;
5   import com.atlassian.plugin.ModuleDescriptor;
6   import com.atlassian.plugin.ModuleDescriptorFactory;
7   import com.atlassian.plugin.Plugin;
8   import com.atlassian.plugin.PluginArtifact;
9   import com.atlassian.plugin.PluginParseException;
10  import com.atlassian.plugin.classloader.PluginClassLoader;
11  import com.atlassian.plugin.impl.DefaultDynamicPlugin;
12  import com.atlassian.plugin.loaders.classloading.DeploymentUnit;
13  import com.atlassian.plugin.parsers.DescriptorParser;
14  import com.atlassian.plugin.parsers.XmlDescriptorParserFactory;
15  import com.google.common.base.Predicate;
16  import com.google.common.collect.ImmutableSet;
17  import org.apache.commons.io.IOUtils;
18  import org.apache.commons.lang.StringUtils;
19  import org.dom4j.Element;
20  
21  import java.io.File;
22  import java.io.InputStream;
23  
24  import static com.google.common.base.Preconditions.checkNotNull;
25  import static com.google.common.base.Preconditions.checkState;
26  
27  /**
28   * Deploys version 1.0 plugins into the legacy custom classloader structure that gives each plugin its own classloader.
29   *
30   * @since 2.0.0
31   */
32  public final class LegacyDynamicPluginFactory extends AbstractPluginFactory {
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.<Application>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 new Predicate<Integer>() {
59              @Override
60              public boolean apply(final Integer input) {
61                  return input != null && input <= Plugin.VERSION_1;
62              }
63          };
64      }
65  
66      /**
67       * Deploys the plugin artifact
68       *
69       * @param pluginArtifact          the plugin artifact to deploy
70       * @param moduleDescriptorFactory The factory for plugin modules
71       * @return The instantiated and populated plugin
72       * @throws PluginParseException If the descriptor cannot be parsed
73       * @since 2.2.0
74       */
75      public Plugin create(PluginArtifact pluginArtifact, ModuleDescriptorFactory moduleDescriptorFactory) throws PluginParseException {
76          checkNotNull(pluginArtifact, "The deployment unit must not be null");
77          checkNotNull(moduleDescriptorFactory, "The module descriptor factory must not be null");
78  
79          final File file = pluginArtifact.toFile();
80          Plugin plugin = null;
81          InputStream pluginDescriptor = null;
82          PluginClassLoader loader = null;
83          try {
84              pluginDescriptor = pluginArtifact.getResourceAsStream(pluginDescriptorFileName);
85              if (pluginDescriptor == null) {
86                  throw new PluginParseException("No descriptor found in classloader for : " + file);
87              }
88  
89              // 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
90              DescriptorParser parser = descriptorParserFactory.getInstance(pluginDescriptor, ImmutableSet.<Application>of());
91              loader = new PluginClassLoader(file, Thread.currentThread().getContextClassLoader(), tempDirectory);
92              plugin = parser.configurePlugin(moduleDescriptorFactory, createPlugin(pluginArtifact, loader));
93          }
94          // Under normal conditions, the deployer would be closed when the plugins are undeployed. However,
95          // these are not normal conditions, so we need to make sure that we close them explicitly.
96          catch (PluginParseException e) {
97              if (loader != null) loader.close();
98              throw e;
99          } catch (RuntimeException e) {
100             if (loader != null) loader.close();
101             throw new PluginParseException(e);
102         } catch (Error e) {
103             if (loader != null) loader.close();
104             throw e;
105         } finally {
106             IOUtils.closeQuietly(pluginDescriptor);
107         }
108         return plugin;
109     }
110 
111     @Override
112     public ModuleDescriptor<?> createModule(final Plugin plugin, final Element module, final ModuleDescriptorFactory moduleDescriptorFactory) {
113         if (plugin instanceof DefaultDynamicPlugin) {
114             InputStream pluginDescriptor = null;
115             try {
116                 final PluginArtifact pluginArtifact = plugin.getPluginArtifact();
117                 pluginDescriptor = pluginArtifact.getResourceAsStream(pluginDescriptorFileName);
118                 final DescriptorParser parser = descriptorParserFactory.getInstance(pluginDescriptor, ImmutableSet.<Application>of());
119 
120                 return parser.addModule(moduleDescriptorFactory, plugin, module);
121             } finally {
122                 IOUtils.closeQuietly(pluginDescriptor);
123             }
124         }
125         return null;
126     }
127 
128     /**
129      * @deprecated Since 2.2.0, use {@link #createPlugin(PluginArtifact, PluginClassLoader)} instead
130      */
131     protected Plugin createPlugin(DeploymentUnit deploymentUnit, PluginClassLoader loader) {
132         return createPlugin(new JarPluginArtifact(deploymentUnit.getPath()), loader);
133     }
134 
135     /**
136      * Creates the plugin.  Override to use a different Plugin class
137      *
138      * @param pluginArtifact The plugin artifact
139      * @param loader         The plugin loader
140      * @return The plugin instance
141      * @since 2.2.0
142      */
143     protected Plugin createPlugin(PluginArtifact pluginArtifact, PluginClassLoader loader) {
144         return new DefaultDynamicPlugin(pluginArtifact, loader);
145     }
146 }