Sonntag, 2. September 2012

Apache Felix auf Android - Problemzonen

"Warum läuft Apache Felix nicht ohne weiteres auf Android ?" Wenn ein OSGi Framework wie Apache Felix in der JavaVM läuft und ein Bundle nachgeladen wird, ist es meiste eine Jar das vom Framework nachgeladen wird. In der Jar liegen die ganzen Klassen, sie sind im *.class Format also JavaByteCode. Wer sich selbst davon überzeugen möchte kann einfach mal Folgendes versuchen:
jar tf datei.jar
Inhalt der Jar wird angezeigt. Oder:
jar xf datei.jar
Inhalt der Jar wird entpackt. (In zip umbenennen geht aber auch ;-) )

Wenn Felix auf der JavaVM läuft wird einfach nur der ByteCode aus den classfiles ausgeführt. Auch die Bundles beinhalten nur JavaByteCode.
Auf Android wird DalvikByteCode ausgeführt wie man auch in der Grafik erkennen kann. Diesen kann man sich durch die erneute Kompilierung des JavaByteCodes generieren (Bei Android werden Register benutzt - bei Java Stacks).

Wenn wir also Apache Felix auf Android laufen lassen und ein ganz normales Bundle installieren und starten wird folgendes passieren:

I/dalvikvm(6713): Zip is good, but no classes.dex inside, and no valid .odex file in the same directory 
E/dalvikvm(6713): ERROR: defineClass(0x41198008, org.apache.felix.http.bundle.internal.CombinedActivator, 0x411912c8, 0, 1673) 
I/System.out(6713): ERROR: Bundle org.apache.felix.http.bundle [14] Error starting reference:file:mnt/sdcard/bundles/org.apache.felix.http.bundle-2.2.0.jar (org.osgi.framework.BundleException: Activator start error in bundle org.apache.felix.http.bundle [14].) 
W/System.err(6713): java.lang.UnsupportedOperationException: can't load this type of class file 
W/System.err(6713): at java.lang.VMClassLoader.defineClass(Native Method) 

Die erste Zeile drück es in etwa aus, es gibt kein dex file im Jar welches deployt werden könnte.
Die Lösung: Wir packen in jeden Jar ein Dex File. Wenn die DalvikVM dann versucht den Code während der laufzeit zu laden, so ist DalvikByteCode (in der classes.dex im root des bundles) vorhanden. Ein Beispiel: Wir wollen die Bundles org.apache.felix.http.bundle, org.apache.felix.shell, und org.apache.felix.shell.remote installieren und starten. Dazu müssen wir sie dex'en ;).
dx --dex --output=classes.dex datei.jar
Damit wird die dex generiert.
aapt add datei.jar classes.dex
So wird die dex in die jar gepackt.

OSGi Error: NoClassDefFoundError

If we look at this example
java.lang.NoClassDefFoundError: Could not initialize class com.example.MyClass

This error means that the class "MyClass" was found, but the classloader couldn't finish loading it. The Reason could be that MyClass depends on a other class which is missing.

This can happen when the bundle was compiled when the depending classes where present but they won't be in the bundle or any other exporting bundle.

To debug such an error you need to trace back through the dependencies. The problem is that the real missing dependency may be at the bottom of the dep.-stack.

By the Way: The "ClassNotFoundException" means that the given class (e.g.: MyClass) was not found.