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