Skip to content

Commit 5a67868

Browse files
committed
support threading with proxy
1 parent f35a055 commit 5a67868

File tree

8 files changed

+148
-30
lines changed

8 files changed

+148
-30
lines changed

README.md

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ list.addSync('item1');
5959
* [setStaticFieldValue](#javaSetStaticFieldValue)
6060
* [newArray](#javaNewArray)
6161
* [newByte](#javaNewByte)
62+
* [newProxy](#javaNewProxy)
6263

6364
## java objects
6465
* [Call Method](#javaObjectCallMethod)
@@ -188,6 +189,28 @@ __Example__
188189

189190
var b = java.newByte(12);
190191

192+
<a name="javaNewProxy" />
193+
**java.newProxy(interfaceName, functions)**
194+
195+
Creates a new java Proxy for the given interface. Functions passed in will run on the v8 main thread and not a new thread.
196+
197+
__Arguments__
198+
199+
* interfaceName - The name of the interface to proxy. For subclasses seperate using a '$' (eg. com.nearinfinty.MyClass$SubClass)
200+
* functions - A hash of functions matching the function in the interface.
201+
202+
__Example__
203+
204+
var myProxy = java.newDynamicProxy('java.lang.Runnable', {
205+
run: function () {
206+
// This is actually run on the v8 thread and not the new java thread
207+
console.log("hello from thread");
208+
}
209+
});
210+
211+
var thread = java.newInstanceSync("java.lang.Thread", myProxy);
212+
thread.start();
213+
191214
<a name="javaObject"/>
192215
## java object
193216

lib/nodeJavaBridge.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
'use strict';
22

3+
var path = require('path');
34
var bindings = require("../build/Release/nodejavabridge_bindings");
45

56
var java = module.exports = new bindings.Java();
67
java.classpath.push(__dirname + "/../commons-lang3-node-java.jar");
78
java.classpath.push(__dirname + "/../src-java");
9+
java.nativeBindingLocation = path.resolve(path.join(__dirname, "../build/Release/nodejavabridge_bindings.node"));
810

911
var MODIFIER_PUBLIC = 1;
1012
var MODIFIER_STATIC = 8;
-71 Bytes
Binary file not shown.

src-java/node/NodeDynamicProxyClass.java

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,12 @@ public class NodeDynamicProxyClass implements java.lang.reflect.InvocationHandle
55
private native Object callJs(int ptr, java.lang.reflect.Method m, Object[] args) throws Throwable;
66
public int ptr;
77

8-
static {
8+
public NodeDynamicProxyClass(String path, int ptr) {
99
try{
10-
Runtime.getRuntime().load("/home/jshimty/dev/node-java/build/Release/nodejavabridge_bindings.node");
10+
Runtime.getRuntime().load(path);
1111
}catch(Exception e){
1212
System.out.println(e.toString());
1313
}
14-
}
15-
16-
public NodeDynamicProxyClass(int ptr) {
1714
this.ptr = ptr;
1815
}
1916

src/java.cpp

Lines changed: 89 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -6,19 +6,40 @@
66
#include "node_NodeDynamicProxyClass.h"
77
#include <sstream>
88

9+
std::string nativeBindingLocation;
10+
int v8ThreadId;
11+
912
/*static*/ v8::Persistent<v8::FunctionTemplate> Java::s_ct;
1013

14+
void my_sleep(int dur) {
15+
#ifdef WINDOWS
16+
Sleep(dur);
17+
#else
18+
usleep(dur);
19+
#endif
20+
}
21+
22+
int my_getThreadId() {
23+
#ifdef WINDOWS
24+
return (int)GetCurrentThreadId();
25+
#else
26+
return (int)pthread_self();
27+
#endif
28+
}
29+
1130
/*static*/ void Java::Init(v8::Handle<v8::Object> target) {
1231
v8::HandleScope scope;
1332

33+
v8ThreadId = my_getThreadId();
34+
1435
v8::Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New(New);
1536
s_ct = v8::Persistent<v8::FunctionTemplate>::New(t);
1637
s_ct->InstanceTemplate()->SetInternalFieldCount(1);
1738
s_ct->SetClassName(v8::String::NewSymbol("Java"));
1839

1940
NODE_SET_PROTOTYPE_METHOD(s_ct, "newInstance", newInstance);
2041
NODE_SET_PROTOTYPE_METHOD(s_ct, "newInstanceSync", newInstanceSync);
21-
NODE_SET_PROTOTYPE_METHOD(s_ct, "newDynamicProxy", newDynamicProxy);
42+
NODE_SET_PROTOTYPE_METHOD(s_ct, "newProxy", newProxy);
2243
NODE_SET_PROTOTYPE_METHOD(s_ct, "callStaticMethod", callStaticMethod);
2344
NODE_SET_PROTOTYPE_METHOD(s_ct, "callStaticMethodSync", callStaticMethodSync);
2445
NODE_SET_PROTOTYPE_METHOD(s_ct, "findClassSync", findClassSync);
@@ -38,6 +59,7 @@
3859

3960
self->handle_->Set(v8::String::New("classpath"), v8::Array::New());
4061
self->handle_->Set(v8::String::New("options"), v8::Array::New());
62+
self->handle_->Set(v8::String::New("nativeBindingLocation"), v8::String::New("Not Set"));
4163

4264
return args.This();
4365
}
@@ -89,6 +111,11 @@ v8::Handle<v8::Value> Java::createJVM(JavaVM** jvm, JNIEnv** env) {
89111
classPath << *arrayItemStr;
90112
}
91113

114+
// set the native binding location
115+
v8::Local<v8::Value> v8NativeBindingLocation = handle_->Get(v8::String::New("nativeBindingLocation"));
116+
v8::String::AsciiValue nativeBindingLocationStr(v8NativeBindingLocation);
117+
nativeBindingLocation = *nativeBindingLocationStr;
118+
92119
// get other options
93120
v8::Local<v8::Value> optionsValue = handle_->Get(v8::String::New("options"));
94121
if(!optionsValue->IsArray()) {
@@ -202,7 +229,7 @@ v8::Handle<v8::Value> Java::createJVM(JavaVM** jvm, JNIEnv** env) {
202229
return scope.Close(result);
203230
}
204231

205-
/*static*/ v8::Handle<v8::Value> Java::newDynamicProxy(const v8::Arguments& args) {
232+
/*static*/ v8::Handle<v8::Value> Java::newProxy(const v8::Arguments& args) {
206233
v8::HandleScope scope;
207234
Java* self = node::ObjectWrap::Unwrap<Java>(args.This());
208235
v8::Handle<v8::Value> ensureJvmResults = self->ensureJvm();
@@ -232,9 +259,10 @@ v8::Handle<v8::Value> Java::createJVM(JavaVM** jvm, JNIEnv** env) {
232259
}
233260

234261
// find constructor
235-
jclass objectClazz = env->FindClass("java/lang/Integer");
236-
jobjectArray methodArgs = env->NewObjectArray(1, objectClazz, NULL);
237-
env->SetObjectArrayElement(methodArgs, 0, v8ToJava(env, v8::Integer::New((int)dynamicProxyData)));
262+
jclass objectClazz = env->FindClass("java/lang/Object");
263+
jobjectArray methodArgs = env->NewObjectArray(2, objectClazz, NULL);
264+
env->SetObjectArrayElement(methodArgs, 0, v8ToJava(env, v8::String::New(nativeBindingLocation.c_str())));
265+
env->SetObjectArrayElement(methodArgs, 1, v8ToJava(env, v8::Integer::New((int)dynamicProxyData)));
238266
jobject method = javaFindConstructor(env, clazz, methodArgs);
239267
if(method == NULL) {
240268
std::ostringstream errStr;
@@ -565,31 +593,71 @@ v8::Handle<v8::Value> Java::createJVM(JavaVM** jvm, JNIEnv** env) {
565593
return v8::Undefined();
566594
}
567595

568-
JNIEXPORT jobject JNICALL Java_node_NodeDynamicProxyClass_callJs(JNIEnv *env, jobject src, jint ptr, jobject method, jobjectArray args) {
569-
v8::HandleScope scope;
596+
void EIO_CallJs(uv_work_t* req) {
597+
}
570598

571-
DynamicProxyData* dynamicProxyData = (DynamicProxyData*)ptr;
572-
jclass methodClazz = env->FindClass("java/lang/reflect/Method");
573-
jmethodID method_getName = env->GetMethodID(methodClazz, "getName", "()Ljava/lang/String;");
574-
std::string methodName = javaObjectToString(env, env->CallObjectMethod(method, method_getName));
599+
void EIO_AfterCallJs(uv_work_t* req) {
600+
DynamicProxyData* dynamicProxyData = static_cast<DynamicProxyData*>(req->data);
601+
602+
JNIEnv* env = dynamicProxyData->env;
575603

576-
v8::Local<v8::Value> fnObj = dynamicProxyData->functions->Get(v8::String::New(methodName.c_str()));
604+
v8::HandleScope scope;
605+
v8::Array* v8Args;
606+
v8::Function* fn;
607+
v8::Handle<v8::Value>* argv;
608+
int argc;
609+
int i;
610+
v8::Local<v8::Value> v8Result;
611+
612+
v8::Local<v8::Value> fnObj = dynamicProxyData->functions->Get(v8::String::New(dynamicProxyData->methodName.c_str()));
577613
if(fnObj->IsUndefined() || fnObj->IsNull()) {
578-
printf("ERROR: Could not find method %s", methodName.c_str());
614+
printf("ERROR: Could not find method %s", dynamicProxyData->methodName.c_str());
615+
goto CleanUp;
579616
}
580617
if(!fnObj->IsFunction()) {
581-
printf("ERROR: %s is not a function.", methodName.c_str());
618+
printf("ERROR: %s is not a function.", dynamicProxyData->methodName.c_str());
619+
goto CleanUp;
582620
}
583-
v8::Function* fn = v8::Function::Cast(*fnObj);
621+
fn = v8::Function::Cast(*fnObj);
584622

585-
v8::Array* v8Args = v8::Array::Cast(*javaArrayToV8(dynamicProxyData->java, env, args));
586-
int argc = v8Args->Length();
587-
v8::Handle<v8::Value>* argv = new v8::Handle<v8::Value>[argc];
588-
for(int i=0; i<argc; i++) {
623+
v8Args = v8::Array::Cast(*javaArrayToV8(dynamicProxyData->java, env, dynamicProxyData->args));
624+
argc = v8Args->Length();
625+
argv = new v8::Handle<v8::Value>[argc];
626+
for(i=0; i<argc; i++) {
589627
argv[i] = v8Args->Get(i);
590628
}
591-
v8::Local<v8::Value> result = fn->Call(dynamicProxyData->functions, argc, argv);
629+
v8Result = fn->Call(dynamicProxyData->functions, argc, argv);
592630
delete[] argv;
593631

594-
return v8ToJava(env, result);
632+
dynamicProxyData->result = v8ToJava(env, v8Result);
633+
634+
CleanUp:
635+
dynamicProxyData->done = true;
636+
}
637+
638+
JNIEXPORT jobject JNICALL Java_node_NodeDynamicProxyClass_callJs(JNIEnv *env, jobject src, jint ptr, jobject method, jobjectArray args) {
639+
int myThreadId = my_getThreadId();
640+
641+
DynamicProxyData* dynamicProxyData = (DynamicProxyData*)ptr;
642+
dynamicProxyData->env = env;
643+
dynamicProxyData->args = args;
644+
dynamicProxyData->done = false;
645+
646+
jclass methodClazz = env->FindClass("java/lang/reflect/Method");
647+
jmethodID method_getName = env->GetMethodID(methodClazz, "getName", "()Ljava/lang/String;");
648+
dynamicProxyData->methodName = javaObjectToString(env, env->CallObjectMethod(method, method_getName));
649+
650+
uv_work_t* req = new uv_work_t();
651+
req->data = dynamicProxyData;
652+
if(myThreadId == v8ThreadId) {
653+
EIO_AfterCallJs(req);
654+
} else {
655+
uv_queue_work(uv_default_loop(), req, EIO_CallJs, EIO_AfterCallJs);
656+
657+
while(!dynamicProxyData->done) {
658+
my_sleep(100);
659+
}
660+
}
661+
662+
return dynamicProxyData->result;
595663
}

src/java.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ class Java : public node::ObjectWrap {
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-
static v8::Handle<v8::Value> newDynamicProxy(const v8::Arguments& args);
24+
static v8::Handle<v8::Value> newProxy(const v8::Arguments& args);
2525
static v8::Handle<v8::Value> callStaticMethod(const v8::Arguments& args);
2626
static v8::Handle<v8::Value> callStaticMethodSync(const v8::Arguments& args);
2727
static v8::Handle<v8::Value> findClassSync(const v8::Arguments& args);

src/utils.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
#include <list>
99
#include <vector>
1010
#include <string>
11+
#include <uv.h>
1112

1213
class Java;
1314

@@ -24,9 +25,14 @@ typedef enum _jvalueType {
2425
} jvalueType;
2526

2627
struct DynamicProxyData {
28+
JNIEnv* env;
2729
Java* java;
2830
std::string interfaceName;
2931
v8::Persistent<v8::Object> functions;
32+
std::string methodName;
33+
jobjectArray args;
34+
jobject result;
35+
int done;
3036
};
3137

3238
std::list<jobject> javaReflectionGetMethods(JNIEnv *env, jclass clazz);

test/dynamicProxy-test.js

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ exports['Dynamic Proxy'] = nodeunit.testCase({
88
"0 Arguments": function (test) {
99
var callCount = 0;
1010

11-
var myProxy = java.newDynamicProxy('RunInterface$Interface0Arg', {
11+
var myProxy = java.newProxy('RunInterface$Interface0Arg', {
1212
run: function () {
1313
callCount++;
1414
}
@@ -25,7 +25,7 @@ exports['Dynamic Proxy'] = nodeunit.testCase({
2525
"1 Arguments": function (test) {
2626
var runData = '';
2727

28-
var myProxy = java.newDynamicProxy('RunInterface$Interface1Arg', {
28+
var myProxy = java.newProxy('RunInterface$Interface1Arg', {
2929
run: function (str) {
3030
runData += str;
3131
}
@@ -40,7 +40,7 @@ exports['Dynamic Proxy'] = nodeunit.testCase({
4040
},
4141

4242
"1 Arguments with return data": function (test) {
43-
var myProxy = java.newDynamicProxy('RunInterface$InterfaceWithReturn', {
43+
var myProxy = java.newProxy('RunInterface$InterfaceWithReturn', {
4444
run: function (i) {
4545
return i + 1;
4646
}
@@ -52,5 +52,27 @@ exports['Dynamic Proxy'] = nodeunit.testCase({
5252
test.equals(result, 43);
5353

5454
test.done();
55+
},
56+
57+
"thread": function (test) {
58+
var callCount = 0;
59+
60+
var myProxy = java.newProxy('java.lang.Runnable', {
61+
run: function () {
62+
callCount++;
63+
}
64+
});
65+
66+
var thread = java.newInstanceSync("java.lang.Thread", myProxy);
67+
thread.startSync();
68+
69+
function waitForThread() {
70+
if (callCount === 1) {
71+
return test.done();
72+
}
73+
setTimeout(waitForThread, 100);
74+
}
75+
76+
waitForThread();
5577
}
5678
});

0 commit comments

Comments
 (0)