View Javadoc
1   package it.com.atlassian.plugin.manager;
2   
3   import com.atlassian.plugin.DefaultModuleDescriptorFactory;
4   import com.atlassian.plugin.ModuleDescriptor;
5   import com.atlassian.plugin.PluginAccessor;
6   import com.atlassian.plugin.PluginException;
7   import com.atlassian.plugin.PluginInternal;
8   import com.atlassian.plugin.PluginParseException;
9   import com.atlassian.plugin.descriptors.AbstractModuleDescriptor;
10  import com.atlassian.plugin.descriptors.UnrecognisedModuleDescriptor;
11  import com.atlassian.plugin.event.PluginEventListener;
12  import com.atlassian.plugin.event.PluginEventManager;
13  import com.atlassian.plugin.event.events.PluginModuleDisabledEvent;
14  import com.atlassian.plugin.event.events.PluginModuleDisablingEvent;
15  import com.atlassian.plugin.event.events.PluginModuleEnabledEvent;
16  import com.atlassian.plugin.event.events.PluginModuleEnablingEvent;
17  import com.atlassian.plugin.event.impl.DefaultPluginEventManager;
18  import com.atlassian.plugin.factories.LegacyDynamicPluginFactory;
19  import com.atlassian.plugin.factories.PluginFactory;
20  import com.atlassian.plugin.hostcontainer.DefaultHostContainer;
21  import com.atlassian.plugin.loaders.ClassPathPluginLoader;
22  import com.atlassian.plugin.loaders.DirectoryPluginLoader;
23  import com.atlassian.plugin.loaders.PluginLoader;
24  import com.atlassian.plugin.loaders.SinglePluginLoader;
25  import com.atlassian.plugin.manager.DefaultPluginManager;
26  import com.atlassian.plugin.manager.store.MemoryPluginPersistentStateStore;
27  import com.atlassian.plugin.module.ModuleFactory;
28  import com.atlassian.plugin.test.PluginJarBuilder;
29  import com.google.common.base.Function;
30  import com.google.common.collect.ImmutableList;
31  import org.dom4j.Element;
32  import org.dom4j.dom.DOMElement;
33  import org.hamcrest.Matchers;
34  import org.junit.After;
35  import org.junit.Before;
36  import org.junit.Rule;
37  import org.junit.Test;
38  import org.junit.rules.ExpectedException;
39  import org.junit.runner.RunWith;
40  import org.junit.runners.Parameterized;
41  
42  import java.io.File;
43  import java.nio.file.Files;
44  import java.util.ArrayList;
45  import java.util.Collection;
46  import java.util.Collections;
47  import java.util.List;
48  
49  import static org.hamcrest.Matchers.containsString;
50  import static org.hamcrest.Matchers.instanceOf;
51  import static org.hamcrest.Matchers.nullValue;
52  import static org.hamcrest.collection.IsEmptyCollection.empty;
53  import static org.hamcrest.collection.IsEmptyIterable.emptyIterable;
54  import static org.hamcrest.core.Is.is;
55  import static org.junit.Assert.assertThat;
56  import static org.mockito.Mockito.mock;
57  import static org.mockito.Mockito.when;
58  
59  /**
60   * Tests for addition, enumeration and removal of dynamic modules for a DefaultDynamicPlugin.
61   */
62  @RunWith(Parameterized.class)
63  public class TestDefaultPluginManagerDynamicModules {
64      protected static final String PLUGIN_KEY = "pluginKey";
65      protected static final String PLUGIN_NAME = "pluginName";
66      protected static final String PLUGIN_VERSION = "3.2.1";
67  
68      private static final String MODULE_TYPE = "moduleType";
69      private static final String MODULE_KEY = "moduleKey";
70      private static final String MODULE_CLASS = "moduleClass";
71  
72      @Rule
73      public final ExpectedException expectedException = ExpectedException.none();
74  
75      private PluginLoader pluginLoader;
76  
77      protected PluginEventManager pluginEventManager;
78  
79      private DefaultPluginManager defaultPluginManager;
80  
81      private PluginInternal plugin;
82  
83      private List<Class> moduleEvents;
84  
85      @Parameterized.Parameters(name = "{0}")
86      public static Collection<Object[]> data() {
87          return ImmutableList.of(
88                  new Object[]{"ClassPathPluginLoader", new Function<PluginEventManager, PluginLoader>() {
89                      @Override
90                      public PluginLoader apply(final PluginEventManager pluginEventManager) {
91                          return new ClassPathPluginLoader("test-plugin-dynamic-modules.xml");
92                      }
93                  }},
94                  new Object[]{"SinglePluginLoader", new Function<PluginEventManager, PluginLoader>() {
95                      @Override
96                      public PluginLoader apply(final PluginEventManager pluginEventManager) {
97                          return new SinglePluginLoader("test-plugin-dynamic-modules.xml");
98                      }
99                  }},
100                 new Object[]{"ScanningPluginLoader (DirectoryPluginLoader) <-> LegacyDynamicPluginFactory", new Function<PluginEventManager, PluginLoader>() {
101                     @Override
102                     public PluginLoader apply(final PluginEventManager pluginEventManager) {
103                         try {
104                             // fresh plugin directory for each test
105                             final File pluginDir = Files.createTempDirectory(null).toFile();
106 
107                             // first create a plugin to load
108                             new PluginJarBuilder(PLUGIN_NAME)
109                                     .addPluginInformation(PLUGIN_KEY, PLUGIN_NAME, PLUGIN_VERSION, 1)
110                                     .build(pluginDir);
111 
112                             // now create the plugin loader that will get the above plugin
113                             final PluginFactory pluginFactory = new LegacyDynamicPluginFactory(PluginAccessor.Descriptor.FILENAME);
114                             return new DirectoryPluginLoader(pluginDir, ImmutableList.of(pluginFactory), pluginEventManager);
115                         } catch (Exception e) {
116                             throw new RuntimeException(e);
117                         }
118                     }
119                 }}
120         );
121     }
122 
123     public TestDefaultPluginManagerDynamicModules(final String bleh, final Function<PluginEventManager, PluginLoader> function) {
124         // listen for plugin module events
125         pluginEventManager = new DefaultPluginEventManager();
126         pluginEventManager.register(this);
127 
128         this.pluginLoader = function.apply(pluginEventManager);
129     }
130 
131     @Before
132     public void setUp() throws Exception {
133         // dummy module factory
134         final DefaultModuleDescriptorFactory moduleDescriptorFactory = new DefaultModuleDescriptorFactory(new DefaultHostContainer());
135         moduleDescriptorFactory.addModuleDescriptor(MODULE_TYPE, DummyModuleDescriptor.class);
136 
137         // plugin manager that we are testing
138         defaultPluginManager = new DefaultPluginManager(new MemoryPluginPersistentStateStore(), ImmutableList.of(pluginLoader), moduleDescriptorFactory, pluginEventManager);
139         defaultPluginManager.init();
140 
141         // retrieve the loaded plugin
142         assertThat(defaultPluginManager.getPlugin(PLUGIN_KEY), instanceOf(PluginInternal.class));
143         plugin = (PluginInternal) defaultPluginManager.getPlugin(PLUGIN_KEY);
144 
145         // only interested in events from now on
146         moduleEvents = Collections.synchronizedList(new ArrayList<Class>());
147     }
148 
149     @After
150     public void tearDown() throws Exception {
151         pluginEventManager.unregister(this);
152     }
153 
154     @Test
155     public void removeNonExistent() {
156         final ModuleDescriptor moduleDescriptor = mock(ModuleDescriptor.class);
157         when(moduleDescriptor.getKey()).thenReturn("dodgeyModuleKey");
158 
159         expectedException.expect(PluginException.class);
160         expectedException.expectMessage(containsString("dodgeyModuleKey"));
161         expectedException.expectMessage(containsString(PLUGIN_KEY));
162 
163         defaultPluginManager.removeDynamicModule(plugin, moduleDescriptor);
164     }
165 
166     @Test
167     public void addRemove() throws Exception {
168         // no modules, dynamic or otherwise
169         assertThat(plugin.getModuleDescriptors(), emptyIterable());
170         assertThat(plugin.getDynamicModuleDescriptors(), emptyIterable());
171         assertThat(plugin.getModuleDescriptor(MODULE_KEY), nullValue());
172         assertThat(moduleEvents, empty());
173 
174         final ModuleDescriptor moduleDescriptor = addModule();
175 
176         // it's added as dynamic and regular
177         assertThat(plugin.getDynamicModuleDescriptors(), Matchers.<ModuleDescriptor<?>>containsInAnyOrder(moduleDescriptor));
178         assertThat(plugin.getModuleDescriptors(), Matchers.<ModuleDescriptor<?>>containsInAnyOrder(moduleDescriptor));
179         assertThat(plugin.getModuleDescriptor(MODULE_KEY), Matchers.<ModuleDescriptor<?>>is(moduleDescriptor));
180 
181         // should be enabled
182         assertThat(moduleDescriptor.isEnabled(), is(true));
183 
184         // events were published
185         assertThat(moduleEvents, Matchers.<Class>contains(PluginModuleEnablingEvent.class, PluginModuleEnabledEvent.class));
186 
187         // may be removed
188         defaultPluginManager.removeDynamicModule(plugin, moduleDescriptor);
189 
190         // no more modules
191         assertThat(plugin.getModuleDescriptors(), emptyIterable());
192         assertThat(plugin.getDynamicModuleDescriptors(), emptyIterable());
193         assertThat(plugin.getModuleDescriptor(MODULE_KEY), nullValue());
194 
195         // events were published
196         assertThat(moduleEvents, Matchers.<Class>contains(PluginModuleEnablingEvent.class, PluginModuleEnabledEvent.class, PluginModuleDisablingEvent.class, PluginModuleDisabledEvent.class));
197     }
198 
199     @Test
200     public void addTwice() {
201         addModule();
202 
203         // events were published
204         assertThat(moduleEvents, Matchers.<Class>contains(PluginModuleEnablingEvent.class, PluginModuleEnabledEvent.class));
205 
206         expectedException.expect(PluginException.class);
207         expectedException.expectMessage(containsString(MODULE_KEY));
208 
209         addModule();
210 
211         // no further events were published
212         assertThat(moduleEvents, Matchers.<Class>contains(PluginModuleEnablingEvent.class, PluginModuleEnabledEvent.class));
213     }
214 
215     @Test
216     public void addDisabled() {
217         // add a rubbish module that will be unrecognised
218         final Element e = new DOMElement("bazza");
219         e.addAttribute("key", "mckenzie");
220         final ModuleDescriptor moduleDescriptor = defaultPluginManager.addDynamicModule(plugin, e);
221 
222         // shouldn't be enabled as it is an UnrecognisedModuleDescriptor
223         assertThat(moduleDescriptor.isEnabled(), is(false));
224         assertThat(moduleDescriptor, is(instanceOf(UnrecognisedModuleDescriptor.class)));
225 
226         // events were not published
227         assertThat(moduleEvents, empty());
228     }
229 
230     private ModuleDescriptor addModule() {
231         // add a component which uses a class in the plugin
232         final Element e = new DOMElement(MODULE_TYPE);
233         e.addAttribute("key", MODULE_KEY);
234         e.addAttribute("class", MODULE_CLASS);
235         return defaultPluginManager.addDynamicModule(plugin, e);
236     }
237 
238     @PluginEventListener
239     public void onBeforePluginModuleEnabledEvent(PluginModuleEnablingEvent event) {
240         if (MODULE_KEY.equals(event.getModule().getKey())) {
241             moduleEvents.add(PluginModuleEnablingEvent.class);
242         }
243     }
244 
245     @PluginEventListener
246     public void onPluginModuleEnabledEvent(PluginModuleEnabledEvent event) {
247         if (MODULE_KEY.equals(event.getModule().getKey())) {
248             moduleEvents.add(PluginModuleEnabledEvent.class);
249         }
250     }
251 
252     @PluginEventListener
253     public void onBeforePluginModuleDisabledEvent(PluginModuleDisablingEvent event) {
254         if (MODULE_KEY.equals(event.getModule().getKey())) {
255             moduleEvents.add(PluginModuleDisablingEvent.class);
256         }
257     }
258 
259     @PluginEventListener
260     public void onPluginModuleDisableEvent(PluginModuleDisabledEvent event) {
261         if (MODULE_KEY.equals(event.getModule().getKey())) {
262             moduleEvents.add(PluginModuleDisabledEvent.class);
263         }
264     }
265 
266     public static class DummyModuleDescriptor extends AbstractModuleDescriptor<Object> {
267         public DummyModuleDescriptor() {
268             super(new DummyModuleFactory());
269         }
270 
271         @Override
272         public Object getModule() {
273             return null;
274         }
275     }
276 
277     public static class DummyModuleFactory implements ModuleFactory {
278         @Override
279         public <T> T createModule(final String name, final ModuleDescriptor<T> moduleDescriptor)
280                 throws PluginParseException {
281             return null;
282         }
283     }
284 }