ルート情報を通知するJNIクラスを一部修正しました。
どうもsave_class()関数のロジックでjfieldID等を取得する部分を毎回実行するって言うのが気に入らなくて色々と試して見ました。
まぁ、ロジックが冗長になったのは、Javaから呼び出した時に内部エラーが発生していたので、てっきりC++のロジックの作り方の関係だろうと思って、落ち着いたのが先週までのロジックだったわけですが、その後試していて次のような流れでも問題なく動作することが分かったので公開します。
RouteCheck.cpp
#include "jniroutecheck.h" #include "jninet.h" static listener_list listener; static JavaVM *jvm = NULL; jclass ric_class; /* RouterInfo */ jmethodID ric_ctor; /* RouterInfo() */ jfieldID ric_indexID; /* RouterInfo.index */ jfieldID ric_destID; /* RouterInfo.dest */ jfieldID ric_maskID; /* RouterInfo.mask */ jfieldID ric_gatewayID; /* RouterInfo.gateway */ jfieldID ric_typeID; /* RouterInfo.type */ jfieldID ric_metricID; /* RouterInfo.metric */ jfieldID ric_defaultGwID; /* RouterInfo.defaultGw */ jfieldID ric_primaryGwID; /* RouterInfo.primaryGw */ jobject save_class(JNIEnv *env, jclass ric_class, RouteInfo info) { jobject netifObj = env->NewObject(ric_class, ric_ctor); env->SetIntField(netifObj, ric_indexID, info.getIndex()); env->SetObjectField(netifObj, ric_destID, cStringToJString(env, info.getDest())); env->SetObjectField(netifObj, ric_maskID, cStringToJString(env, info.getMask())); env->SetObjectField(netifObj, ric_gatewayID, cStringToJString(env, info.getGateway())); env->SetIntField(netifObj, ric_typeID, info.getType()); env->SetIntField(netifObj, ric_metricID, info.getMetric()); env->SetBooleanField(netifObj, ric_defaultGwID, info.isDefaultGateway()); env->SetBooleanField(netifObj, ric_primaryGwID, info.isPrimaryGateway()); return netifObj; } void WINAPI route_modified(route_list added_table, route_list removed_table) { jobjectArray add_routes = NULL; jobjectArray remove_routes = NULL; jint err; JNIEnv *base_env = NULL; JavaVMAttachArgs t_args = {JNI_VERSION_1_6, NULL, NULL}; if ((err = jvm->AttachCurrentThread((void**)&base_env, &t_args)) != JNI_OK) { return; } if (!added_table.empty()) { jsize rt_count = (jsize)added_table.size(); if (rt_count > 0) { add_routes = base_env->NewObjectArray(rt_count, ric_class, NULL); for (jsize i = 0; i < rt_count; i++) { RouteInfo info = added_table.at(i); jobject one_route = save_class(base_env, ric_class, info); base_env->SetObjectArrayElement(add_routes, i, one_route); } } } if (!removed_table.empty()) { jsize rt_count = (jsize)removed_table.size(); if (rt_count > 0) { remove_routes = base_env->NewObjectArray(rt_count, ric_class, NULL); for (jsize i = 0; i < rt_count; i++) { RouteInfo info = removed_table.at(i); jobject one_route = save_class(base_env, ric_class, info); base_env->SetObjectArrayElement(remove_routes, i, one_route); } } } for (ll_iter iter = listener.begin(); iter != listener.end(); iter++) { jobject rc_obj = (*iter)->caller; jclass rc_class = (jclass)base_env->GetObjectClass(rc_obj); jmethodID rc_callback = base_env->GetMethodID(rc_class, "routeModified", "([Lnet/RouteInfo;[Lnet/RouteInfo;)V"); base_env->CallVoidMethod(rc_obj, rc_callback, add_routes, remove_routes, NULL); } jvm->DetachCurrentThread(); } JNIEXPORT void JNICALL Java_net_RouteCheck_init(JNIEnv *env, jclass cls) { /* * RouteInfoクラスの情報を設定 */ ric_class = env->FindClass("net/RouteInfo"); ric_ctor = env->GetMethodID(ric_class, "<init>", "()V"); ric_indexID = env->GetFieldID(ric_class, "index", "I"); ric_destID = env->GetFieldID(ric_class, "dest", "Ljava/lang/String;"); ric_maskID = env->GetFieldID(ric_class, "mask", "Ljava/lang/String;"); ric_gatewayID = env->GetFieldID(ric_class, "gateway", "Ljava/lang/String;"); ric_typeID = env->GetFieldID(ric_class, "type", "I"); ric_metricID = env->GetFieldID(ric_class, "metric", "I"); ric_defaultGwID = env->GetFieldID(ric_class, "defaultGw", "Z"); ric_primaryGwID = env->GetFieldID(ric_class, "primaryGw", "Z"); env->GetJavaVM(&jvm); } JNIEXPORT void JNICALL Java_net_RouteCheck_addRouteChangeListener(JNIEnv *env, jclass cls, jobject target) { PLISTENER obj = new LISTENER; obj->base = target; obj->caller = env->NewGlobalRef(target); listener.push_back(obj); if (listener.size() == 1) { RouteCheck::addCallBackRoutine(route_modified); } return ; } JNIEXPORT void JNICALL Java_net_RouteCheck_removeRouteChangeListener(JNIEnv *env, jclass cls, jobject target) { jobject remove = env->NewGlobalRef(target); for (ll_iter iter = listener.begin(); iter != listener.end(); iter++) { PLISTENER obj = (*iter); if (env->IsSameObject(obj->caller, remove) == JNI_TRUE) { env->DeleteGlobalRef(obj->caller); if (listener.size() == 1) { RouteCheck::removeCallBackRoutine(route_modified); } listener.erase(iter); delete(obj); break; } } env->DeleteGlobalRef(remove); return ; } JNIEXPORT void JNICALL Java_net_RouteCheck_startLinstening(JNIEnv *env, jclass cls) { RouteCheck::startRouteModifyCheck(); } JNIEXPORT void JNICALL Java_net_RouteCheck_stopLinstening(JNIEnv *env, jclass cls) { RouteCheck::stopRouteModifyCheck(); }
始めの頃にJavaで実行した時のエラーだったロジックを詳しく覚えていないんですが、うろ覚えではJava_net_RouteCheck_init()の中でFindClassをした後に、NewGlobalRefでクラスをオブジェクトとして参照していたようで、
ric_class = env->FindClass("net/RouteInfo");
ric_class = (jclass)env->NewGlobalRef(ric_class);
これが後々の問題になったみたいです。
(実は以前のIfInfoクラス用のJNIのJava_net_IfInfo_initロジックの中でAddrInfoクラスを取得する時のもやってたんで、コピペしたのが悪かったかな・・・。
こっちも後で修正・テストしてみようっと。)