Skip to content

Commit fc32367

Browse files
committed
call static and classpath
1 parent 4bc56fc commit fc32367

10 files changed

Lines changed: 267 additions & 35 deletions

File tree

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ Bridge API to connect with existing Java APIs.
1010

1111
```javascript
1212
var java = require("nodejavabridge");
13+
java.classpath.push("commons-lang3-3.1.jar");
14+
java.classpath.push("commons-io.jar");
1315

1416
java.newInstance("java.util.ArrayList", function(list) {
1517
// you have a list

src/java.cpp

Lines changed: 127 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
#include <string.h>
44
#include "javaObject.h"
55
#include "methodCallBaton.h"
6+
#include <sstream>
67

78
/*static*/ v8::Persistent<v8::FunctionTemplate> Java::s_ct;
89

@@ -16,6 +17,8 @@
1617

1718
NODE_SET_PROTOTYPE_METHOD(s_ct, "newInstance", newInstance);
1819
NODE_SET_PROTOTYPE_METHOD(s_ct, "newInstanceSync", newInstanceSync);
20+
NODE_SET_PROTOTYPE_METHOD(s_ct, "callStaticMethod", callStaticMethod);
21+
NODE_SET_PROTOTYPE_METHOD(s_ct, "callStaticMethodSync", callStaticMethodSync);
1922

2023
target->Set(v8::String::NewSymbol("Java"), s_ct->GetFunction());
2124
}
@@ -25,36 +28,79 @@
2528

2629
Java *self = new Java();
2730
self->Wrap(args.This());
31+
32+
self->handle_->Set(v8::String::New("classpath"), v8::Array::New());
33+
2834
return args.This();
2935
}
3036

3137
Java::Java() {
3238
this->m_jvm = NULL;
3339
this->m_env = NULL;
34-
createJVM(&this->m_jvm, &this->m_env);
3540
}
3641

3742
Java::~Java() {
3843

3944
}
4045

41-
/*static*/ void Java::createJVM(JavaVM** jvm, JNIEnv** env) {
46+
v8::Handle<v8::Value> Java::ensureJvm() {
47+
if(!m_jvm) {
48+
return createJVM(&this->m_jvm, &this->m_env);
49+
}
50+
51+
return v8::Undefined();
52+
}
53+
54+
v8::Handle<v8::Value> Java::createJVM(JavaVM** jvm, JNIEnv** env) {
4255
JavaVM* jvmTemp;
4356
JavaVMInitArgs args;
4457

45-
args.version = JNI_VERSION_1_4;
58+
std::ostringstream classPath;
59+
classPath << "-Djava.class.path=";
60+
61+
v8::Local<v8::Value> classPathValue = handle_->Get(v8::String::New("classpath"));
62+
if(!classPathValue->IsArray()) {
63+
return ThrowException(v8::Exception::TypeError(v8::String::New("Classpath must be an array")));
64+
}
65+
v8::Local<v8::Array> classPathArray = v8::Array::Cast(*classPathValue);
66+
for(uint32_t i=0; i<classPathArray->Length(); i++) {
67+
if(i != 0) {
68+
classPath << ":"; // TODO: figure out path seperator
69+
}
70+
v8::Local<v8::Value> arrayItemValue = classPathArray->Get(i);
71+
if(!arrayItemValue->IsString()) {
72+
return ThrowException(v8::Exception::TypeError(v8::String::New("Classpath must only contain strings")));
73+
}
74+
v8::Local<v8::String> arrayItem = arrayItemValue->ToString();
75+
v8::String::AsciiValue arrayItemStr(arrayItem);
76+
classPath << *arrayItemStr;
77+
}
78+
79+
JavaVMOption options[1];
80+
options[0].optionString = strdup(classPath.str().c_str());
81+
4682
JNI_GetDefaultJavaVMInitArgs(&args);
83+
args.version = JNI_VERSION_1_6;
84+
args.ignoreUnrecognized = false;
85+
args.options = options;
86+
args.nOptions = 1;
4787
JNI_CreateJavaVM(&jvmTemp, (void **)env, &args);
4888
*jvm = jvmTemp;
89+
90+
return v8::Undefined();
4991
}
5092

5193
/*static*/ v8::Handle<v8::Value> Java::newInstance(const v8::Arguments& args) {
5294
v8::HandleScope scope;
5395
Java* self = node::ObjectWrap::Unwrap<Java>(args.This());
96+
v8::Handle<v8::Value> ensureJvmResults = self->ensureJvm();
97+
if(!ensureJvmResults->IsUndefined()) {
98+
return ensureJvmResults;
99+
}
54100
JNIEnv* env = self->getJavaEnv();
55101

56-
int argsEnd = args.Length();
57-
102+
int argsEnd = args.Length();
103+
58104
// argument - className
59105
if(args.Length() < 1 || !args[0]->IsString()) {
60106
return ThrowException(v8::Exception::TypeError(v8::String::New("Argument 0 must be a string")));
@@ -73,12 +119,17 @@ Java::~Java() {
73119
}
74120

75121
std::list<int> methodArgTypes;
76-
jarray methodArgs = v8ToJava(env, args, 1, argsEnd, &methodArgTypes);
77-
122+
jarray methodArgs = v8ToJava(env, args, 1, argsEnd, &methodArgTypes);
123+
78124
jclass clazz = javaFindClass(env, className);
79-
std::list<jobject> constructors = javaReflectionGetConstructors(env, clazz);
125+
if(clazz == NULL) {
126+
std::ostringstream errStr;
127+
errStr << "Could not create class " << className.c_str();
128+
return javaExceptionToV8(env, errStr.str());
129+
}
130+
std::list<jobject> constructors = javaReflectionGetConstructors(env, clazz);
80131
jobject method = javaFindBestMatchingConstructor(env, constructors, methodArgTypes);
81-
132+
82133
// run
83134
NewInstanceBaton* baton = new NewInstanceBaton(self, clazz, method, methodArgs, callback);
84135
baton->run();
@@ -89,10 +140,14 @@ Java::~Java() {
89140
/*static*/ v8::Handle<v8::Value> Java::newInstanceSync(const v8::Arguments& args) {
90141
v8::HandleScope scope;
91142
Java* self = node::ObjectWrap::Unwrap<Java>(args.This());
143+
v8::Handle<v8::Value> ensureJvmResults = self->ensureJvm();
144+
if(!ensureJvmResults->IsUndefined()) {
145+
return ensureJvmResults;
146+
}
92147
JNIEnv* env = self->getJavaEnv();
93148

94-
int argsEnd = args.Length();
95-
149+
int argsEnd = args.Length();
150+
96151
// argument - className
97152
if(args.Length() < 1 || !args[0]->IsString()) {
98153
return ThrowException(v8::Exception::TypeError(v8::String::New("Argument 0 must be a string")));
@@ -105,13 +160,72 @@ Java::~Java() {
105160
jarray methodArgs = v8ToJava(env, args, 1, argsEnd, &methodArgTypes);
106161

107162
jclass clazz = javaFindClass(env, className);
108-
std::list<jobject> constructors = javaReflectionGetConstructors(env, clazz);
163+
if(clazz == NULL) {
164+
std::ostringstream errStr;
165+
errStr << "Could not create class " << className.c_str();
166+
return javaExceptionToV8(env, errStr.str());
167+
}
168+
std::list<jobject> constructors = javaReflectionGetConstructors(env, clazz);
109169
jobject method = javaFindBestMatchingConstructor(env, constructors, methodArgTypes);
110-
170+
111171
// run
112172
v8::Handle<v8::Value> callback = v8::Object::New();
113173
NewInstanceBaton* baton = new NewInstanceBaton(self, clazz, method, methodArgs, callback);
114174
v8::Handle<v8::Value> result = baton->runSync();
115175
delete baton;
116176
return scope.Close(result);
117177
}
178+
179+
/*static*/ v8::Handle<v8::Value> Java::callStaticMethod(const v8::Arguments& args) {
180+
// TODO: write me
181+
return v8::Undefined();
182+
}
183+
184+
/*static*/ v8::Handle<v8::Value> Java::callStaticMethodSync(const v8::Arguments& args) {
185+
v8::HandleScope scope;
186+
Java* self = node::ObjectWrap::Unwrap<Java>(args.This());
187+
v8::Handle<v8::Value> ensureJvmResults = self->ensureJvm();
188+
if(!ensureJvmResults->IsUndefined()) {
189+
return ensureJvmResults;
190+
}
191+
JNIEnv* env = self->getJavaEnv();
192+
193+
int argsEnd = args.Length();
194+
195+
// argument - className
196+
if(args.Length() < 1 || !args[0]->IsString()) {
197+
return ThrowException(v8::Exception::TypeError(v8::String::New("Argument 0 must be a string")));
198+
}
199+
v8::Local<v8::String> classNameObj = v8::Local<v8::String>::Cast(args[0]);
200+
v8::String::AsciiValue classNameVal(classNameObj);
201+
std::string className = *classNameVal;
202+
203+
// argument - method name
204+
if(args.Length() < 2 || !args[1]->IsString()) {
205+
return ThrowException(v8::Exception::TypeError(v8::String::New("Argument 1 must be a string")));
206+
}
207+
v8::Local<v8::String> methodNameObj = v8::Local<v8::String>::Cast(args[1]);
208+
v8::String::AsciiValue methodNameVal(methodNameObj);
209+
std::string methodName = *methodNameVal;
210+
211+
// build args
212+
std::list<int> methodArgTypes;
213+
jarray methodArgs = v8ToJava(env, args, 2, argsEnd, &methodArgTypes);
214+
215+
// find class and method
216+
jclass clazz = javaFindClass(env, className);
217+
if(clazz == NULL) {
218+
std::ostringstream errStr;
219+
errStr << "Could not create class " << className.c_str();
220+
return javaExceptionToV8(env, errStr.str());
221+
}
222+
std::list<jobject> staticMethods = javaReflectionGetStaticMethods(env, clazz);
223+
jobject method = javaFindBestMatchingMethod(env, staticMethods, methodName.c_str(), methodArgTypes);
224+
225+
// run
226+
v8::Handle<v8::Value> callback = v8::Object::New();
227+
StaticMethodCallBaton* baton = new StaticMethodCallBaton(self, clazz, method, methodArgs, callback);
228+
v8::Handle<v8::Value> result = baton->runSync();
229+
delete baton;
230+
return scope.Close(result);
231+
}

src/java.h

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,15 +16,19 @@ class Java : public node::ObjectWrap {
1616
private:
1717
Java();
1818
~Java();
19-
static void createJVM(JavaVM** jvm, JNIEnv** env);
19+
v8::Handle<v8::Value> createJVM(JavaVM** jvm, JNIEnv** env);
2020

2121
static v8::Handle<v8::Value> New(const v8::Arguments& args);
2222
static v8::Handle<v8::Value> newInstance(const v8::Arguments& args);
2323
static v8::Handle<v8::Value> newInstanceSync(const v8::Arguments& args);
24-
24+
static v8::Handle<v8::Value> callStaticMethod(const v8::Arguments& args);
25+
static v8::Handle<v8::Value> callStaticMethodSync(const v8::Arguments& args);
26+
v8::Handle<v8::Value> ensureJvm();
27+
2528
static v8::Persistent<v8::FunctionTemplate> s_ct;
2629
JavaVM* m_jvm;
2730
JNIEnv* m_env;
31+
std::string m_classPath;
2832
};
2933

3034
#endif

src/javaObject.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@
4040
javaObjectObj->Set(methodNameSync, methodCallSyncTemplate->GetFunction());
4141
}
4242

43+
// TODO: add public field support
44+
4345
return scope.Close(javaObjectObj);
4446
}
4547

src/methodCallBaton.cpp

Lines changed: 50 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,14 @@ void MethodCallBaton::after(JNIEnv *env) {
5858
}
5959

6060
v8::Handle<v8::Value> MethodCallBaton::resultsToV8(JNIEnv *env) {
61+
v8::HandleScope scope;
62+
63+
if(!m_error.IsEmpty() && !m_error->IsNull()) {
64+
v8::Handle<v8::Value> err = m_error;
65+
m_error.Dispose();
66+
return scope.Close(err);
67+
}
68+
6169
switch(m_resultType) {
6270
case TYPE_VOID:
6371
return v8::Undefined();
@@ -66,19 +74,26 @@ v8::Handle<v8::Value> MethodCallBaton::resultsToV8(JNIEnv *env) {
6674
jclass booleanClazz = env->FindClass("java/lang/Boolean");
6775
jmethodID boolean_booleanValue = env->GetMethodID(booleanClazz, "booleanValue", "()Z");
6876
bool result = env->CallBooleanMethod(m_result, boolean_booleanValue);
69-
return v8::Boolean::New(result);
77+
return scope.Close(v8::Boolean::New(result));
78+
}
79+
case TYPE_LONG:
80+
{
81+
jclass longClazz = env->FindClass("java/lang/Long");
82+
jmethodID long_longValue = env->GetMethodID(longClazz, "longValue", "()J");
83+
jlong result = env->CallLongMethod(m_result, long_longValue);
84+
return scope.Close(v8::Number::New(result));
7085
}
7186
case TYPE_INT:
7287
{
7388
jclass integerClazz = env->FindClass("java/lang/Integer");
7489
jmethodID integer_intValue = env->GetMethodID(integerClazz, "intValue", "()I");
75-
int result = env->CallIntMethod(m_result, integer_intValue);
76-
return v8::Integer::New(result);
90+
jint result = env->CallIntMethod(m_result, integer_intValue);
91+
return scope.Close(v8::Integer::New(result));
7792
}
7893
case TYPE_OBJECT:
79-
return JavaObject::New(m_java, m_result);
94+
return scope.Close(JavaObject::New(m_java, m_result));
8095
case TYPE_STRING:
81-
return v8::String::New(javaObjectToString(env, m_result).c_str());
96+
return scope.Close(v8::String::New(javaObjectToString(env, m_result).c_str()));
8297
}
8398
return v8::Undefined();
8499
}
@@ -96,6 +111,21 @@ void NewInstanceBaton::execute(JNIEnv *env) {
96111
}
97112
}
98113

114+
void StaticMethodCallBaton::execute(JNIEnv *env) {
115+
jclass methodClazz = env->FindClass("java/lang/reflect/Method");
116+
jmethodID method_invoke = env->GetMethodID(methodClazz, "invoke", "(Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;");
117+
jmethodID method_getReturnType = env->GetMethodID(methodClazz, "getReturnType", "()Ljava/lang/Class;");
118+
119+
jclass returnType = (jclass)env->CallObjectMethod(m_method, method_getReturnType);
120+
121+
m_resultType = javaGetType(env, returnType);
122+
jobject result = env->CallObjectMethod(m_method, method_invoke, NULL, m_args);
123+
m_result = env->NewGlobalRef(result);
124+
if(env->ExceptionCheck()) {
125+
m_error = v8::Persistent<v8::Value>::New(javaExceptionToV8(env, "Error running method"));
126+
}
127+
}
128+
99129
void InstanceMethodCallBaton::execute(JNIEnv *env) {
100130
jclass methodClazz = env->FindClass("java/lang/reflect/Method");
101131
jmethodID method_invoke = env->GetMethodID(methodClazz, "invoke", "(Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;");
@@ -127,6 +157,21 @@ NewInstanceBaton::~NewInstanceBaton() {
127157
env->DeleteGlobalRef(m_clazz);
128158
}
129159

160+
StaticMethodCallBaton::StaticMethodCallBaton(
161+
Java* java,
162+
jclass clazz,
163+
jobject method,
164+
jarray args,
165+
v8::Handle<v8::Value>& callback) : MethodCallBaton(java, method, args, callback) {
166+
JNIEnv *env = m_java->getJavaEnv();
167+
m_clazz = (jclass)env->NewGlobalRef(clazz);
168+
}
169+
170+
StaticMethodCallBaton::~StaticMethodCallBaton() {
171+
JNIEnv *env = m_java->getJavaEnv();
172+
env->DeleteGlobalRef(m_clazz);
173+
}
174+
130175
InstanceMethodCallBaton::InstanceMethodCallBaton(
131176
Java* java,
132177
JavaObject* obj,

src/methodCallBaton.h

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,14 +20,15 @@ class MethodCallBaton {
2020
static int EIO_AfterMethodCall(eio_req* req);
2121
void run();
2222
v8::Handle<v8::Value> runSync();
23-
23+
2424
protected:
2525
virtual void execute(JNIEnv *env) = 0;
2626
virtual void after(JNIEnv *env);
2727
v8::Handle<v8::Value> resultsToV8(JNIEnv *env);
28-
28+
2929
Java* m_java;
3030
v8::Persistent<v8::Value> m_callback;
31+
v8::Persistent<v8::Value> m_error;
3132
jarray m_args;
3233
jobject m_result;
3334
jobject m_method;
@@ -41,18 +42,29 @@ class InstanceMethodCallBaton : public MethodCallBaton {
4142

4243
protected:
4344
virtual void execute(JNIEnv *env);
44-
45+
4546
JavaObject* m_javaObject;
4647
};
4748

4849
class NewInstanceBaton : public MethodCallBaton {
4950
public:
5051
NewInstanceBaton(Java* java, jclass clazz, jobject method, jarray args, v8::Handle<v8::Value>& callback);
5152
virtual ~NewInstanceBaton();
52-
53+
5354
protected:
5455
virtual void execute(JNIEnv *env);
55-
56+
57+
jclass m_clazz;
58+
};
59+
60+
class StaticMethodCallBaton : public MethodCallBaton {
61+
public:
62+
StaticMethodCallBaton(Java* java, jclass clazz, jobject method, jarray args, v8::Handle<v8::Value>& callback);
63+
virtual ~StaticMethodCallBaton();
64+
65+
protected:
66+
virtual void execute(JNIEnv *env);
67+
5668
jclass m_clazz;
5769
};
5870

0 commit comments

Comments
 (0)