View Javadoc

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