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