1 package com.atlassian.plugin.classloader;
2
3 import com.atlassian.plugin.MockPlugin;
4 import com.atlassian.plugin.MockPluginAccessor;
5 import com.atlassian.plugin.Plugin;
6 import com.atlassian.plugin.PluginAccessor;
7 import com.mockobjects.dynamic.C;
8 import com.mockobjects.dynamic.Mock;
9 import org.junit.After;
10 import org.junit.Before;
11 import org.junit.Test;
12
13 import java.net.MalformedURLException;
14 import java.net.URL;
15 import java.util.Collection;
16 import java.util.Collections;
17 import java.util.LinkedList;
18
19 import static org.junit.Assert.assertEquals;
20 import static org.junit.Assert.assertFalse;
21 import static org.junit.Assert.assertNull;
22 import static org.junit.Assert.assertTrue;
23
24 public class TestPluginsClassLoader {
25
26 private static final String TEST_RESOURCE = "log4j.properties";
27
28 private PluginsClassLoader pluginsClassLoader;
29 private Mock mockPluginAccessor;
30 private Mock mockPlugin;
31 private static final String PLUGIN_KEY = "aPluginKey";
32 private static final String TEST_CLASS = "java.lang.String";
33
34 @Before
35 public void setUp() throws Exception {
36 mockPluginAccessor = new Mock(PluginAccessor.class);
37 pluginsClassLoader = new PluginsClassLoader((PluginAccessor) mockPluginAccessor.proxy());
38
39 mockPlugin = new Mock(Plugin.class);
40 }
41
42 @After
43 public void tearDown() {
44 mockPluginAccessor.verify();
45 mockPlugin.verify();
46
47 mockPluginAccessor = null;
48 pluginsClassLoader = null;
49 }
50
51 @Test
52 public void testFindResourceWhenIndexed() {
53 final StubClassLoader stubClassLoader = new StubClassLoader();
54 loadPluginResource(stubClassLoader);
55 assertTrue(stubClassLoader.getFindResourceNames().contains(TEST_RESOURCE));
56 stubClassLoader.clear();
57
58 mockPlugin.expectAndReturn("getKey", PLUGIN_KEY);
59 mockPluginAccessor.matchAndReturn("isPluginEnabled", C.args(C.eq(PLUGIN_KEY)), Boolean.TRUE);
60 mockPlugin.expectAndReturn("getClassLoader", stubClassLoader);
61 pluginsClassLoader.findResource(TEST_RESOURCE);
62
63 assertTrue(stubClassLoader.getFindResourceNames().contains(TEST_RESOURCE));
64 }
65
66 @Test
67 public void testFindResourceWhenNotIndexed() {
68 final StubClassLoader stubClassLoader = new StubClassLoader();
69 loadPluginResource(stubClassLoader);
70
71 assertTrue(stubClassLoader.getFindResourceNames().contains(TEST_RESOURCE));
72 }
73
74 @Test
75 public void testFindResourceWhenIndexedAndPluginDisabled() {
76 final StubClassLoader stubClassLoader = new StubClassLoader();
77 loadPluginResource(stubClassLoader);
78 assertTrue(stubClassLoader.getFindResourceNames().contains(TEST_RESOURCE));
79 stubClassLoader.clear();
80
81 mockPluginAccessor.expectAndReturn("getEnabledPlugins", Collections.emptyList());
82 mockPlugin.expectAndReturn("getKey", PLUGIN_KEY);
83 mockPluginAccessor.matchAndReturn("isPluginEnabled", C.args(C.eq(PLUGIN_KEY)), Boolean.FALSE);
84 pluginsClassLoader.findResource(TEST_RESOURCE);
85
86 assertFalse(stubClassLoader.getFindResourceNames().contains(TEST_RESOURCE));
87 }
88
89 @Test
90 public void testFindClassWhenIndexed() throws Exception {
91 final StubClassLoader stubClassLoader = new StubClassLoader();
92 loadPluginClass(stubClassLoader);
93 assertTrue(stubClassLoader.getFindClassNames().contains(TEST_CLASS));
94 stubClassLoader.clear();
95
96 mockPlugin.expectAndReturn("getKey", PLUGIN_KEY);
97 mockPluginAccessor.matchAndReturn("isPluginEnabled", C.args(C.eq(PLUGIN_KEY)), Boolean.TRUE);
98 mockPlugin.expectAndReturn("getClassLoader", stubClassLoader);
99 pluginsClassLoader.findClass(TEST_CLASS);
100
101 assertTrue(stubClassLoader.getFindClassNames().contains(TEST_CLASS));
102 }
103
104 @Test
105 public void testFindClassWhenNotIndexed() throws Exception {
106 final StubClassLoader stubClassLoader = new StubClassLoader();
107 loadPluginClass(stubClassLoader);
108
109 assertTrue(stubClassLoader.getFindClassNames().contains(TEST_CLASS));
110 }
111
112 @Test(expected = ClassNotFoundException.class)
113 public void testFindClassWhenIndexedAndPluginDisabled() throws Exception {
114 final StubClassLoader stubClassLoader = new StubClassLoader();
115 loadPluginClass(stubClassLoader);
116 assertTrue(stubClassLoader.getFindClassNames().contains(TEST_CLASS));
117 stubClassLoader.clear();
118
119 mockPluginAccessor.expectAndReturn("getEnabledPlugins", Collections.emptyList());
120 mockPlugin.expectAndReturn("getKey", PLUGIN_KEY);
121 mockPluginAccessor.matchAndReturn("isPluginEnabled", C.args(C.eq(PLUGIN_KEY)), Boolean.FALSE);
122
123 pluginsClassLoader.findClass(TEST_CLASS);
124 }
125
126 @Test
127 public void testGetPluginForClass() {
128 final MockPluginAccessor mockPluginAccessor = new MockPluginAccessor();
129 PluginsClassLoader pluginsClassLoader = new PluginsClassLoader(mockPluginAccessor);
130
131 MockClassLoader mockClassLoaderA = new MockClassLoader();
132 mockClassLoaderA.register("com.acme.Ant", String.class);
133 mockClassLoaderA.register("com.acme.Clash", String.class);
134 MockPlugin pluginA = new MockPlugin("A", mockClassLoaderA);
135 mockPluginAccessor.addPlugin(pluginA);
136
137 MockClassLoader mockClassLoaderB = new MockClassLoader();
138 mockClassLoaderB.register("com.acme.Bat", String.class);
139 mockClassLoaderB.register("com.acme.Clash", String.class);
140 MockPlugin pluginB = new MockPlugin("B", mockClassLoaderB);
141 mockPluginAccessor.addPlugin(pluginB);
142
143
144 assertNull(pluginsClassLoader.getPluginForClass("com.acme.Ant"));
145 assertNull(pluginsClassLoader.getPluginForClass("com.acme.Bat"));
146 assertNull(pluginsClassLoader.getPluginForClass("com.acme.Clash"));
147 assertNull(pluginsClassLoader.getPluginForClass("java.lang.String"));
148
149
150 pluginB.enable();
151 pluginsClassLoader.notifyPluginOrModuleEnabled();
152 assertNull(pluginsClassLoader.getPluginForClass("com.acme.Ant"));
153 assertEquals(pluginB, pluginsClassLoader.getPluginForClass("com.acme.Bat"));
154 assertEquals(pluginB, pluginsClassLoader.getPluginForClass("com.acme.Clash"));
155 assertNull(pluginsClassLoader.getPluginForClass("java.lang.String"));
156
157
158 pluginA.enable();
159 pluginsClassLoader.notifyPluginOrModuleEnabled();
160 assertEquals(pluginA, pluginsClassLoader.getPluginForClass("com.acme.Ant"));
161 assertEquals(pluginB, pluginsClassLoader.getPluginForClass("com.acme.Bat"));
162 assertEquals(pluginB, pluginsClassLoader.getPluginForClass("com.acme.Clash"));
163 assertNull(pluginsClassLoader.getPluginForClass("java.lang.String"));
164
165
166 pluginsClassLoader.notifyUninstallPlugin(pluginB);
167 assertEquals(pluginA, pluginsClassLoader.getPluginForClass("com.acme.Ant"));
168 assertEquals(pluginB, pluginsClassLoader.getPluginForClass("com.acme.Bat"));
169 assertEquals(pluginA, pluginsClassLoader.getPluginForClass("com.acme.Clash"));
170 assertNull(pluginsClassLoader.getPluginForClass("java.lang.String"));
171 }
172
173 private void loadPluginResource(ClassLoader stubClassLoader) {
174 mockPluginAccessor.expectAndReturn("getEnabledPlugins", Collections.singleton(mockPlugin.proxy()));
175 mockPlugin.expectAndReturn("getClassLoader", stubClassLoader);
176 pluginsClassLoader.findResource(TEST_RESOURCE);
177 }
178
179 private void loadPluginClass(ClassLoader stubClassLoader) throws ClassNotFoundException {
180 mockPluginAccessor.expectAndReturn("getEnabledPlugins", Collections.singleton(mockPlugin.proxy()));
181 mockPlugin.expectAndReturn("getClassLoader", stubClassLoader);
182 pluginsClassLoader.findClass(TEST_CLASS);
183 }
184
185 private static final class StubClassLoader extends AbstractClassLoader {
186 private final Collection<String> findResourceNames = new LinkedList<>();
187 private final Collection<String> findClassNames = new LinkedList<>();
188
189 Collection getFindClassNames() {
190 return findClassNames;
191 }
192
193 StubClassLoader() {
194 super(null);
195 }
196
197 protected URL findResource(String name) {
198 findResourceNames.add(name);
199 try {
200 return new URL("file://" + name);
201 } catch (MalformedURLException e) {
202
203 return null;
204 }
205 }
206
207
208
209
210
211 public Class loadClass(String name) {
212 return findClass(name);
213 }
214
215 protected Class findClass(String className) {
216 findClassNames.add(className);
217 return String.class;
218 }
219
220 Collection getFindResourceNames() {
221 return findResourceNames;
222 }
223
224 void clear() {
225 findResourceNames.clear();
226 findClassNames.clear();
227 }
228 }
229 }