View Javadoc
1   package com.atlassian.plugin.loaders;
2   
3   import com.atlassian.plugin.ModuleDescriptor;
4   import com.atlassian.plugin.ModuleDescriptorFactory;
5   import com.atlassian.plugin.Plugin;
6   import com.atlassian.plugin.PluginArtifact;
7   import com.atlassian.plugin.PluginArtifactFactory;
8   import com.atlassian.plugin.PluginException;
9   import com.atlassian.plugin.PluginState;
10  import com.atlassian.plugin.event.PluginEventManager;
11  import com.atlassian.plugin.event.events.PluginFrameworkShutdownEvent;
12  import com.atlassian.plugin.factories.PluginFactory;
13  import com.atlassian.plugin.impl.AbstractDelegatingPlugin;
14  import com.atlassian.plugin.impl.UnloadablePlugin;
15  import com.atlassian.plugin.loaders.classloading.DeploymentUnit;
16  import com.atlassian.plugin.loaders.classloading.Scanner;
17  import com.google.common.collect.Iterables;
18  import org.dom4j.Element;
19  import org.junit.Before;
20  import org.junit.Rule;
21  import org.junit.Test;
22  import org.junit.rules.ExpectedException;
23  import org.junit.runner.RunWith;
24  import org.mockito.Mock;
25  import org.mockito.runners.MockitoJUnitRunner;
26  
27  import java.io.File;
28  import java.util.Arrays;
29  
30  import static org.hamcrest.MatcherAssert.assertThat;
31  import static org.hamcrest.Matchers.containsInAnyOrder;
32  import static org.hamcrest.Matchers.is;
33  import static org.hamcrest.Matchers.nullValue;
34  import static org.junit.Assert.assertEquals;
35  import static org.junit.Assert.assertNotNull;
36  import static org.junit.Assert.assertNotSame;
37  import static org.junit.Assert.assertSame;
38  import static org.junit.Assert.assertTrue;
39  import static org.junit.Assert.fail;
40  import static org.mockito.Matchers.any;
41  import static org.mockito.Mockito.mock;
42  import static org.mockito.Mockito.never;
43  import static org.mockito.Mockito.verify;
44  import static org.mockito.Mockito.when;
45  
46  @RunWith(MockitoJUnitRunner.class)
47  public class TestScanningPluginLoader {
48      private static final String PLUGIN_KEY = "plugin-key";
49  
50      @Rule
51      public ExpectedException expectedException = ExpectedException.none();
52  
53      @Mock
54      private PluginArtifactFactory pluginArtifactFactory;
55  
56      @Mock
57      private PluginArtifact pluginArtifact;
58  
59      @Mock
60      private PluginFactory pluginFactory;
61  
62      @Mock
63      private ModuleDescriptorFactory moduleDescriptorFactory;
64  
65      @Mock
66      private Plugin plugin;
67  
68      @Mock
69      private Scanner scanner;
70  
71      @Mock
72      private PluginEventManager pluginEventManager;
73  
74      @Mock
75      private Element module;
76  
77      private DeploymentUnit deploymentUnit;
78  
79      @Before
80      public void configureMocks() {
81          deploymentUnit = new DeploymentUnit(new File("foo.jar"));
82          when(plugin.getKey()).thenReturn(PLUGIN_KEY);
83          when(pluginArtifactFactory.create(deploymentUnit.getPath().toURI())).thenReturn(pluginArtifact);
84          when(pluginFactory.canCreate(pluginArtifact)).thenReturn("foo");
85          when(scanner.getDeploymentUnits()).thenReturn(Arrays.asList(deploymentUnit));
86      }
87  
88      @Test
89      public void loadAllPluginsLoadsPlugin() {
90          when(pluginFactory.create(pluginArtifact, moduleDescriptorFactory)).thenReturn(plugin);
91  
92          final ScanningPluginLoader loader = buildScanningPluginLoader();
93          final Iterable<Plugin> plugins = loader.loadAllPlugins(moduleDescriptorFactory);
94          assertThat(plugins, containsInAnyOrder(plugin));
95      }
96  
97      @Test
98      public void removeEnabledPluginFails() {
99          when(plugin.getPluginState()).thenReturn(PluginState.ENABLED);
100 
101         final ScanningPluginLoader loader = buildScanningPluginLoader();
102 
103         expectedException.expect(PluginException.class);
104         expectedException.expectMessage(PLUGIN_KEY);
105         loader.removePlugin(plugin);
106     }
107 
108     @Test
109     public void removeNotUninstallablePluginFails() {
110         when(plugin.isUninstallable()).thenReturn(false);
111 
112         final ScanningPluginLoader loader = buildScanningPluginLoader();
113 
114         expectedException.expect(PluginException.class);
115         expectedException.expectMessage(PLUGIN_KEY);
116         loader.removePlugin(plugin);
117     }
118 
119     @Test
120     public void removeDeleteablePluginDoesUninstallAndDelete() {
121         when(plugin.isUninstallable()).thenReturn(true);
122         when(plugin.isDeleteable()).thenReturn(true);
123         when(pluginFactory.create(pluginArtifact, moduleDescriptorFactory)).thenReturn(plugin);
124 
125         final ScanningPluginLoader loader = buildScanningPluginLoader();
126         final Iterable<Plugin> plugins = loader.loadAllPlugins(moduleDescriptorFactory);
127         assertThat(plugins, containsInAnyOrder(plugin));
128         loader.removePlugin(plugin);
129         verify(plugin).uninstall();
130         verify(scanner).remove(deploymentUnit);
131     }
132 
133     @Test
134     public void removeNotDeleteablePluginDoesUninstallButDoesntDelete() {
135         when(plugin.isUninstallable()).thenReturn(true);
136         when(plugin.isDeleteable()).thenReturn(false);
137         when(pluginFactory.create(pluginArtifact, moduleDescriptorFactory)).thenReturn(plugin);
138 
139         final ScanningPluginLoader loader = buildScanningPluginLoader();
140         final Iterable<Plugin> plugins = loader.loadAllPlugins(moduleDescriptorFactory);
141         assertThat(plugins, containsInAnyOrder(plugin));
142         loader.removePlugin(plugin);
143         verify(plugin).uninstall();
144         verify(scanner, never()).remove(any(DeploymentUnit.class));
145     }
146 
147     @Test
148     public void discardedPluginIsNotTracked() {
149         when(plugin.isUninstallable()).thenReturn(true);
150         when(pluginFactory.create(pluginArtifact, moduleDescriptorFactory)).thenReturn(plugin);
151 
152         final ScanningPluginLoader loader = buildScanningPluginLoader();
153         final Iterable<Plugin> plugins = loader.loadAllPlugins(moduleDescriptorFactory);
154         assertThat(plugins, containsInAnyOrder(plugin));
155         loader.discardPlugin(plugin);
156         try {
157             // Discarded, so removal should fail even though isUninstallable
158             loader.removePlugin(plugin);
159             fail();
160         } catch (final PluginException pe) {
161             // Expected
162         }
163         // Shutdown should not result in uninstall of discarded plugin
164         loader.onShutdown(mock(PluginFrameworkShutdownEvent.class));
165         verify(plugin, never()).uninstall();
166     }
167 
168     @Test
169     public void shutdownUninstallsUninstallablePlugin() {
170         when(plugin.isUninstallable()).thenReturn(true);
171         when(pluginFactory.create(pluginArtifact, moduleDescriptorFactory)).thenReturn(plugin);
172 
173         final ScanningPluginLoader loader = buildScanningPluginLoader();
174         loader.loadAllPlugins(moduleDescriptorFactory);
175         loader.onShutdown(mock(PluginFrameworkShutdownEvent.class));
176         verify(plugin).uninstall();
177     }
178 
179     @Test
180     public void shutdownDoesNotUninstallNotUninstallablePlugin() {
181         when(plugin.isUninstallable()).thenReturn(false);
182         when(pluginFactory.create(pluginArtifact, moduleDescriptorFactory)).thenReturn(plugin);
183 
184         final ScanningPluginLoader loader = buildScanningPluginLoader();
185         loader.loadAllPlugins(moduleDescriptorFactory);
186         loader.onShutdown(mock(PluginFrameworkShutdownEvent.class));
187         verify(plugin, never()).uninstall();
188     }
189 
190     @Test
191     public void factoryThrowingRuntimeExceptionYieldsUnloadablePlugin() {
192         factoryThrowingYieldsUnloadablePlugin(new IllegalArgumentException());
193     }
194 
195     @Test
196     public void factoryThrowingErrorYieldsUnloadablePlugin() {
197         factoryThrowingYieldsUnloadablePlugin(new NoClassDefFoundError());
198     }
199 
200     private void factoryThrowingYieldsUnloadablePlugin(final Throwable throwable) {
201         when(pluginFactory.create(pluginArtifact, moduleDescriptorFactory)).thenThrow(throwable);
202 
203         final ScanningPluginLoader loader = buildScanningPluginLoader();
204         final Iterable<Plugin> plugins = loader.loadAllPlugins(moduleDescriptorFactory);
205         assertNotNull(plugins);
206         assertEquals(1, Iterables.size(plugins));
207         assertTrue(Iterables.getOnlyElement(plugins) instanceof UnloadablePlugin);
208     }
209 
210     @Test
211     public void pluginLoaderCallsPostProcess() {
212         when(plugin.isUninstallable()).thenReturn(true);
213         when(pluginFactory.create(pluginArtifact, moduleDescriptorFactory)).thenReturn(plugin);
214 
215         final ScanningPluginLoader loader = new ScanningPluginLoader(
216                 scanner, Arrays.asList(pluginFactory), pluginArtifactFactory, pluginEventManager) {
217             @Override
218             protected Plugin postProcess(final Plugin plugin) {
219                 return new WrappedPlugin(plugin);
220             }
221         };
222         final Iterable<Plugin> allPlugins = loader.loadAllPlugins(moduleDescriptorFactory);
223         assertPluginsIsWrapperFor(allPlugins, plugin);
224 
225         final DeploymentUnit unitB = new DeploymentUnit(new File("bar.jar"));
226         final PluginArtifact pluginArtifactB = mock(PluginArtifact.class);
227         final Plugin pluginB = mock(Plugin.class);
228         when(scanner.scan()).thenReturn(Arrays.asList(unitB));
229         when(pluginArtifactFactory.create(unitB.getPath().toURI())).thenReturn(pluginArtifactB);
230         when(pluginFactory.canCreate(pluginArtifactB)).thenReturn("bar");
231         when(pluginFactory.create(pluginArtifactB, moduleDescriptorFactory)).thenReturn(pluginB);
232 
233         final Iterable<Plugin> foundPlugins = loader.loadFoundPlugins(moduleDescriptorFactory);
234         assertPluginsIsWrapperFor(foundPlugins, pluginB);
235     }
236 
237     @Test
238     public void createModule() {
239         final ModuleDescriptor moduleDescriptor = mock(ModuleDescriptor.class);
240 
241         final ScanningPluginLoader scanningPluginLoader = buildScanningPluginLoader();
242 
243         when(pluginFactory.createModule(plugin, module, moduleDescriptorFactory)).thenReturn(moduleDescriptor);
244 
245         assertThat(scanningPluginLoader.createModule(plugin, module, moduleDescriptorFactory), is(moduleDescriptor));
246     }
247 
248     @Test
249     public void createModuleNoFactory() {
250         final ScanningPluginLoader scanningPluginLoader = buildScanningPluginLoader();
251 
252         when(pluginFactory.createModule(plugin, module, moduleDescriptorFactory)).thenReturn(null);
253 
254         assertThat(scanningPluginLoader.createModule(plugin, module, moduleDescriptorFactory), nullValue());
255     }
256 
257     /**
258      * A wrapper class for the postProcess test.
259      *
260      * By using a wrapper here, we guarantee postProcess is called, because no one else
261      * could have an instance of this private class.
262      */
263     private class WrappedPlugin extends AbstractDelegatingPlugin {
264         public WrappedPlugin(final Plugin plugin) {
265             super(plugin);
266         }
267     }
268 
269     private void assertPluginsIsWrapperFor(final Iterable<Plugin> plugins, final Plugin originalPlugin) {
270         assertNotNull(plugins);
271         assertEquals(1, Iterables.size(plugins));
272         final Plugin loadedPlugin = Iterables.getOnlyElement(plugins);
273         assertNotSame(loadedPlugin, originalPlugin);
274         assertTrue(loadedPlugin instanceof WrappedPlugin);
275         final WrappedPlugin wrappedPlugin = (WrappedPlugin) loadedPlugin;
276         assertSame(wrappedPlugin.getDelegate(), originalPlugin);
277     }
278 
279     private ScanningPluginLoader buildScanningPluginLoader() {
280         return new ScanningPluginLoader(scanner, Arrays.asList(pluginFactory), pluginArtifactFactory, pluginEventManager);
281     }
282 }