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