#include "java.h"
#include
#include "javaObject.h"
#include "methodCallBaton.h"
#include
/*static*/ v8::Persistent<:functiontemplate> Java::s_ct;
/*static*/ void Java::Init(v8::Handle<:object> target) {
v8::HandleScope scope;
v8::Local<:functiontemplate> t = v8::FunctionTemplate::New(New);
s_ct = v8::Persistent<:functiontemplate>::New(t);
s_ct->InstanceTemplate()->SetInternalFieldCount(1);
s_ct->SetClassName(v8::String::NewSymbol("Java"));
NODE_SET_PROTOTYPE_METHOD(s_ct, "newInstance", newInstance);
NODE_SET_PROTOTYPE_METHOD(s_ct, "newInstanceSync", newInstanceSync);
NODE_SET_PROTOTYPE_METHOD(s_ct, "callStaticMethod", callStaticMethod);
NODE_SET_PROTOTYPE_METHOD(s_ct, "callStaticMethodSync", callStaticMethodSync);
NODE_SET_PROTOTYPE_METHOD(s_ct, "findClassSync", findClassSync);
NODE_SET_PROTOTYPE_METHOD(s_ct, "newArray", newArray);
NODE_SET_PROTOTYPE_METHOD(s_ct, "newByte", newByte);
NODE_SET_PROTOTYPE_METHOD(s_ct, "getStaticFieldValue", getStaticFieldValue);
NODE_SET_PROTOTYPE_METHOD(s_ct, "setStaticFieldValue", setStaticFieldValue);
target->Set(v8::String::NewSymbol("Java"), s_ct->GetFunction());
}
/*static*/ v8::Handle<:value> Java::New(const v8::Arguments& args) {
v8::HandleScope scope;
Java *self = new Java();
self->Wrap(args.This());
self->handle_->Set(v8::String::New("classpath"), v8::Array::New());
self->handle_->Set(v8::String::New("options"), v8::Array::New());
return args.This();
}
Java::Java() {
this->m_jvm = NULL;
this->m_env = NULL;
}
Java::~Java() {
}
v8::Handle<:value> Java::ensureJvm() {
if(!m_jvm) {
return createJVM(&this->m_jvm, &this->m_env);
}
return v8::Undefined();
}
v8::Handle<:value> Java::createJVM(JavaVM** jvm, JNIEnv** env) {
JavaVM* jvmTemp;
JavaVMInitArgs args;
// setup classpath
std::ostringstream classPath;
classPath << "-Djava.class.path=";
v8::Local<:value> classPathValue = handle_->Get(v8::String::New("classpath"));
if(!classPathValue->IsArray()) {
return ThrowException(v8::Exception::TypeError(v8::String::New("Classpath must be an array")));
}
v8::Local<:array> classPathArray = v8::Array::Cast(*classPathValue);
for(uint32_t i=0; iLength(); i++) {
if(i != 0) {
#ifdef WIN32
classPath << ";";
#else
classPath << ":";
#endif
}
v8::Local<:value> arrayItemValue = classPathArray->Get(i);
if(!arrayItemValue->IsString()) {
return ThrowException(v8::Exception::TypeError(v8::String::New("Classpath must only contain strings")));
}
v8::Local<:string> arrayItem = arrayItemValue->ToString();
v8::String::AsciiValue arrayItemStr(arrayItem);
classPath << *arrayItemStr;
}
// get other options
v8::Local<:value> optionsValue = handle_->Get(v8::String::New("options"));
if(!optionsValue->IsArray()) {
return ThrowException(v8::Exception::TypeError(v8::String::New("options must be an array")));
}
v8::Local<:array> optionsArray = v8::Array::Cast(*optionsValue);
// create vm options
int vmOptionsCount = optionsArray->Length() + 1;
JavaVMOption* vmOptions = new JavaVMOption[vmOptionsCount];
vmOptions[0].optionString = strdup(classPath.str().c_str());
for(uint32_t i=0; iLength(); i++) {
v8::Local<:value> arrayItemValue = optionsArray->Get(i);
if(!arrayItemValue->IsString()) {
return ThrowException(v8::Exception::TypeError(v8::String::New("options must only contain strings")));
}
v8::Local<:string> arrayItem = arrayItemValue->ToString();
v8::String::AsciiValue arrayItemStr(arrayItem);
vmOptions[i+1].optionString = strdup(*arrayItemStr);
}
JNI_GetDefaultJavaVMInitArgs(&args);
args.version = JNI_VERSION_1_6;
args.ignoreUnrecognized = false;
args.options = vmOptions;
args.nOptions = vmOptionsCount;
JNI_CreateJavaVM(&jvmTemp, (void **)env, &args);
*jvm = jvmTemp;
return v8::Undefined();
}
/*static*/ v8::Handle<:value> Java::newInstance(const v8::Arguments& args) {
v8::HandleScope scope;
Java* self = node::ObjectWrap::Unwrap(args.This());
v8::Handle<:value> ensureJvmResults = self->ensureJvm();
if(!ensureJvmResults->IsUndefined()) {
return ensureJvmResults;
}
JNIEnv* env = self->getJavaEnv();
int argsStart = 0;
int argsEnd = args.Length();
// arguments
ARGS_FRONT_CLASSNAME();
ARGS_BACK_CALLBACK();
// find class
jclass clazz = javaFindClass(env, className);
if(clazz == NULL) {
EXCEPTION_CALL_CALLBACK("Could not find class " << className.c_str());
return v8::Undefined();
}
// get method
jobjectArray methodArgs = v8ToJava(env, args, argsStart, argsEnd);
jobject method = javaFindConstructor(env, clazz, methodArgs);
if(method == NULL) {
EXCEPTION_CALL_CALLBACK("Could not find constructor");
return v8::Undefined();
}
// run
NewInstanceBaton* baton = new NewInstanceBaton(self, clazz, method, methodArgs, callback);
baton->run();
END_CALLBACK_FUNCTION("\"Constructor for class '" << className << "' called without a callback did you mean to use the Sync version?\"");
}
/*static*/ v8::Handle<:value> Java::newInstanceSync(const v8::Arguments& args) {
v8::HandleScope scope;
Java* self = node::ObjectWrap::Unwrap(args.This());
v8::Handle<:value> ensureJvmResults = self->ensureJvm();
if(!ensureJvmResults->IsUndefined()) {
return ensureJvmResults;
}
JNIEnv* env = self->getJavaEnv();
int argsStart = 0;
int argsEnd = args.Length();
// arguments
ARGS_FRONT_CLASSNAME();
// find class
jclass clazz = javaFindClass(env, className);
if(clazz == NULL) {
std::ostringstream errStr;
errStr << "Could not create class " << className.c_str();
return ThrowException(javaExceptionToV8(env, errStr.str()));
}
// find method
jobjectArray methodArgs = v8ToJava(env, args, argsStart, argsEnd);
jobject method = javaFindConstructor(env, clazz, methodArgs);
if(method == NULL) {
std::ostringstream errStr;
errStr << "Could not find constructor";
return ThrowException(javaExceptionToV8(env, errStr.str()));
}
// run
v8::Handle<:value> callback = v8::Object::New();
NewInstanceBaton* baton = new NewInstanceBaton(self, clazz, method, methodArgs, callback);
v8::Handle<:value> result = baton->runSync();
delete baton;
if(result->IsNativeError()) {
return ThrowException(result);
}
return scope.Close(result);
}
/*static*/ v8::Handle<:value> Java::callStaticMethod(const v8::Arguments& args) {
v8::HandleScope scope;
Java* self = node::ObjectWrap::Unwrap(args.This());
v8::Handle<:value> ensureJvmResults = self->ensureJvm();
if(!ensureJvmResults->IsUndefined()) {
return ensureJvmResults;
}
JNIEnv* env = self->getJavaEnv();
int argsStart = 0;
int argsEnd = args.Length();
// arguments
ARGS_FRONT_CLASSNAME();
ARGS_FRONT_STRING(methodName);
ARGS_BACK_CALLBACK();
// find class
jclass clazz = javaFindClass(env, className);
if(clazz == NULL) {
EXCEPTION_CALL_CALLBACK("Could not create class " << className.c_str());
return v8::Undefined();
}
// find method
jobjectArray methodArgs = v8ToJava(env, args, argsStart, argsEnd);
jobject method = javaFindMethod(env, clazz, methodName, methodArgs);
if(method == NULL) {
EXCEPTION_CALL_CALLBACK("Could not find method \"" << methodName.c_str() << "\"");
return v8::Undefined();
}
// run
StaticMethodCallBaton* baton = new StaticMethodCallBaton(self, clazz, method, methodArgs, callback);
baton->run();
END_CALLBACK_FUNCTION("\"Static method '" << methodName << "' called without a callback did you mean to use the Sync version?\"");
}
/*static*/ v8::Handle<:value> Java::callStaticMethodSync(const v8::Arguments& args) {
v8::HandleScope scope;
Java* self = node::ObjectWrap::Unwrap(args.This());
v8::Handle<:value> ensureJvmResults = self->ensureJvm();
if(!ensureJvmResults->IsUndefined()) {
return ensureJvmResults;
}
JNIEnv* env = self->getJavaEnv();
int argsStart = 0;
int argsEnd = args.Length();
// arguments
ARGS_FRONT_CLASSNAME();
ARGS_FRONT_STRING(methodName);
// find class
jclass clazz = javaFindClass(env, className);
if(clazz == NULL) {
std::ostringstream errStr;
errStr << "Could not create class " << className.c_str();
return ThrowException(javaExceptionToV8(env, errStr.str()));
}
// find method
jobjectArray methodArgs = v8ToJava(env, args, argsStart, argsEnd);
jobject method = javaFindMethod(env, clazz, methodName, methodArgs);
if(method == NULL) {
std::ostringstream errStr;
errStr << "Could not find method \"" << methodName.c_str() << "\"";
return ThrowException(javaExceptionToV8(env, errStr.str()));
}
// run
v8::Handle<:value> callback = v8::Object::New();
StaticMethodCallBaton* baton = new StaticMethodCallBaton(self, clazz, method, methodArgs, callback);
v8::Handle<:value> result = baton->runSync();
delete baton;
if(result->IsNativeError()) {
return ThrowException(result);
}
return scope.Close(result);
}
/*static*/ v8::Handle<:value> Java::findClassSync(const v8::Arguments& args) {
v8::HandleScope scope;
Java* self = node::ObjectWrap::Unwrap(args.This());
v8::Handle<:value> ensureJvmResults = self->ensureJvm();
if(!ensureJvmResults->IsUndefined()) {
return ensureJvmResults;
}
JNIEnv* env = self->getJavaEnv();
int argsStart = 0;
int argsEnd = args.Length();
// arguments
ARGS_FRONT_CLASSNAME();
// find class
jclass clazz = javaFindClass(env, className);
if(clazz == NULL) {
std::ostringstream errStr;
errStr << "Could not create class " << className.c_str();
return ThrowException(javaExceptionToV8(env, errStr.str()));
}
// run
v8::Handle<:value> result = javaToV8(self, env, clazz);
return scope.Close(result);
}
/*static*/ v8::Handle<:value> Java::newArray(const v8::Arguments& args) {
v8::HandleScope scope;
Java* self = node::ObjectWrap::Unwrap(args.This());
v8::Handle<:value> ensureJvmResults = self->ensureJvm();
if(!ensureJvmResults->IsUndefined()) {
return ensureJvmResults;
}
JNIEnv* env = self->getJavaEnv();
int argsStart = 0;
int argsEnd = args.Length();
// arguments
ARGS_FRONT_CLASSNAME();
// argument - array
if(args.Length() < argsStart+1 || !args[argsStart]->IsArray()) {
std::ostringstream errStr;
errStr << "Argument " << (argsStart+1) << " must be an array";
return ThrowException(v8::Exception::TypeError(v8::String::New(errStr.str().c_str())));
}
v8::Local<:array> arrayObj = v8::Local<:array>::Cast(args[argsStart]);
UNUSED_VARIABLE(argsEnd);
// find class and method
jarray results;
if(strcmp(className.c_str(), "byte") == 0) {
results = env->NewByteArray(arrayObj->Length());
for(uint32_t i=0; iLength(); i++) {
v8::Local<:value> item = arrayObj->Get(i);
jobject val = v8ToJava(env, item);
jclass byteClazz = env->FindClass("java/lang/Byte");
jmethodID byte_byteValue = env->GetMethodID(byteClazz, "byteValue", "()B");
jbyte byteValues[1];
byteValues[0] = env->CallByteMethod(val, byte_byteValue);
env->SetByteArrayRegion((jbyteArray)results, i, 1, byteValues);
}
}
else
{
jclass clazz = javaFindClass(env, className);
if(clazz == NULL) {
std::ostringstream errStr;
errStr << "Could not create class " << className.c_str();
return ThrowException(javaExceptionToV8(env, errStr.str()));
}
// create array
results = env->NewObjectArray(arrayObj->Length(), clazz, NULL);
for(uint32_t i=0; iLength(); i++) {
v8::Local<:value> item = arrayObj->Get(i);
jobject val = v8ToJava(env, item);
env->SetObjectArrayElement((jobjectArray)results, i, val);
if(env->ExceptionOccurred()) {
std::ostringstream errStr;
v8::String::AsciiValue valStr(item);
errStr << "Could not add item \"" << *valStr << "\" to array.";
return ThrowException(javaExceptionToV8(env, errStr.str()));
}
}
}
return scope.Close(JavaObject::New(self, results));
}
/*static*/ v8::Handle<:value> Java::newByte(const v8::Arguments& args) {
v8::HandleScope scope;
Java* self = node::ObjectWrap::Unwrap(args.This());
v8::Handle<:value> ensureJvmResults = self->ensureJvm();
if(!ensureJvmResults->IsUndefined()) {
return ensureJvmResults;
}
JNIEnv* env = self->getJavaEnv();
if(args.Length() != 1) {
return ThrowException(v8::Exception::TypeError(v8::String::New("newByte only takes 1 argument")));
}
// argument - value
if(!args[0]->IsNumber()) {
return ThrowException(v8::Exception::TypeError(v8::String::New("Argument 1 must be a number")));
}
v8::Local<:number> val = args[0]->ToNumber();
jclass clazz = env->FindClass("java/lang/Byte");
jmethodID constructor = env->GetMethodID(clazz, "", "(B)V");
jobject newObj = env->NewObject(clazz, constructor, (jbyte)val->Value());
return scope.Close(JavaObject::New(self, newObj));
}
/*static*/ v8::Handle<:value> Java::getStaticFieldValue(const v8::Arguments& args) {
v8::HandleScope scope;
Java* self = node::ObjectWrap::Unwrap(args.This());
v8::Handle<:value> ensureJvmResults = self->ensureJvm();
if(!ensureJvmResults->IsUndefined()) {
return ensureJvmResults;
}
JNIEnv* env = self->getJavaEnv();
int argsStart = 0;
int argsEnd = args.Length();
// arguments
ARGS_FRONT_CLASSNAME();
ARGS_FRONT_STRING(fieldName);
UNUSED_VARIABLE(argsEnd);
// find the class
jclass clazz = javaFindClass(env, className);
if(clazz == NULL) {
std::ostringstream errStr;
errStr << "Could not create class " << className.c_str();
return ThrowException(javaExceptionToV8(env, errStr.str()));
}
// get the field
jobject field = javaFindField(env, clazz, fieldName);
if(field == NULL) {
std::ostringstream errStr;
errStr << "Could not find field " << fieldName.c_str() << " on class " << className.c_str();
return ThrowException(javaExceptionToV8(env, errStr.str()));
}
jclass fieldClazz = env->FindClass("java/lang/reflect/Field");
jmethodID field_get = env->GetMethodID(fieldClazz, "get", "(Ljava/lang/Object;)Ljava/lang/Object;");
// get field value
jobject val = env->CallObjectMethod(field, field_get, NULL);
if(env->ExceptionOccurred()) {
std::ostringstream errStr;
errStr << "Could not get field " << fieldName.c_str() << " on class " << className.c_str();
return ThrowException(javaExceptionToV8(env, errStr.str()));
}
return scope.Close(javaToV8(self, env, val));
}
/*static*/ v8::Handle<:value> Java::setStaticFieldValue(const v8::Arguments& args) {
v8::HandleScope scope;
Java* self = node::ObjectWrap::Unwrap(args.This());
v8::Handle<:value> ensureJvmResults = self->ensureJvm();
if(!ensureJvmResults->IsUndefined()) {
return ensureJvmResults;
}
JNIEnv* env = self->getJavaEnv();
int argsStart = 0;
int argsEnd = args.Length();
// arguments
ARGS_FRONT_CLASSNAME();
ARGS_FRONT_STRING(fieldName);
// argument - new value
if(args.Length() < argsStart+1) {
std::ostringstream errStr;
errStr << "setStaticFieldValue requires " << (argsStart+1) << " arguments";
return ThrowException(v8::Exception::TypeError(v8::String::New(errStr.str().c_str())));
}
jobject newValue = v8ToJava(env, args[argsStart]);
argsStart++;
UNUSED_VARIABLE(argsEnd);
// find the class
jclass clazz = javaFindClass(env, className);
if(clazz == NULL) {
std::ostringstream errStr;
errStr << "Could not create class " << className.c_str();
return ThrowException(javaExceptionToV8(env, errStr.str()));
}
// get the field
jobject field = javaFindField(env, clazz, fieldName);
if(field == NULL) {
std::ostringstream errStr;
errStr << "Could not find field " << fieldName.c_str() << " on class " << className.c_str();
return ThrowException(javaExceptionToV8(env, errStr.str()));
}
jclass fieldClazz = env->FindClass("java/lang/reflect/Field");
jmethodID field_set = env->GetMethodID(fieldClazz, "set", "(Ljava/lang/Object;Ljava/lang/Object;)V");
//printf("newValue: %s\n", javaObjectToString(env, newValue).c_str());
// set field value
env->CallObjectMethod(field, field_set, NULL, newValue);
if(env->ExceptionOccurred()) {
std::ostringstream errStr;
errStr << "Could not set field " << fieldName.c_str() << " on class " << className.c_str();
return ThrowException(javaExceptionToV8(env, errStr.str()));
}
return v8::Undefined();
}