Differenze tra le versioni di "Programmazione:Java/JNI Example"
m (→Esempio JNI) |
|||
(5 versioni intermedie di 2 utenti non mostrate) | |||
Riga 3: | Riga 3: | ||
=JNI Tutorial= | =JNI Tutorial= | ||
Per una guida abbastanza esaustiva su JNI, fare riferimento al documento presente sul sito [http://java.sun.com/developer/onlineTraining/Programming/JDCBook/jni.html SUN].<br> | Per una guida abbastanza esaustiva su JNI, fare riferimento al documento presente sul sito [http://java.sun.com/developer/onlineTraining/Programming/JDCBook/jni.html SUN].<br> | ||
− | =JNI | + | =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: | |
− | < | + | <syntaxhighlight lang="java"> |
− | + | package com.netsitech.jni.test; | |
− | class ReadFile { | + | public class ReadFile { |
− | //Native method declaration | + | // Native method declaration |
− | + | native byte[] loadFile(String name); | |
− | //Load the library | + | |
− | + | // 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 { | |
− | //Create class instance | + | if (args.length == 0) { |
− | + | System.out.print("Error file name not found in command line!"); | |
− | //Call native method to load ReadFile.java | + | } |
− | + | byte buffer[]; | |
− | //Print contents of ReadFile.java | + | // 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(); | ||
+ | } | ||
+ | } | ||
} | } | ||
− | + | </syntaxhighlight> | |
− | </ | ||
− | == | + | ==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. | |
− | < | + | <syntaxhighlight lang="java"> |
native byte[] loadFile(String name); | native byte[] loadFile(String name); | ||
− | </ | + | </syntaxhighlight> |
− | == | + | ==Caricamento della libreria con il codice nativo== |
− | + | <syntaxhighlight lang="java"> | |
− | |||
− | < | ||
static { | static { | ||
− | System.loadLibrary(" | + | System.loadLibrary("_ReadFile"); |
} | } | ||
+ | </syntaxhighlight> | ||
− | + | ==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: | |
− | |||
− | |||
− | + | <syntaxhighlight lang="cpp"> | |
− | < | ||
− | |||
− | |||
− | |||
− | = | ||
− | |||
− | |||
− | |||
− | |||
/* | /* | ||
− | * Class: | + | * Class: com_netsitech_jni_test_ReadFile |
* Method: loadFile | * Method: loadFile | ||
* Signature: (Ljava/lang/String;)[B | * Signature: (Ljava/lang/String;)[B | ||
*/ | */ | ||
− | JNIEXPORT jbyteArray JNICALL | + | JNIEXPORT jbyteArray JNICALL Java_com_netsitech_jni_test_ReadFile_loadFile |
(JNIEnv *, jobject, jstring); | (JNIEnv *, jobject, jstring); | ||
− | + | </syntaxhighlight> | |
− | </ | + | |
+ | === Osservazioni sulla signature === | ||
+ | |||
+ | La firma del metodo nativo è mappato Java-C/C++ | ||
− | + | <Tipo di ritorno> Java_<nome package>_<Classe>_<Metodo> | |
− | JNIEnv | + | 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== | |
− | + | <syntaxhighlight lang="cpp"> | |
+ | //#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); | ||
+ | } | ||
+ | </syntaxhighlight> | ||
===Gnu C/Linux=== | ===Gnu C/Linux=== | ||
− | < | + | <syntaxhighlight lang="bash"> |
− | + | g++ -o lib_ReadFile.so -shared -Wl,-soname,lib_ReadFile.so | |
− | -I/ | + | -I/usr/java/jdk1.5.0_15/include |
− | -I/ | + | -I/usr/java/jdk1.5.0_15/include/linux src/com_netsitech_jni_test_ReadFile.cpp |
-static -lc | -static -lc | ||
− | </ | + | </syntaxhighlight> |
− | |||
− | |||
− | + | ===Win32=== | |
− | |||
− | |||
− | |||
− | |||
− | |||
− | = | + | <syntaxhighlight lang="bash"> |
− | + | cl -I "C:\Programmi\Java\jdk1.5.0_15\include" | |
− | cl - | + | -I "C:\Programmi\Java\jdk1.5.0_15\include\win32" |
− | - | + | -LD "src\com_netsitech_jni_test_ReadFile.cpp" -Fe "Debug\_ReadFile.dll" |
− | -LD | + | </syntaxhighlight> |
− | </ | ||
− | + | 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:=== | ===Unix or Linux:=== | ||
Riga 187: | Riga 192: | ||
export LD_LIBRARY_PATH | export LD_LIBRARY_PATH | ||
− | ===Windows | + | ===Windows:=== |
set PATH=%path%;. | 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 == | ||
+ | |||
+ | <syntaxhighlight lang="xml"> | ||
+ | <?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> | ||
+ | </syntaxhighlight> |
Versione attuale delle 15:55, 18 mar 2021
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>