View Javadoc
1   package com.atlassian.plugin.factories;
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.PluginException;
9   import com.atlassian.plugin.PluginParseException;
10  import com.atlassian.plugin.impl.XmlDynamicPlugin;
11  import com.atlassian.plugin.parsers.DescriptorParser;
12  import com.atlassian.plugin.parsers.XmlDescriptorParserFactory;
13  import com.google.common.collect.Sets;
14  import org.apache.commons.io.IOUtils;
15  import org.dom4j.DocumentException;
16  import org.dom4j.Element;
17  import org.slf4j.Logger;
18  import org.slf4j.LoggerFactory;
19  import org.xml.sax.SAXException;
20  
21  import java.io.FileInputStream;
22  import java.io.IOException;
23  import java.io.InputStream;
24  import java.util.Set;
25  import java.util.function.Predicate;
26  
27  import static com.google.common.base.Preconditions.checkNotNull;
28  
29  /**
30   * Deploys plugins that consist of an XML descriptor file.
31   *
32   * @since 2.1.0
33   */
34  public final class XmlDynamicPluginFactory extends AbstractPluginFactory {
35  
36      private static final Logger log = LoggerFactory.getLogger(XmlDynamicPluginFactory.class);
37      private static final Predicate<Integer> ALWAYS_TRUE = input -> true;
38  
39      /**
40       * @param application The application key to use to choose modules
41       * @since 3.0
42       */
43      public XmlDynamicPluginFactory(final Application application) {
44          this(Sets.newHashSet(application));
45      }
46  
47      /**
48       * @param applications The application key to use to choose modules
49       * @since 3.0
50       */
51      public XmlDynamicPluginFactory(final Set<Application> applications) {
52          super(new XmlDescriptorParserFactory(), applications);
53      }
54  
55      @Override
56      protected InputStream getDescriptorInputStream(PluginArtifact pluginArtifact) {
57          return pluginArtifact.getInputStream();
58      }
59  
60      @Override
61      protected Predicate<Integer> isValidPluginsVersion() {
62          return ALWAYS_TRUE;
63      }
64  
65      @Override
66      public String canCreate(PluginArtifact pluginArtifact) throws PluginParseException {
67          try {
68              return super.canCreate(pluginArtifact);
69          } catch (PluginParseException e) {
70              Throwable cause = e.getCause();
71              if (cause instanceof DocumentException || cause instanceof SAXException) {
72                  log.debug("There was an error parsing the plugin descriptor for '{}'", pluginArtifact);
73                  log.debug("This is most probably because we parsed a jar, and the plugin is not an " +
74                          "XML dynamic plugin. See the exception below for confirmation:", e);
75                  return null;
76              }
77              throw e;
78          }
79      }
80  
81      /**
82       * Deploys the plugin artifact
83       *
84       * @param pluginArtifact          the plugin artifact to deploy
85       * @param moduleDescriptorFactory The factory for plugin modules
86       * @return The instantiated and populated plugin
87       * @throws PluginParseException If the descriptor cannot be parsed
88       * @since 2.2.0
89       */
90      public Plugin create(final PluginArtifact pluginArtifact, final ModuleDescriptorFactory moduleDescriptorFactory) throws PluginParseException {
91          checkNotNull(pluginArtifact, "The plugin artifact must not be null");
92          checkNotNull(moduleDescriptorFactory, "The module descriptor factory must not be null");
93  
94          InputStream pluginDescriptor = null;
95          try {
96              pluginDescriptor = new FileInputStream(pluginArtifact.toFile());
97              // 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
98              final DescriptorParser parser = descriptorParserFactory.getInstance(pluginDescriptor, applications);
99              return parser.configurePlugin(moduleDescriptorFactory, new XmlDynamicPlugin(pluginArtifact));
100         } catch (final RuntimeException | IOException e) {
101             throw new PluginParseException(e);
102         } finally {
103             IOUtils.closeQuietly(pluginDescriptor);
104         }
105     }
106 
107     @Override
108     public ModuleDescriptor<?> createModule(final Plugin plugin, final Element module, final ModuleDescriptorFactory moduleDescriptorFactory) {
109         if (plugin instanceof XmlDynamicPlugin) {
110             // It is possible to implement this, however this plugin type is not widely used and has no test coverage.
111             // If a use case emerges, we can revisit.
112             throw new PluginException("cannot create modules for an XmlDynamicPlugin");
113         }
114         return null;
115     }
116 }