View Javadoc

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