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