
JNIはJavaプログラムからC/C++で記述されたライブラリのAPIを呼び出すためのインタフェースです。ずいぶん昔からある仕様ですが、あまりなじみが無いので触ってみた。
イメージ的にはELF形式のjavaプログラムから共有ライブラリに対してdlopenで動的にリンクして呼び出すって感じですな。MySQLのplugin(UDF, pluggable storage engine)と技術的には同類。
ということで以下は、Linux x86 (CentOS4.5) + JavaSE6 でJNI HelloWorldした記録です。
参考:http://www.alles.or.jp/~torutk/oojava/maneuver/2001/jni/jni.html
ここからJavaSE6のJDKを入手してインストール。RPMが楽でいい。お奨め。
既存環境にgcj(java-1.4.2-gcj-compat.noarch)とかが入っているとインストールがうまく行かないので先に抜いておく。
以下のコマンドでインストール完了確認
java -version
どうやらRPMで入れると、
/usr/bin/java等 ↓ /usr/java/default/*** ↓ /usr/java/latest/*** ↓ /usr/java/jdk1.6.0_11
みたいにつながるようにセットアップしてくれる(RPM版)。
ただし、javahコマンドは/usr/binにsymlinkが作られないので
cd /usr/bin && sudo ln -s /usr/java/default/javah
する必要あり。
こんな感じのクラスを作る。
package tritonn;
public class HelloJNI {
static {
System.loadLibrary("HelloJNIImpl");
}
public native void printHello();
public static void main(String[] args) {
new HelloJNI().printHello();
}
}
ポイントはstaticイニシャライザでSystem.loadLibraryを呼ぶこと。JNI経由で呼ぶ予定のメソッドをnative修飾子付きの宣言だけしておくこと。
普通にjavacすればよろし。
javac tritonn/HelloJNI.java
javahコマンドでCプログラム用のヘッダを自動生成する。完全修飾クラス名で指定。
javah tritonn.HelloJNI
作業ディレクトリにこんなヘッダファイルができる。tritonn_HelloJNI.hとか。(パッケージ名_クラス名.hかな)
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class tritonn_HelloJNI */
#ifndef _Included_tritonn_HelloJNI
#define _Included_tritonn_HelloJNI
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: tritonn_HelloJNI
* Method: printHello
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_tritonn_HelloJNI_printHello
(JNIEnv *, jobject);
#ifdef __cplusplus
}
#endif
#endif
これを実装したCプログラムを作る。
いくつかなじみの無いマクロがでてきてますが、気にせずヘッダファイルにある関数シグネチャ通りに実装。
#include <jni.h>
#include "tritonn_HelloJNI.h"
#include <stdio.h>
JNIEXPORT void JNICALL Java_tritonn_HelloJNI_printHello
(JNIEnv * a, jobject b) {
printf("Hello JNI World!!\n");
return;
}
JDKに含まれているjni.hとかは(RPM版でインストールしても)/usr/includeとかには入らないので、-Iで明示する必要有り。また共有ライブラリにするので -fPIC を忘れずに。
gcc -fPIC -I/usr/java/default/include -I/usr/java/default/include/linux -c HelloJNIImpl.c
ここで指定するライブラリ名がJavaプログラムでSystem.loadLibraryする対象となります。
gcc -shared -o libHelloJNIImpl.so HelloJNIImpl.o
とりあえずLD_LIBRARY_PATH指定して実行。
tritonn@dev32:~$ LD_LIBRARY_PATH=`pwd` java tritonn.HelloJNI Hello JNI World!!
おぉぉ!うまく行きましたね!
/usr/libとかに*.soを放り込んでldconfigを実行すればLD_LIBRARY_PATHは不要になると思います。