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