Friday, June 8, 2012

bnd week

Next week Beaulieu will be made unsafe with the bnd(tools) crew. Neil Bartlett (Paremus), PK Sörelde (ComActivity), Bert Bakker (Luminis), Ferry Huberts, Marcel Offermans (Luminis), and Marian Grigoras (Siemems) are coming over to prepare for the next release. Unfortunately, Stuart McCulloch (Sonatype) won't be able to come this time. However, he helped us with a very fresh snapshot release of the maven bundle plugin. It would be highly appreciated if people tested this plugin against their code base. You can find the maven bundle plugin here. Please report any errors or inconsistencies you find on github.
 It will be a heavy week, as usual, because there have been a lot of new functions added. For bnd, this actually means I will move bndlib to version 2.0.0. Except for the significantly new functionality, the API has also changed. When bndlib was small, Map<String,Map<String,String>> worked quite well to maintain the manifest information and package attributes. However, in the current code base it was becoming painful. Especially since Java has a naming fetish. org.example.Foo.X, org/example/Foo$X, org/example/Foo$X.class, Lorg/example/Foo .X and Lorg/example/Foo$X; are all identifying the same class in different contexts. Just imagine how easy it is to confuse these strings. So now bndlib has Parameters, Instructions, and Packages with lots of convenience methods. bndlib is used in ant, maven, sbt, osmorc, bndtools, and other products. Though the number of indirect users is quite large, the developers that program its API is quite small. However, it is a fun library to use if you need to work with JAR files and/or bundles. Some examples:

 File asm = new File("asm.jar");
 Jar jar = new Jar(asm);
 jar.getManifest().write(System.out);
This will output the following manifest:

 Manifest-Version: 1.0
 Implementation-Vendor: France Telecom R&D
 Ant-Version: Apache Ant 1.6.2
 Implementation-Title: ASM
 Implementation-Version: 2.2.2
 Created-By: 1.5.0_04-b05 (Sun Microsystems Inc.)

As manifests go, this is actually quite good. Most people have a significantly more lonely manifests. However, since this is no bundle, we need to add OSGi headers. The following code will set the versions of the bundle version and the version of the org.objectweb.asm packages to 2.2.2. In this case we can use a macro. We could create a special macro, version, for this and use this in the Bundle-Version and Export-Package headers. However, we can also reuse the Bundle-Version header since any header is also a macro. Notice that we use a time stamp on the version to find out about the build date.

 Analyzer analyzer = new Analyzer();
 analyzer.setJar(jar);
 analyzer.setProperty("Bundle-Version", "2.2.2.${tstamp}");
 analyzer.setExportPackage("org.objectweb.asm.*;version=${Bundle-Version}");
 Manifest manifest = analyzer.calcManifest();
 jar.setManifest(manifest);
 jar.getManifest().write(System.out);

This provides the following manifest:

Manifest-Version: 1.0
Export-Package: org.objectweb.asm;version="2.2.2.201206081457",org.obj
 ectweb.asm.signature;version="2.2.2.201206081457"
Implementation-Title: ASM
Implementation-Version: 2.2.2
Tool: Bnd-1.52.2
Bundle-Name: showcase
Created-By: 1.6.0_27 (Apple Inc.)
Implementation-Vendor: France Telecom R&D
Ant-Version: Apache Ant 1.6.2
Bundle-Version: 2.2.2.201206081457
Bnd-LastModified: 1339160222619
Bundle-ManifestVersion: 2
Bundle-SymbolicName: showcase
Originally-Created-By: 1.5.0_04-b05 (Sun Microsystems Inc.)
bnd added defaults for crucial OSGi information that was missing. The name, symbolic name, version, etc. It also copied all the headers from the old jar so that no information is lost. However, most important it calculated the Export-Package header.

Bundle-Version: 2.2.2.201206081456
Export-Package: 
 org.objectweb.asm;version="2.2.2.201206081457",
 org.objectweb.asm.signature;version="2.2.2.201206081457"
So lets save the jar on disk, including the digests so the bundle can be verified:

  jar.calcChecksums(new String[] {"SHA", "MD5"});
  jar.write("asm-2.2.2.jar");
So what more can we do? Lets take some of our own code and create a JAR out of it. The following example takes code from the bin directory, packages it and links it to the asm on disk.

 Builder b = new Builder();
 b.setPrivatePackage("simple");
 b.addClasspath(asm);
 b.addClasspath(new File("bin"));
 Jar simple = b.build();
 simple.getManifest().write(System.out);
The Private-Package will copy any package it specifies from the class path to the Jar. Since the asm on disk has no OSGi headers, we do not get import ranges.

Import-Package: org.objectweb.asm

So lets use the Jar we've just created instead, and lets also export the simple package.

 Builder b = new Builder();
 b.setExportPackage("simple");
 b.addClasspath(jar);
 b.addClasspath(new File("bin"));
 Jar simple = b.build();

Since the asm built jar we added has versions, the imports have version ranges. bnd also calculates the uses constraints on the exported packages:

Export-Package: simple;uses:="org.objectweb.asm";version="1.0.0"
Import-Package: org.objectweb.asm;version="[2.2,3)"

Last, lets say you want to know all the references from a JAR:

 Analyzer analyzer = new Analyzer();
 analyzer.setJar(j3);
 analyzer.analyze();
 System.out.println("Referred    " + analyzer.getReferred());
 System.out.println("Contains    " + analyzer.getContained());
 System.out.println("Uses" );
 for ( Entry<PackageRef, List<PackageRef>> from : analyzer.getUses().entrySet())
   System.out.printf("  %-40s %s\n", from.getKey(), new TreeSet<PackageRef>(from.getValue()) );

Which gives the following output:

Referred    java.lang,org.objectweb.asm
Contains    simple
Imports     [org.objectweb.asm]
Exports     [simple]
Uses
  simple                                   [java.lang, org.objectweb.asm]

This blog could go on forever (ok, for quite a long time); there is quite a lot of useful functionality in the API. However, for normal usage bnd(tools) works best since it has a nicer user interface, integrates with continuous integration, and is tremendously nice to develop with. However, if you find yourself processing jars or OSGi bundles, consider working with the API.

Peter Kriens

1 comment:

  1. What about the Eclipse plugin? Is it going to be released together?

    ReplyDelete