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.hostcontainer.DefaultHostContainer;
7   import com.atlassian.plugin.osgi.AbstractWaitCondition;
8   import com.atlassian.plugin.osgi.AnotherInterface;
9   import com.atlassian.plugin.osgi.Callable2;
10  import com.atlassian.plugin.osgi.DummyModuleDescriptor;
11  import com.atlassian.plugin.osgi.EventTrackingModuleDescriptor;
12  import com.atlassian.plugin.osgi.PluginInContainerTestBase;
13  import com.atlassian.plugin.osgi.SomeInterface;
14  import com.atlassian.plugin.osgi.container.felix.FelixOsgiContainerManager;
15  import com.atlassian.plugin.osgi.factory.OsgiPlugin;
16  import com.atlassian.plugin.osgi.hostcomponents.ComponentRegistrar;
17  import com.atlassian.plugin.osgi.hostcomponents.HostComponentProvider;
18  import com.atlassian.plugin.test.PluginJarBuilder;
19  import com.atlassian.plugin.util.WaitUntil;
20  import org.osgi.util.tracker.ServiceTracker;
21  
22  import java.io.File;
23  import java.util.Collections;
24  import java.util.concurrent.Callable;
25  
26  public class TestPluginDependencies extends PluginInContainerTestBase
27  {
28      public void testPluginDependentOnPackageImport() throws Exception
29      {
30          PluginJarBuilder parentBuilder =  new PluginJarBuilder("parent")
31                  .addFormattedResource("atlassian-plugin.xml",
32                      "<atlassian-plugin name='Test' key='parent' pluginsVersion='2'>",
33                      "    <plugin-info>",
34                      "        <version>1.0</version>",
35                      "        <bundle-instructions>",
36                      "            <Import-Package>foo</Import-Package>",
37                      "            <Export-Package>foo</Export-Package>",
38                      "        </bundle-instructions>",
39                      "    </plugin-info>",
40                      "</atlassian-plugin>")
41                  .addFormattedJava("foo.Bar",
42                          "package foo;",
43                          "public interface Bar {}");
44  
45          new PluginJarBuilder("child", parentBuilder.getClassLoader())
46                  .addFormattedResource("atlassian-plugin.xml",
47                      "<atlassian-plugin name='Test' key='child' pluginsVersion='2'>",
48                      "    <plugin-info>",
49                      "        <version>1.0</version>",
50                      "    </plugin-info>",
51                      "</atlassian-plugin>")
52                  .addFormattedJava("second.MyImpl",
53                          "package second;",
54                          "public class MyImpl {",
55                          "    public MyImpl(foo.Bar config) {",
56                          "    }",
57                          "}")
58                  .build(pluginsDir);
59  
60          parentBuilder.build(pluginsDir);
61          initPluginManager();
62          assertEquals(2, pluginManager.getEnabledPlugins().size());
63          assertEquals(Collections.singleton("parent"), pluginManager.getPlugin("child").getRequiredPlugins());
64      }
65  
66      public void testPluginDependentOnDynamicPackageImport() throws Exception
67      {
68          PluginJarBuilder parentBuilder =  new PluginJarBuilder("parent")
69                  .addFormattedResource("atlassian-plugin.xml",
70                      "<atlassian-plugin name='Test' key='parent' pluginsVersion='2'>",
71                      "    <plugin-info>",
72                      "        <version>1.0</version>",
73                      "        <bundle-instructions>",
74                      "            <Import-Package>foo</Import-Package>",
75                      "            <Export-Package>foo</Export-Package>",
76                      "        </bundle-instructions>",
77                      "    </plugin-info>",
78                      "</atlassian-plugin>")
79                  .addFormattedJava("foo.Bar",
80                          "package foo;",
81                          "public interface Bar {}");
82  
83          new PluginJarBuilder("child", parentBuilder.getClassLoader())
84                  .addFormattedResource("atlassian-plugin.xml",
85                      "<atlassian-plugin name='Test' key='child' pluginsVersion='2'>",
86                      "    <plugin-info>",
87                      "        <version>1.0</version>",
88                      "        <bundle-instructions>",
89                      "          <Import-Package />",
90                      "          <DynamicImport-Package>foo</DynamicImport-Package>",
91                      "        </bundle-instructions>",
92                      "    </plugin-info>",
93                      "</atlassian-plugin>")
94                  .addFormattedJava("second.MyImpl",
95                          "package second;",
96                          "public class MyImpl {",
97                          "    public MyImpl(foo.Bar config) {",
98                          "    }",
99                          "}")
100                 .build(pluginsDir);
101 
102         parentBuilder.build(pluginsDir);
103         initPluginManager();
104         assertEquals(2, pluginManager.getEnabledPlugins().size());
105         Plugin childPlugin = pluginManager.getPlugin("child");
106         childPlugin.loadClass("foo.Bar", null);
107         assertEquals(Collections.singleton("parent"), childPlugin.getRequiredPlugins());
108     }
109 
110     public void testUninstallingPluginDependentOnPackageImport() throws Exception
111     {
112         PluginJarBuilder parentBuilder =  new PluginJarBuilder("parent")
113                 .addFormattedResource("atlassian-plugin.xml",
114                     "<atlassian-plugin name='Test' key='parent' pluginsVersion='2'>",
115                     "    <plugin-info>",
116                     "        <version>1.0</version>",
117                     "        <bundle-instructions>",
118                     "            <Import-Package>foo</Import-Package>",
119                     "            <Export-Package>foo</Export-Package>",
120                     "        </bundle-instructions>",
121                     "    </plugin-info>",
122                     "</atlassian-plugin>")
123                 .addFormattedJava("foo.Bar",
124                         "package foo;",
125                         "public interface Bar {}");
126 
127         new PluginJarBuilder("child", parentBuilder.getClassLoader())
128                 .addFormattedResource("atlassian-plugin.xml",
129                     "<atlassian-plugin name='Test' key='child' pluginsVersion='2'>",
130                     "    <plugin-info>",
131                     "        <version>1.0</version>",
132                     "    </plugin-info>",
133                     "</atlassian-plugin>")
134                 .addFormattedJava("second.MyImpl",
135                         "package second;",
136                         "public class MyImpl {",
137                         "    public MyImpl(foo.Bar config) {",
138                         "    }",
139                         "}")
140                 .build(pluginsDir);
141 
142         parentBuilder.build(pluginsDir);
143         initPluginManager();
144         assertEquals(2, pluginManager.getEnabledPlugins().size());
145         pluginManager.uninstall(pluginManager.getPlugin("parent"));
146         assertEquals(0, pluginManager.getEnabledPlugins().size());
147     }
148 
149     public void testUpgradeWithNewComponentImplementation() 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                 registrar.register(AnotherInterface.class).forInstance(new AnotherInterface()
160                 {});
161             }
162         }, factory);
163 
164         final File pluginJar = new PluginJarBuilder("first")
165                 .addFormattedResource("atlassian-plugin.xml",
166                     "<atlassian-plugin name='Test' key='test.plugin' pluginsVersion='2'>",
167                     "    <plugin-info>",
168                     "        <version>1.0</version>",
169                     "    </plugin-info>",
170                     "    <component key='svc' class='my.ServiceImpl' public='true'>",
171                     "    <interface>java.util.concurrent.Callable</interface>",
172                     "    </component>",
173                     "</atlassian-plugin>")
174                 .addFormattedJava("my.ServiceImpl",
175                     "package my;",
176                     "import java.util.concurrent.Callable;",
177                     "public class ServiceImpl implements Callable {",
178                     "    public Object call() throws Exception { return 'hi';}",
179                     "}")
180                 .build();
181         final File pluginJar2 = new PluginJarBuilder("second")
182                 .addFormattedResource("atlassian-plugin.xml",
183                     "<atlassian-plugin name='Test 2' key='test2.plugin' pluginsVersion='2'>",
184                     "    <plugin-info>",
185                     "        <version>1.0</version>",
186                     "    </plugin-info>",
187                     "    <component-import key='svc' interface='java.util.concurrent.Callable' />",
188                     "    <component key='del' class='my2.ServiceDelegate' public='true'>",
189                     "    <interface>com.atlassian.plugin.osgi.Callable2</interface>",
190                     "    </component>",
191                     "</atlassian-plugin>")
192                 .addFormattedJava("my2.ServiceDelegate",
193                     "package my2;",
194                     "import com.atlassian.plugin.osgi.Callable2;",
195                     "import java.util.concurrent.Callable;",
196                     "public class ServiceDelegate implements Callable2 {",
197                     "    private final Callable delegate;",
198                     "    public ServiceDelegate(Callable foo) { this.delegate = foo;}",
199                     "    public String call() throws Exception { return (String)delegate.call();}",
200                     "}")
201                 .build();
202 
203         pluginManager.installPlugin(new JarPluginArtifact(pluginJar));
204         assertEquals(1, pluginManager.getEnabledPlugins().size());
205 
206         pluginManager.installPlugin(new JarPluginArtifact(pluginJar2));
207         final ServiceTracker tracker = osgiContainerManager.getServiceTracker("com.atlassian.plugin.osgi.Callable2");
208 
209         for (final Object svc : tracker.getServices())
210         {
211             final Callable2 callable = (Callable2) svc;
212             assertEquals("hi", callable.call());
213         }
214         assertEquals(2, pluginManager.getEnabledPlugins().size());
215 
216         final File updatedJar = new PluginJarBuilder("first")
217                 .addFormattedResource("atlassian-plugin.xml",
218                     "<atlassian-plugin name='Test' key='test.plugin' pluginsVersion='2'>",
219                     "    <plugin-info>",
220                     "        <version>1.0</version>",
221                     "    </plugin-info>",
222                     "    <component key='svc' class='my.ServiceImpl' public='true'>",
223                     "    <interface>java.util.concurrent.Callable</interface>",
224                     "    </component>",
225                     "</atlassian-plugin>")
226                 .addFormattedJava("my.ServiceImpl",
227                     "package my;",
228                     "import java.util.concurrent.Callable;",
229                     "public class ServiceImpl implements Callable {",
230                     "    public Object call() throws Exception { return 'bob';}",
231                     "}")
232                 .build();
233 
234         pluginManager.installPlugin(new JarPluginArtifact(updatedJar));
235         WaitUntil.invoke(new AbstractWaitCondition()
236         {
237             public boolean isFinished()
238             {
239                 return pluginManager.getEnabledPlugins().size() == 2;
240             }
241         });
242         assertEquals(2, pluginManager.getEnabledPlugins().size());
243         for (final Object svc : tracker.getServices())
244         {
245             final Callable2 callable = (Callable2) svc;
246             assertEquals("bob", callable.call());
247         }
248     }
249 
250     public void testUpgradeWithNewComponentImplementationWithInterfaceInPlugin() throws Exception
251     {
252         final DefaultModuleDescriptorFactory factory = new DefaultModuleDescriptorFactory(new DefaultHostContainer());
253         factory.addModuleDescriptor("dummy", DummyModuleDescriptor.class);
254         initPluginManager(new HostComponentProvider()
255         {
256             public void provide(final ComponentRegistrar registrar)
257             {
258                 registrar.register(SomeInterface.class).forInstance(new SomeInterface()
259                 {});
260                 registrar.register(AnotherInterface.class).forInstance(new AnotherInterface()
261                 {});
262             }
263         }, factory);
264 
265         final PluginJarBuilder builder1 = new PluginJarBuilder("first")
266                 .addFormattedResource("atlassian-plugin.xml",
267                         "<atlassian-plugin name='Test' key='test.plugin' pluginsVersion='2'>",
268                         "    <plugin-info>",
269                         "        <version>1.0</version>",
270                         "    </plugin-info>",
271                         "    <component key='svc' class='my.ServiceImpl' public='true'>",
272                         "    <interface>my.Service</interface>",
273                         "    </component>",
274                         "</atlassian-plugin>")
275                 .addFormattedJava("my.Service",
276                         "package my;",
277                         "public interface Service {",
278                         "    public Object call() throws Exception;",
279                         "}")
280                 .addFormattedJava("my.ServiceImpl",
281                         "package my;",
282                         "public class ServiceImpl implements Service {",
283                         "    public Object call() throws Exception { return 'hi';}",
284                         "}");
285         final File pluginJar = builder1.build();
286         final File pluginJar2 = new PluginJarBuilder("second", builder1.getClassLoader())
287                 .addFormattedResource("atlassian-plugin.xml",
288                         "<atlassian-plugin name='Test 2' key='test2.plugin' pluginsVersion='2'>",
289                         "    <plugin-info>",
290                         "        <version>1.0</version>",
291                         "    </plugin-info>",
292                         "    <component-import key='svc' interface='my.Service' />",
293                         "    <component key='del' class='my2.ServiceDelegate' public='true'>",
294                         "    <interface>java.util.concurrent.Callable</interface>",
295                         "    </component>",
296                         "</atlassian-plugin>")
297                 .addFormattedJava("my2.ServiceDelegate",
298                         "package my2;",
299                         "import my.Service;",
300                         "import java.util.concurrent.Callable;",
301                         "public class ServiceDelegate implements Callable {",
302                         "    private final Service delegate;",
303                         "    public ServiceDelegate(Service foo) { this.delegate = foo;}",
304                         "    public Object call() throws Exception { return delegate.call();}",
305                         "}")
306                 .build();
307 
308         pluginManager.installPlugin(new JarPluginArtifact(pluginJar));
309         assertEquals(1, pluginManager.getEnabledPlugins().size());
310         final ServiceTracker tracker = osgiContainerManager.getServiceTracker(Callable.class.getName());
311         final ServiceTracker svcTracker = osgiContainerManager.getServiceTracker("my.Service");
312 
313         pluginManager.installPlugin(new JarPluginArtifact(pluginJar2));
314         assertEquals("hi", svcTracker.getService().getClass().getMethod("call").invoke(svcTracker.getService()));
315         assertEquals("hi", ((Callable) tracker.getService()).call());
316 
317         assertEquals(2, pluginManager.getEnabledPlugins().size());
318 
319         final File updatedJar = new PluginJarBuilder("first")
320                 .addFormattedResource("atlassian-plugin.xml",
321                         "<atlassian-plugin name='Test' key='test.plugin' pluginsVersion='2'>",
322                         "    <plugin-info>",
323                         "        <version>1.0</version>",
324                         "    </plugin-info>",
325                         "    <component key='svc' class='my.Service2Impl' public='true'>",
326                         "    <interface>my.Service</interface>",
327                         "    </component>",
328                         "</atlassian-plugin>")
329                 .addFormattedJava("my.Service",
330                         "package my;",
331                         "public interface Service {",
332                         "    public Object call() throws Exception;",
333                         "}")
334                 .addFormattedJava("my.Service2Impl",
335                         "package my;",
336                         "public class Service2Impl implements Service {",
337                         "    public Object call() throws Exception {return 'bob';}",
338                         "}")
339                 .build();
340 
341         pluginManager.installPlugin(new JarPluginArtifact(updatedJar));
342         assertEquals("bob", svcTracker.getService().getClass().getMethod("call").invoke(svcTracker.getService()));
343         tracker.waitForService(5000);
344         assertEquals("bob", ((Callable) tracker.getService()).call());
345     }
346 
347     public void testUpgradeWithRefreshingAffectingOtherPlugins() throws Exception
348     {
349         final DefaultModuleDescriptorFactory factory = new DefaultModuleDescriptorFactory(new DefaultHostContainer());
350         initPluginManager(new HostComponentProvider()
351         {
352             public void provide(final ComponentRegistrar registrar)
353             {
354             }
355         }, factory);
356 
357         PluginJarBuilder pluginBuilder = new PluginJarBuilder("first")
358                 .addFormattedResource("atlassian-plugin.xml",
359                     "<atlassian-plugin name='Test' key='test.plugin' pluginsVersion='2'>",
360                     "    <plugin-info>",
361                     "        <version>1.0</version>",
362                     "        <bundle-instructions><Export-Package>my</Export-Package></bundle-instructions>",
363                     "    </plugin-info>",
364                     "    <component key='svc' class='my.ServiceImpl' public='true'>",
365                     "    <interface>java.util.concurrent.Callable</interface>",
366                     "    </component>",
367                     "</atlassian-plugin>")
368                 .addFormattedJava("my.ServiceImpl",
369                     "package my;",
370                     "import java.util.concurrent.Callable;",
371                     "public class ServiceImpl implements Callable {",
372                     "    public Object call() throws Exception { return 'hi';}",
373                     "}");
374         final File pluginJar = pluginBuilder.build();
375 
376         final File pluginJar2 = new PluginJarBuilder("second", pluginBuilder.getClassLoader())
377                 .addFormattedResource("atlassian-plugin.xml",
378                     "<atlassian-plugin name='Test 2' key='test2.plugin' pluginsVersion='2'>",
379                     "    <plugin-info>",
380                     "        <version>1.0</version>",
381                     "        <bundle-instructions><Import-Package>my,*</Import-Package></bundle-instructions>",
382                     "    </plugin-info>",
383                     "    <component-import key='svc' interface='java.util.concurrent.Callable' />",
384                     "    <component-import key='othersvc' interface='com.atlassian.plugin.osgi.Callable3' />",
385                     "    <component key='del' class='my2.ServiceDelegate' public='true'>",
386                     "    <interface>com.atlassian.plugin.osgi.Callable2</interface>",
387                     "    </component>",
388                     "</atlassian-plugin>")
389                 .addFormattedJava("my2.ServiceDelegate",
390                     "package my2;",
391                     "import com.atlassian.plugin.osgi.Callable2;",
392                     "import com.atlassian.plugin.osgi.Callable3;",
393                     "import java.util.concurrent.Callable;",
394                     "public class ServiceDelegate implements Callable2 {",
395                     "    private final Callable delegate;",
396                     "    private final Callable3 othersvc;",
397                     "    public ServiceDelegate(Callable foo,Callable3 othersvc) {",
398                     "        this.delegate = foo;",
399                     "        this.othersvc = othersvc;",
400                     "    }",
401                     "    public String call() throws Exception { return othersvc.call() + (String)delegate.call();}",
402                     "}")
403                 .build();
404         final File otherSvcJar = new PluginJarBuilder("otherSvc")
405                 .addFormattedResource("atlassian-plugin.xml",
406                     "<atlassian-plugin name='Test' key='test.othersvc.plugin' pluginsVersion='2'>",
407                     "    <plugin-info>",
408                     "        <version>1.0</version>",
409                     "    </plugin-info>",
410                     "    <component key='othersvc' class='othersvc.ServiceImpl' public='true'>",
411                     "    <interface>com.atlassian.plugin.osgi.Callable3</interface>",
412                     "    </component>",
413                     "</atlassian-plugin>")
414                 .addFormattedJava("othersvc.ServiceImpl",
415                     "package othersvc;",
416                     "import com.atlassian.plugin.osgi.Callable3;",
417                     "public class ServiceImpl implements Callable3 {",
418                     "    public String call() throws Exception { return 'hi';}",
419                     "}")
420                 .build();
421 
422 
423         pluginManager.installPlugin(new JarPluginArtifact(pluginJar));
424         assertEquals(1, pluginManager.getEnabledPlugins().size());
425 
426         pluginManager.installPlugin(new JarPluginArtifact(otherSvcJar));
427         pluginManager.installPlugin(new JarPluginArtifact(pluginJar2));
428 
429         assertEquals("hi", ((OsgiPlugin)pluginManager.getPlugin("test2.plugin")).autowire(TestPluginInstall.Callable3Aware.class).call());
430         assertEquals(3, pluginManager.getEnabledPlugins().size());
431 
432         final File updatedJar = new PluginJarBuilder("first")
433                 .addFormattedResource("atlassian-plugin.xml",
434                     "<atlassian-plugin name='Test' key='test.plugin' pluginsVersion='2'>",
435                     "    <plugin-info>",
436                     "        <version>1.0</version>",
437                     "        <bundle-instructions><Export-Package>my</Export-Package></bundle-instructions>",
438                     "    </plugin-info>",
439                     "    <component key='svc' class='my.ServiceImpl' public='true'>",
440                     "    <interface>java.util.concurrent.Callable</interface>",
441                     "    </component>",
442                     "</atlassian-plugin>")
443                 .addFormattedJava("my.ServiceImpl",
444                     "package my;",
445                     "import java.util.concurrent.Callable;",
446                     "public class ServiceImpl implements Callable {",
447                     "    public Object call() throws Exception { return 'bob';}",
448                     "}")
449                 .build();
450 
451         pluginManager.installPlugin(new JarPluginArtifact(updatedJar));
452         assertEquals(3, pluginManager.getEnabledPlugins().size());
453         assertEquals("hi", ((OsgiPlugin)pluginManager.getPlugin("test2.plugin")).autowire(TestPluginInstall.Callable3Aware.class).call());
454     }
455 
456     public void testUpgradeOfBundledPluginWithRefreshingAffectingOtherPluginsCheckingModuleEvents() throws Exception
457     {
458         DefaultModuleDescriptorFactory factory = new DefaultModuleDescriptorFactory(new DefaultHostContainer());
459         factory.addModuleDescriptor("dummy", EventTrackingModuleDescriptor.class);
460 
461 
462         PluginJarBuilder hostBuilder = new PluginJarBuilder("first")
463                 .addFormattedResource("atlassian-plugin.xml",
464                     "<atlassian-plugin name='Test' key='host' pluginsVersion='2'>",
465                     "    <plugin-info>",
466                     "        <version>1.0</version>",
467                     "        <bundle-instructions><Export-Package>my</Export-Package></bundle-instructions>",
468                     "    </plugin-info>",
469                     "    <dummy key='foo'/>",
470                     "</atlassian-plugin>")
471                 .addFormattedJava("my.Serviceable",
472                     "package my;",
473                     "public interface Serviceable {}");
474         final File hostJar = hostBuilder.build();
475 
476         final File clientJar = new PluginJarBuilder("second", hostBuilder.getClassLoader())
477                 .addFormattedResource("atlassian-plugin.xml",
478                     "<atlassian-plugin name='Test 2' key='client' pluginsVersion='2'>",
479                     "    <plugin-info>",
480                     "        <version>1.0</version>",
481                     "    </plugin-info>",
482                     "    <dummy key='foo'/>",
483                     "</atlassian-plugin>")
484                 .addFormattedJava("my2.Service",
485                     "package my2;",
486                     "public class Service implements my.Serviceable {}")
487                 .build();
488 
489         initBundlingPluginManager(factory, hostJar);
490         assertEquals(1, pluginManager.getEnabledPlugins().size());
491 
492         pluginManager.installPlugin(new JarPluginArtifact(clientJar));
493 
494         EventTrackingModuleDescriptor hostModule = (EventTrackingModuleDescriptor) pluginManager.getPlugin("host").getModuleDescriptor("foo");
495         assertEquals(1, hostModule.getEnabledCount());
496         assertEquals(0, hostModule.getDisabledCount());
497 
498         EventTrackingModuleDescriptor clientModule = (EventTrackingModuleDescriptor) pluginManager.getPlugin("client").getModuleDescriptor("foo");
499         assertEquals(1, clientModule.getEnabledCount());
500         assertEquals(0, clientModule.getDisabledCount());
501 
502         pluginManager.installPlugin(new JarPluginArtifact(hostJar));
503 
504         hostModule = (EventTrackingModuleDescriptor) pluginManager.getPlugin("host").getModuleDescriptor("foo");
505         assertEquals(1, hostModule.getEnabledCount());
506         assertEquals(0, hostModule.getDisabledCount());
507 
508         clientModule = (EventTrackingModuleDescriptor) pluginManager.getPlugin("client").getModuleDescriptor("foo");
509         assertEquals(1, clientModule.getDisabledCount());
510         assertEquals(2, clientModule.getEnabledCount());
511     }
512 
513     public void testUpgradeWithRefreshingAffectingOtherPluginsWithClassLoadingOnShutdown() throws Exception
514     {
515         final DefaultModuleDescriptorFactory factory = new DefaultModuleDescriptorFactory(new DefaultHostContainer());
516         initPluginManager(new HostComponentProvider()
517         {
518             public void provide(final ComponentRegistrar registrar)
519             {
520             }
521         }, factory);
522 
523         PluginJarBuilder pluginBuilder = new PluginJarBuilder("first")
524                 .addFormattedResource("atlassian-plugin.xml",
525                     "<atlassian-plugin name='Test' key='test.plugin' pluginsVersion='2'>",
526                     "    <plugin-info>",
527                     "        <version>1.0</version>",
528                     "        <bundle-instructions><Export-Package>my</Export-Package></bundle-instructions>",
529                     "    </plugin-info>",
530                     "    <component key='svc' class='my.ServiceImpl' public='true'>",
531                     "    <interface>java.util.concurrent.Callable</interface>",
532                     "    </component>",
533                     "</atlassian-plugin>")
534                 .addFormattedJava("my.ServiceImpl",
535                     "package my;",
536                     "import java.util.concurrent.Callable;",
537                     "public class ServiceImpl implements Callable {",
538                     "    public Object call() throws Exception { return 'hi';}",
539                     "}");
540         final File pluginJar = pluginBuilder.build();
541 
542         final File pluginJar2 = new PluginJarBuilder("second", pluginBuilder.getClassLoader())
543                 .addFormattedResource("atlassian-plugin.xml",
544                     "<atlassian-plugin name='Test 2' key='test2.plugin' pluginsVersion='2'>",
545                     "    <plugin-info>",
546                     "        <version>1.0</version>",
547                     "        <bundle-instructions><Import-Package>my,*</Import-Package>",
548                     "          <DynamicImport-Package>foo</DynamicImport-Package></bundle-instructions>",
549                     "    </plugin-info>",
550                     "    <component-import key='svc' interface='java.util.concurrent.Callable' />",
551                     "    <component-import key='othersvc' interface='com.atlassian.plugin.osgi.Callable3' />",
552                     "    <component key='del' class='my2.ServiceDelegate' public='true'>",
553                     "    <interface>com.atlassian.plugin.osgi.Callable2</interface>",
554                     "    </component>",
555                     "</atlassian-plugin>")
556                 .addFormattedJava("my2.ServiceDelegate",
557                     "package my2;",
558                     "import com.atlassian.plugin.osgi.Callable2;",
559                     "import com.atlassian.plugin.osgi.Callable3;",
560                     "import java.util.concurrent.Callable;",
561                     "public class ServiceDelegate implements Callable2, org.springframework.beans.factory.DisposableBean {",
562                     "    private final Callable delegate;",
563                     "    private final Callable3 othersvc;",
564                     "    public ServiceDelegate(Callable foo,Callable3 othersvc) {",
565                     "        this.delegate = foo;",
566                     "        this.othersvc = othersvc;",
567                     "    }",
568                     "    public void destroy() {",
569                     "       try {",
570                     "          getClass().getClassLoader().loadClass('foo.bar');",
571                     "       } catch (ClassNotFoundException ex) {}",
572                     "    }",
573                     "    public String call() throws Exception { return othersvc.call() + (String)delegate.call();}",
574                     "}")
575                 .build();
576         final File otherSvcJar = new PluginJarBuilder("otherSvc")
577                 .addFormattedResource("atlassian-plugin.xml",
578                     "<atlassian-plugin name='Test' key='test.othersvc.plugin' pluginsVersion='2'>",
579                     "    <plugin-info>",
580                     "        <version>1.0</version>",
581                     "    </plugin-info>",
582                     "    <component key='othersvc' class='othersvc.ServiceImpl' public='true'>",
583                     "    <interface>com.atlassian.plugin.osgi.Callable3</interface>",
584                     "    </component>",
585                     "</atlassian-plugin>")
586                 .addFormattedJava("othersvc.ServiceImpl",
587                     "package othersvc;",
588                     "import com.atlassian.plugin.osgi.Callable3;",
589                     "public class ServiceImpl implements Callable3 {",
590                     "    public String call() throws Exception { return 'hi';}",
591                     "}")
592                 .build();
593 
594 
595         pluginManager.installPlugin(new JarPluginArtifact(pluginJar));
596         assertEquals(1, pluginManager.getEnabledPlugins().size());
597 
598         pluginManager.installPlugin(new JarPluginArtifact(otherSvcJar));
599         pluginManager.installPlugin(new JarPluginArtifact(pluginJar2));
600 
601         assertEquals("hi", ((OsgiPlugin)pluginManager.getPlugin("test2.plugin")).autowire(TestPluginInstall.Callable3Aware.class).call());
602         assertEquals(3, pluginManager.getEnabledPlugins().size());
603 
604         final File updatedJar = new PluginJarBuilder("first")
605                 .addFormattedResource("atlassian-plugin.xml",
606                     "<atlassian-plugin name='Test' key='test.plugin' pluginsVersion='2'>",
607                     "    <plugin-info>",
608                     "        <version>1.0</version>",
609                     "        <bundle-instructions><Export-Package>my</Export-Package></bundle-instructions>",
610                     "    </plugin-info>",
611                     "    <component key='svc' class='my.ServiceImpl' public='true'>",
612                     "    <interface>java.util.concurrent.Callable</interface>",
613                     "    </component>",
614                     "</atlassian-plugin>")
615                 .addFormattedJava("my.ServiceImpl",
616                     "package my;",
617                     "import java.util.concurrent.Callable;",
618                     "public class ServiceImpl implements Callable {",
619                     "    public Object call() throws Exception { return 'bob';}",
620                     "}")
621                 .build();
622 
623         long start = System.currentTimeMillis();
624         pluginManager.installPlugin(new JarPluginArtifact(updatedJar));
625         long timeWaitingForRefresh = System.currentTimeMillis() - start;
626         assertTrue("Refresh seemed to have timed out, which is bad", timeWaitingForRefresh < FelixOsgiContainerManager.REFRESH_TIMEOUT * 1000);
627         assertEquals(3, pluginManager.getEnabledPlugins().size());
628         assertEquals("hi", ((OsgiPlugin)pluginManager.getPlugin("test2.plugin")).autowire(TestPluginInstall.Callable3Aware.class).call());
629     }
630 
631     public void testUninstallWithShutdownAffectingOtherPluginsWithClassLoadingOnShutdown() throws Exception
632     {
633         final DefaultModuleDescriptorFactory factory = new DefaultModuleDescriptorFactory(new DefaultHostContainer());
634         initPluginManager(new HostComponentProvider()
635         {
636             public void provide(final ComponentRegistrar registrar)
637             {
638             }
639         }, factory);
640 
641         PluginJarBuilder pluginBuilder = new PluginJarBuilder("first")
642                 .addFormattedResource("atlassian-plugin.xml",
643                     "<atlassian-plugin name='Test' key='test.plugin' pluginsVersion='2'>",
644                     "    <plugin-info>",
645                     "        <version>1.0</version>",
646                     "        <bundle-instructions><Export-Package>my</Export-Package></bundle-instructions>",
647                     "    </plugin-info>",
648                     "    <component key='svc' class='my.ServiceImpl' public='true'>",
649                     "    <interface>java.util.concurrent.Callable</interface>",
650                     "    </component>",
651                     "</atlassian-plugin>")
652                 .addFormattedJava("my.ServiceImpl",
653                     "package my;",
654                     "import java.util.concurrent.Callable;",
655                     "public class ServiceImpl implements Callable {",
656                     "    public Object call() throws Exception { return 'hi';}",
657                     "}");
658         final File pluginJar = pluginBuilder.build();
659 
660         final File pluginJar2 = new PluginJarBuilder("second", pluginBuilder.getClassLoader())
661                 .addFormattedResource("atlassian-plugin.xml",
662                     "<atlassian-plugin name='Test 2' key='test2.plugin' pluginsVersion='2'>",
663                     "    <plugin-info>",
664                     "        <version>1.0</version>",
665                     "        <bundle-instructions><Import-Package>my,*</Import-Package>",
666                     "          <DynamicImport-Package>foo</DynamicImport-Package></bundle-instructions>",
667                     "    </plugin-info>",
668                     "    <component-import key='svc' interface='java.util.concurrent.Callable' />",
669                     "    <component key='del' class='my2.Consumer'/>",
670                     "</atlassian-plugin>")
671                 .addFormattedJava("my2.Consumer",
672                     "package my2;",
673                     "import java.util.concurrent.Callable;",
674                     "public class Consumer implements org.springframework.beans.factory.DisposableBean {",
675                     "    private final Callable delegate;",
676                     "    public Consumer(Callable foo) {",
677                     "        this.delegate = foo;",
678                     "    }",
679                     "    public void destroy() {",
680                     "       try {",
681                     "          getClass().getClassLoader().loadClass('foo.bar');",
682                     "       } catch (ClassNotFoundException ex) {}",
683                     "    }",
684                     "}")
685                 .build();
686 
687         pluginManager.installPlugin(new JarPluginArtifact(pluginJar));
688         assertEquals(1, pluginManager.getEnabledPlugins().size());
689 
690         pluginManager.installPlugin(new JarPluginArtifact(pluginJar2));
691 
692         assertEquals(2, pluginManager.getEnabledPlugins().size());
693 
694         long start = System.currentTimeMillis();
695         pluginManager.uninstall(pluginManager.getPlugin("test.plugin"));
696         long timeWaitingForRefresh = System.currentTimeMillis() - start;
697         assertTrue("Refresh seemed to have timed out, which is bad", timeWaitingForRefresh < FelixOsgiContainerManager.REFRESH_TIMEOUT * 1000);
698         assertEquals(0, pluginManager.getEnabledPlugins().size());
699     }
700 }