OSGi und die ClassNotFound-Exception

Stolperstein beim Experimentieren mit Gradle uns OSGi

I had a problem managing n modules, so I thought “I just use OSGi”. Now I have n² problems…

Für ein Experiment wollte ich mein Javaprogramm in „echte“ Module einteilen. OSGi ist da als Gedanke naheliegend, und da es in Gradle OSGi-Support gibt, klang das nach einem vielversprechenden Ansatz: Immerhin deckt das Buildsystem Gradle bereits das ganze Dependency-Management ab, und das OSGi-Modul versprach eine manuelle Pflege der Dependencies (nichts anderes steht ja in einem Bundle-Manifest) überflüssig zu machen. Also flugs ein Multi-Project-Build aufgesetzt:

  • main für das eigentliche Hauptprogramm, welches die Module läd
  • interface für die gemeinsam verwendeten Interface-Klassen
  • service1 und service2 als zwei Test-Bundles

Das OSGi-Plugin kennt zwar die OSGi-Annotationen nicht, aber der Old-School-Weg mit der Activator-Klasse ist kein so großer Abstrich.

Gradle und OSGi: Schon nicht schlecht…

Leider machte das OSGi-Plugin von Gradle nicht ganz so viel, wie ich mir gewünscht hätte: Es trägt zwar die Abhängigkeiten, die als OSGi-Bundle erkannt werden, in das OSGi-Manifest ein, aber die restlichen (nicht-OSGi) JARs werden leider nicht weiter behandelt. Ich habe leider keine Möglichkeit gefunden, diese automatisch mit in das JAR des Moduls hineinzupacken.

Dies führte im ersten Anlauf zu dem Problem, daß beim Laden des Bundles die Interface-Klassen nicht gefunden wurden. Aber da die Interfaces ja ohnehin gezwungenermaßen identisch sein müssen, kann man die nicht gemeinsam nutzen?

Ein fieses Detail…

In der Tat, man kann: Mit Hilfe des Konfigurationsparameters org.osgi.framework.system.packages.extra kann man beim Initialisieren des OSGi-Frameworks bestimmte Packages aus der „Wirtsumgebung“ in die Bundles exponieren. Das klang nach einer Lösung meines Problems, die Bundles wurden anstandslos geladen – und stolperten dann über eine ClassNotFoundException: Angeblich wäre die Klasse meines Bundle Activators nicht auffindbar. Mehrfaches Prüfen der Namen ergab keine Verbesserung.

Einige verzweifelte Zeit später dann die Entdeckung: Mit Hilfe der obigen Konfiguration werden nicht (wie von mir vermutet) die Klassen den Bundles zusätzlich zur Verfügung gestellt, das Package überlagert möglicherweise vorhandene Klassen im selben Package im Bundle. Ich hatte versehentlich den gleichen Package-Namen für die Activator-Klasse verwendet.

Merke also: Bei einem solchen Stunt Unbedingt unterschiedliche Package-Namen verwenden (was ja eigentlich ohnehin guter Ton ist).

Diverse OSGi-Implementierungen und Java 8

Ich habe das Projektsetup und das Problem in etwas Beispielcode fixiert. Das Projekt kann man entweder bei github ansehen oder sich als zip herunterladen. In den Build-Dateien sieht man noch auskommentiert den Eclipse-OSGi-Container. Bei meinen Experimenten hatte ich zwischen Apache Felix und em Eclipse-Container hin- und hergewechselt. Dabei stellte sich heraus, daß der Eclipse-Container offenbar ein Problem mit Java-8-Classfiles hat.

Fazit

Bleibt festzuhalten:

  • In den Modulen andere Packagenamen verwenden (der Modulname in den Package-Pfad aufzunehmen macht ja auch Sinn)
  • Gradle und OSGi ist schon recht komfortabel, aber man könnte sich noch das ein oder andere Feature wünschen
  • Die von mir getesteten alternativen OSGi-Plugins für Gradle, welche die OSGi-Annotationen parsen würden, kommen noch nicht mit Java 8 klar
  • Apache Felix funktioniert mit Java 8
  • Nothing is ever easy :-)