Skip to content

Commit da2fae0

Browse files
committed
added dynamic proxy support
1 parent 2d787e0 commit da2fae0

15 files changed

+255
-5
lines changed

lib/nodeJavaBridge.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ var bindings = require("../build/Release/nodejavabridge_bindings");
44

55
var java = module.exports = new bindings.Java();
66
java.classpath.push(__dirname + "/../commons-lang3-node-java.jar");
7+
java.classpath.push(__dirname + "/../src-java");
78

89
var MODIFIER_PUBLIC = 1;
910
var MODIFIER_STATIC = 8;
1.08 KB
Binary file not shown.
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
package node;
2+
3+
public class NodeDynamicProxyClass implements java.lang.reflect.InvocationHandler
4+
{
5+
private native Object callJs(int ptr, java.lang.reflect.Method m, Object[] args) throws Throwable;
6+
public int ptr;
7+
8+
static {
9+
try{
10+
Runtime.getRuntime().load("/home/jshimty/dev/node-java/build/Release/nodejavabridge_bindings.node");
11+
}catch(Exception e){
12+
System.out.println(e.toString());
13+
}
14+
}
15+
16+
public NodeDynamicProxyClass(int ptr) {
17+
this.ptr = ptr;
18+
}
19+
20+
public Object invoke(Object proxy, java.lang.reflect.Method m, Object[] args) throws Throwable
21+
{
22+
return callJs(this.ptr, m, args);
23+
}
24+
}

src/java.cpp

Lines changed: 84 additions & 2 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 "node_NodeDynamicProxyClass.h"
67
#include <sstream>
78

89
/*static*/ v8::Persistent<v8::FunctionTemplate> Java::s_ct;
@@ -17,6 +18,7 @@
1718

1819
NODE_SET_PROTOTYPE_METHOD(s_ct, "newInstance", newInstance);
1920
NODE_SET_PROTOTYPE_METHOD(s_ct, "newInstanceSync", newInstanceSync);
21+
NODE_SET_PROTOTYPE_METHOD(s_ct, "newDynamicProxy", newDynamicProxy);
2022
NODE_SET_PROTOTYPE_METHOD(s_ct, "callStaticMethod", callStaticMethod);
2123
NODE_SET_PROTOTYPE_METHOD(s_ct, "callStaticMethodSync", callStaticMethodSync);
2224
NODE_SET_PROTOTYPE_METHOD(s_ct, "findClassSync", findClassSync);
@@ -86,14 +88,14 @@ v8::Handle<v8::Value> Java::createJVM(JavaVM** jvm, JNIEnv** env) {
8688
v8::String::AsciiValue arrayItemStr(arrayItem);
8789
classPath << *arrayItemStr;
8890
}
89-
91+
9092
// get other options
9193
v8::Local<v8::Value> optionsValue = handle_->Get(v8::String::New("options"));
9294
if(!optionsValue->IsArray()) {
9395
return ThrowException(v8::Exception::TypeError(v8::String::New("options must be an array")));
9496
}
9597
v8::Local<v8::Array> optionsArray = v8::Array::Cast(*optionsValue);
96-
98+
9799
// create vm options
98100
int vmOptionsCount = optionsArray->Length() + 1;
99101
JavaVMOption* vmOptions = new JavaVMOption[vmOptionsCount];
@@ -200,6 +202,57 @@ v8::Handle<v8::Value> Java::createJVM(JavaVM** jvm, JNIEnv** env) {
200202
return scope.Close(result);
201203
}
202204

205+
/*static*/ v8::Handle<v8::Value> Java::newDynamicProxy(const v8::Arguments& args) {
206+
v8::HandleScope scope;
207+
Java* self = node::ObjectWrap::Unwrap<Java>(args.This());
208+
v8::Handle<v8::Value> ensureJvmResults = self->ensureJvm();
209+
if(!ensureJvmResults->IsUndefined()) {
210+
return ensureJvmResults;
211+
}
212+
JNIEnv* env = self->getJavaEnv();
213+
214+
int argsStart = 0;
215+
int argsEnd = args.Length();
216+
217+
ARGS_FRONT_STRING(interfaceName);
218+
ARGS_FRONT_OBJECT(functions);
219+
220+
DynamicProxyData* dynamicProxyData = new DynamicProxyData();
221+
dynamicProxyData->java = self;
222+
dynamicProxyData->interfaceName = interfaceName;
223+
dynamicProxyData->functions = v8::Persistent<v8::Object>::New(functions);
224+
225+
// find NodeDynamicProxyClass
226+
std::string className = "node.NodeDynamicProxyClass";
227+
jclass clazz = javaFindClass(env, className);
228+
if(clazz == NULL) {
229+
std::ostringstream errStr;
230+
errStr << "Could not create class node/NodeDynamicProxyClass";
231+
return ThrowException(javaExceptionToV8(env, errStr.str()));
232+
}
233+
234+
// 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)));
238+
jobject method = javaFindConstructor(env, clazz, methodArgs);
239+
if(method == NULL) {
240+
std::ostringstream errStr;
241+
errStr << "Could not find constructor";
242+
return ThrowException(javaExceptionToV8(env, errStr.str()));
243+
}
244+
245+
// run constructor
246+
v8::Handle<v8::Value> callback = v8::Object::New();
247+
NewInstanceBaton* baton = new NewInstanceBaton(self, clazz, method, methodArgs, callback);
248+
v8::Handle<v8::Value> result = baton->runSync();
249+
delete baton;
250+
if(result->IsNativeError()) {
251+
return ThrowException(result);
252+
}
253+
return scope.Close(result);
254+
}
255+
203256
/*static*/ v8::Handle<v8::Value> Java::callStaticMethod(const v8::Arguments& args) {
204257
v8::HandleScope scope;
205258
Java* self = node::ObjectWrap::Unwrap<Java>(args.This());
@@ -511,3 +564,32 @@ v8::Handle<v8::Value> Java::createJVM(JavaVM** jvm, JNIEnv** env) {
511564

512565
return v8::Undefined();
513566
}
567+
568+
JNIEXPORT jobject JNICALL Java_node_NodeDynamicProxyClass_callJs(JNIEnv *env, jobject src, jint ptr, jobject method, jobjectArray args) {
569+
v8::HandleScope scope;
570+
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));
575+
576+
v8::Local<v8::Value> fnObj = dynamicProxyData->functions->Get(v8::String::New(methodName.c_str()));
577+
if(fnObj->IsUndefined() || fnObj->IsNull()) {
578+
printf("ERROR: Could not find method %s", methodName.c_str());
579+
}
580+
if(!fnObj->IsFunction()) {
581+
printf("ERROR: %s is not a function.", methodName.c_str());
582+
}
583+
v8::Function* fn = v8::Function::Cast(*fnObj);
584+
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++) {
589+
argv[i] = v8Args->Get(i);
590+
}
591+
v8::Local<v8::Value> result = fn->Call(dynamicProxyData->functions, argc, argv);
592+
delete[] argv;
593+
594+
return v8ToJava(env, result);
595+
}

src/java.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +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);
2425
static v8::Handle<v8::Value> callStaticMethod(const v8::Arguments& args);
2526
static v8::Handle<v8::Value> callStaticMethodSync(const v8::Arguments& args);
2627
static v8::Handle<v8::Value> findClassSync(const v8::Arguments& args);

src/javaObject.cpp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ JavaObject::~JavaObject() {
8282

8383
jobject method = javaFindMethod(env, self->m_class, methodNameStr, methodArgs);
8484
if(method == NULL) {
85-
EXCEPTION_CALL_CALLBACK("Could not call method " << methodNameStr);
85+
EXCEPTION_CALL_CALLBACK("Could not find method " << methodNameStr);
8686
return v8::Undefined();
8787
}
8888

@@ -108,7 +108,9 @@ JavaObject::~JavaObject() {
108108

109109
jobject method = javaFindMethod(env, self->m_class, methodNameStr, methodArgs);
110110
if(method == NULL) {
111-
return v8::Undefined();
111+
std::ostringstream errStr;
112+
errStr << "Could not find method " << methodNameStr;
113+
return ThrowException(javaExceptionToV8(env, errStr.str()));
112114
}
113115

114116
// run

src/node_NodeDynamicProxyClass.h

Lines changed: 21 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/utils.cpp

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -202,7 +202,27 @@ jobject v8ToJava(JNIEnv* env, v8::Local<v8::Value> arg) {
202202
v8::String::AsciiValue constructorName(obj->GetConstructorName());
203203
if(strcmp(*constructorName, "JavaObject") == 0) {
204204
JavaObject* javaObject = node::ObjectWrap::Unwrap<JavaObject>(obj);
205-
return javaObject->getObject();
205+
jobject jobj = javaObject->getObject();
206+
jclass nodeDynamicProxyClass = env->FindClass("node/NodeDynamicProxyClass");
207+
208+
if(env->IsInstanceOf(jobj, nodeDynamicProxyClass)) {
209+
jfieldID ptrField = env->GetFieldID(nodeDynamicProxyClass, "ptr", "I");
210+
DynamicProxyData* proxyData = (DynamicProxyData*)(int)env->GetIntField(jobj, ptrField);
211+
212+
jclass dynamicInterface = javaFindClass(env, proxyData->interfaceName);
213+
jclass classClazz = env->FindClass("java/lang/Class");
214+
jobjectArray classArray = env->NewObjectArray(1, classClazz, NULL);
215+
env->SetObjectArrayElement(classArray, 0, dynamicInterface);
216+
217+
jmethodID class_getClassLoader = env->GetMethodID(classClazz, "getClassLoader", "()Ljava/lang/ClassLoader;");
218+
jobject classLoader = env->CallObjectMethod(dynamicInterface, class_getClassLoader);
219+
220+
jclass proxyClass = env->FindClass("java/lang/reflect/Proxy");
221+
jmethodID proxy_newProxyInstance = env->GetStaticMethodID(proxyClass, "newProxyInstance", "(Ljava/lang/ClassLoader;[Ljava/lang/Class;Ljava/lang/reflect/InvocationHandler;)Ljava/lang/Object;");
222+
jobj = env->CallStaticObjectMethod(proxyClass, proxy_newProxyInstance, classLoader, classArray, jobj);
223+
}
224+
225+
return jobj;
206226
}
207227
}
208228

src/utils.h

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,12 @@ typedef enum _jvalueType {
2323
TYPE_ARRAY = 9
2424
} jvalueType;
2525

26+
struct DynamicProxyData {
27+
Java* java;
28+
std::string interfaceName;
29+
v8::Persistent<v8::Object> functions;
30+
};
31+
2632
std::list<jobject> javaReflectionGetMethods(JNIEnv *env, jclass clazz);
2733
std::list<jobject> javaReflectionGetFields(JNIEnv *env, jclass clazz);
2834
std::string javaToString(JNIEnv *env, jstring str);
@@ -45,6 +51,15 @@ jobject javaFindConstructor(JNIEnv *env, jclass clazz, jobjectArray methodArgs);
4551

4652
#define UNUSED_VARIABLE(var) var = var;
4753

54+
#define ARGS_FRONT_OBJECT(ARGNAME) \
55+
if(args.Length() < argsStart+1 || !args[argsStart]->IsObject()) { \
56+
std::ostringstream errStr; \
57+
errStr << "Argument " << (argsStart+1) << " must be an object"; \
58+
return ThrowException(v8::Exception::TypeError(v8::String::New(errStr.str().c_str()))); \
59+
} \
60+
v8::Local<v8::Object> ARGNAME = v8::Local<v8::Object>::Cast(args[argsStart]); \
61+
argsStart++;
62+
4863
#define ARGS_FRONT_STRING(ARGNAME) \
4964
if(args.Length() < argsStart+1 || !args[argsStart]->IsString()) { \
5065
std::ostringstream errStr; \
204 Bytes
Binary file not shown.

0 commit comments

Comments
 (0)