Java Native Calls Einbinden von C Bibliotheken in Java Klassen Prof. Dr. Nikolaus Wulff GUI und Model • Die DeviceEditor GUI ist in Java implementiert. • Die Anwendung ist allerdings ohne eine fachliche Logik ein reines „Malwerkzeug“. • Die fachliche Logik des elektrischen Bauteile Beispiels besitzt eine C Implementierung. • Was fehlt ist eine Erweiterung der graphischen Klassen um die fachliche Logik: • Entweder die C Implementierung in Java neu zu implementieren oder aber • die graphischen Bauteile als Adapter zu verwenden, die an die fachliche C Implementierung delegiert. © Prof. Dr. Nikolaus Wulff Informatik III 2 MVC beim DeviceEditor View JComponent (from Painter <<Interface>> Paintable manages (from 0..* (from painter) DevicePainter (from v iew ) DeviceController (from controller) delegates DeviceGUI / manage Device peer (from vie w) (from 0..* 1 <<Interface>> MouseListener (from j av a.aw t) ResistorGUI (from v iew ) Resistor /delegates (from model) 1 CapacitorGUI (from vi ew ) CrossWire (from Controller © Prof. Dr. Nikolaus Wulff MouseAdapter Capacitor /delegates (from model) 1 (from j av a.aw t) View Adapter Informatik III Model 3 Einbinden von C in Java • Die Scheinwiderstände der Devices wurden am Anfang des Semesters mit Hilfe von C Methoden realisiert. • Wie lässt sich diese Implementierung wieder verwenden? • Neu- bzw. Nachimplementierung in Java • C Routinen in Java Code einbinden • Welcher Ansatz verwendet wird richtet sich nach dem Aufwand und Nutzen... © Prof. Dr. Nikolaus Wulff Informatik III 4 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>> } ShapInstance; ShapeInstance ShapeClass pos_X pos_Y © Prof. Dr. Nikolaus Wulff Informatik III <<Methods>> ShapeClass 1 moveTo() moveBy() 5 Widerstand ADT /** * Resistor.h * Definition of Resistor ADT * $Id: Resistor.h,v 1.1 2004/10/26 10:07:27 nwulff Exp $ */ #ifndef __RESISTOR_H_ #define __RESISTOR_H_ #include "Device.h" typedef struct resistor_struct* Resistor; struct resistor_struct { Impedance impedance; Conductance conductance; double resistance; char type; char* name; }; extern Device createResistor(double resistance); #endif /* __RESISTOR_H_ defined */ © Prof. Dr. Nikolaus Wulff Informatik III 6 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 Resistor extends Device { /* (non-Javadoc) * @see model.Device#getConductance(double) */ @Override public native Complex getConductance(double omega); } © Prof. Dr. Nikolaus Wulff Informatik III 7 C Header-Dateien generieren • Nach dem Übersetzen von Resistor.java wird mit dem Tool javah ein C Header-Datei Resistor.h generiert. • In dieser stehen die Deklarationen der fehlenden zu implementierenden nativen C/Java Funktionen. • Eine entsprechende Implementierung Resistor.c wird dann zu einer Bibliothek gebunden. Resistor.h Resistor.j ava Resistor.c libResistor.so © Prof. Dr. Nikolaus Wulff Informatik III 8 Resistor.h /* DO NOT EDIT THIS FILE - it is machine generated */ #include <jni.h> /* Header for class model_Resistor */ #ifndef _Included_model_Resistor #define _Included_model_Resistor #ifdef __cplusplus extern "C" { Complex getConductance(double omega); #endif /* * Class: model_Resistor * Method: getConductance * Signature: (D)Lmath/Complex; */ JNIEXPORT jobject JNICALL Java_model_Resistor_getConductance (JNIEnv *, jobject, jdouble); #ifdef __cplusplus } #endif #endif © Prof. Dr. Nikolaus Wulff JVM Informatik III Resistor 9 Resistor.c howto? • Wie kann jetzt die C Methode implementiert werden? • Benötigt wird nicht nur der Wert omega, sondern auch der Widerstand (bzw. die Kapazität etc) des jeweiligen Bauteils. • Ausserdem muss ein neues Java Objekt vom Typ Complex zurückgegeben werden. Dies kann kein einfacher C-struct sein... • Es lassen sich nicht nur von Java aus C Routinen rufen, sondern diese können auch ihrerseits wieder auf Java Klassen, Objekten und Methoden zurückgreifen! © Prof. Dr. Nikolaus Wulff Informatik III 10 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 11 Java Reflection API Cl ass Object Package 1 name : MyClass myAt tribut e <<Interface>> Member myM ethod() Constructor (from © Prof. Dr. Nikolaus Wulff Field (from Informatik III Method (from 12 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 13 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, complex, field, 1.0); • Die zu verwendenen C Methoden sind in der Datei <jni.h> deklariert. © Prof. Dr. Nikolaus Wulff Informatik III 14 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 15 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/JDevices/bin> javap -s -p model.Resistor Compiled from "Resistor.java" public class model.Resistor extends model.Device{ public model.Resistor(); Signature: ()V private native void calculateImpedance(math.Complex, double); Signature: (Lmath/Complex;D)V public math.Complex getImpedance(double); Signature: (D)Lmath/Complex; public native math.Complex getConductance(double); Signature: (D)Lmath/Complex; } © Prof. Dr. Nikolaus Wulff Informatik III 16 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 17 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 18 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 19 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 20