View Javadoc

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