ルート情報を取得するクラス(JNI)

折角ルート情報を取得するクラスを作ったので、これをJavaのクラスとしても利用できるようにJNIで実装してみました。
いつもの様に
RotueInfo.java

package net;

import java.util.Vector;

public class RouteInfo {

    private int             index = 0;
    private String          dest = null;
    private String          mask = null;
    private String          gateway = null;
    private int             type = 0;
    private int             metric = 0;
    private boolean         defaultGw = false;
    private boolean         primaryGw = false;

    static {
        System.loadLibrary("jninet");
        init();
    }

    public RouteInfo() {
    }

    public String getDest() {
        return dest;
    }

    public String getMask() {
        return mask;
    }

    public String getGateway() {
        return gateway;
    }

    public int getIndex() {
        return index;
    }

    public int getType() {
        return type;
    }

    public int getMetric() {
        return metric;
    }

    public boolean isDefaultGateway() {
        return defaultGw;
    }

    public boolean isPrimaryGateway() {
        return primaryGw;
    }

    public boolean isSameRouteInfo(String dest, String mask, String gateway) {
        if ((dest == null) || (!dest.equals(this.dest)))
            return false;
        if ((mask == null) || (!mask.equals(this.mask)))
            return false;
        if ((gateway == null) || (!gateway.equals(this.gateway)))
            return false;
        return true;
    }

    public static RouteInfo getBestRoute(String dest, String mask, Vector<RouteInfo> table) {
        RouteInfo[] routeTable = null;
        if ((table != null) && (table.size() > 0)) {
            routeTable = new RouteInfo[table.size()];
            for (int i = 0; i < table.size(); i++) {
                routeTable[i] = table.get(i);
            }
        }
        return getBestRoute(dest, mask, routeTable);
    }

    private static native void init();
    public static native RouteInfo[] getRouteList();
    public static native RouteInfo getBestRoute(String dest, String mask, RouteInfo[] table);
}

を作ってコンパイルした後で、javahを使ってヘッダファイル作成します。


それから、JNIの実装として
jnirouteinfo.h

#ifndef _JNI_ROUTEINFO_CALL
#define _JNI_ROUTEINFO_CALL

#include <windows.h>

#include "routeinfo.h"
using namespace penguin;

#include "net_RouteInfo.h"

jclass      ri_class;           /* RouterInfo */
jmethodID   ri_ctor;            /* RouterInfo() */
jfieldID    ri_indexID;         /* RouterInfo.index */
jfieldID    ri_destID;          /* RouterInfo.dest */
jfieldID    ri_maskID;          /* RouterInfo.mask */
jfieldID    ri_gatewayID;       /* RouterInfo.gateway */
jfieldID    ri_typeID;          /* RouterInfo.type */
jfieldID    ri_metricID;        /* RouterInfo.metric */
jfieldID    ri_defaultGwID;     /* RouterInfo.defaultGw */
jfieldID    ri_primaryGwID;     /* RouterInfo.primaryGw */

#endif  //  _JNI_ROUTEINFO_CALL


jnirouteinfo.cpp

#include "jnirouteinfo.h"
#include "jninet.h"

jobject load_class(JNIEnv *env, RouteInfo info) {
    jobject netifObj = env->NewObject(ri_class, ri_ctor);

    env->SetIntField(netifObj, ri_indexID, info.getIndex());
    env->SetObjectField(netifObj, ri_destID, stringToJString(env, info.getDest()));
    env->SetObjectField(netifObj, ri_maskID, stringToJString(env, info.getMask()));
    env->SetObjectField(netifObj, ri_gatewayID, stringToJString(env, info.getGateway()));
    env->SetIntField(netifObj, ri_typeID, info.getType());
    env->SetIntField(netifObj, ri_metricID, info.getMetric());
    env->SetBooleanField(netifObj, ri_defaultGwID, info.isDefaultGateway());
    env->SetBooleanField(netifObj, ri_primaryGwID, info.isPrimaryGateway());

    return netifObj;
}

RouteInfo load_value(JNIEnv *env, jobject rt_info) {

    unsigned long   index = env->GetIntField(rt_info, ri_indexID);
    String          dest = jStringToString(env, (jstring)env->GetObjectField(rt_info, ri_destID));
    String          mask = jStringToString(env, (jstring)env->GetObjectField(rt_info, ri_maskID));
    String          gateway = jStringToString(env, (jstring)env->GetObjectField(rt_info, ri_gatewayID));;
    unsigned long   type = env->GetIntField(rt_info, ri_typeID);
    unsigned long   metric = env->GetIntField(rt_info, ri_metricID);
    bool            defaultGw = (env->GetBooleanField(rt_info, ri_defaultGwID) != 0) ? true : false;

    RouteInfo route_entry = RouteInfo(dest, mask, gateway, index, metric, defaultGw);
    return route_entry;
}

JNIEXPORT void JNICALL Java_net_RouteInfo_init(JNIEnv *env, jclass cls) {
    ri_class = (jclass)env->NewGlobalRef(cls);
    ri_indexID = env->GetFieldID(ri_class, "index", "I");
    ri_destID = env->GetFieldID(ri_class, "dest", "Ljava/lang/String;");
    ri_maskID = env->GetFieldID(ri_class, "mask", "Ljava/lang/String;");
    ri_gatewayID = env->GetFieldID(ri_class, "gateway", "Ljava/lang/String;");
    ri_typeID = env->GetFieldID(ri_class, "type", "I");
    ri_metricID = env->GetFieldID(ri_class, "metric", "I");
    ri_defaultGwID = env->GetFieldID(ri_class, "defaultGw", "Z");
    ri_primaryGwID = env->GetFieldID(ri_class, "primaryGw", "Z");
    ri_ctor = env->GetMethodID(ri_class, "<init>", "()V");
}

JNIEXPORT jobjectArray JNICALL Java_net_RouteInfo_getRouteList(JNIEnv *env, jclass cls) {
    jobjectArray rt_list = NULL;

    route_list list = RouteInfo::getRouteList();
    if (!list.empty()) {
        jsize rt_count = (jsize)list.size();
        if (rt_count > 0) {
            rt_list = env->NewObjectArray(rt_count, ri_class, NULL);
            for (jsize i = 0; i < rt_count; i++) {
                RouteInfo info = list.at(i);
                jobject one_route = load_class(env, info);
                env->SetObjectArrayElement(rt_list, i, one_route);
            }
        }
        list.clear();
    }
    return rt_list;
}

JNIEXPORT jobject JNICALL Java_net_RouteInfo_getBestRoute(JNIEnv *env, jclass cls, jstring dest, jstring mask, jobjectArray table) {
    jobject one_route = NULL;

    String dest_value = jStringToString(env, dest);
    String mask_value = jStringToString(env, mask);

    route_list route_table;
    if (table != NULL) {
        jsize len = env->GetArrayLength(table);
        for (jsize i = 0; i < len; i++) {
            jobject rt_entry = env->GetObjectArrayElement(table, i);
            RouteInfo entry = load_value(env, rt_entry);
            route_table.push_back(entry);
        }
    }
    RouteInfo info = RouteInfo::getBestRoute(dest_value, mask_value, route_table);
    if (info.getIndex() >= 0) {
        one_route = load_class(env, info);
    }

    return one_route;
}

を実装します。


JNIのライブラリ(dll)をjninetで前のJNIライブラリと一つにまとめてしまったので、jStringToString()関数をそのままaddrinfo.cppからコピーして使うと、リンク時に同じ名前の関数が存在するということでエラーになってしまうため、
stringToJString()関数と共に、jninet.cppに移動して実装しました。
ちなみにjninet.cppは

String jStringToString(JNIEnv *env, jstring j_string) {
    const jchar *words = env->GetStringChars(j_string, NULL);
    _bstr_t orginal = _bstr_t((wchar_t *)words);
    env->ReleaseStringChars(j_string, words);
    String c_string = String((_TCHAR *)orginal);
    return c_string;
}

jstring stringToJString(JNIEnv *env, String c_string) {
    _bstr_t value = _bstr_t((_TCHAR *)c_string.c_str());
    jstring j_string = env->NewString((const jchar *)((wchar_t *)value), (jsize)value.length());
    return j_string;
}

が実装されています。
jStringToString()関数とstringToJString()関数は、以前作ったjniifinfoでも利用可能なので、後日そちらも修正しておきます。