View Javadoc

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