View Javadoc

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