Skip to content

Commit 17b916a

Browse files
committed
almost working method calls
1 parent 65fda63 commit 17b916a

10 files changed

Lines changed: 269 additions & 27 deletions

File tree

src/java.cpp

Lines changed: 6 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -76,9 +76,9 @@ Java::~Java() {
7676

7777
/*static*/ void Java::EIO_NewInstance(eio_req* req) {
7878
NewInstanceBaton* baton = static_cast<NewInstanceBaton*>(req->data);
79-
JNIEnv *env = baton->m_java->attachCurrentThread();
79+
JNIEnv *env = javaAttachCurrentThread(baton->m_java->getJvm());
8080
baton->run(env);
81-
baton->m_java->detachCurrentThread();
81+
javaDetachCurrentThread(baton->m_java->getJvm());
8282
}
8383

8484
/*static*/ int Java::EIO_AfterNewInstance(eio_req* req) {
@@ -89,20 +89,6 @@ Java::~Java() {
8989
return 0;
9090
}
9191

92-
JNIEnv* Java::attachCurrentThread() {
93-
JNIEnv* env;
94-
JavaVMAttachArgs attachArgs;
95-
attachArgs.version = JNI_VERSION_1_4;
96-
attachArgs.name = NULL;
97-
attachArgs.group = NULL;
98-
m_jvm->AttachCurrentThread((void**)&env, &attachArgs);
99-
return env;
100-
}
101-
102-
void Java::detachCurrentThread() {
103-
m_jvm->DetachCurrentThread();
104-
}
105-
10692
NewInstanceBaton::NewInstanceBaton(Java* java, const char *className, v8::Handle<v8::Value> &callback) {
10793
m_java = java;
10894
m_className = className;
@@ -137,11 +123,11 @@ void NewInstanceBaton::run(JNIEnv *env) {
137123
}
138124

139125
void NewInstanceBaton::doCallback() {
140-
v8::Handle<v8::Value> argv[2];
141-
argv[0] = v8::Undefined();
142-
argv[1] = JavaObject::New(m_java, m_result);
143-
144126
if(m_callback->IsFunction()) {
127+
v8::Handle<v8::Value> argv[2];
128+
argv[0] = v8::Undefined();
129+
argv[1] = JavaObject::New(m_java, m_result);
130+
145131
v8::Function::Cast(*this->m_callback)->Call(v8::Context::GetCurrent()->Global(), 2, argv);
146132
}
147133
}

src/java.h

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,6 @@ class Java : public node::ObjectWrap {
1717

1818
friend class NewInstanceBaton;
1919

20-
protected:
21-
JNIEnv* attachCurrentThread();
22-
void detachCurrentThread();
23-
2420
private:
2521
Java();
2622
~Java();

src/javaObject.cpp

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11

22
#include "javaObject.h"
33
#include "java.h"
4+
#include "utils.h"
45

56
/*static*/ v8::Persistent<v8::FunctionTemplate> JavaObject::s_ct;
67

@@ -22,14 +23,59 @@
2223
JavaObject *self = new JavaObject(java, obj);
2324
self->Wrap(javaObjectObj);
2425

26+
JNIEnv *env = self->m_java->getJavaEnv();
27+
28+
self->m_methods = javaReflectionGetDeclaredMethods(env, self->m_class);
29+
jclass methodClazz = env->FindClass("java/lang/reflect/Method");
30+
jmethodID method_getNameMethod = env->GetMethodID(methodClazz, "getName", "()Ljava/lang/String;");
31+
for(std::list<jobject>::iterator it = self->m_methods.begin(); it != self->m_methods.end(); it++) {
32+
v8::Handle<v8::String> methodName = v8::String::New(javaToString(env, (jstring)env->CallObjectMethod(*it, method_getNameMethod)).c_str());
33+
v8::Local<v8::FunctionTemplate> methodCallTemplate = v8::FunctionTemplate::New(methodCall, methodName);
34+
javaObjectObj->Set(methodName, methodCallTemplate->GetFunction());
35+
}
36+
2537
return scope.Close(javaObjectObj);
2638
}
2739

2840
JavaObject::JavaObject(Java *java, jobject obj) {
2941
m_java = java;
3042
m_obj = obj;
43+
m_class = java->getJavaEnv()->GetObjectClass(obj);
3144
}
3245

3346
JavaObject::~JavaObject() {
3447

3548
}
49+
50+
/*static*/ v8::Handle<v8::Value> JavaObject::methodCall(const v8::Arguments& args) {
51+
v8::HandleScope scope;
52+
JavaObject* self = node::ObjectWrap::Unwrap<JavaObject>(args.This());
53+
JNIEnv *env = self->m_java->getJavaEnv();
54+
55+
v8::String::AsciiValue methodName(args.Data());
56+
57+
int argsLength = args.Length();
58+
59+
// argument - callback
60+
v8::Handle<v8::Value> callback;
61+
if(args[args.Length()-1]->IsFunction()) {
62+
callback = args[argsLength-1];
63+
argsLength--;
64+
} else {
65+
callback = v8::Null();
66+
}
67+
68+
std::list<jobject> methodArgs; // TODO: build args
69+
70+
jobject method = javaFindBestMatchingMethod(env, self->m_methods, *methodName, methodArgs);
71+
if(method == NULL) {
72+
return v8::Undefined();
73+
}
74+
75+
// run
76+
MethodCallBaton* baton = new MethodCallBaton(self->m_java, self, method, methodArgs, callback);
77+
eio_custom(MethodCallBaton::EIO_MethodCall, EIO_PRI_DEFAULT, MethodCallBaton::EIO_AfterMethodCall, baton);
78+
ev_ref(EV_DEFAULT_UC);
79+
80+
return v8::Undefined();
81+
}

src/javaObject.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,21 +5,29 @@
55
#include <v8.h>
66
#include <node.h>
77
#include <jni.h>
8+
#include <list>
9+
#include "methodCallBaton.h"
810

911
class Java;
12+
class MethodCallBaton;
1013

1114
class JavaObject : node::ObjectWrap {
1215
public:
1316
static void Init(v8::Handle<v8::Object> target);
1417
static v8::Local<v8::Object> New(Java* java, jobject obj);
1518

19+
friend class MethodCallBaton;
20+
1621
private:
1722
JavaObject(Java* java, jobject obj);
1823
~JavaObject();
24+
static v8::Handle<v8::Value> methodCall(const v8::Arguments& args);
1925

2026
static v8::Persistent<v8::FunctionTemplate> s_ct;
2127
Java* m_java;
2228
jobject m_obj;
29+
jclass m_class;
30+
std::list<jobject> m_methods;
2331
};
2432

2533
#endif

src/methodCallBaton.cpp

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
2+
#include "methodCallBaton.h"
3+
#include "java.h"
4+
#include "javaObject.h"
5+
6+
MethodCallBaton::MethodCallBaton(Java* java, JavaObject* obj, jobject method, std::list<jobject> args, v8::Handle<v8::Value> &callback) {
7+
m_java = java;
8+
m_javaObject = obj;
9+
m_method = method;
10+
m_args = args;
11+
m_callback = v8::Persistent<v8::Value>::New(callback);
12+
m_javaObject->Ref();
13+
}
14+
15+
MethodCallBaton::~MethodCallBaton() {
16+
m_callback.Dispose();
17+
m_javaObject->Unref();
18+
}
19+
20+
/*static*/ void MethodCallBaton::EIO_MethodCall(eio_req* req) {
21+
MethodCallBaton* self = static_cast<MethodCallBaton*>(req->data);
22+
JNIEnv *env = javaAttachCurrentThread(self->m_java->getJvm());
23+
24+
jclass intClazz = env->FindClass("java/lang/Integer");
25+
//jclass clazzClazz = env->FindClass("java/lang/Class");
26+
jclass methodClazz = env->FindClass("java/lang/reflect/Method");
27+
jmethodID method_invoke = env->GetMethodID(methodClazz, "invoke", "(Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;");
28+
jmethodID method_getReturnType = env->GetMethodID(methodClazz, "getReturnType", "()Ljava/lang/Class;");
29+
//printf("a %d\n", (int)clazzClazz);
30+
//jmethodID clazz_isInstance = env->GetMethodID(clazzClazz, "isInstance", "(Ljava/lang/Object;)Z");
31+
32+
printf("1\n");
33+
jclass returnType = (jclass)env->CallObjectMethod(self->m_method, method_getReturnType);
34+
35+
jclass objectClazz = env->FindClass("java/lang/Object");
36+
jobjectArray parameters = env->NewObjectArray(0, objectClazz, NULL); // TODO: init parameters
37+
printf("2 %d %d %s %s\n", (int)intClazz, (int)returnType, javaObjectToString(env, intClazz).c_str(), javaObjectToString(env, intClazz).c_str());
38+
//if(env->CallBooleanMethod(intClazz, clazz_isInstance, returnType)) {
39+
if(returnType == intClazz) {
40+
printf("4\n");
41+
self->m_result.i = env->CallIntMethod(self->m_method, method_invoke, self->m_javaObject->m_obj, parameters);
42+
self->m_resultType = TYPE_INT;
43+
} else {
44+
printf("5\n");
45+
self->m_result.l = env->CallObjectMethod(self->m_method, method_invoke, self->m_javaObject->m_obj, parameters);
46+
self->m_resultType = TYPE_OBJECT;
47+
}
48+
printf("3\n");
49+
if(env->ExceptionCheck()) {
50+
env->ExceptionDescribe(); // TODO: handle error
51+
return;
52+
}
53+
54+
javaDetachCurrentThread(self->m_java->getJvm());
55+
}
56+
57+
/*static*/ int MethodCallBaton::EIO_AfterMethodCall(eio_req* req) {
58+
MethodCallBaton* self = static_cast<MethodCallBaton*>(req->data);
59+
60+
if(self->m_callback->IsFunction()) {
61+
v8::Handle<v8::Value> argv[2];
62+
argv[0] = v8::Undefined();
63+
switch(self->m_resultType) {
64+
case TYPE_INT:
65+
argv[1] = v8::Integer::New(self->m_result.i);
66+
break;
67+
case TYPE_OBJECT:
68+
argv[1] = JavaObject::New(self->m_java, self->m_result.l);
69+
break;
70+
}
71+
v8::Function::Cast(*self->m_callback)->Call(v8::Context::GetCurrent()->Global(), 2, argv);
72+
}
73+
74+
ev_unref(EV_DEFAULT_UC);
75+
delete self;
76+
return 0;
77+
}
78+

src/methodCallBaton.h

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
2+
#ifndef _methodcallbaton_h_
3+
#define _methodcallbaton_h_
4+
5+
#include <v8.h>
6+
#include <node.h>
7+
#include <jni.h>
8+
#include <list>
9+
#include "utils.h"
10+
11+
class Java;
12+
class JavaObject;
13+
14+
typedef enum _jvalueType {
15+
TYPE_INT,
16+
TYPE_OBJECT
17+
} jvalueType;
18+
19+
class MethodCallBaton {
20+
public:
21+
MethodCallBaton(Java* java, JavaObject* obj, jobject method, std::list<jobject> args, v8::Handle<v8::Value> &callback);
22+
~MethodCallBaton();
23+
24+
static void EIO_MethodCall(eio_req* req);
25+
static int EIO_AfterMethodCall(eio_req* req);
26+
27+
private:
28+
Java* m_java;
29+
JavaObject* m_javaObject;
30+
v8::Persistent<v8::Value> m_callback;
31+
jobject m_method;
32+
std::list<jobject> m_args;
33+
jvalue m_result;
34+
int m_resultType;
35+
};
36+
37+
#endif

src/utils.cpp

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
2+
#include "utils.h"
3+
4+
std::list<jobject> javaReflectionGetDeclaredMethods(JNIEnv *env, jclass clazz) {
5+
std::list<jobject> results;
6+
7+
jclass clazzclazz = env->GetObjectClass(clazz);
8+
jmethodID methodId = env->GetMethodID(clazzclazz, "getMethods", "()[Ljava/lang/reflect/Method;");
9+
jobjectArray methodObjects = (jobjectArray)env->CallObjectMethod(clazz, methodId);
10+
jsize methodCount = env->GetArrayLength(methodObjects);
11+
for(jsize i=0; i<methodCount; i++) {
12+
jobject obj = env->GetObjectArrayElement(methodObjects, i);
13+
results.push_back(obj);
14+
}
15+
16+
return results;
17+
}
18+
19+
std::string javaToString(JNIEnv *env, jstring str) {
20+
const char* chars = env->GetStringUTFChars(str, NULL);
21+
std::string results = chars;
22+
env->ReleaseStringUTFChars(str, chars);
23+
return results;
24+
}
25+
26+
std::string javaObjectToString(JNIEnv *env, jobject obj) {
27+
jclass objClazz = env->GetObjectClass(obj);
28+
jmethodID methodId = env->GetMethodID(objClazz, "toString", "()Ljava/lang/String;");
29+
jstring result = (jstring)env->CallObjectMethod(obj, methodId);
30+
return javaToString(env, result);
31+
}
32+
33+
jobject javaFindBestMatchingMethod(
34+
JNIEnv *env,
35+
std::list<jobject>& methods,
36+
const char *methodName,
37+
std::list<jobject>& args) {
38+
39+
jclass methodClazz = env->FindClass("java/lang/reflect/Method");
40+
jmethodID method_getNameMethod = env->GetMethodID(methodClazz, "getName", "()Ljava/lang/String;");
41+
42+
for(std::list<jobject>::iterator it = methods.begin(); it != methods.end(); it++) {
43+
std::string itMethodName = javaToString(env, (jstring)env->CallObjectMethod(*it, method_getNameMethod));
44+
if(itMethodName == methodName) {
45+
return *it; // TODO: check parameters
46+
}
47+
}
48+
return NULL;
49+
}
50+
51+
JNIEnv* javaAttachCurrentThread(JavaVM* jvm) {
52+
JNIEnv* env;
53+
JavaVMAttachArgs attachArgs;
54+
attachArgs.version = JNI_VERSION_1_4;
55+
attachArgs.name = NULL;
56+
attachArgs.group = NULL;
57+
jvm->AttachCurrentThread((void**)&env, &attachArgs);
58+
return env;
59+
}
60+
61+
void javaDetachCurrentThread(JavaVM* jvm) {
62+
jvm->DetachCurrentThread();
63+
}

src/utils.h

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
2+
#ifndef _utils_h_
3+
#define _utils_h_
4+
5+
#include <jni.h>
6+
#include <list>
7+
#include <string>
8+
9+
std::list<jobject> javaReflectionGetDeclaredMethods(JNIEnv *env, jclass clazz);
10+
std::string javaToString(JNIEnv *env, jstring str);
11+
std::string javaObjectToString(JNIEnv *env, jobject obj);
12+
jobject javaFindBestMatchingMethod(
13+
JNIEnv *env,
14+
std::list<jobject>& methods,
15+
const char *methodName,
16+
std::list<jobject>& args);
17+
JNIEnv* javaAttachCurrentThread(JavaVM* jvm);
18+
void javaDetachCurrentThread(JavaVM* jvm);
19+
20+
#endif

test/simple-test.js

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,15 @@ exports['Simple'] = nodeunit.testCase({
88
"create an instance of a class (async)": function(test) {
99
java.newInstance("java.util.ArrayList", function(err, list) {
1010
if(err) { console.log(err); return; }
11-
console.log(list);
1211
test.ok(list);
13-
test.done();
12+
if(list) {
13+
list.size(function(err, result) {
14+
if(err) { console.log(err); return; }
15+
console.log("result", result);
16+
//test.equal(result, 0);
17+
test.done();
18+
});
19+
}
1420
});
1521
}
1622
});

wscript

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,5 +37,7 @@ def build(bld):
3737
obj.source = " ".join([
3838
"src/nodeJavaBridge.cpp",
3939
"src/java.cpp",
40-
"src/javaObject.cpp"])
40+
"src/javaObject.cpp",
41+
"src/methodCallBaton.cpp",
42+
"src/utils.cpp"])
4143
obj.includes = "src/"

0 commit comments

Comments
 (0)