View Javadoc
1   package com.atlassian.plugin.loaders;
2   
3   import com.atlassian.plugin.ModuleDescriptor;
4   import com.atlassian.plugin.ModuleDescriptorFactory;
5   import com.atlassian.plugin.Plugin;
6   import com.atlassian.plugin.PluginException;
7   import com.atlassian.plugin.PluginParseException;
8   import com.atlassian.plugin.impl.StaticPlugin;
9   import com.atlassian.plugin.impl.UnloadablePlugin;
10  import com.atlassian.plugin.impl.UnloadablePluginFactory;
11  import com.atlassian.plugin.parsers.DescriptorParser;
12  import com.atlassian.plugin.parsers.DescriptorParserFactory;
13  import com.atlassian.plugin.parsers.XmlDescriptorParserFactory;
14  import com.atlassian.plugin.util.ClassLoaderUtils;
15  import com.google.common.collect.ImmutableList;
16  import com.google.common.collect.ImmutableSet;
17  import org.dom4j.Element;
18  import org.slf4j.Logger;
19  import org.slf4j.LoggerFactory;
20  
21  import java.io.IOException;
22  import java.io.InputStream;
23  import java.net.URL;
24  import java.util.Collection;
25  import java.util.Collections;
26  
27  import static com.atlassian.plugin.parsers.XmlDescriptorParserUtils.addModule;
28  import static com.atlassian.plugin.util.Assertions.notNull;
29  
30  /**
31   * Loads a single plugin from the descriptor provided, which can either be an InputStream
32   * or a resource on the classpath. The classes used by the plugin must already be available
33   * on the classpath because this plugin loader does <b>not</b> load any classes.
34   *
35   * @see PluginLoader
36   * @see ClassPathPluginLoader
37   */
38  public class SinglePluginLoader implements PluginLoader {
39      protected Collection<Plugin> plugins;
40  
41      /**
42       * to load the Stream from the classpath.
43       */
44      private final String resource;
45  
46      /**
47       * to load the Stream directly.
48       */
49      private final URL url;
50  
51      private final DescriptorParserFactory descriptorParserFactory = new XmlDescriptorParserFactory();
52  
53      private static final Logger log = LoggerFactory.getLogger(SinglePluginLoader.class);
54  
55  
56      public SinglePluginLoader(final String resource) {
57          this.resource = notNull("resource", resource);
58          url = null;
59      }
60  
61      public SinglePluginLoader(final URL url) {
62          this.url = notNull("url", url);
63          resource = null;
64      }
65  
66      public Iterable<Plugin> loadAllPlugins(final ModuleDescriptorFactory moduleDescriptorFactory) {
67          if (plugins == null) {
68              Plugin plugin;
69              try {
70                  plugin = loadPlugin(moduleDescriptorFactory);
71              } catch (RuntimeException ex) {
72                  String id = getIdentifier();
73                  log.error("Error loading plugin or descriptor: " + id, ex);
74                  plugin = new UnloadablePlugin(id + ": " + ex);
75                  plugin.setKey(id);
76              }
77              plugins = Collections.singleton(plugin);
78          }
79          return ImmutableList.copyOf(plugins);
80      }
81  
82      public boolean supportsRemoval() {
83          return false;
84      }
85  
86      public boolean supportsAddition() {
87          return false;
88      }
89  
90      public Iterable<Plugin> loadFoundPlugins(final ModuleDescriptorFactory moduleDescriptorFactory) {
91          throw new UnsupportedOperationException("This PluginLoader does not support addition.");
92      }
93  
94      public void removePlugin(final Plugin plugin) throws PluginException {
95          throw new PluginException("This PluginLoader does not support removal.");
96      }
97  
98      @Override
99      public boolean isDynamicPluginLoader() {
100         return false;
101     }
102 
103     @Override
104     public ModuleDescriptor<?> createModule(final Plugin plugin, final Element module, final ModuleDescriptorFactory moduleDescriptorFactory) {
105         if (plugins.contains(plugin)) {
106             return addModule(moduleDescriptorFactory, plugin, module);
107         } else {
108             return null;
109         }
110     }
111 
112     protected Plugin loadPlugin(final ModuleDescriptorFactory moduleDescriptorFactory) throws PluginParseException {
113         final InputStream source = getSource();
114         if (source == null) {
115             throw new PluginParseException("Invalid resource or inputstream specified to load plugins from.");
116         }
117 
118         Plugin plugin;
119         try {
120             final DescriptorParser parser = descriptorParserFactory.getInstance(source, ImmutableSet.of());
121             plugin = parser.configurePlugin(moduleDescriptorFactory, getNewPlugin());
122             if (plugin.getPluginsVersion() == 2) {
123                 UnloadablePlugin unloadablePlugin = UnloadablePluginFactory.createUnloadablePlugin(plugin);
124                 final StringBuilder errorText = new StringBuilder("OSGi plugins cannot be deployed via the classpath, which is usually WEB-INF/lib.");
125                 if (resource != null) {
126                     errorText.append("\n Resource is: ").append(resource);
127                 }
128                 if (url != null) {
129                     errorText.append("\n URL is: ").append(url);
130                 }
131                 unloadablePlugin.setErrorText(errorText.toString());
132                 plugin = unloadablePlugin;
133             }
134         } catch (final PluginParseException e) {
135             throw new PluginParseException("Unable to load plugin resource: " + resource + " - " + e.getMessage(), e);
136         }
137 
138         return plugin;
139     }
140 
141     private String getIdentifier() {
142         if (resource != null) {
143             return resource;
144         }
145         if (url != null) {
146             return url.getPath();
147         }
148         return null;
149     }
150 
151     protected StaticPlugin getNewPlugin() {
152         return new StaticPlugin();
153     }
154 
155     protected InputStream getSource() {
156         if (resource != null) {
157             return ClassLoaderUtils.getResourceAsStream(resource, this.getClass());
158         }
159 
160         if (url != null) {
161             try {
162                 return url.openConnection().getInputStream();
163             } catch (IOException e) {
164                 throw new PluginParseException(e);
165             }
166         }
167 
168         throw new IllegalStateException("No defined method for getting an input stream.");
169     }
170 }