JNIでHelloWorld!
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
する必要あり。
Javaプログラム作成
こんな感じのクラスを作る。
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修飾子付きの宣言だけしておくこと。
Cヘッダ作成
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プログラムを作る。
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; }
Cプログラムのコンパイル
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は不要になると思います。