[Java,JNI]JNIでクラス検索するときのパッケージ名

2016年12月26日月曜日

Java JNI

OracleのJNIドキュメントに載っているサンプルプログラムを参考にして、Cからパッケージ名付きクラスのmainメソッドを呼びだそうとしたんだけど、うまくいかなかった話。

Java Native Interface Specification
5. The Invocation API


これを元にして、 local.PropertiesSample クラスの main メソッドを呼び出してみよう。
#include <jni.h>
#include <stdio.h>

int main(void) {
    JavaVM *jvm;
    JNIEnv *env;

    JavaVMInitArgs vm_args;
    JavaVMOption options[1];
    options[0].optionString = "-Djava.class.path=/path/to/classpath";
    vm_args.version = JNI_VERSION_1_8;
    vm_args.nOptions = 1;
    vm_args.options = options;
    vm_args.ignoreUnrecognized = 0;

    JNI_CreateJavaVM(&jvm, (void**)&env, &vm_args);

    jclass cls = (*env)->FindClass(env, "local.PropertiesSample");
    jmethodID mid = (*env)->GetStaticMethodID(env, cls, "main", "([Ljava/lang/String;)V");

    jclass stringCls = (*env)->FindClass(env, "java.lang.String");
    jobjectArray mainArgs = (*env)->NewObjectArray(env, 0, stringCls, NULL);

    (*env)->CallStaticVoidMethod(env, cls, mid, mainArgs);

    (*env)->DeleteLocalRef(env, mainArgs);
    (*env)->DeleteLocalRef(env, stringCls);
    (*env)->DeleteLocalRef(env, cls);
    (*jvm)->DestroyJavaVM(jvm);

    return 0;
}

コンパイルして実行してみたけど、どうも、 local.PropertiesSample クラスが見つからないようだ...

結局、区切り記号をJavaで使う"."ではなく、"/"にしなければいけないことがわかった。
ので、以下のように修正したらうまくいった。
#include <jni.h>
#include <stdio.h>

int main(void) {
    JavaVM *jvm;
    JNIEnv *env;

    JavaVMInitArgs vm_args;
    JavaVMOption options[1];
    options[0].optionString = "-Djava.class.path=/path/to/classpath";
    vm_args.version = JNI_VERSION_1_8;
    vm_args.nOptions = 1;
    vm_args.options = options;
    vm_args.ignoreUnrecognized = 0;

    JNI_CreateJavaVM(&jvm, (void**)&env, &vm_args);

    jclass cls = (*env)->FindClass(env, "local/PropertiesSample");
    jmethodID mid = (*env)->GetStaticMethodID(env, cls, "main", "([Ljava/lang/String;)V");

    jclass stringCls = (*env)->FindClass(env, "java/lang/String");
    jobjectArray mainArgs = (*env)->NewObjectArray(env, 0, stringCls, NULL);

    (*env)->CallStaticVoidMethod(env, cls, mid, mainArgs);

    (*env)->DeleteLocalRef(env, mainArgs);
    (*env)->DeleteLocalRef(env, stringCls);
    (*env)->DeleteLocalRef(env, cls);
    (*jvm)->DestroyJavaVM(jvm);

    return 0;
}

ファイル名を指定しているわけではないのに、なんでかなーと思ったけど、JVMのシグネチャに近い記法にしてるのかな。