// // jnibinding.c // libionu-xcode // // Created by Tim Beres on 9/7/12. // Copyright (c) 2012 Tim Beres. All rights reserved. // #include "K2jnibinding.h" #include #include #include #include #ifndef WIN32 #include #endif #include #include #ifndef WIN32 #include #endif #include "platform_binding.h" // we can't use ourself to debug ourself #undef SLDEBUG #undef DEBUG // comment out for debugging //#define DEBUG 1 #if defined(DEBUG) #if defined(ANDROID) #include #define SLDEBUG(...) __android_log_print(ANDROID_LOG_DEBUG, "[K2LOG]", __VA_ARGS__) #else #define SLDEBUG(...) fputs("[K2LOG]",stdout);printf(__VA_ARGS__);fflush(stdout); #endif #else #define SLDEBUG(...) #endif static JavaVM *_k2jvm; static jclass _k2cbClass; static jmethodID _k2cbMeth; /** * This is the callback supplied to K2_Init. * It's purpose is to send the info back into the JVM. */ static void _k2onMessage(const char* info) { SLDEBUG("_k2onMessage %s\n", info); JNIEnv *env; #if defined(ANDROID) jint rs = _k2jvm->AttachCurrentThread(&env, NULL); #else jint rs = _k2jvm->AttachCurrentThread(reinterpret_cast(&env), NULL); #endif assert (rs == JNI_OK); if (_k2cbMeth != 0){ jstring cbInfo = env->NewStringUTF(info); SLDEBUG("_k2onMessage invoking cbMeth %p\n", _k2cbMeth); env->CallStaticVoidMethod(_k2cbClass, _k2cbMeth, cbInfo, 0); } else { SLDEBUG("_k2onMessage drop message; null callback\n"); } SLDEBUG("_k2onMessage leave: %s\n", info); _k2jvm->DetachCurrentThread(); } /** * Using the global ref _k2cbClass, look for a given method name and save ref in _k2cbMeth. * We assume this MUST succeed or its a coding error. */ bool checkCallbackMethod(JNIEnv *env, const char* meth){ _k2cbMeth = env->GetStaticMethodID(_k2cbClass, meth, "(Ljava/lang/String;)V"); SLDEBUG("checkCallback have method %p\n", _k2cbMeth); if (_k2cbMeth != NULL){ // leads to: Invalid indirect reference 0x43955f98 in decodeIndirectRef // _k2cbMeth = env->NewGlobalRef(env, _k2cbMeth); } return _k2cbMeth != NULL; } /** * Try to find given 'clazz' name which is allowed not to be found. * If found set global ref. */ bool checkCallback(JNIEnv *env, const char* clazz, const char* meth){ SLDEBUG("checkCallback using JNIEnv %p look for class %s\n", env, clazz); _k2cbClass = env->FindClass(clazz); SLDEBUG("checkCallback have class %p\n", _k2cbClass); if (!_k2cbClass){ if (env->ExceptionCheck()) { SLDEBUG("Clear exception\n"); env->ExceptionClear(); } return false; } // Android GC moves sh*t around!!! _k2cbClass = static_cast(env->NewGlobalRef(_k2cbClass)); return checkCallbackMethod(env, meth); } /** * Try to setup to use SyncAndMessageDispatch as the handler in JVM. * Since CG also wants to pretend to be a client, this will gracefully fail and set refs * to NULL for that particular case. */ jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved) { JNIEnv *env; SLDEBUG("JNI_OnLoad\n"); vm->GetEnv (reinterpret_cast(&env), JNI_VERSION_1_6); //if (!checkCallback(env, "com/sequencelogic/cgsl/DataManager", "k2dataReceived")) { checkCallback(env, "com/sequencelogic/ijw/client/SyncAndMessageDispatch", "k2dataReceived"); } return JNI_VERSION_1_6; } /** * Initialize passing optional class loader object and the callback class * which will be SyncAndMessageDispatch or DataManager. Due to Tomcat class loading * issues, this may work where the OnLoad will fail. */ void JNICALL Java_com_sequencelogic_K2Client_initialize(JNIEnv *env, jobject obj, jobject webCL, jstring callbackClassNameDotted, jstring hosts, jstring urn) { const char* chost = env->GetStringUTFChars(hosts, 0); const char* curn = env->GetStringUTFChars(urn, 0); SLDEBUG("K2Client_initialize\n"); if (_k2cbMeth == 0 && webCL != 0){ SLDEBUG("Did not find callback class/method during onLoad; trying with class loader\n"); jclass classLoaderClass = env->GetObjectClass(webCL); jmethodID loadClassMethod = env->GetMethodID(classLoaderClass, "loadClass", "(Ljava/lang/String;)Ljava/lang/Class;"); // This needs dots because it's the arg for Java _k2cbClass = static_cast(env->CallObjectMethod(webCL, loadClassMethod, callbackClassNameDotted)); SLDEBUG("_k2cbClass have class %p\n", _k2cbClass); if (env->ExceptionCheck()) { SLDEBUG("Clear exception\n"); env->ExceptionClear(); } else { _k2cbClass = static_cast(env->NewGlobalRef(_k2cbClass)); checkCallbackMethod(env, "k2dataReceived"); } } jint rs = env->GetJavaVM(&_k2jvm); assert (rs == JNI_OK); // @todo how to log this SLDEBUG("Java_com_sequencelogic_K2Client_initialize %s %s\n", chost, curn); K2_Start ( curn, chost, _k2onMessage ); env->ReleaseStringUTFChars(urn, chost); env->ReleaseStringUTFChars(urn, curn); } void JNICALL Java_com_sequencelogic_K2Client_login(JNIEnv *env, jobject obj) { SLDEBUG("login/restart"); K2_Restart(); } void JNICALL Java_com_sequencelogic_K2Client_logout(JNIEnv *env, jobject obj) { K2_Stop(); } void JNICALL Java_com_sequencelogic_K2Client_shutdown(JNIEnv *env, jobject obj) { K2_Stop(); } void JNICALL Java_com_sequencelogic_K2Client_pollDB(JNIEnv *env, jobject obj) { K2_Poll_DB(); } jstring JNICALL Java_com_sequencelogic_K2Client_statistics(JNIEnv *env, jobject obj) { static const int SBUFSZ = 4096; char buf[SBUFSZ]; jstring result = NULL; *buf = 0; K2_GetStats(buf, SBUFSZ); //printf("Java_com_sequencelogic_K2Client_statistics result %d : %s\n", b, buf); if (*buf){ result = env->NewStringUTF(buf); } return result; } void JNICALL Java_com_sequencelogic_K2Client_statusSelect(JNIEnv *env, jobject obj, jstring offices) { const char* coffices = env->GetStringUTFChars(offices, 0); K2_StatusSelect(coffices); env->ReleaseStringUTFChars(offices, coffices); }