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