View Javadoc

1   package com.atlassian.plugin.loaders;
2   
3   import com.atlassian.plugin.AutowireCapablePlugin;
4   import com.atlassian.plugin.Plugin;
5   import com.atlassian.plugin.PluginArtifact;
6   import com.atlassian.plugin.PluginArtifactBackedPlugin;
7   import com.atlassian.plugin.event.PluginEventManager;
8   import com.atlassian.plugin.factories.PluginFactory;
9   import com.atlassian.plugin.impl.AbstractDelegatingPlugin;
10  import com.atlassian.plugin.loaders.classloading.Scanner;
11  import com.atlassian.plugin.module.ContainerAccessor;
12  import com.atlassian.plugin.module.ContainerManagedPlugin;
13  import org.apache.commons.io.FileUtils;
14  
15  import java.io.File;
16  import java.io.IOException;
17  import java.net.URL;
18  import java.util.ArrayList;
19  import java.util.List;
20  
21  /**
22   * Plugin loader that can find plugins via a single URL, and treats all plugins loaded from
23   * the directory as bundled plugins, meaning they can can be upgraded, but not deleted.
24   * <p>
25   * Depending on the URL:
26   * <ul>
27   *     <li>If it is a file:// url and represents a directory, all the files in that directory are scanned.</li>
28   *     <li>if it is a file:// url and represents a file with a <code>.list</code> suffix, each line in that files
29   *     is read as a path to a plugin jar.</li>
30   *     <li>Otherwise it assumes the URL is a zip and unzips plugins from it into a local directory,
31   *     and ensures that directory only contains plugins from that zip file.  It also</li>
32   * </ul>
33   *
34   */
35  public class BundledPluginLoader extends ScanningPluginLoader
36  {
37      public BundledPluginLoader(final URL zipUrl, final File pluginPath, final List<PluginFactory> pluginFactories, final PluginEventManager eventManager)
38      {
39          super(buildScanner(zipUrl, pluginPath), pluginFactories, eventManager);
40      }
41  
42      @Override
43      protected Plugin postProcess(final Plugin plugin)
44      {
45          if (plugin instanceof ContainerManagedPlugin)
46          {
47              return new BundledPluginContainerManagedPluginDelegate((ContainerManagedPlugin) plugin);
48          }
49          else if (plugin instanceof PluginArtifactBackedPlugin)
50          {
51              return new BundledPluginArtifactBackedPluginDelegate((PluginArtifactBackedPlugin)plugin);
52          }
53          return new BundledPluginDelegate(plugin);
54      }
55  
56      private static Scanner buildScanner(final URL url, final File pluginPath)
57      {
58          if (url == null)
59          {
60              throw new IllegalArgumentException("Bundled plugins url cannot be null");
61          }
62  
63          Scanner scanner = null;
64  
65          final File file = FileUtils.toFile(url);
66  
67          if (file != null)
68          {
69              if (file.isDirectory())
70              {
71                  // URL points directly to a directory of jars
72                  scanner = new DirectoryScanner(file);
73              }
74              else if (file.isFile() && file.getName().endsWith(".list"))
75              {
76                  // URL points to a file containg a list of jars
77                  final List<File> files = readListFile(file);
78                  scanner = new FileListScanner(files);
79              }
80          }
81  
82          if (scanner == null)
83          {
84              // default: assume it is a zip
85              com.atlassian.plugin.util.FileUtils.conditionallyExtractZipFile(url, pluginPath);
86              scanner = new DirectoryScanner(pluginPath);
87          }
88  
89          return scanner;
90      }
91  
92      private static List<File> readListFile(final File file)
93      {
94          try
95          {
96              final List<String> fnames = (List<String>) FileUtils.readLines(file);
97              final List<File> files = new ArrayList<File>();
98              for (String fname : fnames)
99              {
100                 files.add(new File(fname));
101             }
102             return files;
103         }
104         catch (IOException e)
105         {
106             throw new IllegalStateException("Unable to read list from " + file, e);
107         }
108     }
109 
110 
111     /**
112      * Delegate that overrides methods to enforce bundled plugin behavior
113      *
114      * @since 2.2.0
115      */
116     private static class BundledPluginDelegate extends AbstractDelegatingPlugin
117     {
118 
119         public BundledPluginDelegate(Plugin delegate)
120         {
121             super(delegate);
122         }
123 
124         @Override
125         public boolean isBundledPlugin()
126         {
127             return true;
128         }
129 
130         @Override
131         public boolean isDeleteable()
132         {
133             return false;
134         }
135     }
136 
137     /**
138      * Delegate that overrides methods to enforce bundled plugin behavior for {@link PluginArtifactBackedPlugin} implementors
139      * @since 2.9.3
140      */
141     private static class BundledPluginArtifactBackedPluginDelegate extends BundledPluginDelegate implements PluginArtifactBackedPlugin
142     {
143         private final PluginArtifactBackedPlugin delegate;
144 
145         private BundledPluginArtifactBackedPluginDelegate(final PluginArtifactBackedPlugin delegate)
146         {
147             super(delegate);
148             this.delegate = delegate;
149         }
150 
151         public PluginArtifact getPluginArtifact()
152         {
153             return delegate.getPluginArtifact();
154         }
155     }
156 
157     private static class BundledPluginContainerManagedPluginDelegate extends BundledPluginArtifactBackedPluginDelegate implements ContainerManagedPlugin,
158             AutowireCapablePlugin
159     {
160         private final ContainerManagedPlugin delegate;
161 
162         private BundledPluginContainerManagedPluginDelegate(ContainerManagedPlugin delegate)
163         {
164             super(delegate);
165             this.delegate = delegate;
166         }
167 
168         @Override
169         public ContainerAccessor getContainerAccessor()
170         {
171             return delegate.getContainerAccessor();
172         }
173 
174         @Override
175         public <T> T autowire(Class<T> clazz)
176         {
177             if (delegate instanceof AutowireCapablePlugin)
178             {
179                 return ((AutowireCapablePlugin)delegate).autowire(clazz);
180             }
181             else
182             {
183                 return delegate.getContainerAccessor().createBean(clazz);
184             }
185         }
186 
187         @Override
188         public <T> T autowire(Class<T> clazz, AutowireStrategy autowireStrategy)
189         {
190             if (delegate instanceof AutowireCapablePlugin)
191             {
192                 return ((AutowireCapablePlugin)delegate).autowire(clazz, autowireStrategy);
193             }
194             else
195             {
196                 return delegate.getContainerAccessor().createBean(clazz);
197             }
198         }
199 
200         @Override
201         public void autowire(Object instance)
202         {
203             if (delegate instanceof AutowireCapablePlugin)
204             {
205                 ((AutowireCapablePlugin)delegate).autowire(instance);
206             }
207             else
208             {
209                 delegate.getContainerAccessor().injectBean(instance);
210             }
211         }
212 
213         @Override
214         public void autowire(Object instance, AutowireStrategy autowireStrategy)
215         {
216             if (delegate instanceof AutowireCapablePlugin)
217             {
218                 ((AutowireCapablePlugin)delegate).autowire(instance, autowireStrategy);
219             }
220             else
221             {
222                 delegate.getContainerAccessor().injectBean(instance);
223             }
224         }
225     }
226 }