Differenze tra le versioni di "Programmazione:Java/JNI Example"
m (→Esempio JNI)  | 
				|||
| (7 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 183: | 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 14: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>