t
t
t
t
t t B) L'Interface Java Natif (JNI)
tttt
B) L'Interface Java Natif (JNI)
Texte original t Traducteur : Phillipe BOITE
t
t
    
Ce chapitre contient 1 page
    
t t t
t t t
t
05.07.01 - version 1.2 :
- Ajout des tags de séparation de pages pour le site (armel).
28.04.01 - version 1.1 :
- Nettoyage du code html (titres, paragraphes, tableaux, images, ancres) par Armel.
- Enlevé de nombreuses balises de type {font}.
- Laissé les {strong} et les {em}.
25.06.2000 - Version 1.0 :
- Dernière mise à jour de la version française
Traducteur :
- Philippe BOITE
Texte original :
-Thinking in Java, 2nd edition, Revision 10
© 2000 by Bruce Eckel
t t t
B: The Java Native Interface (JNI) t B: L'Interface Native Java [Java Native Interface] (JNI)
t t t
The material in this appendix was contributed by and used with the permission of Andrea Provaglio (www.AndreaProvaglio.com). t Le contenu de cette annexe a été fourni et est utilisé avec la permission d'Andrea Provaglio (www.AndreaProvaglio.com).
t t t
The Java language and its standard API are rich enough to write full-fledged applications. But in some cases you must call non-Java code; for example, if you want to access operating-system-specific features, interface with special hardware devices, reuse a preexisting, non-Java code base, or implement time-critical sections of code. t Le langage Java et son API standard sont suffisamment riches pour écrire des applications complètes. Mais dans certains cas on doit appeler du code non-Java ; par exemple, pour accéder à des fonctionnalités spécifiques du système d'exploitation, s'interfacer avec des matériels particuliers, réutiliser une base de code non-Java existante, ou implémenter des parties de codes à contraintes temps réel fortes.
t t t
Interfacing with non-Java code requires dedicated support in the compiler and in the Virtual Machine, and additional tools to map the Java code to the non-Java code. The standard solution for calling non-Java code that is provided by JavaSoft is called the Java Native Interface, which will be introduced in this appendix. This is not an in-depth treatment, and in some cases you’re assumed to have partial knowledge of the related concepts and techniques. t S'interfacer avec du code non-Java nécessite un support dédié dans le compilateur et dans la machine virtuelle, ainsi que des outils supplémentaires pour associer le code Java au code non-Java. La solution standard fournie par JavaSoft pour appeler du code non-Java est appelée la Java Native Interface, qui sera introduite dans cette annexe. Ceci n'est pas un traitement en profondeur, et dans certains cas vous êtes supposés avoir une connaissance partielle des concepts et techniques concernés.
t t t
JNI is a fairly rich programming interface that allows you to call native methods from a Java application. It was added in Java 1.1, maintaining a certain degree of compatibility with its Java 1.0 equivalent: the native method interface (NMI). NMI has design characteristics that make it unsuitable for adoption across all virtual machines. For this reason, future versions of the language might no longer support NMI, and it will not be covered here. t JNI est une interface de programmation assez riche qui permet d'appeler des méthodes natives depuis une application Java. Elle a été ajoutée dans Java 1.1, en maintenant un certain degré de compatibilité avec son équivalent en Java 1.0 : la native method interface (NMI). NMI a des caractéristiques de conception qui la rendent impropre à l'utilisation sur certaines machines virtuelles. Pour cette raison, les versions ultérieures du langage pourraient ne plus supporter NMI, et elle ne sera pas couverte ici.
t t t
Currently, JNI is designed to interface with native methods written only in C or C++. Using JNI, your native methods can: t Actuellement, JNI est conçue pour s'interfacer uniquement avec les méthodes natives écrites en C ou C++. En utilisant JNI, vos méthodes natives peuvent :
t t t
  • Create, inspect, and update Java objects (including arrays and Strings)
  • Call Java methods
  • Catch and throw exceptions
  • Load classes and obtain class information
  • Perform run-time type checking
t
  • créer, inspecter et modifier des objets Java (y compris les tableaux et les Strings),
  • appeler des méthodes Java,
  • intercepter [catch] et générer [throw] des exceptions,
  • charger des classes et obtenir des informations sur les classes,
  • effectuer des contrôles lors de l'exécution (run-time type checking).
t t t
Thus, virtually everything you can do with classes and objects in ordinary Java you can also do in native methods. t De ce fait, pratiquement tout ce qu'on peut faire avec des classes et des objets en Java ordinaire, on peut aussi le faire dans des méthodes natives.
t t t

Calling a native method

t

Appeler une méthode native

t t t
We’ll start with a simple example: a Java program that calls a native method, which in turn calls the standard C library function printf( ). t Nous allons commencer avec un exemple simple : un programme Java qui appelle une méthode native, qui à son tour appelle la fonction printf( ) de la bibliothèque C standard.
t t t
The first step is to write the Java code declaring a native method and its arguments: t La première opération consiste à écrire le code Java qui déclare une méthode native et ses arguments :
t t t
//: appendixb:ShowMessage.java
public class ShowMessage {
  private native void ShowMessage(String msg);
  static {
    System.loadLibrary("MsgImpl");
    // Linux hack, if you can't get your library
    // path set in your environment:
    // System.load(
    //  "/home/bruce/tij2/appendixb/MsgImpl.so");
  }
  public static void main(String[] args) {
    ShowMessage app = new ShowMessage();
    app.ShowMessage("Generated with JNI");
  }
} ///:~
t
//: appendixb:ShowMessage.java
public class ShowMessage {
  privatenative void ShowMessage(String msg);
  static {
System.loadLibrary("MsgImpl");
   // Astuce Linux, si vous ne pouvez pas
   // positionner le chemin des bibliothèques
   // dans votre environnement :
   // System.load(
   //"/home/bruce/tij2/appendixb/UseObjImpl.so");
}
  public static void main(String[] args) {
ShowMessage app = new ShowMessage();
app.ShowMessage("Generated with JNI");
}
} ///:~
t t t
The native method declaration is followed by a static block that calls System.loadLibrary( ) (which you could call at any time, but this style is more appropriate). System.loadLibrary( ) loads a DLL in memory and links to it. The DLL must be in your system library path. The file name extension is automatically added by the JVM depending on the platform. t La déclaration de la méthode native est suivie par un bloc static qui appelle System.loadLibrary( ) (qu'on pourrait appeler à n'importe quel moment, mais ce style est plus clair). System.loadLibrary( ) charge une DLL en mémoire et se lie à elle. La DLL doit être dans votre chemin d'accès aux bibliothèques système (system library path). L'extension de nom de fichier est automatiquement ajoutée par la JVM dépendant de la plateforme.
t t t
In the above code you can also see a call to the System.load( ) method, which is commented out. The path specified here is an absolute path, rather than relying on an environment variable. Using an environment variable is naturally the better and more portable solution, but if you can’t figure that out you can comment out the loadLibrary( ) call and uncomment this one, adjusting the path to your own directory. t Dans le code ci-dessus on peut voir également un appel à la méthode System.load( ) , qui est mis en commentaire. Le chemin spécifié ici est un chemin absolu, plutôt que dépendant d'une variable d'environnement. Utiliser une variable d'environnement est naturellement une meilleure solution et est plus portable, mais si vous n'y arrivez pas vous pouvez mettre en commentaire l'appel à loadLibrary( ) et enlever la mise en commentaire de l'appel à la méthode System.load(), en ajustant le chemin de votre propre répertoire.
t t t

The header file generator: javah

t

Le générateur d'entête [ header file generator] : javah

t t t
Now compile your Java source file and run javah on the resulting .class file, specifying the —jni switch (this is done automatically for you by the makefile in the source code distribution for this book): t Maintenant, compilez votre fichier source Java et lancez javah sur le fichier .class résultant, en précisant le paramètre -jni (ceci est déjà fait dans le makefile inclus dans la fourniture du code source pour ce livre) :
t t t
javah —jni ShowMessage
t
javah -jni ShowMessage
t t t
javah reads the Java class file
and for each native method declaration it generates a function prototype in a C
or C++ header file. Here’s the output: the ShowMessage.h source
file (edited slightly to fit into this book):

t javahlit la classe Java et pour chaque déclaration de méthode native il génère un prototype de fonction dans un fichier d'entête C ou C++. Voici le résultat : le fichier source ShowMessage.h (légèrement modifié pour l'inclure dans ce livre) :
t t t
/* DO NOT EDIT THIS FILE
   - it is machine generated */
#include <jni.h>
/* Header for class ShowMessage */

#ifndef _Included_ShowMessage
#define _Included_ShowMessage
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class:     ShowMessage
* Method:    ShowMessage
* Signature: (Ljava/lang/String;)V
*/
JNIEXPORT void JNICALL
Java_ShowMessage_ShowMessage
  (JNIEnv *, jobject, jstring);

#ifdef __cplusplus
}
#endif
#endif
t
/* DO NOT EDIT THIS FILE
- it is machine generated */
#include <jni.h>
/* Header for class ShowMessage */

#ifndef _Included_ShowMessage
#define _Included_ShowMessage
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class:     ShowMessage
* Method:    ShowMessage
* Signature: (Ljava/lang/String;)V
*/
JNIEXPORT void JNICALL
Java_ShowMessage_ShowMessage
(JNIEnv *, jobject, jstring);

#ifdef __cplusplus
}
#endif
#endif
t t t
As you can see by the #ifdef __cplusplus preprocessor directive, this file can be compiled either by a C or a C++ compiler. The first #include directive includes jni.h, a header file that, among other things, defines the types that you can see used in the rest of the file. JNIEXPORT and JNICALL are macros that expand to match platform-specific directives. JNIEnv, jobject and jstring are JNI data type definitions, which will be explained shortly. t Comme on peut le voir par la directive préprocesseur #ifdef __cplusplus , ce fichier peut être compilé aussi bien par un compilateur C que C++. La première directive #include inclut jni.h, un fichier d'entête qui, entre autres choses, définit les types qu'on peut voir utilisés dans le reste du fichier. JNIEXPORT et JNICALL sont des macros qui une fois étendues génèrent les directives spécifiques aux différentes plateformes. JNIEnv, jobject et jstring sont des définitions de types de données, qui seront expliquées par la suite.
t t t

Name mangling and function signatures

t

Les conventions de nommage [name mangling]et les signatures de fonctions

t t t
JNI imposes a naming convention (called name mangling) on native methods. This is important, since it’s part of the mechanism by which the virtual machine links Java calls to native methods. Basically, all native methods start with the word “Java,” followed by the name of the class in which the Java native declaration appears, followed by the name of the Java method. The underscore character is used as a separator. If the Java native method is overloaded, then the function signature is appended to the name as well; you can see the native signature in the comments preceding the prototype. For more information about name mangling and native method signatures, please refer to the JNI documentation. t JNI impose une convention de nommage (appelée name mangling) aux méthodes natives. Ceci est important car cela fait partie du mécanisme par lequel la machine virtuelle lie les appels Java aux méthodes natives. Fondamentalement, toutes les méthodes natives commencent par le mot "Java", suivi par le nom de la méthode native. Le caractère sous-tiret est utilisé comme séparateur. Si la méthode java native est surchargée, alors la signature de fonction est ajoutée au nom également ; on peut voir la signature native dans les commentaires précédant le prototype. Pour plus d'informations sur les conventions de nommage, se référer à la documentation de JNI.
t t t

Implementing your DLL

t

Implémenter votre DLL

t t t
At this point, all you have to do is write a C or C++ source code file that includes the javah-generated header file and implements the native method, then compile it and generate a dynamic link library. This part is platform-dependent. The code below is compiled and linked into a file called MsgImpl.dll for Windows or MsgImpl.so for Unix/Linux (the makefile packaged with the code listings contains the commands to do this—it is available on the CD ROM bound into this book, or as a free download from www.BruceEckel.com): t Arrivé ici, il ne reste plus qu'à écrire un fichier source C ou C++ qui inclut le fichier d'entête généré par javah et implémenter la méthode native, puis le compiler et générer une bibliothèque dynamique. Cette opération dépend de la plateforme. Le code ci-dessous est compilé et lié dans un fichier appelé MsgImpl.dll pour Windows ou MsgImpl.so pourUnix/Linux (le makefile fourni avec les listings du code contient les commandes pour faire ceci ; il est disponible sur le CD ROM fourni avec ce livre, ou en téléchargement libre sur www.BruceEckel.com) :
t t t
//: appendixb:MsgImpl.cpp
//# Tested with VC++ & BC++. Include path must
//# be adjusted to find the JNI headers. See
//# the makefile for this chapter (in the
//# downloadable source code) for an example.
#include <jni.h>
#include <stdio.h>
#include "ShowMessage.h"

extern "C" JNIEXPORT void JNICALL
Java_ShowMessage_ShowMessage(JNIEnv* env,
jobject, jstring jMsg) {
  const char* msg=env->GetStringUTFChars(jMsg,0);
  printf("Thinking in Java, JNI: %s\n", msg);
  env->ReleaseStringUTFChars(jMsg, msg);
} ///:~
t
//: appendixb:MsgImpl.cpp
//# Testé avec VC++ & BC++. Le chemin d'include
//# doit être adapté pour trouver les en-têtes
//# JNI. Voir le makefile de ce chapitre
//# (dans le code source téléchargeable)
//# pour exemple.
#include <jni.h>
#include <stdio.h>
#include "ShowMessage.h"

extern "C" JNIEXPORT> void JNICALL
Java_ShowMessage_ShowMessage(JNIEnv* env,
jobject, jstring jMsg) {
  const char* msg=env->GetStringUTFChars(jMsg,0);
printf("Thinking in Java, JNI: %s\n", msg);
env->ReleaseStringUTFChars(jMsg, msg);
///:~
t t t
The arguments that are passed into the native method are the gateway back into Java. The first, of type JNIEnv, contains all the hooks that allow you to call back into the JVM. (We’ll look at this in the next section.) The second argument has a different meaning depending on the type of method. For non-static methods like the example above, the second argument is the equivalent of the “this” pointer in C++ and similar to this in Java: it’s a reference to the object that called the native method. For static methods, it’s a reference to the Class object where the method is implemented. t Les arguments qui sont passés à la méthode native permettent de revenir dans Java. Le premier, de type JNIEnv, contient tous les points d'ancrage [hooks] (nous allons revenir là-dessus dans la section suivante). Le deuxième argument a une signification différente selon le type de la méthode. Pour des méthodes non-static comme dans l'exemple ci-dessus, le deuxième argument est l'équivalent du pointeur "this" en C++ et similaire au this en Java : c'est une référence à l'objet qui a appelé la méthode native. Pour les méthodes static, c'est une référence à l'objet Class où la méthode est implémentée.
t t t
The remaining arguments represent the Java objects passed into the native method call. Primitives are also passed in this way, but they come in by value. t Les arguments restants représentent les objets java passés dans l'appel de la méthode native. Les scalaires sont aussi passés de cette façon, mais en les passant par valeur.
t t t
In the following sections we’ll explain this code by looking at the ways that you access and control the JVM from inside a native method. t Dans les sections suivantes nous allons expliquer ce code en regardant de quelle façon on accède à la JVM et comment on la contrôle depuis l'intérieur d'une méthode native.
t t t

Accessing JNI functions: the JNIEnv argument

t

Accéder à des fonctions JNI : l'argument JNIEnv

t t t
JNI functions are those that the programmer uses to interact with the JVM from inside a native method. As you can see in the example above, every JNI native method receives a special argument as its first parameter: the JNIEnv argument, which is a pointer to a special JNI data structure of type JNIEnv_. One element of the JNI data structure is a pointer to an array generated by the JVM. Each element of this array is a pointer to a JNI function. The JNI functions can be called from the native method by dereferencing these pointers (it’s simpler than it sounds). Every JVM provides its own implementation of the JNI functions, but their addresses will always be at predefined offsets. t Les programmeurs utilisent les fonctions JNI pour interagir avec la JVM depuis une méthode native. Comme on l'a vu dans l'exemple ci-dessus, chaque méthode native JNI reçoit un argument spécial comme premier paramètre : l'argument JNIEnv, qui est un pointeur vers une structure de données JNI spéciale de type JNIEnv_. Un élément de la structure de données JNI est un pointeur vers un tableau généré par la JVM. Chaque élément de ce tableau est un pointeur vers une fonction JNI. Les fonctions JNI peuvent être appelée depuis la méthode native en déréférençant ces pointeurs (c'est plus simple qu'il n'y paraît). Chaque JVM fournit sa propre implémentation des fonctions JNI, mais leurs adresses seront toujours à des décalages prédéfinis.
t t t
Through the JNIEnv argument, the programmer has access to a large set of functions. These functions can be grouped into the following categories:
  • Obtaining version information
  • Performing class and object operations
  • Handling global and local references to Java objects
  • Accessing instance fields and static fields
  • Calling instance methods and static methods
  • Performing string and array operations
  • Generating and handling Java exceptions
t A l'aide de l'argument JNIEnv, le programmeur a accès à un large éventail de fonctions. Ces fonctions peuvent être regoupées selon les catégories suivantes :
  • obtenir une information de version,
  • effectuer des opérations sur les classes et les objets,
  • gérer des références globales et locales à des objets Java,
  • accéder à des champs d'instances et à des champs statiques [instance fields and static fields],
  • appeler des méthodes d'instances et des méthodes statiques,
  • effectuer des opérations sur des chaînes et des tableaux,
  • générer et gérer des exceptions Java.
t t t
The number of JNI functions is quite large and won’t be covered here. Instead, I’ll show the rationale behind the use of these functions. For more detailed information, consult your compiler’s JNI documentation. t Il existe de nombreuses fonctions JNI qui ne seront pas couvertes ici. A la place, je vais montrer le principe d'utilisation de ces fonctions. Pour des informations plus détaillées, consultez la documentation JNI de votre compilateur.
t t t
If you take a look at the jni.h header file, you’ll see that inside the #ifdef __cplusplus preprocessor conditional, the JNIEnv_ structure is defined as a class when compiled by a C++ compiler. This class contains a number of inline functions that let you access the JNI functions with an easy and familiar syntax. For example, the line of C++ code in the preceding example: t Si on jette un coup d'oeil au fichier d'entête jni.h , on voit qu'à l'intérieur de la directive préprocesseur #ifdef __cplusplus, la structure JNIEnv_ est définie comme une classe quand elle est compilée par un compilateur C++. Cette classe contient un certain nombre de fonctions inline qui permettent d'accéder aux fonctions JNI à l'aide d'une syntaxe simple et familière. Par exemple, la ligne de code C++ de l'exemple précédent :
t t t
env->ReleaseStringUTFChars(jMsg, msg);
t
env->ReleaseStringUTFChars(jMsg, msg);
t t t
could also be called from C like
this:

t pourrait également être appelée en C comme ceci :
t t t
(*env)->ReleaseStringUTFChars(env, jMsg, msg);
t
(*env)->ReleaseStringUTFChars(env, jMsg, msg);
t t t
You’ll notice that the C style is
(naturally) more complicated—you need a double dereferencing of the
env pointer, and you must also pass the same pointer as the first
parameter to the JNI function call. The examples in this appendix use the C++
style.
t On notera que le style C est (naturellement) plus compliqué ; on doit utiliser un double déréférencement du pointeur env, et on doit aussi passer ce même pointeur comme premier paramètre de l'appel à la fonction JNI. Les exemples de cette annexe utilisent le style C++.
t t t


Accessing Java Strings


t

Accéder à des chaînes Java

t t t
As an example of accessing a JNI
function, consider the code in MsgImpl.cpp. Here, the JNIEnv
argument env is used to access a Java String. Java Strings
are in Unicode format, so if you receive one and want to pass it to a
non-Unicode function (printf( ), for example), you must first
convert it into ASCII characters with the JNI function
GetStringUTFChars( ). This function takes a Java String and
converts it to UTF-8 characters. (These are 8 bits wide to hold ASCII values or
16 bits wide to hold Unicode. If the content of the original string was composed
only of ASCII, the resulting string will be ASCII as well.)
t Comme exemple d'accès à une fonction JNI, observez le code dans MsgImpl.cpp. Ici, l'argument JNIEnv env est utilisé pour accéder à une String Java. Les Strings Java sont au format Unicode, donc si on en reçoit une et qu'on veut la passer à une fonction non-Unicode (printf( ), par exemple), il faut d'abord la convertir en caractères ASCII avec la fonction JNI GetStringUTFChars( ). Cette fonction prend une String Java et la convertir en caractères UTF-8 (ceux-ci font 8 bits pour contenir de valeurs ASCII ou 16 bits pour contenir de l'Unicode ; si le contenu de la chaîne d'origine était composée uniquement d'ASCII, la chaîne résultante sera également de l'ASCII).
t t t
GetStringUTFChars( ) is one
of the member functions in JNIEnv. To access the JNI function, we use the
typical C++ syntax for calling a member function though a pointer. You use the
form above to access all of the JNI
functions.
t GetStringUTFChars( )> est une des fonctions membres de JNIEnv. Pour accéder à la fonction JNI, on utilise la syntaxe typique C++ pour appeler une fonction membre à l'aide d'un pointeur. On utilise la forme ci-dessus pour appeler l'ensemble des fonctions JNI.
t t t


Passing and using Java objects


t

Passer et utiliser des objets Java

t t t
In the previous example we passed a
String to the native method. You can also pass Java objects of your own
creation to a native method. Inside your native method, you can access the
fields and methods of the object that was received.
t Dans l'exemple précédent nous avons passé une String à la méthode native. On peut aussi passer des objets Java de sa propre création à une méthode native. A l'intérieur de sa méthode native, on peut accéder aux champs [fields] et aux méthodes de l'objet reçu.
t t t
To pass objects, use the ordinary Java
syntax when declaring the native method. In the example below,
MyJavaClass has one public field and one public method. The
class UseObjects declares a native method that takes an object of class
MyJavaClass. To see if the native method manipulates its argument, the
public field of the argument is set, the native method is called, and
then the value of the public field is printed.

t Pour passer des objets, on utilise la syntaxe Java normale quand on déclare la méthode native. Dans l'exemple ci-dessous, MyJavaClass a un champ public et une méthode public. La classe UseObjects déclare une méthode native qui prend un objet de la classe MyJavaClass. Pour voir si la méthode native manipule son argument, le champ public de l'argument est positionné, la méthode native est appelée, et enfin la valeur du champ public est imprimée.
t t t
//: appendixb:UseObjects.java
class MyJavaClass {
  public int aValue;
  public void divByTwo() { aValue /= 2; }
}

public class UseObjects {
  private native void
    changeObject(MyJavaClass obj);
  static {
    System.loadLibrary("UseObjImpl");
    // Linux hack, if you can't get your library
    // path set in your environment:
    // System.load(
    //"/home/bruce/tij2/appendixb/UseObjImpl.so");
  }
  public static void main(String[] args) {
    UseObjects app = new UseObjects();
    MyJavaClass anObj = new MyJavaClass();
    anObj.aValue = 2;
    app.changeObject(anObj);
    System.out.println("Java: " + anObj.aValue);
  }
} ///:~
t
//: appendixb:UseObjects.java
class MyJavaClass {
  public int aValue;
  public void divByTwo() { aValue /= 2; }
}
public class UseObjects {
  private native void
changeObject(MyJavaClass obj);
  static {
System.loadLibrary("UseObjImpl");
   // Astuce Linux, si vous ne pouvez pas
   // positionner le chemin des bibliothèques
   // dans votre environnement :
   // System.load(
   //"/home/bruce/tij2/appendixb/UseObjImpl.so");
}
  public static void main(String[] args) {
UseObjects app = new UseObjects();
MyJavaClass anObj = new MyJavaClass();
anObj.aValue = 2;
app.changeObject(anObj);
System.out.println("Java: " + anObj.aValue);
}
} ///:~
t t t
After compiling the code and running javah, you can implement the native method. In the example below, once the field and method ID are obtained, they are accessed through JNI functions. t Après avoir compilé le code et exécuté javah, on peut implémenter la méthode native. Dans l'exemple ci-dessous, après avoir obtenu les identificateurs du champ et de la méthode, on y accède à l'aide de fonctions JNI.
t t t
//: appendixb:UseObjImpl.cpp
//# Tested with VC++ & BC++. Include path must
//# be adjusted to find the JNI headers. See
//# the makefile for this chapter (in the
//# downloadable source code) for an example.
#include <jni.h>
extern "C" JNIEXPORT void JNICALL
Java_UseObjects_changeObject(
JNIEnv* env, jobject, jobject obj) {
  jclass cls = env->GetObjectClass(obj);
  jfieldID fid = env->GetFieldID(
    cls, "aValue", "I");
  jmethodID mid = env->GetMethodID(
    cls, "divByTwo", "()V");
  int value = env->GetIntField(obj, fid);
  printf("Native: %d\n", value);
  env->SetIntField(obj, fid, 6);
  env->CallVoidMethod(obj, mid);
  value = env->GetIntField(obj, fid);
  printf("Native: %d\n", value);
} ///:~
t
//: appendixb:UseObjImpl.cpp
//# Testé avec VC++ & BC++. Le chemin d'include
//# doit être adapté pour trouver les en-têtes
//# JNI. Voir le makefile de ce chapitre
//# (dans le code source téléchargeable)
//# pour exemple.

#include <jni.h>
extern "C" JNIEXPORT void JNICALL
Java_UseObjects_changeObject(
JNIEnv* env, jobject, jobject obj) {
jclass cls = env->GetObjectClass(obj);
jfieldID fid = env->GetFieldID(
cls, "aValue", "I");
jmethodID mid = env->GetMethodID(
cls, "divByTwo", "()V");
  int value = env->GetIntField(obj, fid);
printf("Native: %d\n", value);
env->SetIntField(obj, fid, 6);
env->CallVoidMethod(obj, mid);
value = env->GetIntField(obj, fid);
printf("Native: %d\n", value);
} ///:~
t t t
Ignoring the “this” equivalent, the C++ function receives a jobject, which is the native side of the Java object reference we pass from the Java code. We simply read aValue, print it out, change the value, call the object’s divByTwo( ) method, and print the value out again. t Ignorant l'équivalent de "this", la fonction C++ reçoit un jobject, qui est l'aspect natif de la référence à l'objet Java que nous passons depuis le code Java. Nous lisons simplement aValue, l'imprimons, changeons la valeur, appelons la méthode divByTwo() de l'objet, et imprimons la valeur à nouveau.
t t t
To access a Java field or method, you must first obtain its identifier using GetFieldID( ) for fields and GetMethodID( ) for methods. These functions take the class object, a string containing the element name, and a string that gives type information: the data type of the field, or signature information for a method (details can be found in the JNI documentation). These functions return an identifier that you use to access the element. This approach might seem convoluted, but your native method has no knowledge of the internal layout of the Java object. Instead, it must access fields and methods through indexes returned by the JVM. This allows different JVMs to implement different internal object layouts with no impact on your native methods. t Pour accéder à un champ ou une méthode Java, on doit d'abord obtenir son identificateur en utilisant GetFieldID( ) pour les champs et GetMethodID( ) pour les méthodes. Ces fonctions prennent la classe, une chaîne contenant le nom de l'élément, et une chaîne donnant le type de l'information : le type de donnée du champ, ou l'information de signature d'une méthode (des détails peuvent être trouvés dans la documentation de JNI). Ces fonctions retournent un identificateur qu'on doit utiliser pour accéder à l'élément. Cette approche pourrait paraître tordue, mais notre méthode n'a aucune connaissance de la disposition interne de l'objet Java. Au lieu de cela, il doit accéder aux champs et méthodes à travers les index renvoyés par la JVM. Ceci permet à différentes JVMs d'implémenter différentes dispositions des objets sans impact sur vos méthodes natives.
t t t
If you run the Java program, you’ll see that the object that’s passed from the Java side is manipulated by your native method. But what exactly is passed? A pointer or a Java reference? And what is the garbage collector doing during native method calls? t Si on exécute le programme Java, on verra que l'objet qui est passé depuis le côté Java est manipulé par notre méthode native. Mais qu'est ce qui est exactement passé ? Un pointeur ou une référence Java? Et que fait le ramasse-miettes pendant l'appel à des méthodes natives ?
t t t
The garbage collector continues to operate during native method execution, but it’s guaranteed that your objects will not be garbage-collected during a native method call. To ensure this, local references are created before, and destroyed right after, the native method call. Since their lifetime wraps the call, you know that the objects will be valid throughout the native method call. t Le ramasse-miettes continue à travailler pendant l'exécution de la méthode native, mais il est garanti que vos objets ne seront pas réclamés par le ramasse-miettes durant l'appel à une méthode native. Pour assurer ceci, des références locales sont créées auparavant, et détruites juste après l'appel à la méthode native. Puisque leur durée de vie englobe l'appel, on sait que les objets seront valables tout au long de l'appel à la méthode native.
t t t
Since these references are created and subsequently destroyed every time the function is called, you cannot make local copies in your native methods, in static variables. If you want a reference that lasts across function invocations, you need a global reference. Global references are not created by the JVM, but the programmer can make a global reference out of a local one by calling specific JNI functions. When you create a global reference, you become responsible for the lifetime of the referenced object. The global reference (and the object it refers to) will be in memory until the programmer explicitly frees the reference with the appropriate JNI function. It’s similar to malloc( ) and free( ) in C. t Comme ces références sont créées et ensuite détruites à chaque fois que la fonction est appelée, on ne peut pas faire des copies locales dans les méthodes natives, dans des variables static. Si on veut une référence qui dure tout le temps des appels de fonctions, on a besoin d'une référence globale. Les références globales ne sont pas créées par la JVM, mais le programmeur peut créer une référence globale à partir d'une locale en appelant des fonctions JNI spécifiques. Lorsqu'on crée une référence globale, on devient responsable de la durée de vie de l'objet référencé. La référence globale (et l'objet qu'il référence) sera en mémoire jusqu'à ce que le programmeur libère explicitement la référence avec la fonction JNI appropriée. C'est similaire à malloc() et free() en C.
t t t

JNI and Java exceptions

t

JNI et les exceptions Java

t t t
With JNI, Java exceptions can be thrown, caught, printed, and rethrown just as they are inside a Java program. But it’s up to the programmer to call dedicated JNI functions to deal with exceptions. Here are the JNI functions for exception handling:
  • Throw( )Throws an existing exception object. Used in native methods to rethrow an exception.
  • ThrowNew( )Generates a new exception object and throws it.
  • ExceptionOccurred( )Determines if an exception was thrown and not yet cleared.
  • ExceptionDescribe( )Prints an exception and the stack trace.
  • ExceptionClear( )Clears a pending exception.
  • FatalError( )Raises a fatal error. Does not return.
t Avec JNI, les exceptions Java peuvent être générées, interceptées, imprimées et retransmises exactement comme dans un programme Java. Mais c'est au programmeur d'appeler des fonctions JNI dédiées pour traiter les exceptions. Voici les fonctions JNI pour gérer les exceptions :
  • Throw( ) Émet un objet exception existant. Utilisé dans les méthodes natives pour réémettre une exception.
  • ThrowNew( ) Génère un nouvel objet exception et l'émet.
  • ExceptionOccurred( ) Détermine si une exception a été émise et pas encore effacée [cleared].
  • ExceptionDescribe( ) Imprime une exception et la trace de la pile [stack trace].
  • ExceptionClear( )> Efface une exception en cours.
  • FatalError( ) Lève [raises] une erreur fatale. Ne revient pas [does not return].
t t t
Among these, you can’t ignore ExceptionOccurred( ) and ExceptionClear( ). Most JNI functions can generate exceptions, and there is no language feature that you can use in place of a Java try block, so you must call ExceptionOccurred( ) after each JNI function call to see if an exception was thrown. If you detect an exception, you may choose to handle it (and possibly rethrow it). You must make certain, however, that the exception is eventually cleared. This can be done in your function using ExceptionClear( ) or in some other function if the exception is rethrown, but it must be done. t Parmi celles-ci, on ne peut pas ignorer ExceptionOccurred( ) et ExceptionClear( ). La plupart des fonctions JNI peuvent générer des exceptions, et comme le langage ne dispose pas de construction équivalente à un bloc try Java, il faut appeler ExceptionOccurred( ) après chaque appel à une fonction JNI pour voir si une exception a été émise. Si on détecte une exception, on peut choisir de la traiter (et peut-être la réémettre). Il faut cependant s'assurer qu'une exception est effacée en final. Ceci peut être fait dans notre fonction en utilisant ExceptionClear( ) ou dans une autre fonction si l'exception est réémise, mais cela doit être fait.
t t t
You must ensure that the exception is cleared, because otherwise the results will be unpredictable if you call a JNI function while an exception is pending. There are few JNI functions that are safe to call during an exception; among these, of course, are all the exception handling functions. t Il faut s'assurer que l'exception est effacée, sinon les résultats seront imprévisibles si on appelle une fonction JNI alors qu'une exception est en cours. Il y a peu de fonctions JNI qu'on peut appeler sans risque durant une exception ; parmi celles-ci, évidemment, il y a toutes les fonctions de traitement des exceptions.
t t t

JNI and threading

t

JNI et le threading

t t t
Since Java is a multithreaded language, several threads can call a native method concurrently. (The native method might be suspended in the middle of its operation when a second thread calls it.) It’s entirely up to the programmer to guarantee that the native call is thread-safe; i.e., it does not modify shared data in an unmonitored way. Basically, you have two options: declare the native method as synchronized, or implement some other strategy within the native method to ensure correct, concurrent data manipulation. t Puisque Java est un langage multithread, plusieurs threads peuvent appeler une méthode native en même temps (la méthode native peut être suspendue au milieu de son traitement lorsqu'un second thread l'appelle). C'est au programmeur de garantir que l'appel natif est "thread-safe" ; c'est à dire qu'il ne modifie pas des données partagées de façon non contrôlée. Fondamentalement, il y a deux possibilités : déclarer la méthode native synchronized, ou implémenter une autre stratégie à l'intérieur de la méthode native pour assurer une manipulation correcte des données concurrentes.
t t t
Also, you should never pass the JNIEnv pointer across threads, since the internal structure it points to is allocated on a per-thread basis and contains information that makes sense only in that particular thread. t De plus, on ne devrait jamais passer le pointeur JNIEnv à travers des threads, car la structure interne sur laquelle il pointe est allouée thread par thread et contient des informations qui n'ont de sens que dans ce thread particulier.
t t t

Using a preexisting code base

t

Utiliser une base de code préexistantes

t t t
The easiest way to implement JNI native methods is to start writing native method prototypes in a Java class, compile that class, and run the .class file through javah. But what if you have a large, preexisting code base that you want to call from Java? Renaming all the functions in your DLLs to match the JNI name mangling convention is not a viable solution. The best approach is to write a wrapper DLL “outside” your original code base. The Java code calls functions in this new DLL, which in turn calls your original DLL functions. This solution is not just a work-around; in most cases you must do this anyway because you must call JNI functions on the object references before you can use them. t La façon la plus simple d'implémenter des méthodes JNI natives est de commencer à écrire des prototypes de méthodes natives dans une classe Java, de compiler cette classe, et exécuter le fichier .class avec javah. Mais si on possède une large base de code préexistante qu'on désire appeler depuis Java ? Renommer toutes les fonctions de notre DLL pour les faire correspondre aux conventions du JNI name mangling n'est pas une solution viable. La meilleure approche est d'écrire une DLL d'encapsulation "à l'extérieur" de votre base de code d'origine. Le code Java appelle des fonctions de cette nouvelle DLL, qui à son tour appelle les fonctions de votre DLL d'origine. Cette solution n'est pas uniquement un contournement ; dans la plupart des cas on doit le faire de toutes façons parce qu'on doit appeler des fonctions JNI sur les références des objets avant de pouvoir les utiliser.
t t t

Additional information

t

Information complémentaire

t t t
You can find further introductory material, including a C (rather than C++) example and discussion of Microsoft issues, in Appendix A of the first edition of this book, which can be found on the CD ROM bound in with this book, or in a free download from www.BruceEckel.com. More extensive information is available at java.sun.com (in the search engine, select “training & tutorials” for keywords “native methods”). Chapter 11 of Core Java 2, Volume II, by Horstmann & Cornell (Prentice-Hall, 2000) gives excellent coverage of native methods. t Vous pouvez trouver des éléments d'introduction, y compris un exemple C (plutôt que C++) et une discussion des problèmes Microsoft, dans l'Annexe A de la première édition de ce livre, que vos pouvez trouver sur le CD ROM fourni avec ce livre, ou en téléchargement libre sur www.BruceEckel.com. Des informations plus complètes sont disponibles sur java.sun.com (dans le moteur de recherche, sélectionnez "training & tutorials" avec "native methods" comme mots-clés). Le chapitre 11 de Core Java 2, Volume II, par Horstmann & Cornell (Prentice-Hall, 2000) donne une excellente description des méthodes natives.
t t t
t t t
t t
    
    
t t t
t
     
Sommaire Le site de Bruce Eckel