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