Java Native Interface Eine Einführung. 2 Motivation Java: abstrahiert von Hardware und...

Preview:

Citation preview

Java Native Interface

Eine Einführung

2

Motivation

Java: abstrahiert von Hardware und Betriebssystem+ hohe Portabilität+ hohe Sicherheit (Sandbox-Prinzip)- verminderte Performanz

(mit heutigen JIT-Compilern i.A. kein Problem mehr)- kein direkter Zugriff auf Hardware-/Betriebssystem

C/C++: Programmiersprache für OS-Entwicklung (UNIX)+ maschinennah, hohe Performanz+ alt, weit verbreitet, umfangreiche Sammlung von Legacy-Code- Programmierung möglicherweise umständlich (GUI in C?)- hohe Anforderungen an Programmierer

3

Das Java Native Interface (JNI)

Schnittstelle, um C/C++ -Code in Java einzubindenund umgekehrt

Beispiele - C/C++ in Java: Treiber, Betriebssystemfunktionen ansprechen

(SWT) Vorhandene Bibliotheken einbinden Zeitkritische Funktionen in C oder Assembler

einbinden Möglicherweise Verlust der Portabilität!

Beispiel – Java in C/C++: Browser, der Applets ausführt

4

JNI - Architektur

Schnittstelle definiert Datentypen und Funktionen… zum Aufruf nativer Funktionen in Java zur Abbildung von Java-Datentypen in

C/C++ zur Erzeugung und Mani-

pulation von Java-Objekten in C/C++ (OO in C?)

zum Aufruf einer JVM aus C/C++ heraus

5

C in Java : Hallo Welt

Minimal-Beispiel: Aufruf einer nativen Methode, die „Hallo Welt!“ auf der Konsole ausgibt.

Prinzipielles Vorgehen (immer gleich):

6

Hallo Welt – Java-Code

Definition einer nativen Methode ohne Rumpf

Dynamisches Laden der Bibliothek, die Methode bereitstellt

Kompilieren mit: javac HelloWorld.java

class HelloWorld {

private native void print(); public static void main(String[] args) { new HelloWorld().print(); }

static { System.loadLibrary("HelloWorld"); }}

7

Hallo Welt – C/C++-Header

Die C-Methode, die in HelloWorld aufgerufen wird, muss gewisse Konventionen erfüllen

javah –jni HelloWorld.class erzeugt Header-Datei HelloWorld.h mit Prototypen aller nativen Methoden:

Methoden-Name muss in C eindeutig sein, daher:Java_packageName_className_methodName

Parameterlose Funktion print() erhält in C zwei Parameter !?

include <jni.h> […]

JNIEXPORT void JNICALL Java_HelloWorld_print (JNIEnv *env, jobject); […]

8

Hallo Welt – C-Code

Implementierung der in Header-Datei definierten Funktion in Datei HelloWorld.c:

Übersetzen in gemeinsam genutzte Bibliothekgcc -fPIC -c HelloWorld.c

ld -shared -soname libHelloWorld.so.l -o libHelloWorld.so HelloWorld.o

#include "HelloWorld.h"#include <stdio.h>

JNIEXPORT void JNICALL Java_HelloWorld_print (JNIEnv *env, jobject obj){ printf("Hallo Welt!\n");}

9

Hallo Welt - Finale

Ausführen des Java-Programms: java HelloWorld java.lang.UnsatisfiedLinkError: no HelloWorld in library path

at java.lang.Runtime.loadLibrary(Runtime.java)at java.lang.System.loadLibrary(System.java)at HelloWorld.main(HelloWorld.java)

Pfad zur Bibliothek noch unbekannt! Entweder1. Bibliothek in /usr/lib verschieben (root-Rechte!),2. setenv LD_LIBRARY_PATH .:${LD_LIBRARY_PATH},3. oder Bibliothekpfad explizit an Java übergeben:

java –Djava.library.path=. HelloWorld

10

Abbildung der Datentypen

Bisher: kein Datenaustausch zwischen Java und C

Datentypen in Java: Primitive Datentypen, Klassen, Objekte, Arrays

Unterschiede Java vs. C: Java: Einheitliche Spezifikation primitiver

Datentypen auf allen Plattformen C: plattformabhängig (z.B. 2Byte int, 4Byte int)

Java: ObjektorientierungC: keine Objekte, aber strukturierte Datentypen und Funktionszeiger

11

Abbildung primitiver Datentypen

Java-Typ Nativer Typ Beschreibung

boolean jboolean 8 Bit ohne Vorzeichen

byte jbyte 8 Bit mit Vorzeichen

char jchar 16 Bit ohne Vorzeichen

short jshort 16 Bit mit Vorzeichen

int jint 32 Bit mit Vorzeichen

long jlong 64 Bit mit Vorzeichen

float jfloat 32 Bit

double jdouble 64 Bit

void void

12

Abb. primitiver Datentypen – Beispiel

public class Sum{ static{ System.loadLibrary("Sum"); } public static native int sum(int a, int b); public static void main(String[] args){ System.out.println("sum(3,4)=" + sum(3,4));

}}

#include <jni.h> […]

JNIEXPORT jint JNICALL Java_Sum_sum (JNIEnv *env, jclass, jint, jint); […]

JNIEXPORT jint JNICALL Java_Sum_sum(JNIEnv *env, jclass cl, jint a, jint b){ jint result = a + b; return result;}

13

Abbildung von Referenz-Datentypen

Objekte, Klassen und Arrays werden in Java über Referenzen angesprochen

An C wird ein „transparenter“ Pointer übergeben, der auf die Objekte in der JVM zeigt.

Zugriff auf die Objekte erfolgt immer über die JNIEnv-Umgebung, die jeder nativen Methode übergeben wird:

14

Abb. von Referenztypen – z.B. Arrays

import java.util.Random;public class Sort{

static{ System.loadLibrary("QSort"); } public static native void sort(double[] arr); public static void main(String[] args){ […] double[] arr = new double[Integer.parseInt(args[0])]; Random r = new Random(); for(int i=0; i< arr.length; arr[i++] = r.nextInt(10000)); long s = System.currentTimeMillis(); sort(arr); long e = System.currentTimeMillis(); System.out.println("Sortierung dauerte "+(e-s)+"ms"); }}

15

Abb. von Referenztypen – z.B. Arrays

Prototyp:

Zugriff auf Array-Elemente:

JNIEXPORT void JNICALL Java_Sort_sort (JNIEnv *env, jclass, jdoubleArray);

JNIEXPORT void JNICALLJava_Sort_sort (JNIEnv *env, jclass cl, jdoubleArray arr){

jdouble *cArray = (jdouble*) arr; // FALSCH!!!// arr ist lediglich ein Pointer auf eine JVM-// Datenstruktur. Der Zugriff auf die Array-Elemente// muss über die JNIEnv Umgebung erfolgen![…]

}

16

Abb. von Referenztypen – z.B. Arrays

int compare(const void *a, const void *b){return *((jdouble*) a) < *((jdouble*) b) ? -1 : +1;

}

JNIEXPORT void JNICALLJava_Sort_sort (JNIEnv *env, jclass cl, jdoubleArray arr){

jboolean isCopy;jdouble* cArray = (*env)->GetDoubleArrayElements(env, arr,

isCopy);jint length = (*env)->GetArrayLength(env, array);

qsort(cArray, length, sizeof(jdouble), &compare); if(isCopy) (*env)->ReleaseDoubleArrayElements(env, array, cArray, 0);}

17

Array-Zugriffsmethoden

Get<Type>ArrayRegion, Set<Type>ArrayRegion Kopiert Bereich aus oder in ein Array mit

primitivem Datentyp Get<Type>ArrayElements,

Release<Type>ArrayElements Erhält einen Pointer auf ein Array (ggf. auch

Kopie) und gibt ihn wieder frei (ansonsten Speicherleck!)

GetArrayLength Ermittelt die Länge eines Arrays

New<Type>Array Legt ein neues Array an

Nur auf primitive Arrays anwendbar!

18

Zugriff auf Objekt-Arrays

Beispiel: String[] strArray; int[][] intArray; Object[] objArray;

Unmöglich, gesamtes Array abzufragen Lediglich einzelne Einträge zugreifbar

GetObjectArrayElement SetObjectArrayElement

19

Beispiel: Anlegen eines 2D-Feldes

Zuerst: Anweisung an JVM, ein int[] array anzulegen:

private static native int[][] initInt2DArray(int size);

JNIEXPORT jobjectArray JNICALLJava_ObjectArrayTest_initInt2DArray(JNIEnv *env, jclass c, int s){ jobjectArray result; int i; jclass intArrCls = (*env)->FindClass(env, "[I"); if (intArrCls == NULL) return NULL; /* exception thrown */ result = (*env)->NewObjectArray(env, size, intArrCls, NULL); if (result == NULL) return NULL; /* out of memory error thrown */

20

Beispiel: Anlegen eines 2D-Feldes

Dann: Für jeden Eintrag des int[] Arrays ein primitives int[] Array anlegen

…for (i = 0; i < size; i++) {

jint tmp[256]; /* make sure it is large enough! */ int j; jintArray iarr = (*env)->NewIntArray(env, size); if (iarr == NULL) return NULL; /* out of memory error */ for (j = 0; j < size; j++) tmp[j] = i + j; (*env)->SetIntArrayRegion(env, iarr, 0, size, tmp); (*env)->SetObjectArrayElement(env, result, i, iarr); (*env)->DeleteLocalRef(env, iarr); } return result;}

21

Beispiel: Anlegen eines 2D-Feldes

Dann: Für jeden Eintrag des int[] Arrays ein primitives int[] Array anlegen

…for (i = 0; i < size; i++) {

jint tmp[256]; /* make sure it is large enough! */ int j; jintArray iarr = (*env)->NewIntArray(env, size); if (iarr == NULL) return NULL; /* out of memory error */ for (j = 0; j < size; j++) tmp[j] = i + j; (*env)->SetIntArrayRegion(env, iarr, 0, size, tmp); (*env)->SetObjectArrayElement(env, result, i, iarr); (*env)->DeleteLocalRef(env, iarr); } return result;}

Inhalt von tmp nach iarr kopieren

22

Beispiel: Anlegen eines 2D-Feldes

Dann: Für jeden Eintrag des int[] Arrays ein primitives int[] Array anlegen

…for (i = 0; i < size; i++) {

jint tmp[256]; /* make sure it is large enough! */ int j; jintArray iarr = (*env)->NewIntArray(env, size); if (iarr == NULL) return NULL; /* out of memory error */ for (j = 0; j < size; j++) tmp[j] = i + j; (*env)->SetIntArrayRegion(env, iarr, 0, size, tmp); (*env)->SetObjectArrayElement(env, result, i, iarr); (*env)->DeleteLocalRef(env, iarr); } return result;}

Array an übergeordnetes Objekt-Array result übergeben

23

Beispiel: Anlegen eines 2D-Feldes

Dann: Für jeden Eintrag des int[] Arrays ein primitives int[] Array anlegen

…for (i = 0; i < size; i++) {

jint tmp[256]; /* make sure it is large enough! */ int j; jintArray iarr = (*env)->NewIntArray(env, size); if (iarr == NULL) return NULL; /* out of memory error */ for (j = 0; j < size; j++) tmp[j] = i + j; (*env)->SetIntArrayRegion(env, iarr, 0, size, tmp); (*env)->SetObjectArrayElement(env, result, i, iarr); (*env)->DeleteLocalRef(env, iarr); } return result;}

Kontrolle über iarr an JVM geben

24

Arbeiten mit Strings

Java Strings sind Unicode-Codiert, C-Strings sind ASCII-codiert

Anbindung über UTF-8 (kann beide Codierungen abbilden) GetStringChars, ReleaseStringChars GetStringUTFChars, ReleaseStringUTFChar GetStringLength, GetStringUTFLength NewString, NewStringUTF GetStringRegion, SetStringRegion GetStringUTFRegion, SetStringUTFRegion

Anbindung sehr umständlich, Java im Umgang mit Strings komfortabler, daher wenig Relevanz

25

Zugriff auf Objekt-Felder und -Methoden

Bei komplexen Klassen-Instanzen kann aus C heraus auf die Felder und Methoden der Instanz zugegriffen werden.

Mechanismus erscheint relativ umständlich, daher hier nur exemplarisch dargestellt. Für nähere Infos siehe JNI-Referenz

26

Zugriff auf Static-Feld

Java-Klasse mit statischem Feld si si soll in nativer Methode manipuliert

werdenclass StaticFieldAccess { private static int si; private native void accessField(); public static void main(String args[]) { StaticFieldAccess c = new StaticFieldAccess(); StaticFieldAccess.si = 100; c.accessField(); System.out.println("In Java:"); System.out.println(" StaticFieldAccess.si = " + si); } static { System.loadLibrary("StaticFieldAccess"); }}

27

Zugriff auf Static-Feld

JNIEXPORT void JNICALLJava_StaticFieldAccess_accessField(JNIEnv *env, jobject obj){ jfieldID fid; /* store the field ID */ jint si; /* Get a reference to obj’s class */ jclass cls = (*env)->GetObjectClass(env, obj);

/* Look for the static field si in cls */ fid = (*env)->GetStaticFieldID(env, cls, "si", "I"); if (fid == NULL) return; /* field not found */ /* Access the static field si */ si = (*env)->GetStaticIntField(env, cls, fid); printf(" StaticFieldAccess.si = %d\n", si); (*env)->SetStaticIntField(env, cls, fid, 200);}

28

Zugriff auf Instanz-Methode

Methode callback() soll in nativeMethod() gerufen werden

class InstanceMethodCall { private native void nativeMethod(); private void callback() { System.out.println("In Java"); } public static void main(String args[]) { InstanceMethodCall c = new InstanceMethodCall(); c.nativeMethod(); } static { System.loadLibrary("InstanceMethodCall"); }}

29

Zugriff auf Instanz-Methode

JNIEXPORT void JNICALLJava_InstanceMethodCall_nativeMethod(JNIEnv *env, jobject obj){ jclass cls = (*env)->GetObjectClass(env, obj); jmethodID mid = (*env)->GetMethodID(env, cls, "callback", "()V"); if (mid == NULL) return; /* method not found */ printf("In C\n"); (*env)->CallVoidMethod(env, obj, mid);}

30

Das war erst der Anfang...

JNI bietet noch mehr: Erzeugen von Objekten, Aufruf von

Konstruktoren Caching von Methoden-/Feld-Ids Exception-Handling …

Diese Themen werden dem geneigten Hörer zum Selbststudium überlassen

31

Fazit

Enorme Möglichkeiten, positive Aspekte von C, C++ und Java zu integrieren

Aufwand allerdings nicht unerheblich, daher sollten immer auch Alternativen in Betracht gezogen werden

Für Standardaufgaben ist Einsatz von JNI aufgrund des stetig abnehmenden Performanz-Vorsprungs von C/C++ (wahrscheinlich) nicht lohnend

Recommended