Thursday, 4. August 2011
Bundles bauen
Wenn man mit OSGi arbeitet, wird man sehr schnell feststellen, dass man die Bibliotheken, die man gerne einsetzen möchte (oder muss), auch als OSGi-Bundle vorliegen muss. Was macht man aber, wenn man die gewünschte Jar-Datei nicht als Bundle vorfindet? Dann muss man die Jar-Datei um die entsprechende Meta-Information anreichern, die aus einer Jar-Datei ein OSGi-Bundle macht. Wie? Anhand des JDBC-Treibers für DB2 werden wir die einzelnen Schritte durchexerzieren.

Vorbereitungen

Im wesentlichen unterscheidet sich ein Bundle von einer normalen Jar-Datei dadurch, dass unter META-INF/MANIFEST.MF zusätzliche Meta-Informationen wie
  • exportierte Packages
  • importierte Packages
  • und mehr (z.b. importierte Packages)
enthalten sind. Daraus ermittelt die OSGi-Umgebung (wie z.B. Felix, Equinox oder Knopflerfish) die notwendigen Abhängigkeiten zu anderen Bundles.

Man kann die MANIFEST.MF-Datei manuell erstellen, besser und weniger fehleranfällig ist es aber, wenn man dazu die BND-Tools benutzt. Benötigt wird hiervon die Jar-Datei
biz.aQute.bnd.jar.

Falls man mit Eclipse arbeitet, bieten sich auch die bnd-tools als Eclipse-Pluginan, die über die Update-Site http://bndtools-updates.s3.amazonaws.com installiert werden können. Dieses Plugin bietet u.a. einen Viewer für Jar-Dateien an, mit dem man recht komfortabel den Inhalt der Jars und deren Manifest-Datei anschauen kann.

Analyse DB2Driver

Am Beispiel des JDBC-Treibers für DB2, der als Päärchen db2jcc.jar und db2jcc_license_cu.jar daherkommt, wollen wir uns an den Bau eines OSGi-Bundles machen. Fangen wir mit der ersten Jar-Datei, db2jcc.jar, an. Folgende Informationen (neudeutsch: Properties) benötigen wir für das Bundle:
Bundle-Name: db2jcc
Bundle-Description: DB2 JDBC driver
Bundle-SymbolicName: 
Bundle-Version: 
Bundle-RequiredExecutionEnvironment:
Export-Package: 
Import-Package: 
Private-Package: 
Diese Information schreiben wir in die Datei db2jcc.bnd, die in dieser Form von den BND-Tools als Steuer-Datei benutzt werden kann. Die ersten beiden Einträge sind schon ausgefüllt und benötigen eigentlich keiner weiteren Erklärung.

Als Bundle-SymbolicName wird für Jar-Dateien, die bereits als OSGi-Bundles vorliegen, meist der oberste Package-Namen verwendet. So verwendet Commons Lang von Apache "org.apache.commons.lang" als symbolischen Namen. Bei Jar-Dateien, die noch nicht als OSGi-Bundle vorliegen, hat es sich eingebürgert, hier noch den Namen des Projekts oder der Firma voranzustellen, um es von den Original-Jar-Dateien abzugrenzen. In unserem Fall verwenden wir also nicht "com.ibm.db2jcc", sondern z.B. "de.blogger.oli.com.ibm.db2jcc". Man kann aber auch einen beliebigen anderen Namen hierfür verwenden.

Bei der Bundle-Version nimmt man am besten die Version des Treibers. Dies ist im Falle von dbj2cc.jar schon etwas schwieriger, da man diese Version auch nicht innerhalb der Jar-Datei findet. Unter DB2 JDBC Driver Versions findet man aber einige Hinweise, um welche Version es sich handeln könnte (hier war es die Version 3.53.70).

Für die Bundle-RequiredExecutionEnvironment müssen wir wissen, welche Java-Version wir mindestens benötigen. Dazu finden wir in der Manifest-Datei den Hinweis, dass sie mit JDK 1.4.2 gebaut wurde, d.h. der Treiber ist unter J2SE-1.4, J2SE-1.5 und JavaSE-1.6 lauffähig.

Über Export-Package gibt man bekannt, welche Packages nach außen sichtbar sein sollen. Ein Blick in die Jar-Datei lässt vermuten, dass dies die Packages "com.ibm.db2.jcc" und "sqlj.runtime", sowie alle Packages unter "COM.ibm.*" und "sqlj.runtime.*" sind.

Bei Import-Package kann man es sich einfach machen und einfach "*" als Wildcard eingeben. Dann ermittelt das BND-Tool die Imports selbst - allerdings kann er nur die direkten Imports erkennen oder schießt manchmal auch über das
Ziel hinaus und findet auch Imports, die nur in Ausnahmefällen benötigt werden. Da es sich in unserem Fall um einen JDBC-Treiber handelt und dafür die Packages "java(x).sql" benötigt werden, helfen wir BND mit com.ibm.db2.jcc.licenses, java.sql*, javax.sql*, *;resolution:=optional auf die Sprünge. *;resolution:=optional bedeutet dabei, dass alle restlichen Imports optional sein sollen.

Bei Private-Package machen wir es uns einfach - alles was übrig bleibt, kennzeichen wir als "private". Damit ergeben sich folgende Einträge für db2jcc.bnd:
bsn: de.blogger.oli.com.ibm.db2jcc
version=3.53.70
Bundle-Name: db2jcc
Bundle-Description: DB2 JDBC driver
Bundle-SymbolicName: ${bsn}
Bundle-RequiredExecutionEnvironment: J2SE-1.4,J2SE-1.5,JavaSE-1.6
Bundle-Version: ${version}
Export-Package: com.ibm.db2.jcc,COM.ibm.*,sqlj.runtime,sqlj.runtime.*;version=${version}
Import-Package: com.ibm.db2.jcc.licenses,java.sql*,javax.sql*,*;resolution:=optional
Private-Package: *
-classpath: db2jcc.jar
-output: ${bsn}-${version}.jar
Mit den ersten beiden Zeilen definieren wir zwei Variablen, die wir intern verwenden, um uns die Arbeit etwas zu erleichtern. Die letzten beiden Zeilen werden durch ein Minus "-" eingeleitet und werden als entsprechende Option an das BND-Tool durchgereicht.

BND-Tool aufrufen

java -jar biz.aQute.bnd.jar db2jcc.bnd
Damit wird die Datei de.blogger.oli.com.ibm.db2jcc-3.53.70.jar erzeugt, die sich jetzt als Bundle in Felix oder anderen OSGi-Containern laden lässt. Wichtig beim Aufruf ist, dass die Steuer-Datei die Endung ".bnd" trägt.

Weitere Information zum Bau von Bundles findet sich z.B. unter
http://swik.net/Spring/Interface21+Team+Blog/Creating+OSGi+bundles/b2yoy

Zusammenfassung

Um aus einer normalen Jar-Datei ein OSGi-Bundle zu machen, sind folgende Schritte nötig:
  • biz.aQute.bnd.jar herunterladen
  • .bnd-Datei erstellen (s. Beispiel db2jcc.bnd)
  • Original-Jar wrappen: java -jar biz.aQute.bnd.jar db2jcc.bnd
Analog kann damit die zweite Jar-Datei für db2jcc_license_cu.jar erstellt werden.

... comment