1 package com.atlassian.plugin.osgi.container.felix;
2
3 import java.io.File;
4 import java.net.MalformedURLException;
5 import java.net.URL;
6 import java.net.URLClassLoader;
7 import java.util.Arrays;
8 import java.util.Collection;
9 import java.util.Collections;
10 import java.util.List;
11
12 import javax.management.DescriptorAccess;
13 import javax.print.attribute.AttributeSet;
14 import javax.print.attribute.HashAttributeSet;
15 import javax.servlet.ServletContext;
16 import javax.swing.table.DefaultTableModel;
17 import javax.swing.table.TableModel;
18
19 import com.atlassian.plugin.osgi.container.PackageScannerConfiguration;
20 import com.atlassian.plugin.osgi.container.impl.DefaultPackageScannerConfiguration;
21 import com.atlassian.plugin.osgi.hostcomponents.HostComponentRegistration;
22 import com.atlassian.plugin.osgi.hostcomponents.impl.MockRegistration;
23 import com.atlassian.plugin.testpackage1.Dummy1;
24 import com.atlassian.plugin.util.PluginFrameworkUtils;
25
26 import com.google.common.collect.ImmutableList;
27 import com.google.common.collect.ImmutableMap;
28
29 import org.hamcrest.Description;
30 import org.hamcrest.Matcher;
31 import org.hamcrest.Matchers;
32 import org.hamcrest.TypeSafeMatcher;
33 import org.junit.After;
34 import org.junit.Before;
35 import org.junit.Rule;
36 import org.junit.Test;
37 import org.junit.contrib.java.lang.system.RestoreSystemProperties;
38 import org.junit.rules.ExpectedException;
39 import org.junit.runner.RunWith;
40 import org.mockito.Mock;
41 import org.mockito.runners.MockitoJUnitRunner;
42 import org.twdata.pkgscanner.DefaultOsgiVersionConverter;
43 import org.twdata.pkgscanner.ExportPackage;
44
45 import static com.atlassian.plugin.test.Matchers.fileNamed;
46 import static org.apache.commons.io.FileUtils.toFile;
47 import static org.hamcrest.MatcherAssert.assertThat;
48 import static org.hamcrest.Matchers.any;
49 import static org.hamcrest.Matchers.containsString;
50 import static org.hamcrest.Matchers.equalTo;
51 import static org.hamcrest.Matchers.is;
52 import static org.hamcrest.Matchers.not;
53 import static org.hamcrest.Matchers.notNullValue;
54 import static org.mockito.Mockito.mock;
55 import static org.mockito.Mockito.when;
56
57 @RunWith (MockitoJUnitRunner.class)
58 public class TestExportsBuilder
59 {
60 private static final String JAVA_VERSION_KEY = "java.specification.version";
61
62 @Rule
63 public ExpectedException expectedException = ExpectedException.none();
64
65 @Rule
66 public RestoreSystemProperties restoreSystemProperties =
67 new RestoreSystemProperties(JAVA_VERSION_KEY, ExportsBuilder.getLegacyScanModeProperty());
68
69 @Mock
70 private ExportsBuilder.CachedExportPackageLoader loader;
71
72 private DefaultPackageScannerConfiguration configuration;
73
74 private ExportsBuilder builder;
75
76 @Before
77 public void setUp()
78 {
79 when(loader.load()).thenReturn(null);
80
81 configuration = new DefaultPackageScannerConfiguration();
82 builder = new ExportsBuilder(loader);
83 }
84
85 @After
86 public void tearDown()
87 {
88 builder = null;
89 }
90
91 @Test
92 public void testDetermineExports()
93 {
94 final String exports = builder.determineExports(emptyRegistrations(), configuration);
95 assertThat(exports, not(containsString(",,")));
96 }
97
98 @Test
99 public void testDetermineExportsIncludeServiceInterfaces()
100 {
101 final List<HostComponentRegistration> registrations = ImmutableList.<HostComponentRegistration>of(
102 new MockRegistration(new HashAttributeSet(), AttributeSet.class),
103 new MockRegistration(new DefaultTableModel(), TableModel.class));
104 final String imports = builder.determineExports(registrations, configuration);
105 assertThat(imports, notNullValue());
106 assertThat(imports, containsString(AttributeSet.class.getPackage().getName()));
107 assertThat(imports, containsString("javax.swing.event"));
108 }
109
110 @Test
111 public void testConstructJdkExportsWithJdk6And7()
112 {
113 setJavaVersion(ExportsBuilder.JDK_6);
114 assertThat(exports(), containsString("javax.script"));
115
116 setJavaVersion(ExportsBuilder.JDK_7);
117 assertThat(exports(), containsString("javax.script"));
118 }
119
120 private static void setJavaVersion(final String jdkVersion)
121 {
122 System.setProperty(JAVA_VERSION_KEY, jdkVersion);
123 }
124
125 private String exports()
126 {
127 return builder.determineExports(emptyRegistrations(), configuration);
128 }
129
130 private List<HostComponentRegistration> emptyRegistrations()
131 {
132 return ImmutableList.of();
133 }
134
135 @Test
136 public void testDetermineExportWhileConflictExists()
137 {
138 final DescriptorAccess descriptorAccess = mock(DescriptorAccess.class);
139
140 final List<HostComponentRegistration> registrations = ImmutableList.<HostComponentRegistration>of(
141 new MockRegistration(descriptorAccess, DescriptorAccess.class));
142
143 configuration.setPackageVersions(ImmutableMap.of("javax.management", "1.2.3"));
144
145 final String exports = builder.determineExports(registrations, configuration);
146
147 int packageCount = 0;
148 for(final String imp : exports.split("[,]"))
149 {
150 if (imp.split("[;]")[0].equals("javax.management"))
151 {
152 packageCount++;
153 }
154 }
155
156 assertThat("even though the package is found twice, we must export it only once", packageCount, is(1));
157 assertThat("found earlier always wins", exports, containsString(",javax.management,"));
158 }
159
160 @Test
161 public void testPrecalculatedPackages()
162 {
163 when(loader.load()).thenReturn(new ExportsBuilder.PackageScannerExportsFileLoader("precalc-exports.xml").load());
164 final String exports = builder.determineExports(emptyRegistrations(), configuration);
165 assertThat(exports, containsString("bar;version=1"));
166 }
167
168 @Test
169 public void testPackagesUnderPluginFrameworkExportedAsPluginFrameworkVersion()
170 {
171 configuration.setPackageVersions(ImmutableMap.of("com.atlassian.plugin.testpackage1", "98.76.54"));
172 configuration.setPackageIncludes(ImmutableList.of("org.slf4j*"));
173
174 final ImmutableList<HostComponentRegistration> registrations = ImmutableList.<HostComponentRegistration>of(
175 new MockRegistration(new Dummy1() {}, Dummy1.class));
176 final String exports = builder.determineExports(registrations, configuration);
177
178 final String osgiVersionString = new DefaultOsgiVersionConverter().getVersion(PluginFrameworkUtils.getPluginFrameworkVersion());
179
180 assertThat("packages under com.atlassian.plugin are exported as the framework version",
181 exports, containsString("com.atlassian.plugin.testpackage1;version=" + osgiVersionString + ","));
182 assertThat("packages under com.atlassian.plugin are exported as the framework version",
183 exports, not(containsString("com.atlassian.plugin.testpackage1;version=98.76.54,")));
184 }
185
186 @Test
187 public void defaultGenerateExportsFindsStandardLog4j() throws Exception
188 {
189 configuration.setServletContext(mockServletContext());
190 configuration.setPackageIncludes(Arrays.asList("javax.*", "org.*"));
191
192 final Collection<ExportPackage> exports = builder.generateExports(configuration);
193
194 assertThat(exports, notNullValue());
195
196
197 assertThat(exports,
198 Matchers.<ExportPackage>hasItem(isExportPackage("org.apache.log4j", "1.2.15", "log4j-1.2.15.jar")));
199 }
200
201 @Test
202 public void generateExportsFallsThroughToServletContextScanning() throws Exception
203 {
204 configuration.setServletContext(mockServletContext());
205
206
207 configuration.setJarIncludes(Arrays.asList("testlog*", "mock*"));
208 configuration.setJarExcludes(Arrays.asList("log4j*"));
209 configuration.setPackageIncludes(Arrays.asList("javax.*", "org.*"));
210
211 final Collection<ExportPackage> exports = builder.generateExports(configuration);
212
213 assertThat(exports, notNullValue());
214
215
216 assertThat(exports,
217 Matchers.<ExportPackage>hasItem(isExportPackage("org.apache.log4j", "1.2.15", "testlog-1.2.15.jar")));
218 }
219
220 @Test
221 public void generateExportsFailsWhenFallbackServletContextScanningFails() throws Exception
222 {
223 configuration.setServletContext(mockServletContext());
224 configuration.setJarIncludes(Arrays.asList("testlog4j23*"));
225 configuration.setJarExcludes(Collections.<String>emptyList());
226 configuration.setPackageIncludes(Arrays.asList("javax.*", "org.*"));
227
228 expectedException.expect(IllegalStateException.class);
229 builder.generateExports(configuration);
230 }
231
232 @Test
233 public void generateExportsFailsWhenFallbackAndNoServletContext()
234 {
235
236 configuration.setJarIncludes(Arrays.asList("testlog4j23*"));
237 configuration.setJarExcludes(Collections.<String>emptyList());
238 configuration.setPackageIncludes(Arrays.asList("javax.*", "org.*"));
239
240 expectedException.expect(IllegalStateException.class);
241 builder.generateExports(configuration);
242 }
243
244 @Test
245 public void testGenerateExportsWithCorrectServletVersion() throws Exception
246 {
247 configuration.setServletContext(mockServletContext());
248 configuration.setPackageIncludes(Arrays.asList("javax.*", "org.*"));
249
250 final Collection<ExportPackage> exports = builder.generateExports(configuration);
251
252
253
254 assertThat(exports, Matchers.<ExportPackage>hasItem(isExportPackage("javax.servlet", "5.3.0")));
255
256 assertThat(exports, Matchers.<ExportPackage>hasItem(isExportPackage("javax.servlet.http", "5.3.0")));
257 }
258
259 private ServletContext mockServletContext() throws MalformedURLException
260 {
261 final ServletContext context = mock(ServletContext.class);
262 when(context.getMajorVersion()).thenReturn(5);
263 when(context.getMinorVersion()).thenReturn(3);
264 final ClassLoader classLoader = getClass().getClassLoader();
265 when(context.getResource("/WEB-INF/lib")).thenReturn(classLoader.getResource("scanbase/WEB-INF/lib"));
266 when(context.getResource("/WEB-INF/classes")).thenReturn(classLoader.getResource("scanbase/WEB-INF/classes"));
267 return context;
268 }
269
270 @Test
271 public void testPackagesNotConsidedInPluginsItself()
272 {
273 assertThat(ExportsBuilder.UNDER_PLUGIN_FRAMEWORK.apply("com.atlassian.jira.not.in.plugins"), is(false));
274 assertThat(ExportsBuilder.UNDER_PLUGIN_FRAMEWORK.apply("com.atlassian.plugin.osgi"), is(true));
275 assertThat(ExportsBuilder.UNDER_PLUGIN_FRAMEWORK.apply("com.atlassian.plugin.remotable.cheese"), is(false));
276 assertThat(ExportsBuilder.UNDER_PLUGIN_FRAMEWORK.apply("com.atlassian.plugin.webresource"), is(false));
277 assertThat(ExportsBuilder.UNDER_PLUGIN_FRAMEWORK.apply("com.atlassian.plugin.webresource.transformer"), is(false));
278 assertThat(ExportsBuilder.UNDER_PLUGIN_FRAMEWORK.apply("com.atlassian.plugin.cache.filecache"), is(false));
279 assertThat(ExportsBuilder.UNDER_PLUGIN_FRAMEWORK.apply("com.atlassian.plugin.cache.filecache.impl"), is(false));
280 assertThat(ExportsBuilder.UNDER_PLUGIN_FRAMEWORK.apply("com.atlassian.plugin.web"), is(false));
281 assertThat(ExportsBuilder.UNDER_PLUGIN_FRAMEWORK.apply("com.atlassian.plugin.web.conditions"), is(false));
282 }
283
284 @Test
285 public void testJarWithImplicitDirectories() throws Exception
286 {
287
288
289
290
291
292 final ImplicitDirectoriesTestHelper helper = new ImplicitDirectoriesTestHelper();
293 final Collection<ExportPackage> exportPackages = helper.getExportPackages(builder);
294
295
296
297 assertThat(exportPackages, Matchers.<ExportPackage>hasItem(helper.getLeafPackageMatcher()));
298
299 assertThat(exportPackages, not(Matchers.<ExportPackage>hasItem(helper.getIntermediatePackageMatcher())));
300 }
301
302 @Test
303 public void testLegacyScanMode() throws Exception
304 {
305
306
307
308
309
310
311 System.setProperty(ExportsBuilder.getLegacyScanModeProperty(), "true");
312
313 final ImplicitDirectoriesTestHelper helper = new ImplicitDirectoriesTestHelper();
314 final Collection<ExportPackage> exportPackages = helper.getExportPackages(builder);
315
316
317
318 assertThat(exportPackages, not(Matchers.<ExportPackage>hasItem(helper.getLeafPackageMatcher())));
319
320 assertThat(exportPackages, not(Matchers.<ExportPackage>hasItem(helper.getIntermediatePackageMatcher())));
321 }
322
323 @Test
324 public void productSuppliedServletVersionIsRespected() throws Exception
325 {
326 configuration.setServletContext(mockServletContext());
327 configuration.setPackageIncludes(Arrays.asList("javax.*", "org.*"));
328 configuration.setPackageVersions(ImmutableMap.<String, String>builder().put("javax.servlet*", "4.6").build());
329
330 final Collection<ExportPackage> exports = builder.generateExports(configuration);
331
332
333
334 assertThat(exports, Matchers.<ExportPackage>hasItem(isExportPackage("javax.servlet", "4.6.0")));
335
336 assertThat(exports, Matchers.<ExportPackage>hasItem(isExportPackage("javax.servlet.http", "4.6.0")));
337 }
338
339
340
341
342 private static class ImplicitDirectoriesTestHelper
343 {
344 private static final String JAR_NAME = "implicitDirectories.jar";
345 private static final String ROOT_PACKAGE = "com.atlassian.implicit";
346 private static final String INTERMEDIATE_PACKAGE = ROOT_PACKAGE + ".intermediate";
347 private static final String LEAF_PACKAGE = INTERMEDIATE_PACKAGE + ".leaf";
348 private final File jarFile;
349 private final ClassLoader classLoader;
350 private final PackageScannerConfiguration configuration;
351
352 ImplicitDirectoriesTestHelper()
353 {
354 final URL jarUrl = getClass().getClassLoader().getResource(JAR_NAME);
355 jarFile = toFile(jarUrl);
356 configuration = mock(PackageScannerConfiguration.class);
357 when(configuration.getJarIncludes()).thenReturn(ImmutableList.of("*.jar"));
358 when(configuration.getPackageIncludes()).thenReturn(ImmutableList.of("org.slf4j", ROOT_PACKAGE + ".*"));
359 classLoader = new URLClassLoader(new URL[] { jarUrl });
360 }
361
362 Matcher<ExportPackage> getIntermediatePackageMatcher()
363 {
364 return isExportPackage(INTERMEDIATE_PACKAGE, null, jarFile);
365 }
366
367 Matcher<ExportPackage> getLeafPackageMatcher()
368 {
369 return isExportPackage(LEAF_PACKAGE, null, jarFile);
370 }
371
372 Collection<ExportPackage> getExportPackages(final ExportsBuilder exportsBuilder)
373 {
374 Collection<ExportPackage> exportPackages = null;
375 final ClassLoader savedContextClassLoader = Thread.currentThread().getContextClassLoader();
376 try
377 {
378 Thread.currentThread().setContextClassLoader(classLoader);
379 exportPackages = exportsBuilder.generateExports(configuration);
380 }
381 finally
382 {
383 Thread.currentThread().setContextClassLoader(savedContextClassLoader);
384 }
385 return exportPackages;
386 }
387 }
388
389
390
391
392 private static Matcher<ExportPackage> isExportPackage(final String packageName, final String version)
393 {
394 return new ExportPackageMatcher(packageName, version);
395 }
396
397
398
399
400
401
402 private static Matcher<ExportPackage> isExportPackage(
403 final String packageName, final String version, final String locationName)
404 {
405 return new ExportPackageMatcher(packageName, version, locationName);
406 }
407
408
409
410
411
412
413 private static Matcher<ExportPackage> isExportPackage(
414 final String packageName, final String version, final File location)
415 {
416 return new ExportPackageMatcher(packageName, version, location);
417 }
418
419
420
421
422
423
424
425 private static class ExportPackageMatcher extends TypeSafeMatcher<ExportPackage>
426 {
427 private final String packageName;
428 private final String version;
429 private final Matcher<File> locationMatcher;
430
431 public ExportPackageMatcher(final String packageName, final String version)
432 {
433 this.packageName = packageName;
434 this.version = version;
435 this.locationMatcher = any(File.class);
436 }
437
438 public ExportPackageMatcher(final String packageName, final String version, final String locationName)
439 {
440 this.packageName = packageName;
441 this.version = version;
442 this.locationMatcher = fileNamed(locationName);
443 }
444
445
446 public ExportPackageMatcher(final String packageName, final String version, final File location)
447 {
448 this.packageName = packageName;
449 this.version = version;
450 this.locationMatcher = equalTo(location);
451 }
452
453 public boolean matchesSafely(final ExportPackage exportPackage)
454 {
455 return equalTo(packageName).matches(exportPackage.getPackageName())
456 && equalTo(version).matches(exportPackage.getVersion())
457 && locationMatcher.matches(exportPackage.getLocation());
458 }
459
460 public void describeTo(final Description description)
461 {
462 description.appendText("ExportPackage named ");
463 description.appendValue(packageName);
464 description.appendText(" with version ");
465 description.appendValue(version);
466 description.appendText(" and location ");
467 locationMatcher.describeTo(description);
468 }
469 }
470 }