package test; import java.io.File; import java.io.IOException; import java.util.Arrays; import java.util.Collection; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Properties; import java.util.StringTokenizer; import java.util.TreeSet; import java.util.jar.Attributes; import java.util.jar.Manifest; import aQute.bnd.header.Attrs; import aQute.bnd.header.Parameters; import aQute.bnd.osgi.Analyzer; import aQute.bnd.osgi.Builder; import aQute.bnd.osgi.Clazz; import aQute.bnd.osgi.Constants; import aQute.bnd.osgi.Domain; import aQute.bnd.osgi.FileResource; import aQute.bnd.osgi.Jar; import aQute.bnd.osgi.Packages; import aQute.bnd.osgi.Processor; import aQute.bnd.test.BndTestCase; import aQute.lib.io.IO; class T0 {} abstract class T1 extends T0 {} class T2 extends T1 {} class T3 extends T2 {} @SuppressWarnings("resource") public class AnalyzerTest extends BndTestCase { static File cwd = new File(System.getProperty("user.dir")); /** * #1352 support globbing during includeresource's buildpath reference * resolution * * @throws Exception */ public void testIncludeResourceFromClasspathWithGlobbing() throws Exception { Builder b = new Builder(); b.addClasspath(IO.getFile("jar/osgi.jar")); b.addClasspath(IO.getFile("jar/asm.jar")); b.addClasspath(IO.getFile("jar/bcel.jar")); b.setProperty("-includeresource", "@o*i.jar, a*m.jar"); Jar jar = b.build(); assertTrue(b.check()); assertNotNull(jar.getResource("LICENSE")); assertNotNull(jar.getResource("asm.jar")); } public void testIncludeResourceFromClasspathWithGlobbingMultiple() throws Exception { Builder b = new Builder(); b.addClasspath(IO.getFile("jar/osgi.jar")); b.addClasspath(IO.getFile("jar/asm.jar")); b.addClasspath(IO.getFile("jar/bcel.jar")); List<Jar> jars = b.getJarsFromName("*.jar", ""); assertEquals(3, jars.size()); } /** * #525 Test if exceptions are imported */ public void testExceptionImports() throws Exception { Builder b = new Builder(); b.addClasspath(new File("bin")); b.addClasspath(IO.getFile("jar/osgi.jar")); b.setExportPackage("test.exceptionimport"); b.build(); assertTrue(b.check()); assertNotNull(b.getImports().containsFQN("org.osgi.framework")); } /** * Verify that the OSGi and the bnd Version annotation both work */ public void testVersionAnnotation() throws Exception { Builder b = new Builder(); try { b.addClasspath(new File("bin")); b.setExportPackage("test.version.annotations.*"); b.build(); assertTrue(b.check()); b.getJar().getManifest().write(System.out); Attrs bnd = b.getExports().getByFQN("test.version.annotations.bnd"); Attrs osgi = b.getExports().getByFQN("test.version.annotations.osgi"); assertEquals("1.2.3.bnd", bnd.getVersion()); assertEquals("1.2.3.osgi", osgi.getVersion()); } finally { b.close(); } } /** * For the following annotation class in an OSGi * bundle @Retention(RetentionPolicy.RUNTIME) @Target({ ElementType.TYPE, * ElementType.METHOD }) public @interface Transactional { @Nonbinding * Class<? extends Annotation>[] qualifier() default Any.class; } * maven-bundle-plugin fails to generate the package import for * javax.enterprise.inject.Any, the default value of the annotation method. * At runtime, this leads to a non-descriptive exception * * <pre> * Caused by: * java.lang.ArrayStoreException: * sun.reflect.annotation.TypeNotPresentExceptionProxy at * sun.reflect.annotation.AnnotationParser.parseClassArray(AnnotationParser. * java:673) ~[na:1.7.0_04] at * sun.reflect.annotation.AnnotationParser.parseArray(AnnotationParser.java: * 480) ~[na:1.7.0_04] at * sun.reflect.annotation.AnnotationParser.parseMemberValue(AnnotationParser * .java:306) ~[na:1.7.0_04] at * java.lang.reflect.Method.getDefaultValue(Method.java:726) ~[na:1.7.0_04] * at sun.reflect.annotation.AnnotationType.<init>(AnnotationType.java:117) * ~[na:1.7.0_04] at * sun.reflect.annotation.AnnotationType.getInstance(AnnotationType.java:84) * ~[na:1.7.0_04] at * sun.reflect.annotation.AnnotationParser.parseAnnotation(AnnotationParser. * java:221) ~[na:1.7.0_04] at * sun.reflect.annotation.AnnotationParser.parseAnnotations2( * AnnotationParser.java:88) ~[na:1.7.0_04] at * sun.reflect.annotation.AnnotationParser.parseAnnotations(AnnotationParser * .java:70) ~[na:1.7.0_04] at * java.lang.Class.initAnnotationsIfNecessary(Class.java:3089) * ~[na:1.7.0_04] at java.lang.Class.getDeclaredAnnotations(Class.java:3077) * ~[na:1.7.0_04] * </pre> */ public void testAnnotationWithDefaultClass() throws Exception { Builder b = new Builder(); try { b.addClasspath(IO.getFile(cwd, "bin")); b.setExportPackage("test.annotation"); b.build(); assertTrue(b.check()); assertTrue(b.getExports().containsKey(b.getPackageRef("test/annotation"))); assertFalse(b.getContained().containsKey(b.getPackageRef("test/annotation/any"))); assertTrue(b.getImports().containsKey(b.getPackageRef("test/annotation/any"))); } finally { b.close(); } } /** * Check the cross references */ /** * The -removeheaders header can be used as a whitelist. */ public static void testRemoveheadersAsWhiteList() throws Exception { Builder b = new Builder(); try { b.addClasspath(IO.getFile("jar/asm.jar")); b.setExportPackage("*"); b.setImportPackage("something"); b.set("Foo", "Foo"); b.set("Bar", "Bar"); b.set(Constants.REMOVEHEADERS, "!Bundle-*,!*-Package,!Service-Component,*"); b.build(); assertTrue(b.check()); Manifest m = b.getJar().getManifest(); assertNotNull(m.getMainAttributes().getValue(Constants.BUNDLE_MANIFESTVERSION)); assertNotNull(m.getMainAttributes().getValue(Constants.BUNDLE_NAME)); assertNotNull(m.getMainAttributes().getValue(Constants.BUNDLE_SYMBOLICNAME)); assertNotNull(m.getMainAttributes().getValue(Constants.IMPORT_PACKAGE)); assertNotNull(m.getMainAttributes().getValue(Constants.EXPORT_PACKAGE)); assertNull(m.getMainAttributes().getValue("Foo")); assertNull(m.getMainAttributes().getValue("Bar")); } finally { b.close(); } } /** * Check if bnd detects references to private packages and gives a warning. */ public static void testExportReferencesToPrivatePackages() throws Exception { Builder b = new Builder(); try { b.addClasspath(IO.getFile("jar/osgi.jar")); b.addClasspath(new File("bin")); b.setExportPackage("test.referApi"); // refers to Event Admin b.setConditionalPackage("org.osgi.service.*"); Jar jar = b.build(); assertTrue(b.check( "((, )?(org.osgi.service.event|org.osgi.service.component|org.osgi.service.http|org.osgi.service.log|org.osgi.service.condpermadmin|org.osgi.service.wireadmin|org.osgi.service.device)){7,7}")); } finally { b.close(); } } /** * Test basic functionality of he BCP */ public static void testBundleClasspath() throws Exception { Builder b = new Builder(); try { b.setProperty(Constants.BUNDLE_CLASSPATH, "foo"); b.setProperty(Constants.INCLUDE_RESOURCE, "foo/test/refer=bin/test/refer"); b.setProperty(Constants.EXPORT_CONTENTS, "test.refer"); Jar jar = b.build(); Manifest m = jar.getManifest(); assertTrue(b.check()); m.write(System.err); } finally { b.close(); } } public static void testBundleClasspathWithVersionedExports() throws Exception { Builder b = new Builder(); try { b.setBundleClasspath("foo"); b.setIncludeResource("foo/test/refer_versioned=bin/test/refer_versioned"); b.setProperty(Constants.EXPORT_CONTENTS, "${packages;ANNOTATED;org.osgi.annotation.versioning.Version}"); Jar jar = b.build(); Manifest m = jar.getManifest(); assertTrue(b.check()); Packages exports = b.getExports(); String version = exports.getByFQN("test.refer_versioned").getVersion(); assertEquals("3.5.7", version); m.write(System.err); } finally { b.close(); } } /** * Very basic sanity test */ public static void testSanity() throws Exception { Builder b = new Builder(); try { b.set("Export-Package", "thinlet;version=1.0"); b.addClasspath(IO.getFile("jar/thinlet.jar")); b.build(); assertTrue(b.check()); assertEquals("1.0", b.getExports().getByFQN("thinlet").getVersion()); assertTrue(b.getJar().getDirectories().containsKey("thinlet")); assertTrue(b.getJar().getResources().containsKey("thinlet/Thinlet.class")); } finally { b.close(); } } /** * Fastest way to create a manifest * * @throws Exception */ public static void testGenerateManifest() throws Exception { Analyzer analyzer = new Analyzer(); try { Jar bin = new Jar(IO.getFile("jar/osgi.jar")); bin.setManifest(new Manifest()); analyzer.setJar(bin); analyzer.addClasspath(IO.getFile("jar/spring.jar")); analyzer.setProperty("Bundle-SymbolicName", "org.osgi.core"); analyzer.setProperty("Export-Package", "org.osgi.framework,org.osgi.service.event"); analyzer.setProperty("Bundle-Version", "1.0.0.x"); analyzer.setProperty("Import-Package", "*"); Manifest manifest = analyzer.calcManifest(); assertTrue(analyzer.check()); manifest.write(System.err); Domain main = Domain.domain(manifest); Parameters export = main.getExportPackage(); Parameters expected = new Parameters( "org.osgi.framework;version=\"1.3\",org.osgi.service.event;uses:=\"org.osgi.framework\";version=\"1.0.1\""); assertTrue(expected.isEqual(export)); assertEquals("1.0.0.x", manifest.getMainAttributes().getValue("Bundle-Version")); } finally { analyzer.close(); } } /** * Make sure packages from embedded directories referenced from * Bundle-Classpath are considered during import/export calculation. */ public static void testExportContentsDirectory() throws Exception { Builder b = new Builder(); try { File embedded = IO.getFile("bin/test/refer").getCanonicalFile(); assertTrue(embedded.isDirectory()); // sanity check b.setProperty("Bundle-ClassPath", ".,jars/some.jar"); b.setProperty("-includeresource", "jars/some.jar/test/refer=" + embedded.getAbsolutePath()); b.setProperty("-exportcontents", "test.refer"); b.build(); assertTrue(b.check("Bundle-ClassPath uses a directory 'jars/some.jar'")); assertTrue(b.getImports().toString(), b.getImports().getByFQN("org.osgi.service.event") != null); } finally { b.close(); } } /** * Uses constraints must be filtered by imports or exports. * * @throws Exception */ public static void testUsesFiltering() throws Exception { Builder b = new Builder(); try { b.setTrace(true); b.addClasspath(IO.getFile("jar/osgi.jar")); b.setProperty("Export-Package", "org.osgi.service.event"); Jar jar = b.build(); assertTrue(b.check()); assertNotNull(jar.getResource("org/osgi/service/event/EventAdmin.class")); String exports = jar.getManifest().getMainAttributes().getValue("Export-Package"); System.err.println(exports); assertTrue(exports.contains("uses:=\"org.osgi.framework\"")); b = new Builder(); b.addClasspath(IO.getFile("jar/osgi.jar")); b.setProperty("Import-Package", ""); b.setProperty("Export-Package", "org.osgi.service.event"); b.setPedantic(true); jar = b.build(); exports = jar.getManifest().getMainAttributes().getValue("Export-Package"); System.err.println(exports); assertTrue(b.check("Empty Import-Package header")); exports = jar.getManifest().getMainAttributes().getValue("Export-Package"); assertFalse(exports.contains("uses:=\"org.osgi.framework\"")); } finally { b.close(); } } /** * Test if require works * * @throws Exception */ public static void testRequire() throws Exception { Builder b = new Builder(); try { b.addClasspath(IO.getFile("jar/osgi.jar")); b.setProperty("Private-Package", "org.osgi.framework"); b.setProperty("-require-bnd", "10000"); b.build(); System.err.println(b.getErrors()); System.err.println(b.getWarnings()); assertEquals(1, b.getErrors().size()); assertEquals(0, b.getWarnings().size()); } finally { b.close(); } } public static void testComponentImportReference() throws Exception { Builder b = new Builder(); try { b.addClasspath(IO.getFile("jar/osgi.jar")); b.setProperty("Private-Package", "org.osgi.framework"); b.setProperty("Import-Package", "not.here,*"); b.setProperty("Service-Component", "org.osgi.framework.Bundle;ref=not.here.Reference"); b.build(); System.err.println(b.getErrors()); System.err.println(b.getWarnings()); assertEquals(0, b.getErrors().size()); assertEquals(0, b.getWarnings().size()); } finally { b.close(); } } public static void testFindClass() throws Exception { Builder a = new Builder(); try { a.setProperty("Export-Package", "org.osgi.service.io"); a.addClasspath(IO.getFile("jar/osgi.jar")); a.build(); System.err.println(a.getErrors()); System.err.println(a.getWarnings()); Collection<Clazz> c = a.getClasses("", "IMPORTS", "javax.microedition.io"); System.err.println(c); } finally { a.close(); } } public static void testMultilevelInheritance() throws Exception { try (Analyzer a = new Analyzer()) { a.setJar(new File("bin")); a.analyze(); String result = a._classes("cmd", "named", "*T?", "extends", "test.T0", "concrete"); System.err.println(result); assertTrue(result.contains("test.T2")); assertTrue(result.contains("test.T3")); } } public static void testClassQuery() throws Exception { try (Analyzer a = new Analyzer()) { a.setJar(IO.getFile("jar/osgi.jar")); a.analyze(); String result = a._classes("cmd", "named", "org.osgi.service.http.*", "abstract"); TreeSet<String> r = new TreeSet<String>(Processor.split(result)); assertEquals( new TreeSet<String>( Arrays.asList("org.osgi.service.http.HttpContext", "org.osgi.service.http.HttpService")), r); } } /** * Use a private activator, check it is not imported. * * @throws Exception */ public static void testEmptyHeader() throws Exception { try (Builder a = new Builder()) { a.setProperty("Bundle-Blueprint", " <<EMPTY>> "); a.setProperty("Export-Package", "org.osgi.framework"); a.addClasspath(IO.getFile("jar/osgi.jar")); a.build(); Manifest manifest = a.getJar().getManifest(); System.err.println(a.getErrors()); System.err.println(a.getWarnings()); assertEquals(0, a.getErrors().size()); assertEquals(0, a.getWarnings().size()); String bb = manifest.getMainAttributes().getValue("Bundle-Blueprint"); System.err.println(bb); assertNotNull(bb); assertEquals("", bb); } } /** * Test name section. */ public static void testNameSection() throws Exception { Builder a = new Builder(); try { a.setProperty("Export-Package", "org.osgi.service.event, org.osgi.service.io"); a.addClasspath(IO.getFile("jar/osgi.jar")); a.setProperty("@org@osgi@service@event@Specification-Title", "spec title"); a.setProperty("@org@osgi@service@io@Specification-Title", "spec title io"); a.setProperty("@org@osgi@service@event@Specification-Version", "1.1"); a.setProperty("@org@osgi@service@event@Implementation-Version", "5.1"); Jar jar = a.build(); Manifest m = jar.getManifest(); Attributes attrs = m.getAttributes("org/osgi/service/event"); assertNotNull(attrs); assertEquals("5.1", attrs.getValue("Implementation-Version")); assertEquals("1.1", attrs.getValue("Specification-Version")); assertEquals("spec title", attrs.getValue("Specification-Title")); attrs = m.getAttributes("org/osgi/service/io"); assertNotNull(attrs); assertEquals("spec title io", attrs.getValue("Specification-Title")); } finally { a.close(); } } /** * Check if calcManifest sets the version */ /** * Test if mandatory attributes are augmented even when the version is not * set. */ public static void testMandatoryWithoutVersion() throws Exception { Builder a = new Builder(); try { Properties p = new Properties(); p.put("Import-Package", "*"); p.put("Private-Package", "org.apache.mina.management.*"); a.setClasspath(new Jar[] { new Jar(IO.getFile("jar/mandatorynoversion.jar")) }); a.setProperties(p); Jar jar = a.build(); assertTrue(a.check()); String imports = jar.getManifest().getMainAttributes().getValue("Import-Package"); System.err.println(imports); assertTrue(imports.indexOf("x=1") >= 0); assertTrue(imports.indexOf("y=2") >= 0); } finally { a.close(); } } /** * Test Import-Packages marked with resolution:=dynamic are expanded, moved * to DynamicImport-Packages with no original DIP instruction. */ public static void testDynamicImportExpansionPackagesAreSet() throws Exception { Builder a = new Builder(); try { Properties p = new Properties(); p.put("Import-Package", "org.osgi.service.*;resolution:=dynamic, *"); p.put("Private-Package", "test.dynamicimport"); a.addClasspath(new File("bin")); a.addClasspath(IO.getFile("jar/osgi.jar")); a.setProperties(p); Jar jar = a.build(); assertTrue(a.check()); String imports = jar.getManifest().getMainAttributes().getValue("Import-Package"); String dynamicImports = jar.getManifest().getMainAttributes().getValue("DynamicImport-Package"); assertTrue(imports.indexOf("org.osgi.framework;version=\"[1.3,2)\"") >= 0); assertTrue(imports.indexOf("org.osgi.service.cm;version=\"[1.2,2)\"") < 0); assertTrue(imports.indexOf("org.osgi.service.event;version=\"[1.0,2)\"") < 0); assertTrue(dynamicImports.indexOf("org.osgi.service.cm;version=\"[1.2,2)\"") >= 0); assertTrue(dynamicImports.indexOf("org.osgi.service.event;version=\"[1.0,2)\"") >= 0); } finally { a.close(); } } /** * Test Import-Packages marked with resolution:=dynamic are expanded, moved * to DynamicImport-Packages and added to the original DIP instruction. */ public static void testDynamicImportExpansionPackagesAreAdded() throws Exception { Builder a = new Builder(); try { Properties p = new Properties(); p.put("Import-Package", "org.osgi.service.*;resolution:=dynamic, *"); p.put("DynamicImport-Package", "javax.servlet.*"); p.put("Private-Package", "test.dynamicimport"); a.addClasspath(new File("bin")); a.addClasspath(IO.getFile("jar/osgi.jar")); a.setProperties(p); Jar jar = a.build(); assertTrue(a.check()); String imports = jar.getManifest().getMainAttributes().getValue("Import-Package"); String dynamicImports = jar.getManifest().getMainAttributes().getValue("DynamicImport-Package"); assertTrue(imports.indexOf("org.osgi.framework;version=\"[1.3,2)\"") >= 0); assertTrue(imports.indexOf("org.osgi.service.cm;version=\"[1.2,2)\"") < 0); assertTrue(imports.indexOf("org.osgi.service.event;version=\"[1.0,2)\"") < 0); assertTrue(dynamicImports.indexOf("org.osgi.service.cm;version=\"[1.2,2)\"") >= 0); assertTrue(dynamicImports.indexOf("org.osgi.service.event;version=\"[1.0,2)\"") >= 0); assertTrue(dynamicImports.indexOf("javax.servlet.*") >= 0); } finally { a.close(); } } /** * Use a private activator, check it is not imported. * * @throws Exception */ public static void testPrivataBundleActivatorNotImported() throws Exception { Builder a = new Builder(); try { Properties p = new Properties(); p.put("Import-Package", "!org.osgi.service.component, *"); p.put("Private-Package", "test.activator"); p.put("Bundle-Activator", "test.activator.Activator"); a.addClasspath(new File("bin")); a.setProperties(p); a.build(); Manifest manifest = a.getJar().getManifest(); assertTrue(a.check()); String imports = manifest.getMainAttributes().getValue("Import-Package"); System.err.println(imports); assertEquals("org.osgi.framework", imports); } finally { a.close(); } } /** * Use an activator that is not in the bundle but do not allow it to be * imported, this should generate an error. * * @throws Exception */ public static void testBundleActivatorNotImported() throws Exception { Builder a = new Builder(); try { Properties p = new Properties(); p.put("Import-Package", "!org.osgi.framework,*"); p.put("Private-Package", "org.objectweb.*"); p.put("Bundle-Activator", "org.osgi.framework.BundleActivator"); a.setClasspath(new Jar[] { new Jar(IO.getFile("jar/asm.jar")), new Jar(IO.getFile("jar/osgi.jar")) }); a.setProperties(p); a.build(); assertTrue(a.check("Bundle-Activator not found")); Manifest manifest = a.getJar().getManifest(); String imports = manifest.getMainAttributes().getValue("Import-Package"); assertNull(imports); } finally { a.close(); } } /** * Use an activator that is on the class path but that is not in the bundle. * * @throws Exception */ public static void testBundleActivatorImport() throws Exception { Builder a = new Builder(); try { Properties p = new Properties(); p.put("Private-Package", "org.objectweb.*"); p.put("Bundle-Activator", "org.osgi.framework.BundleActivator"); a.setClasspath(new Jar[] { new Jar(IO.getFile("jar/asm.jar")), new Jar(IO.getFile("jar/osgi.jar")) }); a.setProperties(p); a.build(); Manifest manifest = a.getJar().getManifest(); assertEquals(0, a.getErrors().size()); assertEquals(1, a.getWarnings().size()); assertTrue( a.check("Bundle-Activator org.osgi.framework.BundleActivator is being imported into the bundle")); String imports = manifest.getMainAttributes().getValue("Import-Package"); assertNotNull(imports); assertTrue(imports.indexOf("org.osgi.framework") >= 0); } finally { a.close(); } } /** * Use an activator that abstract, and so cannot be instantiated. * * @throws Exception */ public static void testBundleActivatorAbstract() throws Exception { Builder a = new Builder(); try { Properties p = new Properties(); p.put("Private-Package", "test.activator"); p.put("Bundle-Activator", "test.activator.AbstractActivator"); a.addClasspath(new File("bin")); a.setProperties(p); a.build(); Manifest manifest = a.getJar().getManifest(); assertEquals(1, a.getErrors().size()); assertEquals(0, a.getWarnings().size()); assertTrue(a.check("The Bundle Activator test.activator.AbstractActivator is abstract")); } finally { a.close(); } } /** * Use an activator that is an interface, and so cannot be instantiated. * * @throws Exception */ public static void testBundleActivatorInterface() throws Exception { Builder a = new Builder(); try { Properties p = new Properties(); p.put("Private-Package", "test.activator"); p.put("Bundle-Activator", "test.activator.IActivator"); a.addClasspath(new File("bin")); a.setProperties(p); a.build(); Manifest manifest = a.getJar().getManifest(); assertEquals(1, a.getErrors().size()); assertEquals(0, a.getWarnings().size()); assertTrue(a.check("The Bundle Activator test.activator.IActivator is an interface")); } finally { a.close(); } } /** * Use an activator that has no default constructor, and so cannot be * instantiated. * * @throws Exception */ public static void testBundleActivatorNoDefaultConstructor() throws Exception { Builder a = new Builder(); try { Properties p = new Properties(); p.put("Private-Package", "test.activator"); p.put("Bundle-Activator", "test.activator.MissingNoArgsConstructorActivator"); a.addClasspath(new File("bin")); a.setProperties(p); a.build(); Manifest manifest = a.getJar().getManifest(); assertEquals(1, a.getErrors().size()); assertEquals(0, a.getWarnings().size()); assertTrue(a.check( "Bundle Activator classes must have a public zero-argument constructor and test.activator.MissingNoArgsConstructorActivator does not")); } finally { a.close(); } } /** * Use an activator that is not public, and so cannot be instantiated. * * @throws Exception */ public static void testBundleActivatorNotPublic() throws Exception { Builder a = new Builder(); try { Properties p = new Properties(); p.put("Private-Package", "test.activator"); p.put("Bundle-Activator", "test.activator.DefaultVisibilityActivator"); a.addClasspath(new File("bin")); a.setProperties(p); a.build(); Manifest manifest = a.getJar().getManifest(); assertEquals(2, a.getErrors().size()); assertEquals(0, a.getWarnings().size()); assertTrue(a.check( "Bundle Activator classes must be public, and test.activator.DefaultVisibilityActivator is not", "Bundle Activator classes must have a public zero-argument constructor and test.activator.DefaultVisibilityActivator does not")); } finally { a.close(); } } /** * Use an activator that is not an instance of BundleActivator, and so * cannot be used. * * @throws Exception */ public static void testNotABundleActivator() throws Exception { Builder a = new Builder(); try { Properties p = new Properties(); p.put("Private-Package", "test.activator"); p.put("Bundle-Activator", "test.activator.NotAnActivator"); a.addClasspath(new File("bin")); a.setProperties(p); a.build(); Manifest manifest = a.getJar().getManifest(); assertEquals(1, a.getErrors().size()); assertEquals(0, a.getWarnings().size()); assertTrue( a.check("The Bundle Activator test.activator.NotAnActivator does not implement BundleActivator")); } finally { a.close(); } } /** * Scan for a BundleActivator, but there are no matches! * * @throws Exception */ public static void testBundleActivatorNoType() throws Exception { Builder a = new Builder(); try { Properties p = new Properties(); p.put("Private-Package", "test.api"); p.put("Bundle-Activator", ""); a.addClasspath(new File("bin")); a.setProperties(p); a.build(); Manifest manifest = a.getJar().getManifest(); assertEquals(a.getErrors().toString(), 0, a.getErrors().size()); assertEquals(a.getWarnings().toString(), 1, a.getWarnings().size()); assertTrue( a.check("A Bundle-Activator header was present but no activator class was defined")); } finally { a.close(); } } /** * Scan for a BundleActivator, but the value is not a Java type identifier! * * @throws Exception */ public static void testBundleActivatorNotAType() throws Exception { Builder a = new Builder(); try { Properties p = new Properties(); p.put("Private-Package", "test.api"); p.put("Bundle-Activator", "123"); a.addClasspath(new File("bin")); a.setProperties(p); a.build(); Manifest manifest = a.getJar().getManifest(); assertEquals(a.getErrors().toString(), 2, a.getErrors().size()); assertEquals(a.getWarnings().toString(), 0, a.getWarnings().size()); assertTrue(a.check("A Bundle-Activator header is present and its value is not a valid type name 123", "The default package '.' is not permitted by the Import-Package syntax.")); } finally { a.close(); } } /** * Scan for a BundleActivator, but there are no matches! * * @throws Exception */ public static void testScanForABundleActivatorNoMatches() throws Exception { Builder a = new Builder(); try { Properties p = new Properties(); p.put("Private-Package", "test.api"); p.put("Bundle-Activator", "${classes;IMPLEMENTS;org.osgi.framework.BundleActivator}"); a.addClasspath(new File("bin")); a.setProperties(p); a.build(); Manifest manifest = a.getJar().getManifest(); assertEquals(a.getErrors().toString(), 1, a.getErrors().size()); assertEquals(a.getWarnings().toString(), 0, a.getWarnings().size()); assertTrue( a.check("A Bundle-Activator header is present but no activator class was found using the macro \\$\\{classes;IMPLEMENTS;org\\.osgi\\.framework\\.BundleActivator\\}")); } finally { a.close(); } } /** * Scan for a BundleActivator, but there are multiple matches! * * @throws Exception */ public static void testScanForABundleActivatorMultipleMatches() throws Exception { Builder a = new Builder(); try { Properties p = new Properties(); p.put("Private-Package", "test.activator"); p.put("Bundle-Activator", "${classes;IMPLEMENTS;org.osgi.framework.BundleActivator}"); a.addClasspath(new File("bin")); a.setProperties(p); a.build(); Manifest manifest = a.getJar().getManifest(); assertEquals(a.getErrors().toString(), 1, a.getErrors().size()); assertEquals(a.getWarnings().toString(), 0, a.getWarnings().size()); assertTrue( a.check("The Bundle-Activator header only supports a single type. The following types were found: " + "test.activator.AbstractActivator,test.activator.Activator,test.activator.Activator11,test.activator.Activator2,test.activator.Activator3,test.activator.ActivatorPackage,test.activator.ActivatorPrivate,test.activator.DefaultVisibilityActivator,test.activator.IActivator,test.activator.MissingNoArgsConstructorActivator" + ". This usually happens when a macro resolves to multiple types")); } finally { a.close(); } } /** * The -removeheaders header removes any necessary after the manifest is * calculated. */ public static void testRemoveheaders() throws Exception { try (Analyzer a = new Analyzer()) { a.setJar(IO.getFile("jar/asm.jar")); Manifest m = a.calcManifest(); assertNotNull(m.getMainAttributes().getValue("Implementation-Title")); } try (Analyzer a = new Analyzer()) { a.setJar(IO.getFile("jar/asm.jar")); a.setProperty("-removeheaders", "Implementation-Title"); Manifest m = a.calcManifest(); assertNull(m.getMainAttributes().getValue("Implementation-Title")); } } /** * There was an export generated for a jar file. * * @throws Exception */ public static void testExportForJar() throws Exception { try (Analyzer an = new Analyzer()) { Jar jar = new Jar("dot"); jar.putResource("target/aopalliance.jar", new FileResource(IO.getFile("jar/asm.jar"))); an.setJar(jar); an.setProperty("Export-Package", "target"); Manifest manifest = an.calcManifest(); assertTrue(an.check()); String exports = manifest.getMainAttributes().getValue(Analyzer.EXPORT_PACKAGE); Parameters map = Analyzer.parseHeader(exports, null); assertEquals(1, map.size()); assertEquals("target", map.keySet().iterator().next()); } } /** * Test if version works * * @throws IOException */ public static void testVersion() throws IOException { try (Analyzer a = new Analyzer()) { String v = a.getBndVersion(); assertNotNull(v); } } /** * asm is a simple library with two packages. No imports are done. */ public static void testAsm() throws Exception { Properties base = new Properties(); base.put(Analyzer.IMPORT_PACKAGE, "*"); base.put(Analyzer.EXPORT_PACKAGE, "*;-noimport:=true"); try (Analyzer analyzer = new Analyzer()) { analyzer.setJar(IO.getFile("jar/asm.jar")); analyzer.setProperties(base); analyzer.calcManifest().write(System.err); assertTrue(analyzer.check()); assertTrue(analyzer.getExports().getByFQN("org.objectweb.asm.signature") != null); assertTrue(analyzer.getExports().getByFQN("org.objectweb.asm") != null); assertFalse(analyzer.getImports().getByFQN("org.objectweb.asm.signature") != null); assertFalse(analyzer.getImports().getByFQN("org.objectweb.asm") != null); assertEquals("Expected size", 2, analyzer.getExports().size()); } } /** * See if we set attributes on export * * @throws IOException */ public static void testAsm2() throws Exception { Properties base = new Properties(); base.put(Analyzer.IMPORT_PACKAGE, "*"); base.put(Analyzer.EXPORT_PACKAGE, "org.objectweb.asm;name=short, org.objectweb.asm.signature;name=long"); try (Analyzer h = new Analyzer()) { h.setJar(IO.getFile("jar/asm.jar")); h.setProperties(base); h.calcManifest().write(System.err); assertTrue(h.check()); Packages exports = h.getExports(); assertTrue(exports.getByFQN("org.objectweb.asm.signature") != null); assertTrue(exports.getByFQN("org.objectweb.asm") != null); assertTrue(Arrays.asList("org.objectweb.asm", "org.objectweb.asm.signature") .removeAll(h.getImports().keySet()) == false); assertEquals("Expected size", 2, h.getExports().size()); assertEquals("short", get(h.getExports(), h.getPackageRef("org.objectweb.asm"), "name")); assertEquals("long", get(h.getExports(), h.getPackageRef("org.objectweb.asm.signature"), "name")); } } public static void testDs() throws Exception { Properties base = new Properties(); base.put(Analyzer.IMPORT_PACKAGE, "*"); base.put(Analyzer.EXPORT_PACKAGE, "*;-noimport:=true"); File tmp = IO.getFile("jar/ds.jar"); try (Analyzer analyzer = new Analyzer()) { analyzer.setJar(tmp); analyzer.setProperties(base); analyzer.calcManifest().write(System.err); assertTrue(analyzer.check()); assertPresent(analyzer.getImports().keySet(), "org.osgi.service.packageadmin, " + "org.xml.sax, org.osgi.service.log," + " javax.xml.parsers," + " org.xml.sax.helpers," + " org.osgi.framework," + " org.eclipse.osgi.util," + " org.osgi.util.tracker, " + "org.osgi.service.component, " + "org.osgi.service.cm"); assertPresent(analyzer.getExports().keySet(), "org.eclipse.equinox.ds.parser, " + "org.eclipse.equinox.ds.tracker, " + "org.eclipse.equinox.ds, " + "org.eclipse.equinox.ds.instance, " + "org.eclipse.equinox.ds.model, " + "org.eclipse.equinox.ds.resolver, " + "org.eclipse.equinox.ds.workqueue"); } } public static void testDsSkipOsgiImport() throws Exception { Properties base = new Properties(); base.put(Analyzer.IMPORT_PACKAGE, "!org.osgi.*, *"); base.put(Analyzer.EXPORT_PACKAGE, "*;-noimport:=true"); File tmp = IO.getFile("jar/ds.jar"); try (Analyzer h = new Analyzer()) { h.setJar(tmp); h.setProperties(base); h.calcManifest().write(System.err); assertPresent(h.getImports().keySet(), "org.xml.sax, " + " javax.xml.parsers," + " org.xml.sax.helpers," + " org.eclipse.osgi.util"); System.err.println("IMports " + h.getImports()); assertNotPresent(h.getImports().keySet(), "org.osgi.service.packageadmin, " + "org.osgi.service.log," + " org.osgi.framework," + " org.osgi.util.tracker, " + "org.osgi.service.component, " + "org.osgi.service.cm"); assertPresent(h.getExports().keySet(), "org.eclipse.equinox.ds.parser, " + "org.eclipse.equinox.ds.tracker, " + "org.eclipse.equinox.ds, " + "org.eclipse.equinox.ds.instance, " + "org.eclipse.equinox.ds.model, " + "org.eclipse.equinox.ds.resolver, " + "org.eclipse.equinox.ds.workqueue"); } } public static void testDsNoExport() throws Exception { Properties base = new Properties(); base.put(Analyzer.IMPORT_PACKAGE, "*"); base.put(Analyzer.EXPORT_PACKAGE, "!*"); File tmp = IO.getFile("jar/ds.jar"); try (Analyzer h = new Analyzer()) { h.setJar(tmp); h.setProperties(base); h.calcManifest().write(System.err); assertPresent(h.getImports().keySet(), "org.osgi.service.packageadmin, " + "org.xml.sax, org.osgi.service.log," + " javax.xml.parsers," + " org.xml.sax.helpers," + " org.osgi.framework," + " org.eclipse.osgi.util," + " org.osgi.util.tracker, " + "org.osgi.service.component, " + "org.osgi.service.cm"); assertNotPresent(h.getExports().keySet(), "org.eclipse.equinox.ds.parser, " + "org.eclipse.equinox.ds.tracker, " + "org.eclipse.equinox.ds, " + "org.eclipse.equinox.ds.instance, " + "org.eclipse.equinox.ds.model, " + "org.eclipse.equinox.ds.resolver, " + "org.eclipse.equinox.ds.workqueue"); System.err.println(h.getUnreachable()); } } public static void testClasspath() throws Exception { Properties base = new Properties(); base.put(Analyzer.IMPORT_PACKAGE, "*"); base.put(Analyzer.EXPORT_PACKAGE, "*;-noimport:=true"); File tmp = IO.getFile("jar/ds.jar"); File osgi = IO.getFile("jar/osgi.jar"); try (Analyzer h = new Analyzer()) { h.setJar(tmp); h.setProperties(base); h.setClasspath(new File[] { osgi }); h.calcManifest().write(System.err); assertEquals("Version from osgi.jar", "[1.2,2)", get(h.getImports(), h.getPackageRef("org.osgi.service.packageadmin"), "version")); assertEquals("Version from osgi.jar", "[1.3,2)", get(h.getImports(), h.getPackageRef("org.osgi.util.tracker"), "version")); assertEquals("Version from osgi.jar", null, get(h.getImports(), h.getPackageRef("org.xml.sax"), "version")); } } /** * We detect that there are instruction on im/export package headers that * are never used. This usually indicates a misunderstanding or a change in * the underlying classpath. These are reflected as warnings. If there is an * extra import, and it contains no wildcards, then it is treated as a * wildcard * * @throws IOException */ public static void testSuperfluous() throws Exception { Properties base = new Properties(); base.put(Analyzer.IMPORT_PACKAGE, "*, =com.foo, com.foo.bar.*"); base.put(Analyzer.EXPORT_PACKAGE, "*, com.bar, baz.*"); File tmp = IO.getFile("jar/ds.jar"); try (Analyzer h = new Analyzer()) { h.setJar(tmp); h.setProperties(base); Manifest m = h.calcManifest(); m.write(System.err); assertTrue(h.check( // "Unused Export-Package instructions: \\[baz.*\\]", // "Unused Import-Package instructions: \\[com.foo.bar.*\\]")); assertTrue(h.getImports().getByFQN("com.foo") != null); assertTrue(h.getExports().getByFQN("com.bar") != null); } } static void assertNotPresent(Collection< ? > map, String string) { Collection<String> ss = new HashSet<String>(); for (Object o : map) ss.add(o + ""); StringTokenizer st = new StringTokenizer(string, ", "); while (st.hasMoreTokens()) { String member = st.nextToken(); assertFalse("Must not contain " + member, map.contains(member)); } } static void assertPresent(Collection< ? > map, String string) { Collection<String> ss = new HashSet<String>(); for (Object o : map) ss.add(o + ""); StringTokenizer st = new StringTokenizer(string, ", "); while (st.hasMoreTokens()) { String member = st.nextToken(); assertTrue("Must contain " + member, ss.contains(member)); } } static <K, V> V get(Map<K, ? extends Map<String,V>> headers, K key, String attr) { Map<String,V> clauses = headers.get(key); if (clauses == null) return null; return clauses.get(attr); } }