View Javadoc

1   package com.atlassian.plugin.main;
2   
3   import static com.atlassian.plugin.util.Assertions.isTrue;
4   import static com.atlassian.plugin.util.Assertions.notNull;
5   
6   import com.atlassian.plugin.DefaultModuleDescriptorFactory;
7   import com.atlassian.plugin.ModuleDescriptorFactory;
8   import com.atlassian.plugin.PluginAccessor;
9   import com.atlassian.plugin.PluginStateStore;
10  import com.atlassian.plugin.hostcontainer.DefaultHostContainer;
11  import com.atlassian.plugin.osgi.container.PackageScannerConfiguration;
12  import com.atlassian.plugin.osgi.hostcomponents.ComponentRegistrar;
13  import com.atlassian.plugin.osgi.hostcomponents.HostComponentProvider;
14  import com.atlassian.plugin.store.MemoryPluginStateStore;
15  
16  import java.io.File;
17  import java.io.IOException;
18  import java.net.URL;
19  import java.util.concurrent.TimeUnit;
20  
21  /**
22   * The builder for {@link PluginsConfiguration} instances that additionally performs validation and default creation.
23   * For a usage example, see the package javadocs.
24   * <p>
25   * Not thread-safe. Instances of this class should be thread and preferably method local.
26   * 
27   * @since 2.2
28   */
29  public class PluginsConfigurationBuilder
30  {
31      /**
32       * Static factory for creating a new builder.
33       * @return a new builder.
34       */
35      public static PluginsConfigurationBuilder pluginsConfiguration()
36      {
37          return new PluginsConfigurationBuilder();
38      }
39  
40      private PackageScannerConfiguration packageScannerConfiguration;
41      private HostComponentProvider hostComponentProvider;
42      private File frameworkBundlesDirectory;
43      private File bundleCacheDirectory;
44      private File pluginDirectory;
45      private URL bundledPluginUrl;
46      private File bundledPluginCacheDirectory;
47      private String pluginDescriptorFilename;
48      private ModuleDescriptorFactory moduleDescriptorFactory;
49      private PluginStateStore pluginStateStore;
50      private long hotDeployPollingPeriod;
51  
52      /**
53       * Sets the package scanner configuration instance that contains information about what packages to expose to plugins.
54       * 
55       * @param packageScannerConfiguration The configuration instance
56       * @return this
57       */
58      public PluginsConfigurationBuilder packageScannerConfiguration(final PackageScannerConfiguration packageScannerConfiguration)
59      {
60          this.packageScannerConfiguration = notNull("packageScannerConfiguration", packageScannerConfiguration);
61          return this;
62      }
63  
64      /**
65       * Sets the host component provider instance, used for registering application services as OSGi services so that
66       * they can be automatically available to plugins
67       *
68       * @param hostComponentProvider The host component provider implementation
69       * @return this
70       */
71      public PluginsConfigurationBuilder hostComponentProvider(final HostComponentProvider hostComponentProvider)
72      {
73          this.hostComponentProvider = notNull("hostComponentProvider", hostComponentProvider);
74          return this;
75      }
76  
77      /**
78       * Sets caching directory to extract framework bundles into.  Doesn't have to be preserved and will be automatically
79       * cleaned out if it detects any modification.
80       *
81       * @param frameworkBundlesDirectory A directory that exists
82       * @return this
83       */
84      public PluginsConfigurationBuilder frameworkBundlesDirectory(final File frameworkBundlesDirectory)
85      {
86          this.frameworkBundlesDirectory = frameworkBundlesDirectory;
87          return this;
88      }
89  
90      /**
91       * Sets the directory to use for the OSGi framework's bundle cache.  Doesn't have to be preserved across restarts
92       * but shouldn't be externally modified at runtime.
93       *
94       * @param bundleCacheDirectory A directory that exists and is empty
95       * @return this
96       */
97      public PluginsConfigurationBuilder bundleCacheDirectory(final File bundleCacheDirectory)
98      {
99          this.bundleCacheDirectory = bundleCacheDirectory;
100         return this;
101     }
102 
103     /**
104      * Sets the directory that contains the plugins and will be used to store installed plugins.
105      *
106      * @param pluginDirectory A directory that exists
107      * @return this
108      */
109     public PluginsConfigurationBuilder pluginDirectory(final File pluginDirectory)
110     {
111         this.pluginDirectory = pluginDirectory;
112         return this;
113     }
114 
115     /**
116      * Sets the URL to a ZIP file containing plugins that are to be started before any user plugins but after
117      * framework bundles.  Must be set if {@link #bundledPluginCacheDirectory(java.io.File)} is set.
118      *
119      * @param bundledPluginUrl A URL to a ZIP of plugin JAR files
120      * @return this
121      */
122     public PluginsConfigurationBuilder bundledPluginUrl(final URL bundledPluginUrl)
123     {
124         this.bundledPluginUrl = bundledPluginUrl;
125         return this;
126     }
127 
128     /**
129      * Sets the directory to unzip bundled plugins into.  The directory will automatically be cleaned out if the
130      * framework detects any modification.  Must be set if {@link #bundledPluginUrl(java.net.URL)} is set.
131      *
132      * @param bundledPluginCacheDirectory A directory that exists
133      * @return this
134      */
135     public PluginsConfigurationBuilder bundledPluginCacheDirectory(final File bundledPluginCacheDirectory)
136     {
137         this.bundledPluginCacheDirectory = bundledPluginCacheDirectory;
138         return this;
139     }
140 
141     /**
142      * Sets the plugin descriptor file name to expect in a plugin JAR artifact
143      *
144      * @param pluginDescriptorFilename A valid file name
145      * @return this
146      */
147     public PluginsConfigurationBuilder pluginDescriptorFilename(final String pluginDescriptorFilename)
148     {
149         this.pluginDescriptorFilename = pluginDescriptorFilename;
150         return this;
151     }
152 
153     /**
154      * Sets the module descriptor factory that will be used to create instances of discovered module descriptors.
155      * Usually, the {@link DefaultModuleDescriptorFactory} is what is used, which takes class instances of module
156      * descriptors to instantiate.
157      *
158      * @param moduleDescriptorFactory A module descriptor factory instance
159      * @return this
160      */
161     public PluginsConfigurationBuilder moduleDescriptorFactory(final ModuleDescriptorFactory moduleDescriptorFactory)
162     {
163         this.moduleDescriptorFactory = moduleDescriptorFactory;
164         return this;
165     }
166 
167     /**
168      * Sets the plugin state store implementation used for persisting which plugins and modules are enabled or disabled
169      * across restarts.
170      *
171      * @param pluginStateStore The plugin state store implementation
172      * @return this
173      */
174     public PluginsConfigurationBuilder pluginStateStore(final PluginStateStore pluginStateStore)
175     {
176         this.pluginStateStore = pluginStateStore;
177         return this;
178     }
179 
180     /**
181      * Sets the polling frequency for scanning for new plugins
182      *
183      * @param hotDeployPollingFrequency The quantity of time periods
184      * @param timeUnit The units for the frequency
185      */
186     public PluginsConfigurationBuilder hotDeployPollingFrequency(final long hotDeployPollingFrequency, final TimeUnit timeUnit)
187     {
188         hotDeployPollingPeriod = hotDeployPollingFrequency * timeUnit.toMillis(hotDeployPollingFrequency);
189         return this;
190     }
191 
192     /**
193      * Builds a {@link com.atlassian.plugin.main.PluginsConfiguration} instance by processing the configuration that
194      * was previously set, validating the input, and setting any defaults where not explicitly specified.
195      *
196      * @return A valid {@link PluginsConfiguration} instance to pass to {@link AtlassianPlugins}
197      */
198     public PluginsConfiguration build()
199     {
200         notNull("Plugin directory must be defined", pluginDirectory);
201         isTrue("Plugin directory must exist", pluginDirectory.exists());
202 
203         if (packageScannerConfiguration == null)
204         {
205             packageScannerConfiguration = new PackageScannerConfigurationBuilder().build();
206         }
207         if (pluginDescriptorFilename == null)
208         {
209             pluginDescriptorFilename = PluginAccessor.Descriptor.FILENAME;
210         }
211 
212         if (hostComponentProvider == null)
213         {
214             hostComponentProvider = new HostComponentProvider()
215             {
216                 public void provide(final ComponentRegistrar registrar)
217                 {}
218             };
219         }
220 
221         if (pluginStateStore == null)
222         {
223             pluginStateStore = new MemoryPluginStateStore();
224         }
225 
226         if (moduleDescriptorFactory == null)
227         {
228             moduleDescriptorFactory = new DefaultModuleDescriptorFactory(new DefaultHostContainer());
229         }
230 
231         if (bundleCacheDirectory == null)
232         {
233             try
234             {
235                 bundleCacheDirectory = File.createTempFile("atlassian-plugins-bundle-cache", AtlassianPlugins.TEMP_DIRECTORY_SUFFIX);
236                 bundleCacheDirectory.delete();
237             }
238             catch (final IOException e)
239             {
240                 throw new IllegalStateException("Should be able to create tmp files", e);
241             }
242             bundleCacheDirectory.mkdir();
243         }
244         if (!bundleCacheDirectory.exists())
245         {
246             throw new IllegalArgumentException("Bundle cache directory should exist");
247         }
248 
249         if (frameworkBundlesDirectory == null)
250         {
251             try
252             {
253                 frameworkBundlesDirectory = File.createTempFile("atlassian-plugins-framework-bundles", AtlassianPlugins.TEMP_DIRECTORY_SUFFIX);
254                 frameworkBundlesDirectory.delete();
255             }
256             catch (final IOException e)
257             {
258                 throw new IllegalStateException("Should be able to create tmp files", e);
259             }
260             frameworkBundlesDirectory.mkdir();
261         }
262         isTrue("Framework bundles directory should exist", frameworkBundlesDirectory.exists());
263 
264         if ((bundledPluginCacheDirectory == null) ^ (bundledPluginUrl == null))
265         {
266             throw new IllegalArgumentException("Both bundled plugin cache directory and bundle plugin URL must be defined or not at all.");
267         }
268 
269         return new InternalPluginsConfiguration(this);
270     }
271 
272     private static class InternalPluginsConfiguration implements PluginsConfiguration
273     {
274         private final PackageScannerConfiguration packageScannerConfiguration;
275         private final HostComponentProvider hostComponentProvider;
276         private final File frameworkBundlesDirectory;
277         private final File bundleCacheDirectory;
278         private final File pluginDirectory;
279         private final URL bundledPluginUrl;
280         private final File bundledPluginCacheDirectory;
281         private final String pluginDescriptorFilename;
282         private final ModuleDescriptorFactory moduleDescriptorFactory;
283         private final PluginStateStore pluginStateStore;
284         private final long hotDeployPollingPeriod;
285 
286         InternalPluginsConfiguration(final PluginsConfigurationBuilder builder)
287         {
288             packageScannerConfiguration = builder.packageScannerConfiguration;
289             hostComponentProvider = builder.hostComponentProvider;
290             frameworkBundlesDirectory = builder.frameworkBundlesDirectory;
291             bundleCacheDirectory = builder.bundleCacheDirectory;
292             pluginDirectory = builder.pluginDirectory;
293             bundledPluginUrl = builder.bundledPluginUrl;
294             bundledPluginCacheDirectory = builder.bundledPluginCacheDirectory;
295             pluginDescriptorFilename = builder.pluginDescriptorFilename;
296             moduleDescriptorFactory = builder.moduleDescriptorFactory;
297             pluginStateStore = builder.pluginStateStore;
298             hotDeployPollingPeriod = builder.hotDeployPollingPeriod;
299         }
300 
301         public PackageScannerConfiguration getPackageScannerConfiguration()
302         {
303             return packageScannerConfiguration;
304         }
305 
306         public HostComponentProvider getHostComponentProvider()
307         {
308             return hostComponentProvider;
309         }
310 
311         public File getFrameworkBundlesDirectory()
312         {
313             return frameworkBundlesDirectory;
314         }
315 
316         public File getBundleCacheDirectory()
317         {
318             return bundleCacheDirectory;
319         }
320 
321         public String getPluginDescriptorFilename()
322         {
323             return pluginDescriptorFilename;
324         }
325 
326         public File getPluginDirectory()
327         {
328             return pluginDirectory;
329         }
330 
331         public URL getBundledPluginUrl()
332         {
333             return bundledPluginUrl;
334         }
335 
336         public File getBundledPluginCacheDirectory()
337         {
338             return bundledPluginCacheDirectory;
339         }
340 
341         public ModuleDescriptorFactory getModuleDescriptorFactory()
342         {
343             return moduleDescriptorFactory;
344         }
345 
346         public PluginStateStore getPluginStateStore()
347         {
348             return pluginStateStore;
349         }
350 
351         public long getHotDeployPollingPeriod()
352         {
353             return hotDeployPollingPeriod;
354         }
355     }
356 }