Programmazione:Java/JNI Example
Indice
JNI Tutorial
Per una guida abbastanza esaustiva su JNI, fare riferimento al documento presente sul sito SUN.
Esempio JNI
JNI è l'acronimo di Java Native Interface, questa tecnologia consente di interfacciarsi con funzioni di libreria scritte in codice nativo, in questo modo è possibile sfruttare da programmi Java librerie scritte appositamente per una piattaforma. I passi necessari per effettuare uuna chaiamata JNI sono:
- Dichiarare nella classe Java quali sono i metodi che invocheranno funzioni native con la keyword native;
- Caricare dalla classe Java la libreria per la funzione nativa;
- Compilazione della classe Java e generazione del file header (file .h) che rappresenti linterfaccia della funzione nativa;
- Scrivere il codice nativo che implementi la funzione del file header generato;
Vediamo un esempio con una classe Java che legge e visualizza sullo standard output il contenuto di un file utilizzando funzioni C per l'accesso al filesystem:
package com.netsitech.jni.test;
public class ReadFile {
// Native method declaration
native byte[] loadFile(String name);
// Load the library
static {
try {
System.loadLibrary("_ReadFile");
} catch (UnsatisfiedLinkError e) {
System.out.println("Could not load native code for resd file.");
System.exit(1);
}
}
public static void main(String args[]) {
try {
if (args.length == 0) {
System.out.print("Error file name not found in command line!");
}
byte buffer[];
// Create class instance
ReadFile mappedFile = new ReadFile();
// Call native method to load ReadFile.java
buffer = mappedFile.loadFile(args[0]);
// Print contents of ReadFile.java
for (int i = 0; i < buffer.length; i++) {
System.out.print((char) buffer[i]);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
Dichiarazione del metodo nativo
La dichiarazione del metodo loadFile con la keyword native indica alla JVM il mapping con la funzione C chiamata Java_com_netsitech_jni_test_ReadFile_loadFile.
native byte[] loadFile(String name);
Caricamento della libreria con il codice nativo
static {
System.loadLibrary("_ReadFile");
}
Compilazine della classe Java e generazione del Header File
/develop/workspace_jni/_Scrapbook/src>javac com.netsitech.jni.test.ReadFile.java
/develop/workspace_jni/_Scrapbook/bin>javah -d /develop/workspace_jni_test/_Scrapbook/src -jni com.netsitech.jni.test.ReadFile
La signature generata del metodo nativo avrà alcuni parametri il cui significato è riportato sotto:
/*
* Class: com_netsitech_jni_test_ReadFile
* Method: loadFile
* Signature: (Ljava/lang/String;)[B
*/
JNIEXPORT jbyteArray JNICALL Java_com_netsitech_jni_test_ReadFile_loadFile
(JNIEnv *, jobject, jstring);
Osservazioni sulla signature
La firma del metodo nativo è mappato Java-C/C++
<Tipo di ritorno> Java_<nome package>_<Classe>_<Metodo>
le JNIEXPORT e JNICALL sono delle macro dipendenti dal sistema operativo definite in jni.h, il primo parametro JNIEnv è il puntatore all'environment JNI, il secondo è il puntatore alla classe stessa (cioè 'this'), poi segue la lista dei parametri del metodo.
Implementazione del metodo nativo
//#include "stdafx.h"
#include <stdlib.h>
#include <string.h>
#include <jni.h>
#include "com_netsitech_jni_test_ReadFile.h"
inline void prepareException(JNIEnv *env, char* exname, char *des){
jclass ex = env->FindClass(exname);
if (ex != 0){
env->ThrowNew(ex, des);
}
}
/*
* Class: com_netsitech_jni_test_ReadFile
* Method: loadFile
* Signature:
*/
JNIEXPORT jbyteArray JNICALL Java_com_netsitech_jni_test_ReadFile_loadFile
(JNIEnv *env, jobject jobj, jstring name) {
jbyteArray jb;
jboolean iscopy = true;
int result;
char *m;
//Cpp Version GetStringUTFChars
const char *mfile = env->GetStringUTFChars(name, &iscopy);
//Open in read_only and binary mode to skip CFLF system difference
FILE* fd = fopen(mfile, "rb");
if (fd == NULL) {
char des[255] = "File not found: ";
strcat(des, mfile);
prepareException(env, "java/io/FileNotFoundException", des);
return (0);
}
result = fseek(fd, 0, SEEK_END);
if (result != 0){
prepareException(env, "java/io/IOException", "Problem getting file size information");
return (0);
}
int size = ftell(fd);
result = fseek(fd, 0, SEEK_SET);
if( result != 0 ) {
prepareException(env, "java/io/IOException", "Problem getting file information");
return (0);
}
m = (char*) malloc (size);
memset(m,0,size);
if (size != fread(m, sizeof (char), size, fd)) {
free (m);
fclose(fd);
prepareException(env, "java/io/IOException", "Problem during file reading");
return (0);
}
jb=env->NewByteArray(size);
env->SetByteArrayRegion(jb, 0, size, (jbyte *)m);
fclose(fd);
env->ReleaseStringUTFChars(name, mfile);
free (m);
return (jb);
}
Gnu C/Linux
g++ -o lib_ReadFile.so -shared -Wl,-soname,lib_ReadFile.so
-I/usr/java/jdk1.5.0_15/include
-I/usr/java/jdk1.5.0_15/include/linux src/com_netsitech_jni_test_ReadFile.cpp
-static -lc
Win32
cl -I "C:\Programmi\Java\jdk1.5.0_15\include"
-I "C:\Programmi\Java\jdk1.5.0_15\include\win32"
-LD "src\com_netsitech_jni_test_ReadFile.cpp" -Fe "Debug\_ReadFile.dll"
Nota: Con l'ambiente MS Visual Studio è utile definire una environment variabile JAVA_HOME e aggiungere al progetto nella property "Additional Include Directories" il path "$(JAVA_HOME)/include";"$(JAVA_HOME)/include/win32" per i jni*.h headers.
Avviare l'esempio
Per poter avviare l'esempio è necessario indicare alla JVM dove ricercare la libreria nativa per renderla diponibile all'applicazione.
Unix or Linux:
LD_LIBRARY_PATH=`pwd` export LD_LIBRARY_PATH
Windows:
set PATH=%path%;.
Nota: Se la JVM non trova la libreria riporta il segeunte errore,
java.lang.UnsatisfiedLinkException: no _ReadFile in java.library.path
con la stampa dello stack delle chiamate.
/develop/workspace_jni/_Scrapbook/src>java com/netsitech/jni/test/ReadFile.java
Script Ant per generazione header
<?xml version="1.0" encoding="UTF-8"?>
<project name="ReadFile_ANT" basedir=".">
<description>
Script Ant per generare gli header C/C++ per la classe ReadFile
</description>
<target name="jni_ReadFile" description="Genera gli header C/C++ per la classe ReadFile">
<javah destdir="./src" classpath="../_ReadFile/bin" verbose="true">
<class name="com.netsitech.jni.test.ReadFile" />
</javah>
</target>
</project>