Differenze tra le versioni di "Programmazione:Java/JNI Example"

Da WikiSitech.
Vai alla navigazioneVai alla ricerca
 
(2 versioni intermedie di 2 utenti non mostrate)
Riga 6: Riga 6:
 
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.
 
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:
 
I passi necessari per effettuare uuna chaiamata JNI sono:
  1. Dichiarare nella classe Java quali sono i metodi che invocheranno funzioni native con la keyword native;
+
# Dichiarare nella classe Java quali sono i metodi che invocheranno funzioni native con la keyword native;
  2. Caricare dalla classe Java la libreria per la funzione nativa;
+
# Caricare dalla classe Java la libreria per la funzione nativa;
  3. Compilazione della classe Java e generazione del file header (file .h) che rappresenti l’interfaccia della funzione nativa;
+
# Compilazione della classe Java e generazione del file header (file .h) che rappresenti l’interfaccia della funzione nativa;
  4. Scrivere il codice nativo che implementi la funzione del file header generato;
+
# 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:
 
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:
  
<code java>
+
<syntaxhighlight lang="java">
 
package com.netsitech.jni.test;
 
package com.netsitech.jni.test;
  
Riga 49: Riga 49:
 
}
 
}
 
}
 
}
</code>
+
</syntaxhighlight>
  
 
==Dichiarazione del metodo nativo==
 
==Dichiarazione del metodo nativo==
Riga 55: Riga 55:
 
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.
 
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.
  
<code java>
+
<syntaxhighlight lang="java">
 
   native byte[] loadFile(String name);
 
   native byte[] loadFile(String name);
</code>
+
</syntaxhighlight>
  
 
==Caricamento della libreria con il codice nativo==
 
==Caricamento della libreria con il codice nativo==
  
<code java>
+
<syntaxhighlight lang="java">
 
   static {
 
   static {
 
     System.loadLibrary("_ReadFile");
 
     System.loadLibrary("_ReadFile");
 
   }
 
   }
</code>
+
</syntaxhighlight>
  
 
==Compilazine della classe Java e generazione del Header File==
 
==Compilazine della classe Java e generazione del Header File==
  
<code>
+
/develop/workspace_jni/_Scrapbook/src>javac com.netsitech.jni.test.ReadFile.java
  /develop/workspace_jni/_Scrapbook/src>javac com.netsitech.jni.test.ReadFile.java
 
</code>
 
  
<code>
+
/develop/workspace_jni/_Scrapbook/bin>javah -d /develop/workspace_jni_test/_Scrapbook/src -jni com.netsitech.jni.test.ReadFile
  /develop/workspace_jni/_Scrapbook/bin>javah -d /develop/workspace_jni_test/_Scrapbook/src -jni com.netsitech.jni.test.ReadFile
 
</code>
 
  
 
La signature generata del metodo nativo avrà alcuni parametri il cui significato è riportato sotto:
 
La signature generata del metodo nativo avrà alcuni parametri il cui significato è riportato sotto:
  
<code cpp>
+
<syntaxhighlight lang="cpp">
 
   /*
 
   /*
 
   * Class:    com_netsitech_jni_test_ReadFile
 
   * Class:    com_netsitech_jni_test_ReadFile
Riga 87: Riga 83:
 
   JNIEXPORT jbyteArray JNICALL Java_com_netsitech_jni_test_ReadFile_loadFile
 
   JNIEXPORT jbyteArray JNICALL Java_com_netsitech_jni_test_ReadFile_loadFile
 
     (JNIEnv *, jobject, jstring);
 
     (JNIEnv *, jobject, jstring);
</code>
+
</syntaxhighlight>
  
 
=== Osservazioni sulla signature ===
 
=== Osservazioni sulla signature ===
Riga 95: Riga 91:
 
<Tipo di ritorno> Java_<nome package>_<Classe>_<Metodo>
 
<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.
+
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==
 
==Implementazione del metodo nativo==
  
<code cpp>
+
<syntaxhighlight lang="cpp">
 
//#include "stdafx.h"
 
//#include "stdafx.h"
 
#include <stdlib.h>
 
#include <stdlib.h>
Riga 167: Riga 163:
 
return (jb);
 
return (jb);
 
}
 
}
</code>
+
</syntaxhighlight>
  
 
===Gnu C/Linux===
 
===Gnu C/Linux===
  
<code>
+
<syntaxhighlight lang="bash">
 
g++  -o lib_ReadFile.so -shared -Wl,-soname,lib_ReadFile.so
 
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
 
     -I/usr/java/jdk1.5.0_15/include/linux src/com_netsitech_jni_test_ReadFile.cpp
 
     -I/usr/java/jdk1.5.0_15/include/linux src/com_netsitech_jni_test_ReadFile.cpp
 
     -static -lc
 
     -static -lc
</code>
+
</syntaxhighlight>
  
 
===Win32===
 
===Win32===
  
<code>
+
<syntaxhighlight lang="bash">
 
cl -I "C:\Programmi\Java\jdk1.5.0_15\include"
 
cl -I "C:\Programmi\Java\jdk1.5.0_15\include"
 
   -I "C:\Programmi\Java\jdk1.5.0_15\include\win32"
 
   -I "C:\Programmi\Java\jdk1.5.0_15\include\win32"
 
   -LD "src\com_netsitech_jni_test_ReadFile.cpp" -Fe "Debug\_ReadFile.dll"
 
   -LD "src\com_netsitech_jni_test_ReadFile.cpp" -Fe "Debug\_ReadFile.dll"
</code>
+
</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.
 
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.
Riga 205: Riga 201:
 
con la stampa dello stack delle chiamate.
 
con la stampa dello stack delle chiamate.
  
<code>
 
 
   /develop/workspace_jni/_Scrapbook/src>java com/netsitech/jni/test/ReadFile.java
 
   /develop/workspace_jni/_Scrapbook/src>java com/netsitech/jni/test/ReadFile.java
</code>
 
  
 
== Script Ant per generazione header ==
 
== Script Ant per generazione header ==
  
<code xml>
+
<syntaxhighlight lang="xml">
 
<?xml version="1.0" encoding="UTF-8"?>
 
<?xml version="1.0" encoding="UTF-8"?>
 
<project name="ReadFile_ANT" basedir=".">
 
<project name="ReadFile_ANT" basedir=".">
Riga 225: Riga 219:
  
 
</project>
 
</project>
</code>
+
</syntaxhighlight>

Versione attuale delle 15:55, 18 mar 2021

<< Back to Java

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:

  1. Dichiarare nella classe Java quali sono i metodi che invocheranno funzioni native con la keyword native;
  2. Caricare dalla classe Java la libreria per la funzione nativa;
  3. Compilazione della classe Java e generazione del file header (file .h) che rappresenti l’interfaccia della funzione nativa;
  4. 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>