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

Da WikiSitech.
Vai alla navigazioneVai alla ricerca
Riga 95: Riga 95:
 
<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==

Versione delle 21:14, 10 set 2008

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

  1. include <stdlib.h>
  2. include <string.h>
  3. include <jni.h>
  4. 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>