View Javadoc

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