1   package com.atlassian.plugin.classloader;
2   
3   import com.atlassian.plugin.test.PluginTestUtils;
4   import com.opensymphony.util.BeanUtils;
5   import junit.framework.TestCase;
6   import org.apache.commons.io.IOUtils;
7   import org.apache.commons.io.FileUtils;
8   
9   import java.io.File;
10  import java.io.IOException;
11  import java.net.URL;
12  import java.util.List;
13  
14  /**
15   * To make this test pass in your IDE, make sure you run Maven to build the necessary plugins and copy
16   * them to the target directory ('mvn package') and when running the tests, set the 'project.version'
17   * system property to the current version in the POM. E.g. -Dproject.version=2.1.0-SNAPSHOT
18   */
19  public class TestPluginClassLoader extends TestCase
20  {
21      private PluginClassLoader pluginClassLoader;
22      private File tmpDir;
23  
24      protected void setUp() throws Exception
25      {
26          super.setUp();
27          tmpDir = new File("target/" + this.getClass().getName());
28          tmpDir.mkdirs();
29  
30          URL url = getClass().getClassLoader().getResource(PluginTestUtils.SIMPLE_TEST_JAR);
31          assertNotNull("Can't find test resource -- see class Javadoc, and " +
32              "be sure to set the 'project.version' system property!", url);
33          pluginClassLoader = new PluginClassLoader(new File(url.getFile()), getClass().getClassLoader(), tmpDir);
34      }
35  
36      protected void tearDown() throws Exception
37      {
38          FileUtils.deleteDirectory(tmpDir);
39          super.tearDown();
40      }
41  
42      public void testPluginClassLoaderFindsInnerJars() throws Exception
43      {
44          List innerJars = pluginClassLoader.getPluginInnerJars();
45          assertEquals(2, innerJars.size());
46      }
47  
48      public void testPluginClassLoaderLoadsResourceFromOuterJarFirst() throws Exception
49      {
50          URL resourceUrl = pluginClassLoader.getResource("testresource.txt");
51          assertNotNull(resourceUrl);
52          assertEquals("outerjar", IOUtils.toString(resourceUrl.openStream()));
53      }
54  
55      public void testPluginClassLoaderLoadsClassFromOuterJar() throws Exception
56      {
57          Class c = pluginClassLoader.loadClass("com.atlassian.plugin.simpletest.TestClassOne");
58          assertEquals("com.atlassian.plugin.simpletest", c.getPackage().getName());  // PLUG-27
59          assertEquals("com.atlassian.plugin.simpletest.TestClassOne", c.getName());
60      }
61  
62      public void testPluginClassLoaderHandlesDeletedExctractedInnerJars() throws Exception
63      {
64          assertNotNull(pluginClassLoader.getResource("innerresource.txt").openStream());
65          FileUtils.deleteDirectory(tmpDir);
66          assertTrue(tmpDir.mkdirs());
67          try
68          {
69              assertNotNull(pluginClassLoader.getResource("innerresource.txt").openStream());
70              fail("underlying extracted inner jar was deleted and should throw FileNotFoundException");
71          }
72          catch (IOException e)
73          {
74              // expected exception because we deleted the jar
75          }
76      }
77  
78      public void testPluginClassLoaderExtractsInnerJarsToSpecifiedDirectory() throws Exception
79      {
80          final List<File> files = pluginClassLoader.getPluginInnerJars();
81          for (File file : files)
82          {
83              assertEquals(tmpDir, file.getParentFile());
84          }
85      }
86  
87      public void testPluginClassLoaderDetectsMissingTempDirectory() throws Exception
88      {
89          try
90          {
91              new PluginClassLoader(null, getClass().getClassLoader(), new File("/doesnotexisthopefully"));
92              fail("should throw IllegalArgumentException when temp directory does not exist");
93          }
94          catch (IllegalArgumentException e)
95          {
96              // expected
97          }
98      }
99  
100     public void testPluginClassLoaderLoadsResourceFromInnerJarIfNotInOuterJar() throws Exception
101     {
102         final URL resourceUrl = pluginClassLoader.getResource("innerresource.txt");
103         assertNotNull(resourceUrl);
104         assertEquals("innerresource", IOUtils.toString(resourceUrl.openStream()));
105     }
106 
107     public void testPluginClassLoaderDoesNotSwallowClassesFromADifferentClassLoader() throws Exception
108     {
109         final Class c = Class.forName(getClass().getName(), true, pluginClassLoader);
110         assertEquals(getClass().getClassLoader(), c.getClassLoader());
111     }
112 
113     public void testPluginClassLoaderOverridesContainerClassesWithInnerJarClasses() throws Exception
114     {
115         Class mockVersionedClass =
116                 Class.forName("com.atlassian.plugin.mock.MockVersionedClass", true, pluginClassLoader);
117         Object instance = mockVersionedClass.newInstance();
118 
119         assertEquals("PluginClassLoader is searching the parent classloader for classes before inner JARs",
120             2, BeanUtils.getValue(instance, "version"));
121     }
122 
123     public void testPluginClassLoaderDoesNotLockTheJarsPermanently() throws Exception
124     {
125         //N.B This will probably never fail on a non Windows machine
126         String fileLoc = getClass().getClassLoader().getResource(PluginTestUtils.SIMPLE_TEST_JAR).getFile();
127         File original = new File(fileLoc);
128         File tmpFile = new File(fileLoc + ".tmp");
129         FileUtils.copyFile(original, tmpFile);
130 
131         PluginClassLoader pluginClassLoaderThatHasNotLockedFileYet = new
132                 PluginClassLoader(tmpFile, getClass().getClassLoader());
133         Class mockVersionedClass =
134                 Class.forName("com.atlassian.plugin.mock.MockVersionedClass", true, pluginClassLoader);
135         assertTrue(tmpFile.delete());
136     }
137 }