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

Da WikiSitech.
Vai alla navigazioneVai alla ricerca
 
 
(9 versioni intermedie di 3 utenti non mostrate)
Riga 1: Riga 1:
=JNI Example=
+
[[Programmazione:Java|<< Back to Java]]
You can call code written in any programming language from a program written in the Java language by declaring a native Java method, loading the library that contains the native code, and then calling the native method. The ReadFile source code below does exactly this.
 
  
However, successfully running the program requires a few additional steps beyond compiling the Java language source file. After you compile, but before you run the example, you have to generate a header file. The native code implements the function defintions contained in the generated header file and implements the business logic as well. The following sections walk through all the steps.  
+
=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>
 +
=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 l’interfaccia della funzione nativa;
 +
# Scrivere il codice nativo che implementi la funzione del file header generato;
  
<code java>
+
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:
import java.util.*;
 
  
class ReadFile {
+
<syntaxhighlight lang="java">
//Native method declaration
+
package com.netsitech.jni.test;
  native byte[] loadFile(String name);
+
 
//Load the library
+
public class ReadFile {
  static {
+
// Native method declaration
    System.loadLibrary("nativelib");
+
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[]) {
+
public static void main(String args[]) {
    byte buf[];
+
try {
//Create class instance
+
if (args.length == 0) {
    ReadFile mappedFile=new ReadFile();
+
System.out.print("Error file name not found in command line!");
//Call native method to load ReadFile.java
+
}
    buf=mappedFile.loadFile("ReadFile.java");
+
byte buffer[];
//Print contents of ReadFile.java
+
// Create class instance
    for(int i=0;i<buf.length;i++) {
+
ReadFile mappedFile = new ReadFile();
      System.out.print((char)buf[i]);
+
// 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>
</code>
 
  
==Native Method Declaration==
+
==Dichiarazione del metodo nativo==
  
The native declaration provides the bridge to run the native function in the Java(As used on this web site, the terms "Java virtual machine" or "JVM" mean a virtual machine for the Java platform) virtual machine. In this example, the loadFile function maps onto a C function called Java_ReadFile_loadFile. The function implementation accepts a String that represents a file name and returns the contents of that file in the byte array.  
+
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>
 
 
==Load the Library==
 
  
The library containing the native code implementation is loaded by a call to System.loadLibrary(). Placing this call in a static initializer ensures this library is only loaded once per class. The library can be loaded outside of the static block if your application requires it. You might need to configure your environment so the loadLibrary method can find your native code library.
+
==Caricamento della libreria con il codice nativo==
  
<code java>
+
<syntaxhighlight lang="java">
 
   static {
 
   static {
     System.loadLibrary("nativelib");
+
     System.loadLibrary("_ReadFile");
 
   }
 
   }
 +
</syntaxhighlight>
  
</code>
+
==Compilazine della classe Java e generazione del Header File==
  
==Compile the Program==
+
/develop/workspace_jni/_Scrapbook/src>javac com.netsitech.jni.test.ReadFile.java
  
To compile the program, just run the javac compiler command as you normally would:
+
/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:
  javac ReadFile.java
 
</code>
 
  
Next, you need to generate a header file with the native method declaration and implement the native method to call the C functions for loading and reading a file.
+
<syntaxhighlight lang="cpp">
 
 
==Generate the Header File==
 
 
 
To generate a a header file, run the javah command on the ReadFile class. In this example, the generated header file is named ReadFile.h. It provides a method signature that you have to use when you implement the loadfile native function.
 
 
 
<code>
 
  javah -jni ReadFile
 
</code>
 
 
 
Note: When running javah on your own classes, be sure to use the fully-qualified class name.
 
 
 
==Method Signature==
 
 
 
The ReadFile.h header file defines the interface to map the Java language method to the native C function. It uses a method signature to map the arguments and return value of the Java language mappedfile.loadFile method to the loadFile native method in the nativelib library. Here is the loadFile native method mapping (method signature):
 
 
 
<code c>
 
 
   /*
 
   /*
   * Class:    ReadFile
+
   * Class:    com_netsitech_jni_test_ReadFile
 
   * Method:    loadFile
 
   * Method:    loadFile
 
   * Signature: (Ljava/lang/String;)[B
 
   * Signature: (Ljava/lang/String;)[B
 
   */
 
   */
   JNIEXPORT jbyteArray JNICALL Java_ReadFile_loadFile
+
   JNIEXPORT jbyteArray JNICALL Java_com_netsitech_jni_test_ReadFile_loadFile
 
     (JNIEnv *, jobject, jstring);
 
     (JNIEnv *, jobject, jstring);
+
</syntaxhighlight>
</code>
 
  
The method signature parameters function as follows:
+
=== Osservazioni sulla signature ===
  
JNIEnv *: A pointer to the JNI environment. This pointer is a handle to the current thread in the Java virtual machine, and contains mapping and other hosuekeeping information.
+
La firma del metodo nativo è mappato Java-C/C++
  
jobject: A reference to the method that called this native code. If the calling method is static, this parameter would be type jclass instead of jobject.
+
<Tipo di ritorno> Java_<nome package>_<Classe>_<Metodo>
  
jstring: The parameter supplied to the native method. In this example, it is the name of the file to be read.  
+
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.
  
==Implement the Native Method==
+
==Implementazione del metodo nativo==
  
In this native C source file, the loadFile definition is a copy and paste of the C declaration contained in ReadFile.h. The definition is followed by the native method implementation. JNI provides a mapping for both C and C++ by default.  
+
<syntaxhighlight lang="cpp">
 +
//#include "stdafx.h"
 +
#include <stdlib.h>
 +
#include <string.h>
 +
#include <jni.h>
 +
#include "com_netsitech_jni_test_ReadFile.h"
  
<code c>
+
inline void prepareException(JNIEnv *env, char* exname, char *des){
#include <jni.h>
+
jclass ex = env->FindClass(exname);
#include <sys/types.h>
+
if (ex != 0){
#include <sys/ipc.h>
+
env->ThrowNew(ex, des);
#include <sys/shm.h>
+
}
#include <sys/mman.h>
+
}
#include <sys/stat.h>
+
 
#include <fcntl.h>
+
/*
#include <unistd.h>
+
* 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;
  
JNIEXPORT jbyteArray JNICALL Java_ReadFile_loadFile
+
//Cpp Version GetStringUTFChars
  (JNIEnv * env, jobject jobj, jstring name) {
+
const char *mfile = env->GetStringUTFChars(name, &iscopy);
    caddr_t m;
+
//Open in read_only and binary mode to skip CFLF system difference
    jbyteArray jb;
+
FILE* fd = fopen(mfile, "rb");
    jboolean iscopy;
 
    struct stat finfo;
 
    const char *mfile = (*env)->GetStringUTFChars(
 
                env, name, &iscopy);
 
    int fd = open(mfile, O_RDONLY);
 
  
    if (fd == -1) {
+
if (fd == NULL) {
      printf("Could not open %s\n", mfile);
+
char des[255] = "File not found: ";
    }
+
strcat(des, mfile);
    lstat(mfile, &finfo);
+
prepareException(env, "java/io/FileNotFoundException", des);
    m = mmap((caddr_t) 0, finfo.st_size,
+
return (0);
                PROT_READ, MAP_PRIVATE, fd, 0);
+
}
    if (m == (caddr_t)-1) {
 
      printf("Could not mmap %s\n", mfile);
 
      return(0);
 
    }
 
    jb=(*env)->NewByteArray(env, finfo.st_size);
 
    (*env)->SetByteArrayRegion(env, jb, 0,
 
finfo.st_size, (jbyte *)m);
 
    close(fd);
 
    (*env)->ReleaseStringUTFChars(env, name, mfile);
 
    return (jb);
 
}
 
 
</code>
 
  
You can approach calling an existing C function instead of implementing one, in one of two ways:
+
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);
  
Map the name generated by JNI to the existing C function name. The Language Issues section shows how to map between Xbase database functions and Java language code
+
result = fseek(fd, 0, SEEK_SET);
 +
if( result != 0 ) {
 +
prepareException(env, "java/io/IOException", "Problem getting file information");
 +
return (0);
 +
}
  
Use the shared stubs code available from the JNI page on the java.sun.com web site.
+
m = (char*) malloc (size);
Compile the Dynamic or Shared Object Library
+
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);
 +
}
  
The library needs to be compiled as a dynamic or shared object library so it can be loaded at runtime. Static or archive libraries are compiled into an executable and cannot be loaded at runtime. The shared object or dynamic library for the loadFile example is compiled as follows:
+
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===
  
<code>
+
<syntaxhighlight lang="bash">
gcc -o libnativelib.so -shared -Wl,-soname,libnative.so
+
g++ -o lib_ReadFile.so -shared -Wl,-soname,lib_ReadFile.so
     -I/export/home/jdk1.2/include  
+
     -I/usr/java/jdk1.5.0_15/include
     -I/export/home/jdk1.2/include/linux nativelib.
+
     -I/usr/java/jdk1.5.0_15/include/linux src/com_netsitech_jni_test_ReadFile.cpp
 
     -static -lc
 
     -static -lc
</code>
+
</syntaxhighlight>
 
 
===Gnu C++/Linux with Xbase===
 
  
<code>
+
===Win32===
g++ -o libdbmaplib.so -shared -Wl,-soname,libdbmap.so 
 
    -I/export/home/jdk1.2/include
 
    -I/export/home/jdk1.2/include/linux
 
    dbmaplib.cc -static -lc -lxbase
 
</code>
 
  
===Win32/WinNT/Win2000===
+
<syntaxhighlight lang="bash">
<code>
+
cl -I "C:\Programmi\Java\jdk1.5.0_15\include"
cl -Ic:/jdk1.2/include  
+
   -I "C:\Programmi\Java\jdk1.5.0_15\include\win32"
   -Ic:/jdk1.2/include/win32  
+
   -LD "src\com_netsitech_jni_test_ReadFile.cpp" -Fe "Debug\_ReadFile.dll"
   -LD nativelib.c -Felibnative.dll
+
</syntaxhighlight>
</code>
 
  
 +
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.
  
==Run the Example==
+
==Avviare l'esempio==
  
To run the example, the Java virtual machine needs to be able to find the native library. To do this, set the library path to the current directory as follows:
+
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 179: Riga 192:
 
   export LD_LIBRARY_PATH
 
   export LD_LIBRARY_PATH
  
===Windows NT/2000/95:===
+
===Windows:===
 
   set PATH=%path%;.
 
   set PATH=%path%;.
 
  
With the library path properly specified for your platform, invoke the program as you normally would with the interpreter command:
+
'''Nota''': Se la JVM non trova la libreria riporta il segeunte errore,
<code>
+
 
  java ReadFile
+
java.lang.UnsatisfiedLinkException: no _ReadFile in java.library.path
</code>
+
 
 +
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

<< 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>