JNI - Java Native interface
JNI 는 자바 가상 머신이 native 메소드를 사용할 수 있도록 하기 위한 표준 이름과 호출 규약을 정의하고 있습니다. 사실, 호스트 운영 체제의 입출력, 그래픽, 네트워킹, 그리고 쓰레딩 연산을 수행하기 위한 로컬 시스템 호출을 자바 가상 머신이 사용하기 위해 JNI 가 자바 가상 머신 내에 만들어져 있습니다.
JNI 예제
ReadFile 자바 소스
import java.util.*; class ReadFile { //Native method declaration native byte[] loadFile(String name); //Load the library static { System.loadLibrary("nativelib"); } public static void main(String args[]) { byte buf[]; //Create class instance ReadFile mappedFile=new ReadFile(); //Call native method to load ReadFile.java buf=mappedFile.loadFile("ReadFile.java"); //Print contents of ReadFile.java for(int i=0;i<buf.length;i++) { System.out.print((char)buf[i]); } } }
Native 메소드 선언
native 선언은 자바 가상 머신에서 native 함수를 실행하기 위한 브리지를 제공한다. 이 예에서는, loadFile 함수를 Java_ReadFile_loadFile 이라는 C 함수로 맵핑한다. 함수 구현은 파일 이름을 나타내는 문자열을 받아서 그 파일의 내용을 바이트 배열로 반환한다.
native byte[] loadFile(String name);
라이브러리 로드
native 코드 구현을 포함하고 있는 라이브러리는 System.loadLibrary() 를 호출함으로써 로드된다. static 초기화 블럭에서 이 함수를 호출하는 것은 라이브러리가 클래스당 한번만 로드되는 것을 보장한다. 라이브러리는 필요하다면 static 블락 밖에서 로드될 수도 있다.
static { System.loadLibrary("nativelib"); }
프로그램 컴파일하기
컴파일하기 위해서는, 아래 명령을 수행하면 된다.
javac ReadFile.java
다음은, native 메소드를 선언하는 헤더 파일을 생성할 필요가 있고 파일을 로드하고 읽는 C 함수를 호출하기 위한 native 메소드를 구현해야 한다.
헤더 파일 생성
헤더 파일을 생성하기 위해선, ReadFile 클래스에 대해 javah 명령어를 수행하면 된다. 이 예에서는, 생성된 헤더 파일은 ReadFile.h 라는 이름을 가진다. 이 파일은 loadFile native 함수를 구현할 때 사용되는 메소드 시그너처를 가지고 있다.
javah -jni ReadFile
Note: javah 를 사용할 때, 완전한 클래스 명을 사용해야 한다.
메소드 시그너처
ReadFile.h 헤더 파일은 Java 언어 메소드를 native C 함수로 매핑하기 위한 인터페이스를 정의한다. Java 언어 mappedfile.loadFile 메소드의 인자와 반환값을 nativelib 라이브러리 내에 있는 loadFile native 메소드로 맵핑하기 위한 메소드 시그너처를 사용한다. 아래 그 예이다.
/* * Class: ReadFile * Method: loadFile * Signature: (Ljava/lang/String;)[B */ JNIEXPORT jbyteArray JNICALL Java_ReadFile_loadFile (JNIEnv *, jobject, jstring);
메소드 시그너처 파라미터는 다음과 같은 기능을 한다.
* JNIEnv *: JNI 환경에 대한 포인터이다. 이 포인터는 자바 가상 머신의 현재 쓰레드에 대한 핸들이고 맵핑 및 기타 정보를 가지고 있다.
* jobject: 이 native code 를 호출한 메소드에 대한 레퍼런스이다. 호출 메소드가 static 이라면, 이 파라미터는 jobject 대신 jclass 일 것이다.
* jstring: native 메소드에 의해 제공되는 파라미터. 이 예제에서는, 읽을 파일의 이름이다.
native 메소드 구현
아래 C 소스 파일에서, loadFile 선언은 ReadFile.h 에 있는 C 선언을 복사해서 붙여넣은 것이다. JNI 는 기본적으로 C / C++ 모두 에 대한 맵핑을 제공한다.
#include <jni.h> #include <sys/types.h> #include <sys/ipc.h> #include <sys/shm.h> #include <sys/mman.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> JNIEXPORT jbyteArray JNICALL Java_ReadFile_loadFile (JNIEnv * env, jobject jobj, jstring name) { caddr_t m; jbyteArray jb; jboolean iscopy; struct stat finfo; const char *mfile = (*env)->GetStringUTFChars( env, name, &iscopy); int fd = open(mfile, O_RDONLY); if (fd == -1) { printf("Could not open %s\n", mfile); } lstat(mfile, &finfo); m = mmap((caddr_t) 0, finfo.st_size, 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); }
기 존재하는 C 함수를 호출하기 위해서는 아래 두가지 방법중 하나를 선택할 수 있다.
1. JNI 에 의해 생성된 이름을 기 존재하는 C 함수 이름과 매핑한다. Language issue 장에서는 XBase 데이터베이스 함수과 Java 언어 코드 간에 매핑하는 방법을 보여준다.
2. java.sun.com 웹사이트의 JNI Page 에서 이용가능한 공유 스텁(shared stubs) 을 사용한다.
동적 또는 정적 객체 라이브러리로 컴파일 하기
라이브러리는 실행시 로드될 수 있도록 동적 또는 정적 객체 라이브러리로 컴파일되어야 한다. 정적 또는 아카이브 라이브러리는 실행화일로 컴파일되어 실행시에 로드될 수 없다. 공유 객체 또는 동적 라이브러리로 컴파일하는 예는 아래와 같다.
Gnu C/Linux: gcc -o libnativelib.so -shared -Wl,-soname,libnative.so -I/export/home/jdk1.2/include -I/export/home/jdk1.2/include/linux nativelib.c -static -lc Gnu C++/Linux with Xbase 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 Win32/WinNT/Win2000 cl -Ic:/jdk1.2/include -Ic:/jdk1.2/include/win32 -LD nativelib.c -Felibnative.dll
예제 실행하기
예제를 실행하기 위해서는, 자바 가상 머신이 native 라이브러리를 찾을수 있어야 한다. 이렇게 하기 위해, 라이브러리 경로에 현재 경로를 추가한다.
Unix or Linux: LD_LIBRARY_PATH=`pwd` export LD_LIBRARY_PATH Windows NT/2000/95: set PATH=%path%;.
라이브러리 경로가 올바르게 지정되었다면, 아래 명령으로 실행한다.
java ReadFile
참고: