Java Native Calls Einbinden von C Bibliotheken in Java Klassen Prof. Dr. Nikolaus Wulff MVC: VC-GUI und natives M • Der FunctionPlotter wird in Java implementiert und stellt die View und Controller Abstraktionen bereit. • In Praktikum I wurden der numerische Integrator und der Differentiator in C entwickelt. Deren Kernfunktionalität kann als Model betrachtet werden. Es gibt nun zwei Möglichkeiten: • Entweder die C Implementierung in Java komplett neu zu implementieren oder aber • eine Java Wrapper Klasse als Adapter zu schreiben, die an die fachliche C Implementierung delegiert. © Prof. Dr. Nikolaus Wulff Informatik III 2 Native Code • Java bietet die Möglichkeit Klassen mit Methoden zu schreiben, die in C realisiert werden. • Hierzu werden die Methoden mit dem Schlüsselwort native markiert, ohne das eine Implementierung in Java erfolgt. public class Differentiator { /** * Differentiate the given function. * @param fct to differentiate * @param x the function argument * @return f'(x) */ public native double differentiate( final Function fct, final double x); } © Prof. Dr. Nikolaus Wulff Informatik III 3 C Header-Dateien generieren • Mit dem Tool javah wird zu Differentiato.java ein C Header PackageName_Differentiato.h generiert. • Die Datei PackageName_Differentiator.c dient als Adapter zu den schon existieren C Routinen. de_lab4inf_Differentiator.h de_lab4inf_Differentiator.c javah javap differentiate.h differentiate.c differentiate.c function.h integrate.h integrate.c libCPrak-I.so © Prof. Dr. Nikolaus Wulff Informatik III 4 de_lab4inf_Differentiator.h /* DO NOT EDIT THIS FILE - it is machine generated */ #include <jni.h> /* Header for class de_lab4inf_jni_Differentiator */ #ifndef _Included_de_lab4inf_Differentiator #define _Included_de_lab4inf_Differentiator #ifdef __cplusplus extern "C" { double differentiate(Function fct, double x) #endif /* * Class: de_lab4inf_Differentiator * Method: differentiate * Signature: (Lde/lab4inf/Function;D)D */ JNIEXPORT jdouble JNICALL Java_de_lab4inf_Differentiator_differentiate (JNIEnv *, jobject, jobject, jdouble); #ifdef __cplusplus } #endif #endif JVM © Prof. Dr. Nikolaus Wulff this Zeiger Informatik III Funktionsargumente 5 Differentiator.c howto? • Wie kann jetzt die C Methode implementiert werden (ohne das alte Pratkium neu zu schreiben)? • Benötigt wird nicht nur der Wert x, sondern auch ein C FunctionPointer für die zugehörige Java Funktion, die nur als Schnittstelle definiert ist. • Es lassen sich nicht nur von Java aus C Routinen rufen, sondern diese können auch ihrerseits wieder auf Java Klassen, Objekten und alle Methoden zurückgreifen! © Prof. Dr. Nikolaus Wulff Informatik III 6 Einbinden der C Routinen • Der Integrator und der Differentiator wurden im ersten Praktikum in C realisiert. • Es werden nun zwei Java Klassen Integrator und Differentiator als Adapter geschrieben, diese sollen jedoch nicht komplett neu implementiert werden, sondern statt dessen die C Routinen aufrufen. • Dies geschieht mit Hilfe der Java Native Interface Schnittstelle JNI. • Es werden C Header Dateien generiert, die nur noch geeignet ausimplementiert werden müssen. • Es werden entsprechende C Methoden als „Glue“ zu den Routinen aus Praktikum I entwickelt. © Prof. Dr. Nikolaus Wulff Informatik III 7 Java Reflection • Jedem Java Objekt ist eineindeutig ein Class Objekt zugeordnet. – Class c = obj.getClass() • Dieses Class Objekt enthält Metainformationen und bietet „Java FunctionPointer“: – Method m = c.getDeclaredMethod(name, ...) – Field f = c.getDeclaredField(name) • Diese „FunctionPointer“ erlauben das Objekt zu manipulieren: – double x = f.getDouble(obj) – m.invoke(obj, ...) © Prof. Dr. Nikolaus Wulff Informatik III 8 Shapes kanonische Form • Die Aufteilung in Methoden und Attribute erfolgt in zwei Structs XXX_Instance und XXX_Class: typedef struct Shape_Instance_struct* Shape; typedef struct Shape_Class_struct { void (*draw) (Shape aShape); void (*erase) (Shape aShape); void (*moveTo) (Shape aShape, int x ,int y); } ShapeClass; typedef struct Shape_Instance_struct { ShapeClass *class; int pos_X; int pos_Y; <<Data>> ShapeInstance } ShapInstance; ShapeClass pos_X pos_Y © Prof. Dr. Nikolaus Wulff Informatik III <<Methods>> ShapeClass 1 moveTo() moveBy() 9 Java Reflection API Class Object Package 1 name : MyClass myAttribute <<Interface>> Member myMethod() Constructor (from © Prof. Dr. Nikolaus Wulff Field (from Informatik III Method (from 10 Native Code API • Alle C Methoden erhalten eine Referenz auf die JVM Umgebung und das entsprechende Java Objekt, dessen native Methode entwickelt wird. • Dieses „Umgebungsobjekt“ ist eine C Struktur, die verschieden FunctionPointer anbietet, um Methoden und Felder beliebiger Objekt zu addressieren. • Ermitteln des Class Objekts von obj jclass clazz = (*env)->GetObjectClass(env, obj); • Ermitteln der Methode „double getValue()“ : Signatur jmethodID method = (*env)->GetMethodID(env, clazz, "getValue", "()D"); © Prof. Dr. Nikolaus Wulff Informatik III 11 Zugriff auf Felder • Felder können direkt aus C addressiert werden. • Es wird keine Rücksicht auf private genommen! jfieldID field; field = (*env)->GetFieldID(env, clazz, "x", "D"); (*env)->SetDoubleField(env, obj, field, 1.0); • Die zu verwendenen C Methoden sind in der Datei <jni.h> deklariert. © Prof. Dr. Nikolaus Wulff Informatik III 12 Aufruf von Funktionen • Aufruf der Methode „getValue“: double x; x = (*env)->CallDoubleMethod(env, obj, method); • Gegenstück „setValue“: Signatur method = (*env)->GetMethodID(env, clazz, "setValue", "(D)V"); (*env)->CallVoidMethod(env, obj, method, x); • Die Signatur dient dazu überladene Methoden eindeutig zu identifizieren. • Wie kann effizient die genaue Signatur der Methode ermittelt werden? © Prof. Dr. Nikolaus Wulff Informatik III 13 Java Disassembler • Der Java Disassembler javap gestattet es in das Innere von Java Klassen zu schauen. • Mit den Parametern -p -s liefert javap die Signatur Informationen: ~/workspace/CPrak-III-1$ javap -s -p de.lab4inf.Differentiator Compiled from "Differentiator.java" public class de.lab4inf.Differentiator extends java.lang.Object{ public de.lab4inf.Differentiator(); Signature: ()V public native double differentiate(de.lab4inf.Function, double); Signature: (Lde/lab4inf/Function;D)D static {}; Signature: ()V } © Prof. Dr. Nikolaus Wulff Informatik III 14 Objekterzeugung • Primitive Datentypen können dirket zurückgegeben werden. Objekte müssen erst erzeugt werden: jclass clazz; jmethodID ctor; jobject complex; clazz = (*env)->FindClass(env, "math/Complex"); assert(clazz != NULL); Konstruktor Signatur ctor = (*env)->GetMethodID(env, clazz, "<init>", "()V"); assert(ctor != NULL); complex = (*env)->NewObject(env, clazz, ctor); assert(complex != NULL); return complex; © Prof. Dr. Nikolaus Wulff Informatik III 15 Einbinden der C Bibliothek • Vor dem Verwenden von native Klassen muß die zugehörige C Bibliothek geladen werden. • Diese hat immer den generischen Namen – libXXX.so für Unixe – libXXX.dll für Windows • Geladen wird sie am Besten beim Start der Anwendung mit dem Aufruf: – System.loadLibrary("XXX"); • gesucht wird dann im java.library.path. Oder mit expliziter Pfadangabe (Achtung nicht portabel!): – System.load("/pfad/libXXX.so"); © Prof. Dr. Nikolaus Wulff Informatik III 16 Warnungen • Mit dem C Code ist es möglich auf jedes Feld und jede Methode der JVM zuzugreifen. • Sogar eigene Semaphore, Mutexe und critical Sections können geschrieben werden. • Es ist möglich die gesamte JVM lahmzulegen. • Es ist daher Vorsicht angebracht. • Am Besten keine eigenen Synchronisationsmechanismen in C! • Kein malloc und free etc. • Keine globalen Referenzen auf Objekte! • Objekte mit Java Mitteln erzeugen, damit der Garbage Collector den Speicher verwaltet. © Prof. Dr. Nikolaus Wulff Informatik III 17 Java und C ein gutes Gespann • Mit Hilfe der Java Native API bleiben keine Wünsche mehr offen. • Ein guter Programmierer kann bis auf Betriebssystemebene gehen, sogar Assembler ist möglich. • Die Eclipse ist ein gutes Beispiel für Java und C. • Eclipse verwendet weder AWT noch Swing für die GUI sondern das selbstentwickelte SWT. • SWT ist eine Bibliothek, die aus graphischen Klassen besteht, die vollkommen in C implementiert sind und direkt delegieren an die Widgets des jeweiligen Betriebsystems. © Prof. Dr. Nikolaus Wulff Informatik III 18