View Javadoc
1   package it.com.atlassian.plugin.osgi;
2   
3   import com.atlassian.plugin.DefaultModuleDescriptorFactory;
4   import com.atlassian.plugin.JarPluginArtifact;
5   import com.atlassian.plugin.Plugin;
6   import com.atlassian.plugin.PluginState;
7   import com.atlassian.plugin.hostcontainer.DefaultHostContainer;
8   import com.atlassian.plugin.impl.UnloadablePlugin;
9   import com.atlassian.plugin.osgi.AnotherInterface;
10  import com.atlassian.plugin.osgi.BasicWaitCondition;
11  import com.atlassian.plugin.osgi.BooleanFlag;
12  import com.atlassian.plugin.osgi.Callable2;
13  import com.atlassian.plugin.osgi.Callable3;
14  import com.atlassian.plugin.osgi.DefaultBooleanFlag;
15  import com.atlassian.plugin.osgi.DummyModuleDescriptor;
16  import com.atlassian.plugin.osgi.HostClassUsingHostComponentConstructor;
17  import com.atlassian.plugin.osgi.HostClassUsingHostComponentSetter;
18  import com.atlassian.plugin.osgi.ObjectModuleDescriptor;
19  import com.atlassian.plugin.osgi.PluginInContainerTestBase;
20  import com.atlassian.plugin.osgi.SomeInterface;
21  import com.atlassian.plugin.osgi.StubServletModuleDescriptor;
22  import com.atlassian.plugin.osgi.external.SingleModuleDescriptorFactory;
23  import com.atlassian.plugin.osgi.factory.OsgiPlugin;
24  import com.atlassian.plugin.osgi.hostcomponents.ComponentRegistrar;
25  import com.atlassian.plugin.osgi.hostcomponents.HostComponentProvider;
26  import com.atlassian.plugin.servlet.DefaultServletModuleManager;
27  import com.atlassian.plugin.servlet.ServletModuleManager;
28  import com.atlassian.plugin.servlet.descriptors.ServletModuleDescriptor;
29  import com.atlassian.plugin.test.PluginJarBuilder;
30  import com.atlassian.plugin.util.PluginUtils;
31  import com.atlassian.plugin.util.WaitUntil;
32  import com.google.common.collect.ImmutableMap;
33  import org.junit.Rule;
34  import org.junit.Test;
35  import org.junit.contrib.java.lang.system.RestoreSystemProperties;
36  import org.osgi.framework.Bundle;
37  import org.osgi.framework.Constants;
38  
39  import javax.servlet.ServletConfig;
40  import javax.servlet.ServletContext;
41  import javax.servlet.http.HttpServlet;
42  import java.io.File;
43  import java.util.Collections;
44  import java.util.concurrent.TimeUnit;
45  import java.util.concurrent.locks.Lock;
46  import java.util.concurrent.locks.ReentrantLock;
47  
48  import static com.atlassian.plugin.PluginState.DISABLED;
49  import static org.hamcrest.Matchers.empty;
50  import static org.hamcrest.Matchers.instanceOf;
51  import static org.hamcrest.Matchers.lessThan;
52  import static org.hamcrest.Matchers.notNullValue;
53  import static org.hamcrest.core.Is.is;
54  import static org.junit.Assert.assertEquals;
55  import static org.junit.Assert.assertFalse;
56  import static org.junit.Assert.assertNotNull;
57  import static org.junit.Assert.assertThat;
58  import static org.junit.Assert.assertTrue;
59  import static org.junit.Assert.fail;
60  import static org.mockito.Mockito.mock;
61  import static org.mockito.Mockito.when;
62  
63  public final class TestPluginInstall extends PluginInContainerTestBase {
64  
65      @Rule
66      public RestoreSystemProperties restoreSystemProperties = new RestoreSystemProperties(
67              PluginUtils.ATLASSIAN_PLUGINS_ENABLE_WAIT,
68              "atlassian.dev.mode"
69      );
70  
71      private static final String SPRING_CONFIG_HEADER = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
72              + "<beans xmlns=\"http://www.springframework.org/schema/beans\"\n"
73              + "       xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n"
74              + "       xmlns:osgi=\"http://www.eclipse.org/gemini/blueprint/schema/blueprint\"\n"
75              + "       xsi:schemaLocation=\""
76              + "           http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd\n"
77              + "           http://www.eclipse.org/gemini/blueprint/schema/blueprint http://www.eclipse.org/gemini/blueprint/schema/blueprint/gemini-blueprint.xsd\"\n"
78              + ">\n";
79      private static final String SPRING_CONFIG_FOOTER = "</beans>\n";
80  
81      private static void assertExists(File f) {
82          assertTrue("File should exist: " + f, f.exists());
83      }
84  
85      private static void assertDoesNotExist(File f) {
86          assertFalse("File should not exist: " + f, f.exists());
87      }
88  
89      @Test
90      public void testUpgradeOfBundledPlugin() throws Exception {
91          final DefaultModuleDescriptorFactory factory = new DefaultModuleDescriptorFactory(hostContainer);
92          factory.addModuleDescriptor("object", ObjectModuleDescriptor.class);
93  
94          final File pluginJar = new PluginJarBuilder("testUpgradeOfBundledPlugin")
95                  .addFormattedResource("atlassian-plugin.xml",
96                          "<atlassian-plugin name='Test' key='test.bundled.plugin' pluginsVersion='2'>",
97                          "    <plugin-info>",
98                          "        <version>1.0</version>",
99                          "    </plugin-info>",
100                         "    <object key='obj' class='my.Foo'/>",
101                         "</atlassian-plugin>")
102                 .addFormattedJava("my.Foo",
103                         "package my;",
104                         "public class Foo {}")
105                 .build();
106         initBundlingPluginManager(factory, pluginJar);
107         assertEquals(1, pluginAccessor.getEnabledPlugins().size());
108         assertEquals("Test", pluginAccessor.getPlugin("test.bundled.plugin").getName());
109         assertEquals("my.Foo", pluginAccessor.getPlugin("test.bundled.plugin").getModuleDescriptor("obj").getModule().getClass().getName());
110 
111         final File pluginJar2 = new PluginJarBuilder("testUpgradeOfBundledPlugin")
112                 .addFormattedResource("atlassian-plugin.xml",
113                         "<atlassian-plugin name='Test' key='test.bundled.plugin' pluginsVersion='2'>",
114                         "    <plugin-info>",
115                         "        <version>1.0</version>",
116                         "    </plugin-info>",
117                         "    <object key='obj' class='my.Bar'/>",
118                         "</atlassian-plugin>")
119                 .addFormattedJava("my.Bar",
120                         "package my;",
121                         "public class Bar {}")
122                 .build();
123 
124         pluginController.installPlugin(new JarPluginArtifact(pluginJar2));
125 
126         assertEquals(1, pluginAccessor.getEnabledPlugins().size());
127         assertEquals("Test", pluginAccessor.getPlugin("test.bundled.plugin").getName());
128         assertEquals("my.Bar", pluginAccessor.getPlugin("test.bundled.plugin").getModuleDescriptor("obj").getModule().getClass().getName());
129 
130     }
131 
132     @Test
133     public void testUpgradeOfBadPlugin() throws Exception {
134         new PluginJarBuilder("testUpgradeOfBundledPlugin-old")
135                 .addFormattedResource("atlassian-plugin.xml",
136                         "<atlassian-plugin name='Test' key='test.bundled.plugin' pluginsVersion='2'>",
137                         "    <plugin-info>",
138                         "        <version>1.0</version>",
139                         "    </plugin-info>",
140                         "    <component key='obj' class='my.Foo'/>",
141                         "</atlassian-plugin>")
142                 .addFormattedJava("my.Foo",
143                         "package my;",
144                         "public class Foo {",
145                         "  public Foo() { throw new RuntimeException('bad plugin');}",
146                         "}")
147                 .build(pluginsDir);
148         new PluginJarBuilder("testUpgradeOfBundledPlugin-new")
149                 .addFormattedResource("atlassian-plugin.xml",
150                         "<atlassian-plugin name='Test' key='test.bundled.plugin' pluginsVersion='2'>",
151                         "    <plugin-info>",
152                         "        <version>2.0</version>",
153                         "    </plugin-info>",
154                         "    <component key='obj' class='my.Foo'/>",
155                         "</atlassian-plugin>")
156                 .addFormattedJava("my.Foo",
157                         "package my;",
158                         "public class Foo {",
159                         "  public Foo() {}",
160                         "}")
161                 .build(pluginsDir);
162         initPluginManager();
163         assertEquals(1, pluginAccessor.getEnabledPlugins().size());
164         assertEquals("Test", pluginAccessor.getPlugin("test.bundled.plugin").getName());
165         assertEquals("2.0", pluginAccessor.getPlugin("test.bundled.plugin").getPluginInformation().getVersion());
166     }
167 
168     @Test
169     public void testUpgradeWithNewComponentImports() throws Exception {
170         final DefaultModuleDescriptorFactory factory = new DefaultModuleDescriptorFactory(new DefaultHostContainer());
171         factory.addModuleDescriptor("dummy", DummyModuleDescriptor.class);
172         initPluginManager(new HostComponentProvider() {
173             public void provide(final ComponentRegistrar registrar) {
174                 registrar.register(SomeInterface.class).forInstance(new SomeInterface() {
175                 });
176                 registrar.register(AnotherInterface.class).forInstance(new AnotherInterface() {
177                 });
178             }
179         }, factory);
180 
181         final File pluginJar = new PluginJarBuilder("first")
182                 .addFormattedResource("atlassian-plugin.xml",
183                         "<atlassian-plugin name='Test' key='test.plugin' pluginsVersion='2'>",
184                         "    <plugin-info>",
185                         "        <version>1.0</version>",
186                         "    </plugin-info>",
187                         "    <component-import key='comp1' interface='com.atlassian.plugin.osgi.SomeInterface' />",
188                         "    <dummy key='dum1'/>", "</atlassian-plugin>")
189                 .build();
190         final File pluginJar2 = new PluginJarBuilder("second")
191                 .addFormattedResource("atlassian-plugin.xml",
192                         "<atlassian-plugin name='Test 2' key='test.plugin' pluginsVersion='2'>",
193                         "    <plugin-info>",
194                         "        <version>1.0</version>",
195                         "    </plugin-info>",
196                         "    <component-import key='comp1' interface='com.atlassian.plugin.osgi.SomeInterface' />",
197                         "    <component-import key='comp2' interface='com.atlassian.plugin.osgi.AnotherInterface' />",
198                         "    <dummy key='dum1'/>",
199                         "    <dummy key='dum2'/>",
200                         "</atlassian-plugin>")
201                 .build();
202 
203         pluginController.installPlugin(new JarPluginArtifact(pluginJar));
204         assertEquals(1, pluginAccessor.getEnabledPlugins().size());
205         assertEquals("Test", pluginAccessor.getPlugin("test.plugin").getName());
206         assertEquals(2, pluginAccessor.getPlugin("test.plugin").getModuleDescriptors().size());
207         pluginController.installPlugin(new JarPluginArtifact(pluginJar2));
208         assertEquals(1, pluginAccessor.getEnabledPlugins().size());
209         assertEquals(4, pluginAccessor.getPlugin("test.plugin").getModuleDescriptors().size());
210         assertEquals("Test 2", pluginAccessor.getPlugin("test.plugin").getName());
211     }
212 
213     @Test
214     public void testInstallWithClassConstructorReferencingHostClassWithHostComponent() throws Exception {
215         final DefaultModuleDescriptorFactory factory = new DefaultModuleDescriptorFactory(hostContainer);
216         factory.addModuleDescriptor("object", ObjectModuleDescriptor.class);
217         initPluginManager(new HostComponentProvider() {
218             public void provide(final ComponentRegistrar registrar) {
219                 registrar.register(SomeInterface.class).forInstance(new SomeInterface() {
220                 });
221             }
222         }, factory);
223 
224         final File pluginJar = new PluginJarBuilder("testUpgradeOfBundledPlugin")
225                 .addFormattedResource("atlassian-plugin.xml",
226                         "<atlassian-plugin name='Test' key='hostClass' pluginsVersion='2'>",
227                         "    <plugin-info>",
228                         "        <version>1.0</version>",
229                         "    </plugin-info>",
230                         "    <object key='hostClass' class='com.atlassian.plugin.osgi.HostClassUsingHostComponentConstructor'/>",
231                         "</atlassian-plugin>")
232                 .build();
233 
234         pluginController.installPlugin(new JarPluginArtifact(pluginJar));
235         assertEquals(1, pluginAccessor.getEnabledPlugins().size());
236         assertEquals("Test", pluginAccessor.getPlugin("hostClass").getName());
237         assertEquals(1, pluginAccessor.getPlugin("hostClass").getModuleDescriptors().size());
238 
239         HostClassUsingHostComponentConstructor module = (HostClassUsingHostComponentConstructor) pluginAccessor.getPlugin("hostClass").getModuleDescriptor("hostClass").getModule();
240         assertNotNull(module);
241     }
242 
243     @Test
244     public void testInstallWithClassSetterReferencingHostClassWithHostComponent() throws Exception {
245         final DefaultModuleDescriptorFactory factory = new DefaultModuleDescriptorFactory(hostContainer);
246         factory.addModuleDescriptor("object", ObjectModuleDescriptor.class);
247 
248         initPluginManager(new HostComponentProvider() {
249             public void provide(final ComponentRegistrar registrar) {
250                 registrar.register(SomeInterface.class).forInstance(new SomeInterface() {
251                 });
252             }
253         }, factory);
254 
255         final File pluginJar = new PluginJarBuilder("testUpgradeOfBundledPlugin")
256                 .addFormattedResource("atlassian-plugin.xml",
257                         "<atlassian-plugin name='Test' key='hostClass' pluginsVersion='2'>",
258                         "    <plugin-info>",
259                         "        <version>1.0</version>",
260                         "    </plugin-info>",
261                         "    <object key='hostClass' class='com.atlassian.plugin.osgi.HostClassUsingHostComponentSetter'/>",
262                         "</atlassian-plugin>")
263                 .build();
264 
265         pluginController.installPlugin(new JarPluginArtifact(pluginJar));
266         assertEquals(1, pluginAccessor.getEnabledPlugins().size());
267         assertEquals("Test", pluginAccessor.getPlugin("hostClass").getName());
268         assertEquals(1, pluginAccessor.getPlugin("hostClass").getModuleDescriptors().size());
269 
270         HostClassUsingHostComponentSetter module = (HostClassUsingHostComponentSetter) pluginAccessor.getPlugin("hostClass").getModuleDescriptor("hostClass").getModule();
271         assertNotNull(module);
272         assertNotNull(module.getSomeInterface());
273     }
274 
275     /* Enable for manual memory leak profiling
276     @Test
277     public void testNoMemoryLeak() throws Exception
278     {
279         DefaultModuleDescriptorFactory factory = new DefaultModuleDescriptorFactory(new DefaultHostContainer());
280         factory.addModuleDescriptor("dummy", DummyModuleDescriptor.class);
281         for (int x=0; x<100; x++)
282         {
283             pluginEventManager = new DefaultPluginEventManager();
284             initPluginManager(new HostComponentProvider(){
285                 public void provide(ComponentRegistrar registrar)
286                 {
287                     registrar.register(SomeInterface.class).forInstance(new SomeInterface(){});
288                     registrar.register(AnotherInterface.class).forInstance(new AnotherInterface(){});
289                 }
290             }, factory);
291             pluginSystemLifecycle.shutdown();
292 
293         }
294         System.out.println("Gentlement, start your profilers!");
295         System.in.read();
296 
297     }
298     */
299 
300     @Test
301     public void testUpgradeWithNoAutoDisable() throws Exception {
302         DefaultModuleDescriptorFactory factory = new DefaultModuleDescriptorFactory(new DefaultHostContainer());
303         factory.addModuleDescriptor("dummy", DummyModuleDescriptor.class);
304         initPluginManager(new HostComponentProvider() {
305             public void provide(ComponentRegistrar registrar) {
306                 registrar.register(SomeInterface.class).forInstance(new SomeInterface() {
307                 });
308                 registrar.register(AnotherInterface.class).forInstance(new AnotherInterface() {
309                 });
310             }
311         }, factory);
312 
313         File pluginJar = new PluginJarBuilder("first")
314                 .addFormattedResource("atlassian-plugin.xml",
315                         "<atlassian-plugin name='Test' key='test.plugin' pluginsVersion='2'>",
316                         "    <plugin-info>",
317                         "        <version>1.0</version>",
318                         "    </plugin-info>",
319                         "    <component-import key='comp1' interface='com.atlassian.plugin.osgi.SomeInterface' />",
320                         "    <dummy key='dum1'/>",
321                         "</atlassian-plugin>")
322                 .build();
323         final File pluginJar2 = new PluginJarBuilder("second")
324                 .addFormattedResource("atlassian-plugin.xml",
325                         "<atlassian-plugin name='Test 2' key='test.plugin' pluginsVersion='2'>",
326                         "    <plugin-info>",
327                         "        <version>1.0</version>",
328                         "    </plugin-info>",
329                         "    <component-import key='comp1' interface='com.atlassian.plugin.osgi.SomeInterface' />",
330                         "    <dummy key='dum1'/>",
331                         "    <dummy key='dum2'/>",
332                         "</atlassian-plugin>")
333                 .build();
334 
335         pluginController.installPlugin(new JarPluginArtifact(pluginJar));
336         assertTrue(pluginAccessor.isPluginEnabled("test.plugin"));
337 
338         final Lock lock = new ReentrantLock();
339         Thread upgradeThread = new Thread() {
340             public void run() {
341                 lock.lock();
342                 pluginController.installPlugin(new JarPluginArtifact(pluginJar2));
343                 lock.unlock();
344             }
345         };
346 
347         Thread isEnabledThread = new Thread() {
348             public void run() {
349                 try {
350                     while (!lock.tryLock(10, TimeUnit.SECONDS))
351                         pluginAccessor.isPluginEnabled("test.plugin");
352                 } catch (InterruptedException e) {
353                     fail();
354                 }
355             }
356         };
357         upgradeThread.start();
358         isEnabledThread.start();
359 
360         upgradeThread.join(10000);
361 
362         assertTrue(pluginAccessor.isPluginEnabled("test.plugin"));
363     }
364 
365     @Test
366     public void testUpgradeTestingForCachedXml() throws Exception {
367         final DefaultModuleDescriptorFactory factory = new DefaultModuleDescriptorFactory(new DefaultHostContainer());
368         factory.addModuleDescriptor("dummy", DummyModuleDescriptor.class);
369         initPluginManager(new HostComponentProvider() {
370             public void provide(final ComponentRegistrar registrar) {
371                 registrar.register(SomeInterface.class).forInstance(new SomeInterface() {
372                 });
373                 registrar.register(AnotherInterface.class).forInstance(new AnotherInterface() {
374                 });
375             }
376         }, factory);
377 
378         final File pluginJar = new PluginJarBuilder("first").addFormattedResource("atlassian-plugin.xml",
379                 "<atlassian-plugin name='Test' key='test.plugin' pluginsVersion='2'>", "    <plugin-info>", "        <version>1.0</version>",
380                 "    </plugin-info>", "    <component key='comp1' interface='com.atlassian.plugin.osgi.SomeInterface' class='my.ServiceImpl' />",
381                 "</atlassian-plugin>").addFormattedJava("my.ServiceImpl", "package my;",
382                 "public class ServiceImpl implements com.atlassian.plugin.osgi.SomeInterface {}").build();
383         final File pluginJar2 = new PluginJarBuilder("second").addFormattedResource("atlassian-plugin.xml",
384                 "<atlassian-plugin name='Test 2' key='test.plugin' pluginsVersion='2'>", "    <plugin-info>", "        <version>1.0</version>",
385                 "    </plugin-info>", "</atlassian-plugin>").build();
386 
387         pluginController.installPlugin(new JarPluginArtifact(pluginJar));
388         assertEquals(1, pluginAccessor.getEnabledPlugins().size());
389         assertEquals("Test", pluginAccessor.getPlugin("test.plugin").getName());
390         pluginController.installPlugin(new JarPluginArtifact(pluginJar2));
391         assertEquals(1, pluginAccessor.getEnabledPlugins().size());
392         assertEquals("Test 2", pluginAccessor.getPlugin("test.plugin").getName());
393     }
394 
395     @Test
396     public void testPluginDependentOnPackageImport() throws Exception {
397         HostComponentProvider prov = new HostComponentProvider() {
398             public void provide(final ComponentRegistrar registrar) {
399                 registrar.register(ServletConfig.class).forInstance(new HttpServlet() {
400                 });
401             }
402         };
403         File servletJar = new PluginJarBuilder("first")
404                 .addFormattedResource("META-INF/MANIFEST.MF",
405                         "Export-Package: javax.servlet.http;version='4.0.0',javax.servlet;version='4.0.0'",
406                         "Import-Package: javax.servlet.http;version='4.0.0',javax.servlet;version='4.0.0'",
407                         "Bundle-SymbolicName: first",
408                         "Bundle-Version: 4.0.0",
409                         "Manifest-Version: 1.0",
410                         "")
411                 .addFormattedJava("javax.servlet.Servlet",
412                         "package javax.servlet;",
413                         "public interface Servlet {}")
414                 .addFormattedJava("javax.servlet.http.HttpServlet",
415                         "package javax.servlet.http;",
416                         "public abstract class HttpServlet implements javax.servlet.Servlet{}")
417                 .build();
418 
419         File pluginJar = new PluginJarBuilder("asecond")
420                 .addFormattedResource("atlassian-plugin.xml",
421                         "<atlassian-plugin name='Test' key='second' pluginsVersion='2'>",
422                         "    <plugin-info>",
423                         "        <version>1.0</version>",
424                         "        <bundle-instructions><Import-Package>javax.servlet.http;version='3.0',javax.servlet;version='3.0',*</Import-Package></bundle-instructions>",
425                         "    </plugin-info>",
426                         "</atlassian-plugin>")
427                 .addFormattedJava("second.MyImpl",
428                         "package second;",
429                         "public class MyImpl {",
430                         "    public MyImpl(javax.servlet.ServletConfig config) {",
431                         "    }",
432                         "}")
433                 .build();
434 
435         initPluginManager(prov);
436         pluginController.installPlugin(new JarPluginArtifact(servletJar));
437         pluginController.installPlugin(new JarPluginArtifact(pluginJar));
438         WaitUntil.invoke(new BasicWaitCondition() {
439             public boolean isFinished() {
440                 return pluginAccessor.getEnabledPlugins().size() == 2;
441             }
442         });
443 
444         assertEquals(2, pluginAccessor.getEnabledPlugins().size());
445         assertNotNull(pluginAccessor.getPlugin("first-4.0.0"));
446         assertNotNull(pluginAccessor.getPlugin("second"));
447     }
448 
449     @Test
450     public void testPluginWithHostComponentUsingOldPackageImport() throws Exception {
451         final PluginJarBuilder firstBuilder = new PluginJarBuilder("oldpkgfirst");
452         firstBuilder
453                 .addFormattedResource("atlassian-plugin.xml",
454                         "<atlassian-plugin name='Test' key='first' pluginsVersion='2'>",
455                         "    <plugin-info>",
456                         "        <version>1.0</version>",
457                         "        <bundle-instructions>",
458                         "           <Export-Package>first</Export-Package>",
459                         "        </bundle-instructions>",
460                         "    </plugin-info>",
461                         "    <servlet key='foo' class='second.MyServlet'>",
462                         "       <url-pattern>/foo</url-pattern>",
463                         "    </servlet>",
464                         "</atlassian-plugin>")
465                 .addFormattedJava("first.MyInterface",
466                         "package first;",
467                         "public interface MyInterface {}")
468                 .build(pluginsDir);
469 
470         new PluginJarBuilder("oldpkgsecond", firstBuilder.getClassLoader())
471                 .addPluginInformation("second", "Some name", "1.0")
472                 .addFormattedJava("second.MyImpl",
473                         "package second;",
474                         "public class MyImpl implements first.MyInterface {}")
475                 .build(pluginsDir);
476 
477         initPluginManager();
478 
479         assertEquals(2, pluginAccessor.getEnabledPlugins().size());
480         assertNotNull(pluginAccessor.getPlugin("first"));
481         assertNotNull(pluginAccessor.getPlugin("second"));
482     }
483 
484     @Test
485     public void testPluginWithServletDependentOnPackageImport() throws Exception {
486         final PluginJarBuilder firstBuilder = new PluginJarBuilder("first");
487         firstBuilder
488                 .addPluginInformation("first", "Some name", "1.0")
489                 .addFormattedJava("first.MyInterface",
490                         "package first;",
491                         "public interface MyInterface {}")
492                 .addFormattedResource("META-INF/MANIFEST.MF",
493                         "Manifest-Version: 1.0",
494                         "Bundle-SymbolicName: first",
495                         "Bundle-Version: 1.0",
496                         "Export-Package: first",
497                         "")
498                 .build(pluginsDir);
499 
500         new PluginJarBuilder("asecond", firstBuilder.getClassLoader())
501                 .addFormattedResource("atlassian-plugin.xml",
502                         "<atlassian-plugin name='Test' key='asecond' pluginsVersion='2'>",
503                         "    <plugin-info>",
504                         "        <version>1.0</version>",
505                         "    </plugin-info>",
506                         "    <servlet key='foo' class='second.MyServlet'>",
507                         "       <url-pattern>/foo</url-pattern>",
508                         "    </servlet>",
509                         "</atlassian-plugin>")
510                 .addFormattedJava("second.MyServlet",
511                         "package second;",
512                         "public class MyServlet extends javax.servlet.http.HttpServlet implements first.MyInterface {}")
513                 .build(pluginsDir);
514 
515         initPluginManager(null, new SingleModuleDescriptorFactory(new DefaultHostContainer(), "servlet", StubServletModuleDescriptor.class));
516 
517         assertEquals(2, pluginAccessor.getEnabledPlugins().size());
518         assertTrue(pluginAccessor.getPlugin("first").getPluginState() == PluginState.ENABLED);
519         assertNotNull(pluginAccessor.getPlugin("asecond").getPluginState() == PluginState.ENABLED);
520     }
521 
522     @Test
523     public void testPluginWithServletRefreshedAfterOtherPluginUpgraded() throws Exception {
524         final PluginJarBuilder firstBuilder = new PluginJarBuilder("first");
525         firstBuilder
526                 .addPluginInformation("first", "Some name", "1.0")
527                 .addFormattedJava("first.MyInterface",
528                         "package first;",
529                         "public interface MyInterface {}")
530                 .addFormattedResource("META-INF/MANIFEST.MF",
531                         "Manifest-Version: 1.0",
532                         "Bundle-SymbolicName: first",
533                         "Bundle-Version: 1.0",
534                         "Export-Package: first",
535                         "")
536                 .build(pluginsDir);
537 
538         new PluginJarBuilder("asecond", firstBuilder.getClassLoader())
539                 .addFormattedResource("atlassian-plugin.xml",
540                         "<atlassian-plugin name='Test' key='asecond' pluginsVersion='2'>",
541                         "    <plugin-info>",
542                         "        <version>1.0</version>",
543                         "    </plugin-info>",
544                         "    <servlet key='foo' class='second.MyServlet'>",
545                         "       <url-pattern>/foo</url-pattern>",
546                         "    </servlet>",
547                         "</atlassian-plugin>")
548                 .addFormattedJava("second.MyServlet",
549                         "package second;",
550                         "import com.atlassian.plugin.osgi.Callable2;",
551                         "public class MyServlet extends javax.servlet.http.HttpServlet implements first.MyInterface {",
552                         "   private Callable2 callable;",
553                         "   public MyServlet(Callable2 cal) { this.callable = cal; }",
554                         "   public String getServletInfo() {",
555                         "       try {return callable.call() + ' bob';} catch (Exception ex) { throw new RuntimeException(ex);}",
556                         "   }",
557                         "}")
558                 .build(pluginsDir);
559 
560         HostComponentProvider prov = new HostComponentProvider() {
561 
562             public void provide(ComponentRegistrar registrar) {
563                 registrar.register(Callable2.class).forInstance(new Callable2() {
564                     public String call() {
565                         return "hi";
566                     }
567                 });
568 
569             }
570         };
571 
572         ServletContext ctx = mock(ServletContext.class);
573         when(ctx.getInitParameterNames()).thenReturn(Collections.<String>enumeration(Collections.<String>emptyList()));
574         ServletConfig servletConfig = mock(ServletConfig.class);
575         when(servletConfig.getServletContext()).thenReturn(ctx);
576 
577         ServletModuleManager mgr = new DefaultServletModuleManager(pluginEventManager);
578         hostContainer = createHostContainer(Collections.<Class<?>, Object>singletonMap(ServletModuleManager.class, mgr));
579         initPluginManager(prov, new SingleModuleDescriptorFactory(
580                 hostContainer,
581                 "servlet",
582                 ServletModuleDescriptor.class));
583 
584         assertEquals(2, pluginAccessor.getEnabledPlugins().size());
585         assertTrue(pluginAccessor.getPlugin("first").getPluginState() == PluginState.ENABLED);
586         assertNotNull(pluginAccessor.getPlugin("asecond").getPluginState() == PluginState.ENABLED);
587         assertEquals("hi bob", mgr.getServlet("/foo", servletConfig).getServletInfo());
588 
589 
590         final File updatedJar = new PluginJarBuilder("first-updated")
591                 .addPluginInformation("foo", "Some name", "1.0")
592                 .addFormattedJava("first.MyInterface",
593                         "package first;",
594                         "public interface MyInterface {}")
595                 .addFormattedResource("META-INF/MANIFEST.MF",
596                         "Manifest-Version: 1.0",
597                         "Bundle-SymbolicName: foo",
598                         "Bundle-Version: 1.0",
599                         "Export-Package: first",
600                         "")
601                 .build();
602         pluginController.installPlugin(new JarPluginArtifact(updatedJar));
603 
604         WaitUntil.invoke(new BasicWaitCondition() {
605             public boolean isFinished() {
606                 return pluginAccessor.isPluginEnabled("asecond");
607             }
608 
609         });
610 
611         assertEquals("hi bob", mgr.getServlet("/foo", servletConfig).getServletInfo());
612     }
613 
614     // PLUG-760
615     @Test
616     public void testModuleDescriptorWithNamespace() throws Exception {
617         new PluginJarBuilder("asecond")
618                 .addFormattedResource("atlassian-plugin.xml",
619                         "<atlassian-plugin name='Test' key='asecond' pluginsVersion='2'",
620                         "    xmlns='http://www.atlassian.com/schema/plugins'",
621                         "    xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance'",
622                         "    xsi:schemaLocation='http://www.atlassian.com/schema/plugins http://schema.atlassian.com/refapp/refapp-2.9.xsd'>",
623                         "    <plugin-info>",
624                         "        <version>1.0</version>",
625                         "    </plugin-info>",
626                         "    <servlet key='foo' class='second.MyServlet'>",
627                         "       <url-pattern>/foo</url-pattern>",
628                         "    </servlet>",
629                         "</atlassian-plugin>")
630                 .addFormattedJava("second.MyServlet",
631                         "package second;",
632                         "public class MyServlet extends javax.servlet.http.HttpServlet {}")
633                 .build(pluginsDir);
634 
635         ServletContext ctx = mock(ServletContext.class);
636         when(ctx.getInitParameterNames()).thenReturn(Collections.<String>enumeration(Collections.<String>emptyList()));
637         ServletConfig servletConfig = mock(ServletConfig.class);
638         when(servletConfig.getServletContext()).thenReturn(ctx);
639 
640         ServletModuleManager mgr = new DefaultServletModuleManager(pluginEventManager);
641         hostContainer = createHostContainer(Collections.<Class<?>, Object>singletonMap(ServletModuleManager.class, mgr));
642         initPluginManager(null, new SingleModuleDescriptorFactory(
643                 hostContainer,
644                 "servlet",
645                 ServletModuleDescriptor.class));
646 
647         assertEquals(1, pluginAccessor.getEnabledPlugins().size());
648         assertNotNull(pluginAccessor.getPlugin("asecond").getPluginState() == PluginState.ENABLED);
649     }
650 
651     @Test
652     public void testLotsOfHostComponents() throws Exception {
653         new PluginJarBuilder("first")
654                 .addFormattedResource("atlassian-plugin.xml",
655                         "<atlassian-plugin name='Test' key='test.plugin' pluginsVersion='2'>",
656                         "    <plugin-info>",
657                         "        <version>1.0</version>",
658                         "    </plugin-info>",
659                         "    <dummy key='dum1'/>",
660                         "</atlassian-plugin>")
661                 .build(pluginsDir);
662         new PluginJarBuilder("second")
663                 .addFormattedResource("atlassian-plugin.xml",
664                         "<atlassian-plugin name='Test 2' key='test.plugin2' pluginsVersion='2'>",
665                         "    <plugin-info>",
666                         "        <version>1.0</version>",
667                         "    </plugin-info>",
668                         "    <dummy key='dum1'/>",
669                         "    <dummy key='dum2'/>",
670                         "</atlassian-plugin>")
671                 .build(pluginsDir);
672 
673         final DefaultModuleDescriptorFactory factory = new DefaultModuleDescriptorFactory(new DefaultHostContainer());
674         factory.addModuleDescriptor("dummy", DummyModuleDescriptor.class);
675         initPluginManager(new HostComponentProvider() {
676             public void provide(final ComponentRegistrar registrar) {
677                 for (int x = 0; x < 100; x++) {
678                     registrar.register(SomeInterface.class).forInstance(new SomeInterface() {
679                     }).withName("some" + x);
680                     registrar.register(AnotherInterface.class).forInstance(new AnotherInterface() {
681                     }).withName("another" + x);
682                 }
683             }
684         }, factory);
685 
686         assertEquals(2, pluginAccessor.getEnabledPlugins().size());
687     }
688 
689     @Test
690     public void testInstallWithManifestNoSpringContextAndComponents() throws Exception {
691         final BooleanFlag flag = new DefaultBooleanFlag(false);
692         new PluginJarBuilder("first")
693                 .addFormattedResource("atlassian-plugin.xml",
694                         "<atlassian-plugin name='Test' key='first' pluginsVersion='2'>",
695                         "    <plugin-info>",
696                         "        <version>1.0</version>",
697                         "    </plugin-info>",
698                         "    <component key='foo' class='first.MyClass' interface='first.MyInterface' public='true'/>",
699                         "</atlassian-plugin>")
700                 .addFormattedJava("first.MyInterface",
701                         "package first;",
702                         "public interface MyInterface {}")
703                 .addFormattedJava("first.MyClass",
704                         "package first;",
705                         "public class MyClass implements MyInterface{",
706                         "  public MyClass(com.atlassian.plugin.osgi.BooleanFlag bool) { bool.set(true); }",
707                         "}")
708                 .addFormattedResource("META-INF/MANIFEST.MF",
709                         "Manifest-Version: 1.0",
710                         "Bundle-SymbolicName: foo",
711                         "Bundle-Version: 1.0",
712                         "Export-Package: first",
713                         "")
714                 .build(pluginsDir);
715 
716         initPluginManager(new HostComponentProvider() {
717             public void provide(ComponentRegistrar registrar) {
718                 registrar.register(BooleanFlag.class).forInstance(flag).withName("bob");
719             }
720         });
721 
722         assertEquals(1, pluginAccessor.getEnabledPlugins().size());
723         assertNotNull(pluginAccessor.getPlugin("first"));
724         assertTrue(flag.get());
725     }
726 
727     @Test
728     public void testInstallWithManifestNoDescriptorWithSpringXml() throws Exception {
729         new PluginJarBuilder("nodesc")
730                 .manifest(new ImmutableMap.Builder<String, String>()
731                         .put(OsgiPlugin.ATLASSIAN_PLUGIN_KEY, "nodesc")
732                         .put(Constants.BUNDLE_VERSION, "1.0")
733                         .build())
734                 .addResource("META-INF/spring/foo.xml", SPRING_CONFIG_HEADER + SPRING_CONFIG_FOOTER)
735                 .build(pluginsDir);
736 
737         initPluginManager();
738 
739         assertEquals(1, pluginAccessor.getEnabledPlugins().size());
740         Plugin plugin = pluginAccessor.getPlugin("nodesc");
741         assertNotNull(plugin);
742         assertEquals("1.0", plugin.getPluginInformation().getVersion());
743         assertEquals("nodesc", plugin.getKey());
744         assertEquals(0, plugin.getModuleDescriptors().size());
745     }
746 
747     @Test
748     public void testInstallWithManifestNoDescriptorWithSpringHeader() throws Exception {
749         new PluginJarBuilder("nodesc")
750                 .manifest(new ImmutableMap.Builder<String, String>()
751                         .put(OsgiPlugin.ATLASSIAN_PLUGIN_KEY, "nodesc")
752                         .put(Constants.BUNDLE_VERSION, "1.0")
753                         .put("Spring-Context", "*;timeout:=60")
754                         .build())
755                 .build(pluginsDir);
756 
757         initPluginManager();
758 
759         assertEquals(1, pluginAccessor.getEnabledPlugins().size());
760         Plugin plugin = pluginAccessor.getPlugin("nodesc");
761         assertNotNull(plugin);
762         assertEquals("1.0", plugin.getPluginInformation().getVersion());
763         assertEquals("nodesc", plugin.getKey());
764         assertEquals(0, plugin.getModuleDescriptors().size());
765     }
766 
767     @Test
768     public void testInstallWithSpringContainerFail() throws Exception {
769         System.setProperty(PluginUtils.ATLASSIAN_PLUGINS_ENABLE_WAIT, "10");
770 
771         new PluginJarBuilder("fail-spring-container")
772                 .manifest(new ImmutableMap.Builder<String, String>()
773                         .put(OsgiPlugin.ATLASSIAN_PLUGIN_KEY, "fail-spring-container")
774                         .put(Constants.BUNDLE_VERSION, "1.0")
775                         .put("Spring-Context", "*")
776                         .build())
777                 .addResource("META-INF/spring/fail.xml",
778                         SPRING_CONFIG_HEADER + "<osgi:reference id=\"notFound\" interface=\"some.class.NotFound\"/>" + SPRING_CONFIG_FOOTER)
779                 .build(pluginsDir);
780 
781         final long beforeStarting = System.currentTimeMillis();
782         initPluginManager();
783         final long startupDuration = System.currentTimeMillis() - beforeStarting;
784 
785         assertThat(pluginAccessor.getEnabledPlugins(), empty());
786 
787         final Plugin plugin = pluginAccessor.getPlugin("fail-spring-container");
788         assertThat(plugin, notNullValue());
789         assertThat(plugin, instanceOf(OsgiPlugin.class));
790 
791         final OsgiPlugin osgiPlugin = (OsgiPlugin) plugin;
792         assertThat(osgiPlugin.getPluginState(), is(DISABLED));
793         assertThat(osgiPlugin.getBundle().getState(), is(Bundle.RESOLVED));
794 
795         assertThat("fail-spring-container failed due to plugin system timeout rather than immediately via PluginContainerFailedEvent", startupDuration, lessThan(10000L));
796     }
797 
798     // TODO: turn this back on PLUG-682
799 //    @Test
800     public void testInstallWithComponentBeanNameConflictedWithHostComponent() throws Exception {
801         new PluginJarBuilder("first")
802                 .addFormattedResource("atlassian-plugin.xml",
803                         "<atlassian-plugin name='Test' key='first' pluginsVersion='2'>",
804                         "    <plugin-info>",
805                         "        <version>1.0</version>",
806                         "    </plugin-info>",
807                         "    <component key='host_component1' class='first.MyClass' interface='first.MyInterface'/>",
808                         "</atlassian-plugin>")
809                 .addFormattedJava("com.atlassian.plugin.osgi.SomeInterface",
810                         "package com.atlassian.plugin.osgi;",
811                         "public interface SomeInterface {}")
812                 .addFormattedJava("first.MyClass",
813                         "package first;",
814                         "import com.atlassian.plugin.osgi.SomeInterface;",
815                         "public class MyClass implements SomeInterface{",
816                         "}")
817                 .build(pluginsDir);
818 
819         initPluginManager(new HostComponentProvider() {
820             public void provide(ComponentRegistrar registrar) {
821                 registrar.register(SomeInterface.class).forInstance(new SomeInterface() {
822                 }).withName("host_component1");
823             }
824         });
825 
826         // there is a name conflict therefore this plugin should not have been enabled.
827         assertEquals(0, pluginAccessor.getEnabledPlugins().size());
828         assertTrue(pluginAccessor.getPlugins().toArray()[0] instanceof UnloadablePlugin);
829 
830         // the error message has to mention the problematic component name otherwise users won't be able to figure out.
831         assertTrue(pluginAccessor.getPlugins().toArray(new UnloadablePlugin[1])[0].getErrorText().contains("host_component1"));
832     }
833 
834     @Test
835     public void testInstallWithStrangePath() throws Exception {
836         File strangeDir = new File(tmpDir, "20%time");
837         strangeDir.mkdir();
838         File oldTmp = tmpDir;
839         try {
840             tmpDir = strangeDir;
841             cacheDir = new File(tmpDir, "felix-cache");
842             cacheDir.mkdir();
843             pluginsDir = new File(tmpDir, "plugins");
844             pluginsDir.mkdir();
845 
846             new PluginJarBuilder("strangePath")
847                     .addFormattedResource("atlassian-plugin.xml",
848                             "<atlassian-plugin name='Test' key='test.plugin' pluginsVersion='2'>",
849                             "    <plugin-info>",
850                             "        <version>1.0</version>",
851                             "    </plugin-info>",
852                             "</atlassian-plugin>")
853                     .build(pluginsDir);
854 
855             final DefaultModuleDescriptorFactory factory = new DefaultModuleDescriptorFactory(new DefaultHostContainer());
856             factory.addModuleDescriptor("dummy", DummyModuleDescriptor.class);
857             initPluginManager(new HostComponentProvider() {
858                 public void provide(final ComponentRegistrar registrar) {
859                 }
860             }, factory);
861 
862             assertEquals(1, pluginAccessor.getEnabledPlugins().size());
863         } finally {
864             tmpDir = oldTmp;
865         }
866     }
867 
868     @Test
869     public void testInstallWithUnsatisifedDependency() throws Exception {
870         File plugin = new PluginJarBuilder("unsatisifiedDependency")
871                 .addFormattedResource("atlassian-plugin.xml",
872                         "<atlassian-plugin name='Test' key='test.plugin' pluginsVersion='2'>",
873                         "    <plugin-info>",
874                         "        <version>1.0</version>",
875                         "    </plugin-info>",
876                         "    <component-import key='foo' interface='java.util.concurrent.Callable' />",
877                         "</atlassian-plugin>")
878                 .build(pluginsDir);
879 
880         long start = System.currentTimeMillis();
881         // Set dev mode temporarily
882         System.setProperty("atlassian.dev.mode", "true");
883         System.setProperty(PluginUtils.ATLASSIAN_PLUGINS_ENABLE_WAIT, "7");
884 
885         initPluginManager();
886 
887         assertTrue(start + (60 * 1000) > System.currentTimeMillis());
888     }
889 
890     @Test
891     public void testInstallSimplePluginNoSpring() throws Exception {
892         File jar = new PluginJarBuilder("strangePath")
893                 .addPluginInformation("no-spring", "foo", "1.0")
894                 .build();
895 
896         initPluginManager();
897         pluginController.installPlugin(new JarPluginArtifact(jar));
898 
899         assertEquals(1, pluginAccessor.getEnabledPlugins().size());
900     }
901 
902     @Test
903     public void testInstallSimplePluginWithNoManifest() throws Exception {
904         File jar = new PluginJarBuilder("strangePath")
905                 .addPluginInformation("no-spring", "foo", "1.0")
906                 .buildWithNoManifest();
907 
908         initPluginManager();
909         pluginController.installPlugin(new JarPluginArtifact(jar));
910 
911         assertEquals(1, pluginAccessor.getEnabledPlugins().size());
912     }
913 
914     @Test
915     public void testInstallPluginTakesTooLong() throws Exception {
916         System.setProperty(PluginUtils.ATLASSIAN_PLUGINS_ENABLE_WAIT, "3");
917         final PluginJarBuilder builder = new PluginJarBuilder("first")
918                 .addFormattedResource("atlassian-plugin.xml",
919                         "<atlassian-plugin name='Test' key='test.plugin' pluginsVersion='2'>",
920                         "    <plugin-info>",
921                         "        <version>1.0</version>",
922                         "    </plugin-info>",
923                         "    <component key='svc' class='my.ServiceImpl' public='true'>",
924                         "    <interface>my.Service</interface>",
925                         "    </component>",
926                         "</atlassian-plugin>")
927                 .addFormattedJava("my.Service",
928                         "package my;",
929                         "public interface Service {",
930                         "    public Object call() throws Exception;",
931                         "}")
932                 .addFormattedJava("my.ServiceImpl",
933                         "package my;",
934                         "public class ServiceImpl implements Service {",
935                         "public ServiceImpl(){",
936                         "try{",
937                         "Thread.sleep(10000);",
938                         "}catch(Exception e){}",
939                         "}",
940                         "    public Object call() throws Exception { ",
941                         "   return 'hi';}",
942                         "}");
943         final File jar = builder.build();
944         initPluginManager();
945 
946         pluginController.installPlugin(new JarPluginArtifact(jar));
947 
948         assertEquals(0, pluginAccessor.getEnabledPlugins().size());
949     }
950 
951     @Test
952     public void testInstallPluginVersion3() throws Exception {
953         initPluginManager();
954 
955         final File v3plugin = new PluginJarBuilder("v3-plugin")
956                 .addFormattedResource("atlassian-plugin.xml",
957                         "<atlassian-plugin name='V3 Plugin' key='v3-plugin' pluginsVersion='3'>",
958                         "    <plugin-info>",
959                         "        <version>1.0</version>",
960                         "        <permissions>",
961                         "            <permission>execute_java</permission>",
962                         "        </permissions>",
963                         "    </plugin-info>",
964                         "</atlassian-plugin>")
965                 .addFormattedResource("META-INF/MANIFEST.MF",
966                         "Bundle-SymbolicName: v3-plugin",
967                         // need the key because of com.atlassian.plugin.osgi.factory.OsgiPluginUninstalledHelper#install,
968                         // and the comparison of keys, using com.atlassian.plugin.osgi.util.OsgiHeaderUtil#getPluginKey !
969                         OsgiPlugin.ATLASSIAN_PLUGIN_KEY + ": v3-plugin",
970                         "Bundle-Version: 1.0.0",
971                         "Manifest-Version: 1.0",
972                         "Spring-Context: *;timeout:=60", // we still need a container, add this to trigger spring.
973                         "")
974                 .build();
975 
976         pluginController.installPlugin(new JarPluginArtifact(v3plugin));
977 
978         assertEquals(1, pluginAccessor.getEnabledPlugins().size());
979         assertNotNull(pluginAccessor.getEnabledPlugin("v3-plugin"));
980     }
981 
982     public static class Callable3Aware {
983         private final Callable3 callable;
984 
985         public Callable3Aware(Callable3 callable) {
986             this.callable = callable;
987         }
988 
989         public String call() throws Exception {
990             return callable.call();
991         }
992     }
993 }