View Javadoc

1   package com.atlassian.plugin.loaders;
2   
3   import java.io.File;
4   import java.io.IOException;
5   import java.net.URISyntaxException;
6   import java.util.Collection;
7   import java.util.Collections;
8   import java.util.List;
9   
10  import com.atlassian.plugin.ModuleDescriptorFactory;
11  import com.atlassian.plugin.Plugin;
12  import com.atlassian.plugin.PluginArtifact;
13  import com.atlassian.plugin.PluginException;
14  import com.atlassian.plugin.event.PluginEventManager;
15  import com.atlassian.plugin.loaders.classloading.DeploymentUnit;
16  import com.atlassian.plugin.loaders.classloading.Scanner;
17  import com.atlassian.plugin.test.PluginJarBuilder;
18  import com.atlassian.plugin.factories.PluginFactory;
19  import com.atlassian.plugin.test.PluginTestUtils;
20  
21  import com.google.common.base.Function;
22  import com.google.common.collect.ImmutableList;
23  import com.google.common.collect.Iterables;
24  import com.google.common.collect.Lists;
25  
26  import org.apache.commons.io.FileUtils;
27  import org.junit.After;
28  import org.junit.Before;
29  import org.junit.Test;
30  import org.mockito.ArgumentCaptor;
31  
32  
33  import static org.hamcrest.Matchers.containsInAnyOrder;
34  import static org.hamcrest.Matchers.instanceOf;
35  import static org.junit.Assert.assertEquals;
36  import static org.junit.Assert.assertThat;
37  import static org.junit.Assert.assertTrue;
38  import static org.junit.Assert.fail;
39  import static org.mockito.Matchers.isA;
40  import static org.mockito.Matchers.same;
41  import static org.mockito.Mockito.mock;
42  import static org.mockito.Mockito.verify;
43  import static org.mockito.Mockito.when;
44  
45  
46  public class TestBundledPluginLoader
47  {
48      private File pluginDir;
49  
50      private static final String fooXml = "foo.xml";
51  
52      @Before
53      public void createTemporaryDirectory() throws IOException, URISyntaxException
54      {
55          pluginDir = PluginTestUtils.createTempDirectory(TestBundledPluginLoader.class);
56      }
57  
58      @After
59      public void deleteTemporaryDirectory() throws Exception
60      {
61          FileUtils.deleteDirectory(pluginDir);
62          pluginDir = null;
63      }
64  
65      @Test
66      public void loaderFromZipContainsExpectedFiles() throws IOException
67      {
68          File bundledZip = buildBundledZip();
69          final BundledPluginLoader loader = buildBundledPluginLoader(bundledZip, Collections.<PluginFactory>emptyList());
70          assertLoaderContains(loader, fooXml);
71      }
72  
73      @Test
74      public void loaderFromDirectoryContainsExpectedFiles() throws IOException
75      {
76          final File dir = PluginTestUtils.createTempDirectory(TestBundledPluginLoader.class);
77          FileUtils.writeStringToFile(new File(dir, "foo.txt"), "hello");
78  
79          final BundledPluginLoader loader = buildBundledPluginLoader(dir, Collections.<PluginFactory>emptyList());
80          assertLoaderContains(loader, "foo.txt");
81      }
82  
83      @Test
84      public void loaderFromFileListContainsExpectedFiles() throws IOException
85      {
86          final File dir = PluginTestUtils.createTempDirectory(TestBundledPluginLoader.class);
87          FileUtils.writeStringToFile(new File(dir, "foo.txt"), "hello");
88          FileUtils.writeStringToFile(new File(dir, "bar.txt"), "world");
89          File listFile = new File(dir, "bundled-plugins.list");
90          FileUtils.writeStringToFile(listFile, "foo.txt\nbar.txt");
91  
92          final BundledPluginLoader loader = buildBundledPluginLoader(listFile, Collections.<PluginFactory>emptyList());
93          assertLoaderContains(loader, "foo.txt", "bar.txt");
94      }
95  
96      @Test
97      public void loaderScannerDoesNotRemoveUnderlyingFiles() throws IOException
98      {
99          File bundledZip = buildBundledZip();
100 
101         final BundledPluginLoader loader = buildBundledPluginLoader(bundledZip, Collections.<PluginFactory>emptyList());
102         final Scanner scanner = loader.scanner;
103         assertLoaderContains(loader, fooXml);
104         final Collection<DeploymentUnit> units = scanner.getDeploymentUnits();
105         final int countBefore = units.size();
106         int found = 0;
107         for (DeploymentUnit unit : units)
108         {
109             final File file = unit.getPath();
110             if (fooXml.equals(file.getName()))
111             {
112                 found += 1;
113                 assertTrue(file.exists());
114                 scanner.remove(unit);
115                 // Yes, it should still exist
116                 assertTrue(file.exists());
117             }
118         }
119         // We should have found a foo.txt
120         assertEquals(1, found);
121         // We should not have actually removed it from the scanner
122         assertEquals(countBefore, scanner.getDeploymentUnits().size());
123     }
124 
125     @Test
126     public void loaderRemoveDoesUninstallPlugin() throws IOException
127     {
128         File bundledZip = buildBundledZip();
129         Plugin mockPlugin = mock(Plugin.class);
130         PluginFactory pluginFactory = mock(PluginFactory.class);
131         ModuleDescriptorFactory moduleDescriptorFactory = mock(ModuleDescriptorFactory.class);
132 
133         final String key = "plugin.key.foo";
134         ArgumentCaptor<PluginArtifact> pluginArtifact = ArgumentCaptor.forClass(PluginArtifact.class);
135 
136         when(mockPlugin.getKey()).thenReturn(key);
137         when(mockPlugin.isDeleteable()).thenReturn(true);
138         when(mockPlugin.isUninstallable()).thenReturn(true);
139         when(pluginFactory.canCreate(isA(PluginArtifact.class))).thenReturn(key);
140         when(pluginFactory.create(pluginArtifact.capture(), same(moduleDescriptorFactory))).thenReturn(mockPlugin);
141 
142         final BundledPluginLoader loader = buildBundledPluginLoader(bundledZip, ImmutableList.of(pluginFactory));
143         Collection<Plugin> plugins = Lists.newArrayList(loader.loadAllPlugins(moduleDescriptorFactory));
144         assertEquals(1, plugins.size());
145         // Note that the plugin we get back is actually wrapped, so we don't test equality
146         Plugin bundledPlugin = Iterables.getOnlyElement(plugins);
147         assertEquals(key, bundledPlugin.getKey());
148         assertTrue(bundledPlugin.isBundledPlugin());
149         assertTrue(bundledPlugin.isDeleteable());
150         verify(mockPlugin).isDeleteable();
151 
152         // Remove it, and check Plugin was uninstalled
153         loader.removePlugin(bundledPlugin);
154         verify(mockPlugin).isUninstallable();
155         verify(mockPlugin).uninstall();
156         // Check file on disk didn't get removed
157         assertTrue(pluginArtifact.getValue().toFile().exists());
158 
159         try
160         {
161             // There's no way to query a loader for its plugins, so we try to unload again and look
162             // for the exception. This currently throws because the Plugin is gone. This is a little
163             // brittle, because code evolution in the test subject means it might throw because the
164             // Plugin state changed, but since the Plugin is a mock, it's state doesn't change
165             // without us asking. So it's likely to be good. We could check the exception text, but
166             // i reckon this is even more brittle.
167             loader.removePlugin(bundledPlugin);
168             fail();
169         }
170         catch (PluginException pe)
171         {
172             // Yep, all good.
173         }
174     }
175 
176     @Test
177     public void pluginArtifactsAllowReference() throws IOException
178     {
179         // Use a list file for the bundle to make it easy to get a jar filename in there
180         final File dir = PluginTestUtils.createTempDirectory(TestBundledPluginLoader.class);
181         final File bundleList = new File(dir, "bundled-plugins.list");
182         FileUtils.writeStringToFile(bundleList, "some.jar\n");
183         final Plugin mockPlugin = mock(Plugin.class);
184         final PluginFactory pluginFactory = mock(PluginFactory.class);
185         final ModuleDescriptorFactory moduleDescriptorFactory = mock(ModuleDescriptorFactory.class);
186 
187         final String key = "plugin.key.foo";
188         final ArgumentCaptor<PluginArtifact> pluginArtifact = ArgumentCaptor.forClass(PluginArtifact.class);
189 
190         when(mockPlugin.getKey()).thenReturn(key);
191         when(pluginFactory.canCreate(isA(PluginArtifact.class))).thenReturn(key);
192         when(pluginFactory.create(pluginArtifact.capture(), same(moduleDescriptorFactory))).thenReturn(mockPlugin);
193 
194         final BundledPluginLoader loader = buildBundledPluginLoader(bundleList, ImmutableList.of(pluginFactory));
195         loader.loadAllPlugins(moduleDescriptorFactory);
196         assertThat(pluginArtifact.getValue(), instanceOf(PluginArtifact.AllowsReference.class));
197         assertTrue(((PluginArtifact.AllowsReference) pluginArtifact.getValue()).allowsReference());
198     }
199 
200     private File buildBundledZip() throws IOException
201     {
202         return new PluginJarBuilder("bundledPlugins")
203             .addResource(fooXml, "<foo/>")
204             .buildWithNoManifest();
205     }
206 
207     private BundledPluginLoader buildBundledPluginLoader(File bundle, List<PluginFactory> pluginFactories) throws IOException
208     {
209         return new BundledPluginLoader(bundle.toURI().toURL(), pluginDir, pluginFactories, mock(PluginEventManager.class));
210     }
211 
212     private void assertLoaderContains(final BundledPluginLoader loader, String ... expectedEntries)
213     {
214         final Collection<DeploymentUnit> scanned = loader.scanner.scan();
215         final Iterable<String> actualEntries = Iterables.transform(scanned, new Function<DeploymentUnit, String>()
216         {
217             @Override
218             public String apply(DeploymentUnit unit)
219             {
220                 return unit.getPath().getName();
221             }
222         });
223         assertThat(actualEntries, containsInAnyOrder(expectedEntries));
224     }
225 }