View Javadoc

1   package com.atlassian.plugin.loaders;
2   
3   import com.atlassian.plugin.Plugin;
4   import com.atlassian.plugin.PluginArtifact;
5   import com.atlassian.plugin.PluginArtifactBackedPlugin;
6   import com.atlassian.plugin.event.PluginEventManager;
7   import com.atlassian.plugin.factories.PluginFactory;
8   import com.atlassian.plugin.impl.AbstractDelegatingPlugin;
9   import com.atlassian.plugin.loaders.classloading.Scanner;
10  import com.atlassian.plugin.util.FileUtils;
11  
12  import java.io.File;
13  import java.io.IOException;
14  import java.net.URL;
15  import java.util.ArrayList;
16  import java.util.List;
17  
18  /**
19   * Plugin loader that can find plugins via a single URL, and treats all plugins loaded from
20   * the directory as bundled plugins, meaning they can can be upgraded, but not deleted.
21   * <p>
22   * Depending on the URL:
23   * <ul>
24   *     <li>If it is a file:// url and represents a directory, all the files in that directory are scanned.</li>
25   *     <li>if it is a file:// url and represents a file with a <code>.list</code> suffix, each line in that files
26   *     is read as a path to a plugin jar.</li>
27   *     <li>Otherwise it assumes the URL is a zip and unzips plugins from it into a local directory,
28   *     and ensures that directory only contains plugins from that zip file.  It also</li>
29   * </ul>
30   *
31   */
32  public class BundledPluginLoader extends ScanningPluginLoader
33  {
34      public BundledPluginLoader(final URL zipUrl, final File pluginPath, final List<PluginFactory> pluginFactories, final PluginEventManager eventManager)
35      {
36          super(buildScanner(zipUrl, pluginPath), pluginFactories, eventManager);
37      }
38  
39      @Override
40      protected Plugin postProcess(final Plugin plugin)
41      {
42          if (plugin instanceof PluginArtifactBackedPlugin)
43          {
44              return new BundledPluginArtifactBackedPluginDelegate((PluginArtifactBackedPlugin)plugin);
45          }
46          return new BundledPluginDelegate(plugin);
47      }
48  
49      private static Scanner buildScanner(final URL url, final File pluginPath)
50      {
51          if (url == null)
52          {
53              throw new IllegalArgumentException("Bundled plugins url cannot be null");
54          }
55  
56          Scanner scanner = null;
57  
58          final File file = FileUtils.toFile(url);
59  
60          if (file != null)
61          {
62              if (file.isDirectory())
63              {
64                  // URL points directly to a directory of jars
65                  scanner = new DirectoryScanner(file);
66              }
67              else if (file.isFile() && file.getName().endsWith(".list"))
68              {
69                  // URL points to a file containg a list of jars
70                  final List<File> files = readListFile(file);
71                  scanner = new FileListScanner(files);
72              }
73          }
74  
75          if (scanner == null) {
76              // default: assume it is a zip
77              FileUtils.conditionallyExtractZipFile(url, pluginPath);
78              scanner = new DirectoryScanner(pluginPath);
79          }
80  
81          return scanner;
82      }
83  
84      private static List<File> readListFile(final File file)
85      {
86          try
87          {
88              final List<String> fnames = (List<String>) org.apache.commons.io.FileUtils.readLines(file);
89              final List<File> files = new ArrayList<File>();
90              for (String fname : fnames)
91              {
92                  files.add(new File(fname));
93              }
94              return files;
95          }
96          catch (IOException e)
97          {
98              throw new IllegalStateException("Unable to read list from " + file, e);
99          }
100     }
101 
102 
103     /**
104      * Delegate that overrides methods to enforce bundled plugin behavior
105      *
106      * @since 2.2.0
107      */
108     private static class BundledPluginDelegate extends AbstractDelegatingPlugin
109     {
110 
111         public BundledPluginDelegate(Plugin delegate)
112         {
113             super(delegate);
114         }
115 
116         @Override
117         public boolean isBundledPlugin()
118         {
119             return true;
120         }
121 
122         @Override
123         public boolean isDeleteable()
124         {
125             return false;
126         }
127     }
128 
129     /**
130      * Delegate that overrides methods to enforce bundled plugin behavior for {@link PluginArtifactBackedPlugin} implementors
131      * @since 2.9.3
132      */
133     private static class BundledPluginArtifactBackedPluginDelegate extends BundledPluginDelegate implements PluginArtifactBackedPlugin
134     {
135         private final PluginArtifactBackedPlugin delegate;
136 
137         private BundledPluginArtifactBackedPluginDelegate(final PluginArtifactBackedPlugin delegate)
138         {
139             super(delegate);
140             this.delegate = delegate;
141         }
142 
143         public PluginArtifact getPluginArtifact()
144         {
145             return delegate.getPluginArtifact();
146         }
147     }
148 }