View Javadoc

1   package com.atlassian.plugin.main;
2   
3   import com.atlassian.plugin.DefaultModuleDescriptorFactory;
4   import com.atlassian.plugin.ModuleDescriptorFactory;
5   import com.atlassian.plugin.PluginAccessor;
6   import com.atlassian.plugin.hostcontainer.DefaultHostContainer;
7   import com.atlassian.plugin.manager.PluginPersistentStateStore;
8   import com.atlassian.plugin.manager.store.MemoryPluginPersistentStateStore;
9   import com.atlassian.plugin.osgi.container.OsgiPersistentCache;
10  import com.atlassian.plugin.osgi.container.PackageScannerConfiguration;
11  import com.atlassian.plugin.osgi.container.impl.DefaultOsgiPersistentCache;
12  import com.atlassian.plugin.osgi.hostcomponents.ComponentRegistrar;
13  import com.atlassian.plugin.osgi.hostcomponents.HostComponentProvider;
14  import static com.atlassian.plugin.util.Assertions.isTrue;
15  import static com.atlassian.plugin.util.Assertions.notNull;
16  
17  import java.io.File;
18  import java.io.IOException;
19  import java.net.URL;
20  import java.util.concurrent.TimeUnit;
21  
22  /**
23   * The builder for {@link PluginsConfiguration} instances that additionally performs validation and default creation.
24   * For a usage example, see the package javadocs.
25   * <p>
26   * Not thread-safe. Instances of this class should be thread and preferably method local.
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 osgiPersistentCache;
43      private File pluginDirectory;
44      private URL bundledPluginUrl;
45      private File bundledPluginCacheDirectory;
46      private String pluginDescriptorFilename;
47      private ModuleDescriptorFactory moduleDescriptorFactory;
48      private PluginPersistentStateStore pluginStateStore;
49      private long hotDeployPollingPeriod;
50      private boolean useLegacyDynamicPluginDeployer = false;
51      private String applicationKey;
52  
53      /**
54       * Sets the package scanner configuration instance that contains information about what packages to expose to plugins.
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       * @param hostComponentProvider The host component provider implementation
68       * @return this
69       */
70      public PluginsConfigurationBuilder hostComponentProvider(final HostComponentProvider hostComponentProvider)
71      {
72          this.hostComponentProvider = notNull("hostComponentProvider", hostComponentProvider);
73          return this;
74      }
75  
76      /**
77       * Sets the set of persistent caching directories the OSGi classes will use.  This includes the caching directory
78       * to extract framework bundles into, the container bundle cache, and the transformed plugin cache.
79       * @param osgiPersistentCache A directory that exists
80       * @return this
81       */
82      public PluginsConfigurationBuilder osgiPersistentCache(final File osgiPersistentCache)
83      {
84          this.osgiPersistentCache = osgiPersistentCache;
85          return this;
86      }
87  
88      /**
89       * Sets the directory that contains the plugins and will be used to store installed plugins.
90       * @param pluginDirectory A directory that exists
91       * @return this
92       */
93      public PluginsConfigurationBuilder pluginDirectory(final File pluginDirectory)
94      {
95          this.pluginDirectory = pluginDirectory;
96          return this;
97      }
98  
99      /**
100      * Sets the URL to a ZIP file containing plugins that are to be started before any user plugins but after
101      * framework bundles.  Must be set if {@link #bundledPluginCacheDirectory(java.io.File)} is set.
102      * @param bundledPluginUrl A URL to a ZIP of plugin JAR files
103      * @return this
104      */
105     public PluginsConfigurationBuilder bundledPluginUrl(final URL bundledPluginUrl)
106     {
107         this.bundledPluginUrl = bundledPluginUrl;
108         return this;
109     }
110 
111     /**
112      * Sets the directory to unzip bundled plugins into.  The directory will automatically be cleaned out if the
113      * framework detects any modification.  Must be set if {@link #bundledPluginUrl(java.net.URL)} is set.
114      * @param bundledPluginCacheDirectory A directory that exists
115      * @return this
116      */
117     public PluginsConfigurationBuilder bundledPluginCacheDirectory(final File bundledPluginCacheDirectory)
118     {
119         this.bundledPluginCacheDirectory = bundledPluginCacheDirectory;
120         return this;
121     }
122 
123     /**
124      * Sets the plugin descriptor file name to expect in a plugin JAR artifact
125      * @param pluginDescriptorFilename A valid file name
126      * @return this
127      */
128     public PluginsConfigurationBuilder pluginDescriptorFilename(final String pluginDescriptorFilename)
129     {
130         this.pluginDescriptorFilename = pluginDescriptorFilename;
131         return this;
132     }
133 
134     /**
135      * Sets the module descriptor factory that will be used to create instances of discovered module descriptors.
136      * Usually, the {@link DefaultModuleDescriptorFactory} is what is used, which takes class instances of module
137      * descriptors to instantiate.
138      * @param moduleDescriptorFactory A module descriptor factory instance
139      * @return this
140      */
141     public PluginsConfigurationBuilder moduleDescriptorFactory(final ModuleDescriptorFactory moduleDescriptorFactory)
142     {
143         this.moduleDescriptorFactory = moduleDescriptorFactory;
144         return this;
145     }
146 
147     /**
148      * Sets the plugin state store implementation used for persisting which plugins and modules are enabled or disabled
149      * across restarts.
150      * @param pluginStateStore The plugin state store implementation
151      * @return this
152      */
153     public PluginsConfigurationBuilder pluginStateStore(final PluginPersistentStateStore pluginStateStore)
154     {
155         this.pluginStateStore = pluginStateStore;
156         return this;
157     }
158 
159     /**
160      * Sets the polling frequency for scanning for new plugins
161      * @param hotDeployPollingFrequency The quantity of time periods
162      * @param timeUnit The units for the frequency
163      * @return {@code this}
164      */
165     public PluginsConfigurationBuilder hotDeployPollingFrequency(final long hotDeployPollingFrequency, final TimeUnit timeUnit)
166     {
167         hotDeployPollingPeriod = hotDeployPollingFrequency * timeUnit.toMillis(hotDeployPollingFrequency);
168         return this;
169     }
170 
171     /**
172      * Defines whether ther legacy plugin deployer should be used or not.
173      * @param useLegacyDynamicPluginDeployer {@code true} if the legacy plugin deployer should be used.
174      * @return {@code this}
175      */
176     public PluginsConfigurationBuilder useLegacyDynamicPluginDeployer(final boolean useLegacyDynamicPluginDeployer)
177     {
178         this.useLegacyDynamicPluginDeployer = useLegacyDynamicPluginDeployer;
179         return this;
180     }
181 
182     /**
183      * Sets the current application key to use when filtering modules in a plugin
184      * @param applicationKey the current application identifier
185      * @return {@code this}
186      */
187     public PluginsConfigurationBuilder applicationKey(final String applicationKey)
188     {
189         this.applicationKey = applicationKey;
190         return this;
191     }
192 
193     /**
194      * Builds a {@link com.atlassian.plugin.main.PluginsConfiguration} instance by processing the configuration that
195      * was previously set, validating the input, and setting any defaults where not explicitly specified.
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 
222         if (pluginStateStore == null)
223         {
224             pluginStateStore = new MemoryPluginPersistentStateStore();
225         }
226 
227         if (moduleDescriptorFactory == null)
228         {
229             moduleDescriptorFactory = new DefaultModuleDescriptorFactory(new DefaultHostContainer());
230         }
231 
232         if (osgiPersistentCache == null)
233         {
234             osgiPersistentCache = createTempDir("atlassian-plugins-osgi-cache");
235         }
236 
237         if (bundledPluginUrl != null && bundledPluginCacheDirectory == null)
238         {
239             throw new IllegalArgumentException("Bundled plugin cache directory MUST be defined when bundled plugin URL is defined");
240         }
241 
242         return new InternalPluginsConfiguration(this);
243     }
244 
245     private File createTempDir(final String prefix)
246     {
247         try
248         {
249             final File directory = File.createTempFile(prefix, AtlassianPlugins.TEMP_DIRECTORY_SUFFIX);
250             directory.delete();
251             directory.mkdir();
252             return directory;
253         }
254         catch (final IOException e)
255         {
256             throw new IllegalStateException("Was not able to create temp file with prefix <" + prefix + ">", e);
257         }
258     }
259 
260     private static class InternalPluginsConfiguration implements PluginsConfiguration
261     {
262         private final PackageScannerConfiguration packageScannerConfiguration;
263         private final HostComponentProvider hostComponentProvider;
264         private final OsgiPersistentCache osgiPersistentCache;
265         private final File pluginDirectory;
266         private final URL bundledPluginUrl;
267         private final File bundledPluginCacheDirectory;
268         private final String pluginDescriptorFilename;
269         private final ModuleDescriptorFactory moduleDescriptorFactory;
270         private final PluginPersistentStateStore pluginStateStore;
271         private final long hotDeployPollingPeriod;
272         private final boolean useLegacyDynamicPluginDeployer;
273         private final String applicationKey;
274 
275         InternalPluginsConfiguration(final PluginsConfigurationBuilder builder)
276         {
277             packageScannerConfiguration = builder.packageScannerConfiguration;
278             hostComponentProvider = builder.hostComponentProvider;
279             osgiPersistentCache = new DefaultOsgiPersistentCache(builder.osgiPersistentCache, builder.packageScannerConfiguration.getCurrentHostVersion());
280             pluginDirectory = builder.pluginDirectory;
281             bundledPluginUrl = builder.bundledPluginUrl;
282             bundledPluginCacheDirectory = builder.bundledPluginCacheDirectory;
283             pluginDescriptorFilename = builder.pluginDescriptorFilename;
284             moduleDescriptorFactory = builder.moduleDescriptorFactory;
285             pluginStateStore = builder.pluginStateStore;
286             hotDeployPollingPeriod = builder.hotDeployPollingPeriod;
287             useLegacyDynamicPluginDeployer = builder.useLegacyDynamicPluginDeployer;
288             applicationKey = builder.applicationKey;
289         }
290 
291         public PackageScannerConfiguration getPackageScannerConfiguration()
292         {
293             return packageScannerConfiguration;
294         }
295 
296         public HostComponentProvider getHostComponentProvider()
297         {
298             return hostComponentProvider;
299         }
300 
301         public String getPluginDescriptorFilename()
302         {
303             return pluginDescriptorFilename;
304         }
305 
306         public File getPluginDirectory()
307         {
308             return pluginDirectory;
309         }
310 
311         public URL getBundledPluginUrl()
312         {
313             return bundledPluginUrl;
314         }
315 
316         public File getBundledPluginCacheDirectory()
317         {
318             return bundledPluginCacheDirectory;
319         }
320 
321         public ModuleDescriptorFactory getModuleDescriptorFactory()
322         {
323             return moduleDescriptorFactory;
324         }
325 
326         public PluginPersistentStateStore getPluginStateStore()
327         {
328             return pluginStateStore;
329         }
330 
331         public long getHotDeployPollingPeriod()
332         {
333             return hotDeployPollingPeriod;
334         }
335 
336         public boolean isUseLegacyDynamicPluginDeployer()
337         {
338             return useLegacyDynamicPluginDeployer;
339         }
340 
341         public String getApplicationKey()
342         {
343             return applicationKey;
344         }
345 
346         public OsgiPersistentCache getOsgiPersistentCache()
347         {
348             return osgiPersistentCache;
349         }
350     }
351 }