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