View Javadoc
1   package com.atlassian.plugin.osgi;
2   
3   import com.atlassian.plugin.DefaultModuleDescriptorFactory;
4   import com.atlassian.plugin.ModuleDescriptorFactory;
5   import com.atlassian.plugin.PluginAccessor;
6   import com.atlassian.plugin.PluginController;
7   import com.atlassian.plugin.PluginRegistry;
8   import com.atlassian.plugin.SplitStartupPluginSystemLifecycle;
9   import com.atlassian.plugin.event.PluginEventManager;
10  import com.atlassian.plugin.event.impl.DefaultPluginEventManager;
11  import com.atlassian.plugin.factories.LegacyDynamicPluginFactory;
12  import com.atlassian.plugin.hostcontainer.SimpleConstructorHostContainer;
13  import com.atlassian.plugin.loaders.BundledPluginLoader;
14  import com.atlassian.plugin.loaders.DirectoryPluginLoader;
15  import com.atlassian.plugin.loaders.PluginLoader;
16  import com.atlassian.plugin.manager.DefaultPluginManager;
17  import com.atlassian.plugin.manager.EnabledModuleCachingPluginAccessor;
18  import com.atlassian.plugin.manager.PluginPersistentStateStore;
19  import com.atlassian.plugin.manager.PluginRegistryImpl;
20  import com.atlassian.plugin.manager.ProductPluginAccessor;
21  import com.atlassian.plugin.manager.store.MemoryPluginPersistentStateStore;
22  import com.atlassian.plugin.module.ClassPrefixModuleFactory;
23  import com.atlassian.plugin.module.ModuleFactory;
24  import com.atlassian.plugin.module.PrefixDelegatingModuleFactory;
25  import com.atlassian.plugin.osgi.container.OsgiPersistentCache;
26  import com.atlassian.plugin.osgi.container.PackageScannerConfiguration;
27  import com.atlassian.plugin.osgi.container.felix.FelixOsgiContainerManager;
28  import com.atlassian.plugin.osgi.container.impl.DefaultOsgiPersistentCache;
29  import com.atlassian.plugin.osgi.container.impl.DefaultPackageScannerConfiguration;
30  import com.atlassian.plugin.osgi.factory.OsgiBundleFactory;
31  import com.atlassian.plugin.osgi.factory.OsgiPluginFactory;
32  import com.atlassian.plugin.osgi.factory.RemotablePluginFactory;
33  import com.atlassian.plugin.osgi.hostcomponents.ComponentRegistrar;
34  import com.atlassian.plugin.osgi.hostcomponents.HostComponentProvider;
35  import com.atlassian.plugin.osgi.hostcomponents.InstanceBuilder;
36  import com.atlassian.plugin.osgi.module.BeanPrefixModuleFactory;
37  import com.atlassian.plugin.repositories.FilePluginInstaller;
38  import com.atlassian.plugin.test.PluginTestUtils;
39  import com.google.common.collect.ImmutableList;
40  import com.google.common.collect.ImmutableSet;
41  import org.apache.commons.io.FileUtils;
42  import org.apache.commons.io.IOUtils;
43  import org.junit.After;
44  import org.junit.Before;
45  import org.junit.runner.RunWith;
46  import org.junit.runners.Parameterized;
47  
48  import java.io.File;
49  import java.io.FileInputStream;
50  import java.io.FileOutputStream;
51  import java.io.IOException;
52  import java.io.InputStream;
53  import java.util.Arrays;
54  import java.util.Collection;
55  import java.util.Collections;
56  import java.util.HashMap;
57  import java.util.List;
58  import java.util.Map;
59  import java.util.zip.ZipEntry;
60  import java.util.zip.ZipOutputStream;
61  
62  /**
63   * Base for in-container unit tests
64   */
65  @RunWith(Parameterized.class)
66  public abstract class PluginInContainerTestBase {
67      protected FelixOsgiContainerManager osgiContainerManager;
68      protected File tmpDir;
69      protected File cacheDir;
70      protected File pluginsDir;
71      protected File frameworkBundlesDir;
72      protected ModuleDescriptorFactory moduleDescriptorFactory;
73      protected PluginController pluginController;
74      protected PluginAccessor pluginAccessor;
75      protected SplitStartupPluginSystemLifecycle pluginSystemLifecycle;
76      protected PluginEventManager pluginEventManager;
77      protected ModuleFactory moduleFactory;
78      protected SimpleConstructorHostContainer hostContainer;
79      protected PluginPersistentStateStore store;
80  
81      private interface PluginManagerProvider {
82          class InitialisedPluginManager {
83              final PluginController pluginController;
84              final PluginAccessor pluginAccessor;
85              final SplitStartupPluginSystemLifecycle pluginSystemLifecycle;
86              final PluginPersistentStateStore store;
87  
88              private InitialisedPluginManager(final PluginController pluginController,
89                                               final PluginAccessor pluginAccessor,
90                                               final SplitStartupPluginSystemLifecycle pluginSystemLifecycle,
91                                               PluginPersistentStateStore store) {
92                  this.pluginController = pluginController;
93                  this.pluginAccessor = pluginAccessor;
94                  this.pluginSystemLifecycle = pluginSystemLifecycle;
95                  this.store = store;
96              }
97          }
98  
99          InitialisedPluginManager provide(final ModuleDescriptorFactory moduleDescriptorFactory,
100                                          final List<PluginLoader> loader,
101                                          final PluginEventManager pluginEventManager,
102                                          final File pluginsDir);
103     }
104 
105     @Parameterized.Parameters(name = "{index}: {0}")
106     public static Collection<Object[]> data() {
107         return Arrays.asList(new Object[][]{
108                 {"DefaultPluginManager", new PluginManagerProvider() {
109                     @Override
110                     public InitialisedPluginManager provide(final ModuleDescriptorFactory moduleDescriptorFactory,
111                                                             final List<PluginLoader> loader,
112                                                             final PluginEventManager pluginEventManager,
113                                                             final File pluginsDir) {
114                         final PluginPersistentStateStore store = new MemoryPluginPersistentStateStore();
115                         DefaultPluginManager pluginManager = new DefaultPluginManager(store, loader, moduleDescriptorFactory, pluginEventManager);
116                         pluginManager.setPluginInstaller(new FilePluginInstaller(pluginsDir));
117                         return new InitialisedPluginManager(pluginManager, pluginManager, pluginManager, store);
118                     }
119                 }},
120                 {"EnabledModuleCachingPluginAccessor", new PluginManagerProvider() {
121                     @Override
122                     public InitialisedPluginManager provide(final ModuleDescriptorFactory moduleDescriptorFactory,
123                                                             final List<PluginLoader> loader,
124                                                             final PluginEventManager pluginEventManager,
125                                                             final File pluginsDir) {
126 
127                         final MemoryPluginPersistentStateStore store = new MemoryPluginPersistentStateStore();
128                         DefaultPluginManager pluginManager = new DefaultPluginManager(store, loader, moduleDescriptorFactory, pluginEventManager);
129                         pluginManager.setPluginInstaller(new FilePluginInstaller(pluginsDir));
130                         return new InitialisedPluginManager(
131                                 pluginManager,
132                                 new EnabledModuleCachingPluginAccessor(pluginManager, pluginEventManager, pluginManager),
133                                 pluginManager,
134                                 store);
135                     }
136                 }},
137                 {"ProductPluginAccessor", new PluginManagerProvider() {
138                     @Override
139                     public InitialisedPluginManager provide(final ModuleDescriptorFactory moduleDescriptorFactory,
140                                                             final List<PluginLoader> loader,
141                                                             final PluginEventManager pluginEventManager,
142                                                             final File pluginsDir) {
143                         PluginRegistry.ReadWrite pluginRegistry = new PluginRegistryImpl();
144                         PluginPersistentStateStore store = new MemoryPluginPersistentStateStore();
145 
146                         PluginAccessor pluginAccessor = new ProductPluginAccessor(pluginRegistry, store, moduleDescriptorFactory, pluginEventManager);
147 
148                         DefaultPluginManager pluginManager = DefaultPluginManager.newBuilder()
149                                 .withPluginRegistry(pluginRegistry)
150                                 .withStore(store)
151                                 .withPluginEventManager(pluginEventManager)
152                                 .withPluginLoaders(loader)
153                                 .withModuleDescriptorFactory(moduleDescriptorFactory)
154                                 .withPluginAccessor(pluginAccessor)
155                                 .build();
156 
157                         pluginManager.setPluginInstaller(new FilePluginInstaller(pluginsDir));
158 
159                         return new InitialisedPluginManager(pluginManager, pluginAccessor, pluginManager, store);
160                     }
161                 }},
162         });
163     }
164 
165     @Parameterized.Parameter(value = 0)
166     public String testDescription;
167 
168     @Parameterized.Parameter(value = 1)
169     public PluginManagerProvider pluginManagerProvider;
170 
171     @Before
172     public void setUp() throws Exception {
173         tmpDir = PluginTestUtils.createTempDirectory(this.getClass().getName() + testDescription);
174         cacheDir = new File(tmpDir, "cache");
175         cacheDir.mkdir();
176         pluginsDir = new File(tmpDir, "plugins");
177         pluginsDir.mkdir();
178         this.pluginEventManager = new DefaultPluginEventManager();
179         moduleFactory = new PrefixDelegatingModuleFactory(ImmutableSet.of(
180                 new ClassPrefixModuleFactory(hostContainer),
181                 new BeanPrefixModuleFactory()));
182         hostContainer = createHostContainer(new HashMap<>());
183 
184         // dirty hack for running inside IDE, as the current directory is the project root, not the module
185         if (new File("atlassian-plugins-osgi").exists()) {
186             frameworkBundlesDir = new File(new File("atlassian-plugins-osgi", "target"), "framework-bundles");
187         } else {
188             frameworkBundlesDir = new File("target", "framework-bundles");
189         }
190         if (!frameworkBundlesDir.exists()) {
191             throw new IllegalStateException(
192                     "frameworkBundlesDir does not exist '" + frameworkBundlesDir.getAbsolutePath() + "', " +
193                             "ensure that you have run a maven build up to process-resources");
194         }
195     }
196 
197     protected SimpleConstructorHostContainer createHostContainer(Map<Class<?>, Object> originalContext) {
198         Map<Class<?>, Object> context = new HashMap<>(originalContext);
199         context.put(ModuleFactory.class, moduleFactory);
200         return new SimpleConstructorHostContainer(context);
201     }
202 
203     @After
204     public void tearDown() throws Exception {
205         if (osgiContainerManager != null) {
206             osgiContainerManager.stop();
207             osgiContainerManager.clearExportCache(); // prevent export cache from being reused across tests
208         }
209         FileUtils.deleteDirectory(tmpDir);
210         osgiContainerManager = null;
211         tmpDir = null;
212         pluginsDir = null;
213         moduleDescriptorFactory = null;
214         pluginEventManager = null;
215         moduleFactory = null;
216         hostContainer = null;
217     }
218 
219     protected void initPluginManager() throws Exception {
220         initPluginManager(null, new DefaultModuleDescriptorFactory(hostContainer));
221     }
222 
223     protected void initPluginManager(final HostComponentProvider hostComponentProvider) throws Exception {
224         initPluginManager(hostComponentProvider, new DefaultModuleDescriptorFactory(hostContainer));
225     }
226 
227     protected void initPluginManager(final HostComponentProvider hostComponentProvider, final ModuleDescriptorFactory moduleDescriptorFactory, final String version)
228             throws Exception {
229         final PackageScannerConfiguration scannerConfig = buildScannerConfiguration(version);
230         HostComponentProvider requiredWrappingProvider = getWrappingHostComponentProvider(hostComponentProvider);
231         OsgiPersistentCache cache = new DefaultOsgiPersistentCache(cacheDir);
232         osgiContainerManager = new FelixOsgiContainerManager(frameworkBundlesDir, cache, scannerConfig, requiredWrappingProvider, pluginEventManager);
233 
234         final LegacyDynamicPluginFactory legacyFactory = new LegacyDynamicPluginFactory(PluginAccessor.Descriptor.FILENAME, tmpDir);
235         final OsgiPluginFactory osgiPluginDeployer = new OsgiPluginFactory(PluginAccessor.Descriptor.FILENAME, Collections.emptySet(), cache, osgiContainerManager, pluginEventManager);
236         final OsgiBundleFactory osgiBundleFactory = new OsgiBundleFactory(osgiContainerManager);
237         final RemotablePluginFactory remotablePluginFactory = new RemotablePluginFactory(PluginAccessor.Descriptor.FILENAME, Collections.emptySet(), osgiContainerManager, pluginEventManager);
238 
239         final DirectoryPluginLoader loader = new DirectoryPluginLoader(pluginsDir,
240                 ImmutableList.of(legacyFactory, osgiPluginDeployer, osgiBundleFactory, remotablePluginFactory),
241                 new DefaultPluginEventManager());
242 
243         initPluginManager(moduleDescriptorFactory, loader);
244     }
245 
246     protected void initPluginManager(final HostComponentProvider hostComponentProvider, final ModuleDescriptorFactory moduleDescriptorFactory)
247             throws Exception {
248         initPluginManager(hostComponentProvider, moduleDescriptorFactory, null);
249     }
250 
251     protected void initPluginManager(final ModuleDescriptorFactory moduleDescriptorFactory, final List<PluginLoader> loader)
252             throws Exception {
253         this.moduleDescriptorFactory = moduleDescriptorFactory;
254         PluginManagerProvider.InitialisedPluginManager initialisedPluginManager = pluginManagerProvider.provide(moduleDescriptorFactory, loader, pluginEventManager, pluginsDir);
255         pluginController = initialisedPluginManager.pluginController;
256         pluginAccessor = initialisedPluginManager.pluginAccessor;
257         pluginSystemLifecycle = initialisedPluginManager.pluginSystemLifecycle;
258         store = initialisedPluginManager.store;
259         pluginSystemLifecycle.init();
260     }
261 
262     protected void initPluginManager(final ModuleDescriptorFactory moduleDescriptorFactory, PluginLoader loader)
263             throws Exception {
264         initPluginManager(moduleDescriptorFactory, Collections.singletonList(loader));
265     }
266 
267     protected void initBundlingPluginManager(final ModuleDescriptorFactory moduleDescriptorFactory, File... bundledPluginJars) throws Exception {
268         this.moduleDescriptorFactory = moduleDescriptorFactory;
269         final PackageScannerConfiguration scannerConfig = buildScannerConfiguration("1.0");
270         HostComponentProvider requiredWrappingProvider = getWrappingHostComponentProvider(null);
271         OsgiPersistentCache cache = new DefaultOsgiPersistentCache(cacheDir);
272         osgiContainerManager = new FelixOsgiContainerManager(frameworkBundlesDir, cache, scannerConfig, requiredWrappingProvider, pluginEventManager);
273 
274         final OsgiPluginFactory osgiPluginDeployer = new OsgiPluginFactory(PluginAccessor.Descriptor.FILENAME, Collections.emptySet(), cache, osgiContainerManager, pluginEventManager);
275 
276         final DirectoryPluginLoader loader = new DirectoryPluginLoader(pluginsDir, Collections.singletonList(osgiPluginDeployer),
277                 new DefaultPluginEventManager());
278 
279         File zip = new File(bundledPluginJars[0].getParentFile(), "bundled-plugins.zip");
280         for (File bundledPluginJar : bundledPluginJars) {
281             ZipOutputStream stream = null;
282             InputStream in = null;
283             try {
284                 stream = new ZipOutputStream(new FileOutputStream(zip));
285                 in = new FileInputStream(bundledPluginJar);
286                 stream.putNextEntry(new ZipEntry(bundledPluginJar.getName()));
287                 IOUtils.copy(in, stream);
288                 stream.closeEntry();
289             } catch (IOException ex) {
290                 IOUtils.closeQuietly(in);
291                 IOUtils.closeQuietly(stream);
292             }
293         }
294         File bundledDir = new File(bundledPluginJars[0].getParentFile(), "bundled-plugins");
295         final BundledPluginLoader bundledLoader = new BundledPluginLoader(zip.toURI().toURL(), bundledDir, Collections.singletonList(osgiPluginDeployer),
296                 new DefaultPluginEventManager());
297 
298         initPluginManager(moduleDescriptorFactory, Arrays.asList(bundledLoader, loader));
299     }
300 
301     private HostComponentProvider getWrappingHostComponentProvider(final HostComponentProvider hostComponentProvider) {
302         HostComponentProvider requiredWrappingProvider = new HostComponentProvider() {
303             public void provide(final ComponentRegistrar registrar) {
304 
305                 if (hostComponentProvider != null) {
306                     hostComponentProvider.provide(new ComponentRegistrar() {
307                         public InstanceBuilder register(Class<?>... mainInterfaces) {
308                             if (!Arrays.asList(mainInterfaces).contains(PluginEventManager.class)) {
309                                 return registrar.register(mainInterfaces);
310                             }
311                             return null;
312                         }
313                     });
314                 }
315                 registrar.register(ModuleFactory.class).forInstance(moduleFactory);
316                 registrar.register(PluginEventManager.class).forInstance(pluginEventManager);
317                 registrar.register(PluginAccessor.class).forInstance(pluginAccessor);
318             }
319         };
320         return requiredWrappingProvider;
321     }
322 
323     private PackageScannerConfiguration buildScannerConfiguration(String version) {
324         final PackageScannerConfiguration scannerConfig = new DefaultPackageScannerConfiguration(version);
325         scannerConfig.getPackageIncludes().add("com.atlassian.plugin*");
326         scannerConfig.getPackageIncludes().add("javax.servlet*");
327         scannerConfig.getPackageIncludes().add("com_cenqua_clover");
328         scannerConfig.getPackageExcludes().add("com.atlassian.plugin.osgi.bridge*");
329         scannerConfig.getPackageExcludes().add("com.atlassian.plugin.osgi.bridge.external");
330         scannerConfig.getPackageExcludes().add("com.atlassian.plugin.osgi.spring.external");
331         scannerConfig.getPackageVersions().put("org.apache.commons.logging", "1.1.3");
332         return scannerConfig;
333     }
334 }