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