Java Database Connectivity-API (JDBC)

Werbung
JNI
Java Native Interface
JOHANNES KEPLER UNIVERSITY LINZ
Research and teaching network
Pratikum SWE 2 © Institut für Systemsoftware, Johannes Kepler Universität Linz
Probleme bei nativem Code in Java
Methodenaufruf
 Wie kann man native Methoden aus Java-Code aufrufen?
 Wie kann man Java-Methoden aus nativem Code aufrufen?
Parameterübergabe und Rückgabewärte
 Wie werden Parameterwerte und Rückgabewerte übergeben
Zugriff auf Java Memory
 Wie kann man Daten in einem Java-Objekt oder Array lesen und
schreiben?
 Wie kann man ein Java-Objekt anlegen?
Garbage Collection
 Wann kann ein Objekt freigegeben werden, das von nativem Code
benutzt wird?
Beispiel: Native Methoden in Java-Code
Class Out definiert native Methode für Ausgabe
Implementierung erfolgt in einer externen Bibliothek
Laden der Library mit der nativen Implementierung vor dem ersten
Aufruf
class Out {
Lädt Library mit Implementierung
nativer Methoden
static {
System.loadLibrary("libout");
}
public static native void print(String text);
}
Methode mit Keyword
native (wie abstract ,d.h.
keine Implementierung)
Workflow
Native Methoden in Java deklarieren (in Out.java)
 public static native void print(String text);
Java Klassen kompilieren (erzeugt Out.class)
 javac Out.java
Header Dateien generieren (erzeugt Out.h)
 javah Out
Header Dateien inkludieren und native Methoden implementieren (in out.c)
 #include "Out.h"

JNIEXPORT void JNICALL Java_Out_print(JNIEnv* env, jclass clazz, jstring text) {...}
Nativen Code compilieren (erzeugt libout.so)
Windows: libout.dll
 gcc -shared -fPIC -I<path-to-jdk-install-dir>/include -o libout.so out.c
Programmstart mit Angabe des Pfads zur nativen Library
 java -Djava.library.path=. Out
Native Library vor dem ersten Aufruf einer native Methode laden (in Out.java)
 static { System.loadLibrary("libout"); }
JNI Headers
javah generiert Header-Dateien aus kompilierten Java-Dateien
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class Out */
#ifndef _Included_Out
#define _Included_Out
Pro native Methode in Java gibt es eine Methode
#ifdef __cplusplus
im Header mit Namen:
extern "C" {
Java_<Klassenname>_<Methodenname>
#endif
/*
* Class:
Out
* Method:
print
* Signature: (Ljava/lang/String;)V
*/
JNIEXPORT void JNICALL Java_Out_print (JNIEnv*, jclass, jstring);
#ifdef __cplusplus
}
#endif
#endif
Macro für
Library-Export
Macro für
C++ Calling
conventions
JNI Environment: Schnittstelle
zur Virtuellen Maschine
Parameter der Methode:
Parameter werden in native
Typen übersetzt
Klasse der Methode (bei
nichtstatischen Methoden
jobject (this)
JNI Type-Mapping
Java Typ
JNI Typ
32-bit Typ
64-bit Typ
Signatur
void
void
void
void
V
byte
jbyte
signed char
signed char
B
short
jshort
short
short
S
int
jint
int
int
I
long
jlong
long long
long
J
float
jfloat
float
float
F
double
jdouble
double
double
D
boolean
jboolean
unsigned char
unsigned char
Z
char
jchar
unsigned short
unsigned short
C
java.lang.Object
jobject
*
*
Ljava/lang/Object;
java.lang.String
jstring
*
*
Ljava/lang/String;
java.lang.Class
jclass
*
*
Ljava/lang/Class;
*
*
Ljava/lang/Throwable;
java.lang.Object[]
jobjectArray *
*
[Ljava/lang/Object;
int[]
jintArray
*
*
[I
?
jobject
*
*
L<full-class-name>;
java.lang.Throwable jthrowable
Weitere JNI-Typen
JNI Typ
Verwendung
jsize
Array Längen
jweak
Weak References
jvalue
Basis für alle primitiven Typen
jfieldID
ID für Felder
jmethodID
ID für Methoden
JNIEnv
Interface zur Java VM
Typen:
 Typen sind definiert in jni.h
 Geänderte Semantik von Typen, z.B.:
•
•
•
Precision von float und double
gleiche oder größere Wertebereiche
kein Java-Overflow
 tatsächliche Typen sind architekturabhängig!
 Vererbungshierarchien gleich zu Java, z.B. jstring erbt von jobject
Object Handles
Jedes Objekt (jobject, jstring, …) ist nur ein Handle für ein Java-Objekt
 Nativer Code bekommt nie Pointer in Java Memory
3 Arten von Handles mit unterschiedlichen Lebenszeiten
 local
 lebt bis der native Frame zerstört wird, in dem der Handle angelegt wurde
 Parameter von nativen Methoden sind immer local
 kann explizit mit
(*env)->DeleteLocalRef(env, obj)
zerstört werden
 global
 lebt über native Frame-Grenzen hinweg
 muss explizit erzeugt und zerstört werden (NewGlobalRef, DeleteGlobalRef)
 weak
 werden explizit erzeugt und zerstört (NewGlobalRef, DeleteGlobalRef)
 Lebenszeiten wie global
 aber wie WeakReferece  Objekt vom Garbage Collector freigegeben, wenn keine
Referenzen in Java VM existieren
Handle wird dann null
Beispiel Object Handle
void Java_Foo_bar(JNIEnv* env, jclass clazz, jobject obj) {
static jobject last_obj = NULL;
if(last_obj != NULL) {
(*env)->DeleteGlobalRef(env, last_obj);
}
last_obj = (*env)->NewGlobalRef(env, obj);
printf(“obj %s last_obj\n”, obj == last_obj ? “==” : “!=”);
printf(“obj is %s the same as last_obj\n”,
(*env)->IsSameObject(obj, last_obj) ? “indeed” : “not”);
}
Output:
obj != last_obj
obj is indeed the same as last_obj
Zugriff auf Java Memory: Felder
 Suche Klasse
jclass GetObjectClass(JNIEnv*, jobject obj)
Ähnlich zu Reflection
jclass FindClass(JNIEnv*, char* name)
 Suche Feld (mit Klasse und Name)
jfieldID GetFieldID(JNIEnv*, jclass clazz, char* name, char* sig)
 Lese / schreibe Feld
<T> Get<T>Field(JNIEnv*, jobject obj, jfieldID field);
void Set<T>Field(JNIEnv*, jobject obj, jfieldID field, <T> value);
<T> … für unterschiedliche Typen
JNIEnv* env = ...
jobject person = ...
jclass clazz = (*env)->GetObjectClass(env, person);
jfieldID field = (*env)->GetFieldID(env, clazz, "age", "I");
jint age = (*env)->GetIntField(env, person, field);
(*env)->SetIntField(env, person, field, age + 1);
(*env)->DeleteLocalRef(clazz);
Zugriff auf Java Memory: Array
3 Zugriffsarten
 Elementweiser Zugriff
<T> … für unterschiedliche Typen
<T> Get<T>ArrayElement(JNIEnv*, j<T>Array array, jsize index)
void set<T>ArrayElement(JNIEnv*, j<T>Array array, jsize index, <T> value)
 Regionsweiser Zugriff
Get<T>ArrayRegion(JNIEnv*, j<T>Array array, jsize start, jsize length, <T>* buffer)
Set<T>ArrayRegion(JNIEnv*, j<T>Array array, jsize start, jsize length, <T>* buffer)
 Arrayweiser Zugriff
<T>* get<T>ArrayElements(JNIEnv*, j<T>Array array)
void Release<T>ArrayElements(JNIEnv*, j<T>Array array, <T>* elems, jint mode)
0
kopiere Elemente in array und gib Buffer frei
JNI_COMMIT
kopiere Elemente in array und gib Buffer nicht frei
JNI_ABORT
kopiere Elemente nicht zurück und gib Buffer frei
Beispiele: Zugriff auf Arrays
JNIEnv* env = ...; jintArray array = …;
//Access each element individually
for(jsize i = 0; i < (*env)->GetArrayLength(env, array); i++) {
jint value = (*env)->GetIntArrayElement(env, array, i);
(*env)->SetIntArrayElement(env, array, i, value + 1);
}
//Access the entire array at once
jint* native_array = (*env)->GetIntArrayElements(env, array);
for(jsize i = 0; i < (*env)->GetArrayLength(env, array); i++) {
native_array[i]++;
}
(*env)->ReleaseIntArrayElements(env, array, native_array, 0);
//Access chunks of the array
jint* chunk = (jint*) calloc(16, sizeof(jint);
for(jsize i = 0; i < (*env)->GetArrayLength(env, array) / 16; i++) {
(*env)->GetIntArrayRegion(env, array, i*16, 16, chunk);
for(int i = 0; i < 16; i++) {
chunk[i]++;
}
(*env)->SetIntArrayRegion(env, array, i*16, 16, chunk);
}
free(chunk);
Zugriff auf Java Memory: Strings
Lesender Zugriff wie auf Arrays
jsize GetStringLength(JNIEnv*, jstring string)
Inhalt wird nicht
zurückgeschrieben!
jchar* GetStringChars(JNIEnv*, jstring string)
void ReleaseStringChars(JNIEnv*, jstring string, jchar* chars)
JNIEXPORT void JNICALL Java_Out_print(JNIEnv* env, jclass clazz, jstring text) {
if(text != NULL) {
jsize length = (*env)->GetStringLength(env, text);
jchar* characters = (*env)->GetStringChars(env, text);
char* native_characters = calloc(length + 1, sizeof(char));
for(jsize i = 0; i < length; i++) {
native_characters[i] = (char) characters[i]; //assume ASCII only
}
native_characters[length] = '\0';
(*env)->ReleaseStringChars(env, text, characters);
printf("%s", native_characters);
free(native_characters);
}
}
clazz und text sind local Handles
und müssen deswegen nicht
explizit gelöscht werden
Aufrufe von Java aus Native-Code
 Suche Klasse wie bei Feldzugriff
 Suche Methode
"(" ParamSig0 ParamSig1 … ")" ReturnSig
zB: "(JJ)Z"
jmethodID GetMethodID(JNIEnv*, jclass clazz, char* name, char* sig)
 Aufruf
Parameters
<T> Call<T>Method(JNIEnv*, jobject thiz, jmethodID method, ...)
<T> CallNonvirtual<T>Method(JNIEnv*, jobject thiz, jclass clazz,
jmethodID method, ...)
<T> CallStatic<T>Method(JNIEnv*, jclass clazz, jmethodID method, ...)
 Spezielle Funktionen zur Objekterzeugung
jobject NewObject(JNIEnv*, jclass clazz, jmethodID constructor, ...)
jstring NewString(JNIEnv*, jchar* chars, jsize length)
j<T>Array New<Type>Array(JNIEnv*, jsize length)
jobject AllocObject(JNIEnv*, jclass clazz)
Erzeugt Objekt ohne
Konstruktor aufzurufen!
Beispiel: Aufrufe von Java aus Native-Code
JNIEnv* env = ...;
jobject person1 = ...; jobject person2 = ...;
jclass object_class = (*env)->FindClass(env, " Ljava/lang/Object");(Object): boolean
const char* sig = "(Ljava/lang/Object;)Z";
jmethodID equals_method = (*env)->GetMethodID(env, object_class, "equals", sig);
jboolean equals = (*env)->CallBooleanMethod(env, person1, equals_method, person2);
(*env)->DeleteLocalRef(env, object_class);
boolean equals(Object)
Exceptions
JNIEXPORT void JNICALL Java_Person_raiseSalary
(JNIEnv* env, jclass clazz, jobject person, jdouble factor) {
if(person == NULL) {
char* exception_name = "java/lang/NullPointerException";
jclass exception_class = (*env)->FindClass(env, exception_name);
(*env)->ThrowNew(env, exception_class, “person must not be null”);
return;
}
...
}
ThrowNew wirft Exception in Java VM erst, wenn die
native Methode beendet ist!
Weitere JNI-Methoden
Methode
Beschreibung
ExceptionOccurred
Prüft, ob der zuletzt ausgeführte Aufruf eine Exception
ausgelöst hat
ExceptionDescribe
Erzeugt einen String, der die Exception beschreibt
IsSameObject
Prüft Objekte auf Referenzgleichheit
IsInstanceOf
Prüft, ob ein Objekt von einem bestimmten Typ ist
http://docs.oracle.com/javase/1.5.0/docs/guide/jni/spec/functions.html
Zusammenfassung
JNI ermöglich native Code in Java einzubinden
Für jeden Java-Typ gibt es einen nativen JNI-Typ
Nativer Code kann Java-Objekte benutzen und Java-Methoden aufrufen
 Nativer Code bekommt nie einen direkten Pointer auf Java Memory
 Manuelle Speicherverwaltung bei Java-Objekten (Gefahr von Memory Leaks!)
Herunterladen