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