Sleds/K2Client/K2jnibinding.cpp

223 lines
5.9 KiB
C++

//
// jnibinding.c
// libionu-xcode
//
// Created by Tim Beres on 9/7/12.
// Copyright (c) 2012 Tim Beres. All rights reserved.
//
#include "K2jnibinding.h"
#include <stdio.h>
#include <stdlib.h>
#include <stdlib.h>
#include <string.h>
#ifndef WIN32
#include <stdbool.h>
#endif
#include <assert.h>
#include <fcntl.h>
#ifndef WIN32
#include <unistd.h>
#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 <android/log.h>
#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<void **>(&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<jclass>(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<void **>(&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<jclass>(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<jclass>(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);
}