Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

call static and classpath

  • Loading branch information...
commit fc32367746088ad6b2fced1bff0f3a623a8a75e8 1 parent 4bc56fc
@joeferner authored
View
2  README.md
@@ -10,6 +10,8 @@ Bridge API to connect with existing Java APIs.
```javascript
var java = require("nodejavabridge");
+java.classpath.push("commons-lang3-3.1.jar");
+java.classpath.push("commons-io.jar");
java.newInstance("java.util.ArrayList", function(list) {
// you have a list
View
140 src/java.cpp
@@ -3,6 +3,7 @@
#include <string.h>
#include "javaObject.h"
#include "methodCallBaton.h"
+#include <sstream>
/*static*/ v8::Persistent<v8::FunctionTemplate> Java::s_ct;
@@ -16,6 +17,8 @@
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);
target->Set(v8::String::NewSymbol("Java"), s_ct->GetFunction());
}
@@ -25,36 +28,79 @@
Java *self = new Java();
self->Wrap(args.This());
+
+ self->handle_->Set(v8::String::New("classpath"), v8::Array::New());
+
return args.This();
}
Java::Java() {
this->m_jvm = NULL;
this->m_env = NULL;
- createJVM(&this->m_jvm, &this->m_env);
}
Java::~Java() {
}
-/*static*/ void Java::createJVM(JavaVM** jvm, JNIEnv** env) {
+v8::Handle<v8::Value> Java::ensureJvm() {
+ if(!m_jvm) {
+ return createJVM(&this->m_jvm, &this->m_env);
+ }
+
+ return v8::Undefined();
+}
+
+v8::Handle<v8::Value> Java::createJVM(JavaVM** jvm, JNIEnv** env) {
JavaVM* jvmTemp;
JavaVMInitArgs args;
- args.version = JNI_VERSION_1_4;
+ std::ostringstream classPath;
+ classPath << "-Djava.class.path=";
+
+ v8::Local<v8::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<v8::Array> classPathArray = v8::Array::Cast(*classPathValue);
+ for(uint32_t i=0; i<classPathArray->Length(); i++) {
+ if(i != 0) {
+ classPath << ":"; // TODO: figure out path seperator
+ }
+ v8::Local<v8::Value> arrayItemValue = classPathArray->Get(i);
+ if(!arrayItemValue->IsString()) {
+ return ThrowException(v8::Exception::TypeError(v8::String::New("Classpath must only contain strings")));
+ }
+ v8::Local<v8::String> arrayItem = arrayItemValue->ToString();
+ v8::String::AsciiValue arrayItemStr(arrayItem);
+ classPath << *arrayItemStr;
+ }
+
+ JavaVMOption options[1];
+ options[0].optionString = strdup(classPath.str().c_str());
+
JNI_GetDefaultJavaVMInitArgs(&args);
+ args.version = JNI_VERSION_1_6;
+ args.ignoreUnrecognized = false;
+ args.options = options;
+ args.nOptions = 1;
JNI_CreateJavaVM(&jvmTemp, (void **)env, &args);
*jvm = jvmTemp;
+
+ return v8::Undefined();
}
/*static*/ v8::Handle<v8::Value> Java::newInstance(const v8::Arguments& args) {
v8::HandleScope scope;
Java* self = node::ObjectWrap::Unwrap<Java>(args.This());
+ v8::Handle<v8::Value> ensureJvmResults = self->ensureJvm();
+ if(!ensureJvmResults->IsUndefined()) {
+ return ensureJvmResults;
+ }
JNIEnv* env = self->getJavaEnv();
- int argsEnd = args.Length();
-
+ int argsEnd = args.Length();
+
// argument - className
if(args.Length() < 1 || !args[0]->IsString()) {
return ThrowException(v8::Exception::TypeError(v8::String::New("Argument 0 must be a string")));
@@ -73,12 +119,17 @@ Java::~Java() {
}
std::list<int> methodArgTypes;
- jarray methodArgs = v8ToJava(env, args, 1, argsEnd, &methodArgTypes);
-
+ jarray methodArgs = v8ToJava(env, args, 1, argsEnd, &methodArgTypes);
+
jclass clazz = javaFindClass(env, className);
- std::list<jobject> constructors = javaReflectionGetConstructors(env, clazz);
+ if(clazz == NULL) {
+ std::ostringstream errStr;
+ errStr << "Could not create class " << className.c_str();
+ return javaExceptionToV8(env, errStr.str());
+ }
+ std::list<jobject> constructors = javaReflectionGetConstructors(env, clazz);
jobject method = javaFindBestMatchingConstructor(env, constructors, methodArgTypes);
-
+
// run
NewInstanceBaton* baton = new NewInstanceBaton(self, clazz, method, methodArgs, callback);
baton->run();
@@ -89,10 +140,14 @@ Java::~Java() {
/*static*/ v8::Handle<v8::Value> Java::newInstanceSync(const v8::Arguments& args) {
v8::HandleScope scope;
Java* self = node::ObjectWrap::Unwrap<Java>(args.This());
+ v8::Handle<v8::Value> ensureJvmResults = self->ensureJvm();
+ if(!ensureJvmResults->IsUndefined()) {
+ return ensureJvmResults;
+ }
JNIEnv* env = self->getJavaEnv();
- int argsEnd = args.Length();
-
+ int argsEnd = args.Length();
+
// argument - className
if(args.Length() < 1 || !args[0]->IsString()) {
return ThrowException(v8::Exception::TypeError(v8::String::New("Argument 0 must be a string")));
@@ -105,9 +160,14 @@ Java::~Java() {
jarray methodArgs = v8ToJava(env, args, 1, argsEnd, &methodArgTypes);
jclass clazz = javaFindClass(env, className);
- std::list<jobject> constructors = javaReflectionGetConstructors(env, clazz);
+ if(clazz == NULL) {
+ std::ostringstream errStr;
+ errStr << "Could not create class " << className.c_str();
+ return javaExceptionToV8(env, errStr.str());
+ }
+ std::list<jobject> constructors = javaReflectionGetConstructors(env, clazz);
jobject method = javaFindBestMatchingConstructor(env, constructors, methodArgTypes);
-
+
// run
v8::Handle<v8::Value> callback = v8::Object::New();
NewInstanceBaton* baton = new NewInstanceBaton(self, clazz, method, methodArgs, callback);
@@ -115,3 +175,57 @@ Java::~Java() {
delete baton;
return scope.Close(result);
}
+
+/*static*/ v8::Handle<v8::Value> Java::callStaticMethod(const v8::Arguments& args) {
+ // TODO: write me
+ return v8::Undefined();
+}
+
+/*static*/ v8::Handle<v8::Value> Java::callStaticMethodSync(const v8::Arguments& args) {
+ v8::HandleScope scope;
+ Java* self = node::ObjectWrap::Unwrap<Java>(args.This());
+ v8::Handle<v8::Value> ensureJvmResults = self->ensureJvm();
+ if(!ensureJvmResults->IsUndefined()) {
+ return ensureJvmResults;
+ }
+ JNIEnv* env = self->getJavaEnv();
+
+ int argsEnd = args.Length();
+
+ // argument - className
+ if(args.Length() < 1 || !args[0]->IsString()) {
+ return ThrowException(v8::Exception::TypeError(v8::String::New("Argument 0 must be a string")));
+ }
+ v8::Local<v8::String> classNameObj = v8::Local<v8::String>::Cast(args[0]);
+ v8::String::AsciiValue classNameVal(classNameObj);
+ std::string className = *classNameVal;
+
+ // argument - method name
+ if(args.Length() < 2 || !args[1]->IsString()) {
+ return ThrowException(v8::Exception::TypeError(v8::String::New("Argument 1 must be a string")));
+ }
+ v8::Local<v8::String> methodNameObj = v8::Local<v8::String>::Cast(args[1]);
+ v8::String::AsciiValue methodNameVal(methodNameObj);
+ std::string methodName = *methodNameVal;
+
+ // build args
+ std::list<int> methodArgTypes;
+ jarray methodArgs = v8ToJava(env, args, 2, argsEnd, &methodArgTypes);
+
+ // find class and method
+ jclass clazz = javaFindClass(env, className);
+ if(clazz == NULL) {
+ std::ostringstream errStr;
+ errStr << "Could not create class " << className.c_str();
+ return javaExceptionToV8(env, errStr.str());
+ }
+ std::list<jobject> staticMethods = javaReflectionGetStaticMethods(env, clazz);
+ jobject method = javaFindBestMatchingMethod(env, staticMethods, methodName.c_str(), methodArgTypes);
+
+ // run
+ v8::Handle<v8::Value> callback = v8::Object::New();
+ StaticMethodCallBaton* baton = new StaticMethodCallBaton(self, clazz, method, methodArgs, callback);
+ v8::Handle<v8::Value> result = baton->runSync();
+ delete baton;
+ return scope.Close(result);
+}
View
8 src/java.h
@@ -16,15 +16,19 @@ class Java : public node::ObjectWrap {
private:
Java();
~Java();
- static void createJVM(JavaVM** jvm, JNIEnv** env);
+ v8::Handle<v8::Value> createJVM(JavaVM** jvm, JNIEnv** env);
static v8::Handle<v8::Value> New(const v8::Arguments& args);
static v8::Handle<v8::Value> newInstance(const v8::Arguments& args);
static v8::Handle<v8::Value> newInstanceSync(const v8::Arguments& args);
-
+ static v8::Handle<v8::Value> callStaticMethod(const v8::Arguments& args);
+ static v8::Handle<v8::Value> callStaticMethodSync(const v8::Arguments& args);
+ v8::Handle<v8::Value> ensureJvm();
+
static v8::Persistent<v8::FunctionTemplate> s_ct;
JavaVM* m_jvm;
JNIEnv* m_env;
+ std::string m_classPath;
};
#endif
View
2  src/javaObject.cpp
@@ -40,6 +40,8 @@
javaObjectObj->Set(methodNameSync, methodCallSyncTemplate->GetFunction());
}
+ // TODO: add public field support
+
return scope.Close(javaObjectObj);
}
View
55 src/methodCallBaton.cpp
@@ -58,6 +58,14 @@ void MethodCallBaton::after(JNIEnv *env) {
}
v8::Handle<v8::Value> MethodCallBaton::resultsToV8(JNIEnv *env) {
+ v8::HandleScope scope;
+
+ if(!m_error.IsEmpty() && !m_error->IsNull()) {
+ v8::Handle<v8::Value> err = m_error;
+ m_error.Dispose();
+ return scope.Close(err);
+ }
+
switch(m_resultType) {
case TYPE_VOID:
return v8::Undefined();
@@ -66,19 +74,26 @@ v8::Handle<v8::Value> MethodCallBaton::resultsToV8(JNIEnv *env) {
jclass booleanClazz = env->FindClass("java/lang/Boolean");
jmethodID boolean_booleanValue = env->GetMethodID(booleanClazz, "booleanValue", "()Z");
bool result = env->CallBooleanMethod(m_result, boolean_booleanValue);
- return v8::Boolean::New(result);
+ return scope.Close(v8::Boolean::New(result));
+ }
+ case TYPE_LONG:
+ {
+ jclass longClazz = env->FindClass("java/lang/Long");
+ jmethodID long_longValue = env->GetMethodID(longClazz, "longValue", "()J");
+ jlong result = env->CallLongMethod(m_result, long_longValue);
+ return scope.Close(v8::Number::New(result));
}
case TYPE_INT:
{
jclass integerClazz = env->FindClass("java/lang/Integer");
jmethodID integer_intValue = env->GetMethodID(integerClazz, "intValue", "()I");
- int result = env->CallIntMethod(m_result, integer_intValue);
- return v8::Integer::New(result);
+ jint result = env->CallIntMethod(m_result, integer_intValue);
+ return scope.Close(v8::Integer::New(result));
}
case TYPE_OBJECT:
- return JavaObject::New(m_java, m_result);
+ return scope.Close(JavaObject::New(m_java, m_result));
case TYPE_STRING:
- return v8::String::New(javaObjectToString(env, m_result).c_str());
+ return scope.Close(v8::String::New(javaObjectToString(env, m_result).c_str()));
}
return v8::Undefined();
}
@@ -96,6 +111,21 @@ void NewInstanceBaton::execute(JNIEnv *env) {
}
}
+void StaticMethodCallBaton::execute(JNIEnv *env) {
+ jclass methodClazz = env->FindClass("java/lang/reflect/Method");
+ jmethodID method_invoke = env->GetMethodID(methodClazz, "invoke", "(Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;");
+ jmethodID method_getReturnType = env->GetMethodID(methodClazz, "getReturnType", "()Ljava/lang/Class;");
+
+ jclass returnType = (jclass)env->CallObjectMethod(m_method, method_getReturnType);
+
+ m_resultType = javaGetType(env, returnType);
+ jobject result = env->CallObjectMethod(m_method, method_invoke, NULL, m_args);
+ m_result = env->NewGlobalRef(result);
+ if(env->ExceptionCheck()) {
+ m_error = v8::Persistent<v8::Value>::New(javaExceptionToV8(env, "Error running method"));
+ }
+}
+
void InstanceMethodCallBaton::execute(JNIEnv *env) {
jclass methodClazz = env->FindClass("java/lang/reflect/Method");
jmethodID method_invoke = env->GetMethodID(methodClazz, "invoke", "(Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;");
@@ -127,6 +157,21 @@ NewInstanceBaton::~NewInstanceBaton() {
env->DeleteGlobalRef(m_clazz);
}
+StaticMethodCallBaton::StaticMethodCallBaton(
+ Java* java,
+ jclass clazz,
+ jobject method,
+ jarray args,
+ v8::Handle<v8::Value>& callback) : MethodCallBaton(java, method, args, callback) {
+ JNIEnv *env = m_java->getJavaEnv();
+ m_clazz = (jclass)env->NewGlobalRef(clazz);
+}
+
+StaticMethodCallBaton::~StaticMethodCallBaton() {
+ JNIEnv *env = m_java->getJavaEnv();
+ env->DeleteGlobalRef(m_clazz);
+}
+
InstanceMethodCallBaton::InstanceMethodCallBaton(
Java* java,
JavaObject* obj,
View
22 src/methodCallBaton.h
@@ -20,14 +20,15 @@ class MethodCallBaton {
static int EIO_AfterMethodCall(eio_req* req);
void run();
v8::Handle<v8::Value> runSync();
-
+
protected:
virtual void execute(JNIEnv *env) = 0;
virtual void after(JNIEnv *env);
v8::Handle<v8::Value> resultsToV8(JNIEnv *env);
-
+
Java* m_java;
v8::Persistent<v8::Value> m_callback;
+ v8::Persistent<v8::Value> m_error;
jarray m_args;
jobject m_result;
jobject m_method;
@@ -41,7 +42,7 @@ class InstanceMethodCallBaton : public MethodCallBaton {
protected:
virtual void execute(JNIEnv *env);
-
+
JavaObject* m_javaObject;
};
@@ -49,10 +50,21 @@ class NewInstanceBaton : public MethodCallBaton {
public:
NewInstanceBaton(Java* java, jclass clazz, jobject method, jarray args, v8::Handle<v8::Value>& callback);
virtual ~NewInstanceBaton();
-
+
protected:
virtual void execute(JNIEnv *env);
-
+
+ jclass m_clazz;
+};
+
+class StaticMethodCallBaton : public MethodCallBaton {
+public:
+ StaticMethodCallBaton(Java* java, jclass clazz, jobject method, jarray args, v8::Handle<v8::Value>& callback);
+ virtual ~StaticMethodCallBaton();
+
+protected:
+ virtual void execute(JNIEnv *env);
+
jclass m_clazz;
};
View
37 src/utils.cpp
@@ -8,6 +8,23 @@ std::list<jobject> javaReflectionGetMethods(JNIEnv *env, jclass clazz) {
jclass clazzclazz = env->GetObjectClass(clazz);
jmethodID methodId = env->GetMethodID(clazzclazz, "getMethods", "()[Ljava/lang/reflect/Method;");
+ // TODO: filter out static and prive methods
+ jobjectArray methodObjects = (jobjectArray)env->CallObjectMethod(clazz, methodId);
+ jsize methodCount = env->GetArrayLength(methodObjects);
+ for(jsize i=0; i<methodCount; i++) {
+ jobject obj = env->GetObjectArrayElement(methodObjects, i);
+ results.push_back(obj);
+ }
+
+ return results;
+}
+
+std::list<jobject> javaReflectionGetStaticMethods(JNIEnv *env, jclass clazz) {
+ std::list<jobject> results;
+
+ jclass clazzclazz = env->GetObjectClass(clazz);
+ jmethodID methodId = env->GetMethodID(clazzclazz, "getDeclaredMethods", "()[Ljava/lang/reflect/Method;");
+ // TODO: filter out instance and prive methods
jobjectArray methodObjects = (jobjectArray)env->CallObjectMethod(clazz, methodId);
jsize methodCount = env->GetArrayLength(methodObjects);
for(jsize i=0; i<methodCount; i++) {
@@ -109,6 +126,8 @@ jvalueType javaGetType(JNIEnv *env, jclass type) {
//printf("%s\n", typeStr);
if(strcmp(typeStr, "int") == 0) {
return TYPE_INT;
+ } else if(strcmp(typeStr, "long") == 0) {
+ return TYPE_LONG;
} else if(strcmp(typeStr, "void") == 0) {
return TYPE_VOID;
} else if(strcmp(typeStr, "boolean") == 0) {
@@ -123,10 +142,6 @@ jvalueType javaGetType(JNIEnv *env, jclass type) {
jclass javaFindClass(JNIEnv* env, std::string className) {
std::replace(className.begin(), className.end(), '.', '/');
jclass clazz = env->FindClass(className.c_str());
- if(env->ExceptionCheck()) {
- env->ExceptionDescribe(); // TODO: handle error
- return NULL;
- }
return clazz;
}
@@ -149,7 +164,7 @@ jarray v8ToJava(JNIEnv* env, const v8::Arguments& args, int start, int end, std:
for(int i=start; i<end; i++) {
int methodArgType;
jobject val = v8ToJava(env, args[i], &methodArgType);
- env->SetObjectArrayElement(results, i, val);
+ env->SetObjectArrayElement(results, i - start, val);
if(methodArgTypes) {
methodArgTypes->push_back(methodArgType);
}
@@ -157,3 +172,15 @@ jarray v8ToJava(JNIEnv* env, const v8::Arguments& args, int start, int end, std:
return results;
}
+
+v8::Handle<v8::Value> javaExceptionToV8(JNIEnv* env, const std::string& alternateMessage) {
+ jthrowable ex = env->ExceptionOccurred();
+ if(ex) {
+ printf("BEGIN Java Exception -------\n");
+ env->ExceptionDescribe(); // TODO: handle error
+ printf("END Java Exception ---------\n");
+ return ThrowException(v8::Exception::TypeError(v8::String::New("java exception")));
+ } else {
+ return ThrowException(v8::Exception::TypeError(v8::String::New(alternateMessage.c_str())));
+ }
+}
View
13 src/utils.h
@@ -8,15 +8,17 @@
#include <string>
typedef enum _jvalueType {
- TYPE_VOID,
- TYPE_INT,
- TYPE_OBJECT,
- TYPE_STRING,
- TYPE_BOOLEAN
+ TYPE_VOID = 1,
+ TYPE_INT = 2,
+ TYPE_LONG = 3,
+ TYPE_OBJECT = 4,
+ TYPE_STRING = 5,
+ TYPE_BOOLEAN = 6
} jvalueType;
std::list<jobject> javaReflectionGetMethods(JNIEnv *env, jclass clazz);
std::list<jobject> javaReflectionGetConstructors(JNIEnv *env, jclass clazz);
+std::list<jobject> javaReflectionGetStaticMethods(JNIEnv *env, jclass clazz);
std::string javaToString(JNIEnv *env, jstring str);
std::string javaObjectToString(JNIEnv *env, jobject obj);
jobject javaFindBestMatchingMethod(
@@ -34,5 +36,6 @@ jvalueType javaGetType(JNIEnv *env, jclass type);
jclass javaFindClass(JNIEnv* env, std::string className);
jarray v8ToJava(JNIEnv* env, const v8::Arguments& args, int start, int end, std::list<int> *methodArgTypes);
jobject v8ToJava(JNIEnv* env, v8::Local<v8::Value> arg, int *methodArgType);
+v8::Handle<v8::Value> javaExceptionToV8(JNIEnv* env, const std::string& alternateMessage);
#endif
View
BIN  test/commons-lang3-3.1.jar
Binary file not shown
View
23 test/simple-test.js
@@ -1,10 +1,33 @@
var java = require("../");
+java.classpath.push("test/commons-lang3-3.1.jar");
+
var nodeunit = require("nodeunit");
var util = require("util");
exports['Simple'] = nodeunit.testCase({
+ "test classpath commons lang": function(test) {
+ var result = java.callStaticMethodSync("org.apache.commons.lang3.ObjectUtils", "toString", "test");
+ console.log("org.apache.commons.lang3.ObjectUtils.toString:", result);
+ test.equal(result, "test");
+ test.done();
+ },
+
+ "test static calls": function(test) {
+ var result = java.callStaticMethodSync("java.lang.System", "currentTimeMillis");
+ console.log("currentTimeMillis:", result);
+ test.ok(result);
+ test.done();
+ },
+
+ "test static calls single argument": function(test) {
+ var result = java.callStaticMethodSync("java.lang.System", "getProperty", "os.version");
+ console.log("os.version:", result);
+ test.ok(result);
+ test.done();
+ },
+
"create an instance of a class and call methods (getName) (async)": function(test) {
java.newInstance("java.util.ArrayList", function(err, list) {
if(err) { console.log(err); return; }
Please sign in to comment.
Something went wrong with that request. Please try again.