Cours Java Native Interface
Cnam Paris
jean-michel Douin,
version de Mars 2009
Notes de cours jni : de Java vers C/C++ et de C/C++ vers Java
Outils utilisés : JDK1.5, Visual C++ 6.0 (utilisé en ligne de commandes) et BlueJ2.2.1
1
JNI
Sommaire
• De Java vers C/C++
• De C/C++ vers Java
– lecture/écriture de données d'instance et de classes
– invocation de méthodes d'instance et de classes
– création d'objet
– création de tableaux et de String
– Levée et filtrage d'exceptions
– utilisation des moniteurs (de hoare)
– Entrées/sorties Série
– création de machine(s) Java
API JNI et une analogie avec l’Architecture de la
machine virtuelle
2
JNI
Bibliographie utilisée
Le tutorial JNI de Beth Stearns
• [Link]
• surtout : [Link]
• jni specification : [Link]
Un outil bien utile distribué par Sun
• [Link]
• [Link]
Un « essentiel » ouvrage sur le sujet
• Essential JNI: Java Native Interface, rob Gordon, Prentice hall.1998
ISBN 0-13-679895-0
• le site de l ’éditeur [Link]
les instructions et l ’architecture de la machine virtuelle Java reflètent les
fonctionnalités de l’ API JNI
• The VM [Link]://[Link]/docs/books/vmspec/html
3
JNI
JNI Pourquoi ?
• Applications existantes dans un environnement Java, avec ou sans les
sources
• Programmation d ’un périphérique, logiciel de base, Entrées/Sorties,
Cartes d ’acquisition, de commandes
– (Adressage physique, Accès au matériel, aux pilotes de la carte,
interruptions…
• Développement en C/C++, tout en bénéficiant de l ’environnement Java
IHM en Java, application en C, Applet, accès à l ’internet
• Code natif pour de meilleures performances en temps d ’exécution
• Portabilité ?
4
JNI
JNI Présentation
• Deux aspects
– de Java vers C/C++
– de C/C++ vers Java
– en venant de Java
– ou depuis une application ordinaire
• L ’API JNI offre l ’accès à la machine virtuelle
– accès aux variables d ’instance, appel de méthodes, chargement d ’une
classe, création d ’instances…
– Mécanisme de bas-niveau...
– Exceptions,
– Threads….
5
JNI
De Java vers C(jc)
• 1) usage du mot clé native
• 2) génération par les outils de SUN de l ’interface « .h » >javah -jni
Exemple
• 3) génération de la DLL (Win32,.dll), ou de la SOL(solaris,.so)
• 4) exécution
notes :
nécessite l ’accès à $JDK_HOME\include
(en fonction de la plate-forme $JDK_HOME\include\win32(ou solaris))
et l ’exécution sous DOS de C:\Program Files\Microsoft Visual Studio\VC98\Bin\[Link]
Setting environment for using Microsoft Visual C++ tools.
6
JNI
jc) 1) Le source Java
public class JavaVersC {
public native void bonjour(); // (1)
public static void main(String args[]) {
new JavaVersC().bonjour();
}
static{
[Link]("VersC"); // (2)
}
}
• >javac [Link]
• (1) emploi de native
• (2) chargement de la librairie (DLL/sol) dans laquelle sera implémentée le
code C de bonjour
7
JNI
jc) Le source C : interface
• 2) génération de l ’interface (.h) par javah
• >javah -jni JavaVersC
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class JavaVersC */
#ifndef _Included_JavaVersC
#define _Included_JavaVersC
#ifdef __cplusplus
extern "C" {
#endif
/* Class: JavaVersC
* Method: bonjour
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_JavaVersC_bonjour(JNIEnv *, jobject);
#ifdef __cplusplus
}
#endif
#endif
8
JNI
jc) le source C : Implémentation
#include <stdio.h>
#include "JavaVersC.h"
JNIEXPORT void JNICALL Java_JavaVersC_bonjour (JNIEnv *env, jobject j){
printf("Java_JavaVersC_bonjour");
}
• 3) Génération de la DLL, ([Link])
– Avec visual c++
– cl -I%JDK_HOME%\include -I%JDK_HOME%\include\win32 -LD JavaVersC.c -
[Link]
• 4) Exécution par
– >java JavaVersC
9
JNI
jc) JNIENV et jobject
• JNIEnv *env
– Il s ’agit de l ’environnement de la machine Java associé au « Thread »
courant, (le Thread ayant engendré l ’appel de la méthode native
bonjour)
• jobject j
– Il s ’agit de l ’objet receveur du message bonjour(), ici l ’instance créée
dans la méthode main
– (jclass j, dans le cas d ’une méthode de classe )
• En résumé
– A chaque appel d ’une méthode native sont transmis par la machine
Java
• un environnement
• l ’objet receveur ou la classe si c'est une méthode classe
• et éventuellement les paramètres
10
JNI
De C vers Java(cj)
• depuis une DLL/SO engendrée par javah
– emploi du mot clé native
– java -----native----------->C/C++
– |
– java <----API_JNI--------- C/C++
• ou depuis une application C/C++ ordinaire
– java <----API_JNI--------- C/C++
11
JNI
cj) De C vers Java
• Primitives d ’accès à la machine Java env
j
Variables
d’instance
Source C class
<jni.h> Méthodes
Byte d ’instances
code méthodes
de classe
Variables de
classe
12
JNI
Fonctions de JNI
• Versions
• Opérations sur les classes
• Exceptions
• Références locales et globales
• Opérations sur les objets
• Accès aux champs des objets
• Appels de méthodes d'instances
• Accès aux champs statiques
• Appels de méthodes de classes
• Opérations sur les instances de type String
• Opérations sur les tableaux
• Accès aux moniteurs
• Interface de la JVM
13
JNI
cj) Accès aux Variables d'instance
• GetFieldID, Get<type>Field, Set<type>Field
• public class Exemple{ obj
– private int x;
x Variables
– public native void setX(int val); d’instance
– }
• En C :
• JNIEXPORT void JNICALL Java_Exemple_setX (JNIENV
*env, jobject obj, jint val){
– jclass classe = (*env)->GetObjectClass(env,obj);
– jfieldID fid = (*env)->GetFieldID(env,classe,"x","I");
– (*env)->SetIntField(env,obj,fid,val) ;
– }
– instructions JVM : getfield, putfield
14
JNI
[Link] au complet
public class Exemple{
private int x;
public native void setX(int val);
static{
[Link]("Exemple");
}
public static void main(String args[]) {
Exemple e = new Exemple();
[Link](33);
[Link](" dites " + e.x);
}
}
15
JNI
Exemple.c au complet
#include "Exemple.h"
JNIEXPORT void JNICALL Java_Exemple_setX(JNIEnv* env,
jobject obj, jint val){
jclass cl = (*env)->GetObjectClass(env,obj);
jfieldID fid = (*env)->GetFieldID(env,cl,"x","I");
(*env)->SetIntField(env,obj,fid,val) ;
}
16
JNI
Les commandes (win32) au complet
javac -classpath . [Link]
javah -jni -classpath . Exemple
cl /I%JDK_HOME%\include /I%JDK_HOME%\include\win32 /LD Exemple.c /[Link]
java -cp . Exemple
17
JNI
Retour sur JNIENV *env
env
GetFieldID
SetIntField
GetObjectClass
etc...
etc...
en C : (*env)->GetFieldID(env,...)
en C++ : env->GetFieldID(...)
18
JNI
JNIENV (un extrait)
typedef const struct JNINativeInterface_ *JNIEnv;
struct JNINativeInterface_ {
void *reserved0;
…
jclass (JNICALL *FindClass) (JNIEnv *env, const char *name);
…
void (JNICALL *SetIntField)(JNIEnv *env, jobject obj, jfieldID fieldID, jint val);
jfieldID (JNICALL *GetFieldID)(JNIEnv *env, jclass clazz, const char *name, const
char *sig);
19
JNI
JNIENV *env
• Inspiré du JRI de Netscape
• Chaque fonction est accessible par un déplacement fixe
dans une table,
• Une table peut donc être substituée par une autre sans
avoir à recompiler,
• L'implantation des fonctions de cette table est à la charge
du fournisseur de la JVM,
• Le code C/C++ d'accès à la JVM devient portable
• Analogue à l'implémentation de la table des méthodes
virtuelles d'une instance
20
JNI
cj) Accès aux Variables de classes
• GetStaticFieldID, GetStatic<type>Field,
SetStatic<type>Field classe
x
• public class Exemple{
– private static int x;
Variables de
– public native void setStaticX(int val); classe
– }
• JNIEXPORT void JNICALL Java_Exemple_setStaticX
• (JNIENV *env, jobject obj, jint val){
– jclass classe = (*env)->GetObjectClass(env,obj);
– jfieldID fid = (*env)->GetStaticFieldID(env,classe,"x","I");
– (*env)->SetStaticIntField(env,classe,fid,val) ;
– }
– instructions JVM : getstatic, putstatic
21
JNI
cj) Appels de méthodes d ’instance
obj
• Call<type>Method
• public class Exemple{
– public void p(){[Link]( "appel de p ");}
– public native void callP(int val); table des
– } méthodes
• JNIEXPORT void JNICALL Java_Exemple_callP
(JNIENV *env, jobject obj){
– jclass classe = (*env)->GetObjectClass(env,obj);
– jmethodID mid = (*env)->GetMethodID(env,classe,"p","()V");
– (*env)->CallVoidMethod(env,obj,mid) ;
– }
– instruction JVM : invokevirtual
22
JNI
cj) Appels de méthodes de classe
classe
• CallStatic<type>Method
• public class Exemple{
– public static void p(){[Link]( "appel de p ");}
– public native void callP(int val); table des
– } méthodes de
classe
• JNIEXPORT void JNICALL Java_Exemple_callP
(JNIENV *env, jobject obj){
– jclass classe = (*env)->GetObjectClass(env,obj);
– jMethodID mid = (*env)->GetStaticMethodID(env,
classe,"p", "()V");
– (*env)->CallStaticVoidMethod(env, classe,mid) ;
– }
– instruction JVM : invokestatic
23
JNI
cj) Types natifs
• jni.h, interpreter.h,oobj.h,typecodes.h
• Types natifs / types java
– jbyte / byte, jshort / short, jint / int
– ….
– jobject / Object, jclass / Class, jstring / String, jarray / array,
– jthrowable / Throwable
– sur la palte-forme Win32
– nous avons typedef jijnt long;
– voir fichier %JDK_HOME%\include\win32\jni_md.h
24
JNI
Signature des méthodes
• FieldType ::= BaseType | ObjectType | ArrayType
• BaseType
• B byte
• C char
• D double
• F float
• I int
• J long
• S short
• Z boolean
• ObjectType
• L<classname>;
• ArrayType
• [ table
• MethodDescriptor ::= ( FieldType *) ReturnDescriptor
• ReturnDescriptor ::= FieldType | V
• V si le type retourné est void
25
JNI
Signature des méthodes en "clair"
• >javap -s -private JavaVersC
Compiled from [Link]
public synchronized class JavaVersC extends [Link]
/* ACC_SUPER bit set */
{
public native void bonjour();
/* ()V */
public static void main([Link][]);
/* ([Ljava/lang/String;)V */
public JavaVersC();
/* ()V */
static static {};
/* ()V */
}
26
JNI
Objets et classe
• NewObject, NewObjectA,NewObjectV
création d ’une instance
obtention de la classe
obtention du constructeur
passage des paramètres et création
...//en Java : ClasseA newObj = new ClasseA(10,"hello");
jclass classe = (*env)->FindClass("ClasseA");
jMethodID mid = (*env)->GetMethodID(classe,
"<init>",
"(ILjava/lang/String;)V");
jint val = 10;
jstring str = (*env)->NewStringUTF(env, "hello");
jobject newObj = (*env)->NewObject(env,
classe,mid,val,str) ;
}
• instruction JVM : new et invokespecial
27
JNI
instanceof
• IsInstanceOf
• class A{}
• class B extends A{void callP(boolean b){…};}
• ...// obj est de classe déclarée A mais constatée B
– jclass classeB = (*env)->FindClass("B");
– if ((*env)->IsInstanceOf(obj,classeB)){
– jMethodID mid = (*env)->GetMethodID(classeB,
– "callP",
– "(Z)V");
– jbool val = JNI_TRUE;
– (*env)->CallVoidMethod(obj,mid,val) ;
– }
– instruction JVM : instanceof, (checkcast)
28
JNI
Tableaux et String
• NewObjectArray,
• public class Exemple{
– public void p(){String sa = newArray(10);}
– public native String [] newArray(int taille);
– }
• ...
– jclass classe = (*env)->FindClass(env, "java/lang/String");
– jObjectArray newArr = (*env)->NewObjectArray(env, taille,classe,NULL);
– for(int i = 0; i< taille; i++){
– str = (*env)->NewStringUTF("hello");
– (*env)->SetObjectArrayElement(env, newArr,i,str);
– (*env)->DeleteLocalRef(env, str);
– }
– return newArr;
– }
– DeleteLocalRef -> str a 2 références, en jni et en java (gc)
– instruction JVM : newarray
29
JNI
Objets et ramasse miettes
• Chaque objet crée par JNI ne peut être collecté par le
ramasse miettes Java, (l'objet str est référencé dans la
machine Java)
– DeleteLocalRef(str) // de l'exemple précédent
– permet de libérér cet objet (autorise la récupération mémoire par le ramasse
miettes)
– NewGlobalRef(obj);
– "bloque" l'objet en mémoire
30
JNI
Levée d ’exceptions
• Throw ThrowNew,...
JNIEXPORT void JNICALL Java_JavaVersC_bonjour (JNIEnv *env){
jclass cl; jmethodID mid; jthrowable exc;
cl = (*env)->FindClass(env,"ExceptionVenantDeC");
mid = (*env)->GetMethodID(env,cl,"<init>","()V");
exc = (jthrowable)(*env)->NewObject(env,cl,mid);
(*env)->Throw(env,exc);
• instruction JVM : athrow
31
JNI
Côté Java
public class ExceptionVenantDeC extends Exception{
public ExceptionVenantDeC(String msg){
super(msg);
}
public ExceptionVenantDeC(){}
}
public native static void bonjour()throws ExceptionVenantDeC;
/* */
{
try{
...
}catch(ExceptionVenantDeC e){
[Link]();
}
}
32
JNI
Une variante : ThrowNew
JNIEXPORT void JNICALL Java_JavaVersC_bonjour (JNIEnv *env){
jclass cl; jmethodID mid; jthrowable exc;
cl = (*env)->FindClass(env,"ExceptionVenantDeC");
(*env)->ThrowNew(env,cl," appel de (*env)->ThrowNew/3 ");
33
JNI
Filtrage des Exceptions en C
• ExceptionClear, ExceptionOccurred, ExceptionDescribe,FatalError
– jthrowable exc;
– exc = (*env)->ExceptionOccurred(env);
– if(exc != NULL){
– // catch(Exception1 e){ …}
– if((*env)->IsInstanceof(env,exc,Exception1){
– ……… //
– (*env)->ExceptionClear();
– }
– }
– Avec
– jclass Exception1 = (*env)->FindClass(env, "Exception1");
34
JNI
Monitor
• MonitorEnter, MonitorExit
– // l'équivalent de l'instruction synchronized
– (*env)->MonitorEnter(env,obj);
– //du code C/C++
– (*env)->MonitorExit(env,obj);
– }
– instructions JVM : monitorenter, monitorexit
– Appels de wait et notify par les primitives GetMethodID et CallVoidMethod
– jclass classe = (*env)->GetObjectClass(env,obj);
– jMethodID waitMid = (*env)->GetMethodID(env,classe,"wait","()V");
– (*env)->CallVoidMethod(env,obj,waitMid);
– if((*env)->ExceptionOccured()!=NULL))
– // une exception est générée en Java, mauvais usage de Wait ...
– }
35
JNI
Deux exemples
• Entrées/Sorties Voie Série
• Accès au « Windows Registry »
• …
36
JNI
Entrées/Sorties Voie Série
• Win32Port Markus Bauer, (disponible sur [Link]
• public class Win32Port{
• public native boolean open(int pnr, String par, boolean handshake);
• public native void clear(int pnr);
• public native void setTimeout(int pnr, int tout);
• public native int getByte(int pnr);
• public native int getBytes(int pnr, byte[] bytes);
• public native boolean putByte(int pnr, int b);
• public native int putBytes(int pnr, byte[] bytes);
• public native boolean close(int pnr);
• static {
• [Link]("portcom");
• }}
37
JNI
Accès à la base de registres sous Windows
• [Link]
eyTest/
• Un lien à suivre … et à mettre en Œuvre …
38
JNI
Appel de la machine Java
• le source C ordinaire
– utilisation de [Link]
– chargement et appel de la machine Java depuis le point d'entrée main
• le source Java ordinaire
– n'importe quelle application
39
JNI
Le source C
#include <jni.h>
int main(int argc, char *argv[]){
// declarations ici
options[0].optionString = "-[Link]=.";
memset(&vm_args, 0, sizeof(vm_args));
vm_args.version = JNI_VERSION_1_2;
vm_args.nOptions = 1;
vm_args.options = options;
res = JNI_CreateJavaVM(&jvm, (void**)&env, &vm_args);
res = JNI_CreateJavaVM(&jvm, &env, &vm_args);
classe = (*env)->FindClass(env, "CVersJava");
methodeID = (*env)->GetStaticMethodID(
env,classe,"main","([Ljava/lang/String;)V");
jstr = (*env)->NewStringUTF(env," depuis du c !!!");
args = (*env)->NewObjectArray(
env,1,(*env)->FindClass(env,"java/lang/String"),jstr);
(*env)->CallStaticVoidMethod(env,classe,methodeID,args);
(*jvm)->DestroyJavaVM(jvm);
} return (0);}
40
JNI
Le source Java
• public class CVersJava{
•
• public static void main(String [] args){
• [Link]("en java dans main " + args[0]);
• }
• }
41
JNI
Le source Complet (1)
#include <jni.h>
#include <stdlib.h>
int main(int argc, char *argv[]){
JNIEnv *env;
JavaVM *jvm;
JavaVMInitArgs vm_args;
JavaVMOption options[1];
jint res;
jclass classe;
jmethodID methodeID;
jstring jstr;
jobjectArray args;
char classpath[1024];
options[0].optionString = "-[Link]=.";
options[1].optionString = "-verbose:jni";
memset(&vm_args, 0, sizeof(vm_args));
vm_args.version = JNI_VERSION_1_2;
vm_args.nOptions = 1;
vm_args.options = options;
42
JNI
Le source Complet (2)
res = JNI_CreateJavaVM(&jvm, (void**)&env, &vm_args);
if (res==JNI_ERR){
printf(" erreur lors de la création de la JVM ....\n");
return 1;
}
classe = (*env)->FindClass(env, "CVersJava");
methodeID = (*env)->GetStaticMethodID( env,classe,"main","([Ljava/lang/String;)V");
jstr = (*env)->NewStringUTF(env," depuis du c !!!");
args = (*env)->NewObjectArray(env,1,(*env)->FindClass(env,"java/lang/String"),jstr);
(*env)->CallStaticVoidMethod(env,classe,methodeID,args);
(*jvm)->DestroyJavaVM(jvm);
return (0);
}
cl -I%JDK_HOME%\include -I%JDK_HOME%include\win32 SimpleHttpd.c
%JDK_HOME%\lib\[Link]
set path = %JDK_HOME\jre\bin\client\;%path% accès à [Link]
43
JNI
Un serveur Web depuis C …
….
res = JNI_CreateJavaVM(&jvm, (void**)&env, &vm_args);
if (res==JNI_ERR){
printf(" erreur lors de la création de la JVM ....\n");
return 1;
}
classe = (*env)->FindClass(env, "SimpleHttpd2");
methodeID = (*env)->GetStaticMethodID( env,classe,"main","([Ljava/lang/String;)V");
args = (*env)->NewObjectArray(env,0,(*env)>FindClass(env,"java/lang/String"),NULL);
(*env)->CallStaticVoidMethod(env,classe,methodeID,args);
return (0);
}
44
JNI
Conclusion
45
JNI