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