Permalink
Browse files

added dynamic proxy support

  • Loading branch information...
1 parent 2d787e0 commit da2fae020e9d60bfd9ddc5d9304733dc79d51b0e @joeferner committed May 31, 2012
@@ -4,6 +4,7 @@ var bindings = require("../build/Release/nodejavabridge_bindings");
var java = module.exports = new bindings.Java();
java.classpath.push(__dirname + "/../commons-lang3-node-java.jar");
+java.classpath.push(__dirname + "/../src-java");
var MODIFIER_PUBLIC = 1;
var MODIFIER_STATIC = 8;
Binary file not shown.
@@ -0,0 +1,24 @@
+package node;
+
+public class NodeDynamicProxyClass implements java.lang.reflect.InvocationHandler
+{
+ private native Object callJs(int ptr, java.lang.reflect.Method m, Object[] args) throws Throwable;
+ public int ptr;
+
+ static {
+ try{
+ Runtime.getRuntime().load("/home/jshimty/dev/node-java/build/Release/nodejavabridge_bindings.node");
+ }catch(Exception e){
+ System.out.println(e.toString());
+ }
+ }
+
+ public NodeDynamicProxyClass(int ptr) {
+ this.ptr = ptr;
+ }
+
+ public Object invoke(Object proxy, java.lang.reflect.Method m, Object[] args) throws Throwable
+ {
+ return callJs(this.ptr, m, args);
+ }
+}
View
@@ -3,6 +3,7 @@
#include <string.h>
#include "javaObject.h"
#include "methodCallBaton.h"
+#include "node_NodeDynamicProxyClass.h"
#include <sstream>
/*static*/ v8::Persistent<v8::FunctionTemplate> Java::s_ct;
@@ -17,6 +18,7 @@
NODE_SET_PROTOTYPE_METHOD(s_ct, "newInstance", newInstance);
NODE_SET_PROTOTYPE_METHOD(s_ct, "newInstanceSync", newInstanceSync);
+ NODE_SET_PROTOTYPE_METHOD(s_ct, "newDynamicProxy", newDynamicProxy);
NODE_SET_PROTOTYPE_METHOD(s_ct, "callStaticMethod", callStaticMethod);
NODE_SET_PROTOTYPE_METHOD(s_ct, "callStaticMethodSync", callStaticMethodSync);
NODE_SET_PROTOTYPE_METHOD(s_ct, "findClassSync", findClassSync);
@@ -86,14 +88,14 @@ v8::Handle<v8::Value> Java::createJVM(JavaVM** jvm, JNIEnv** env) {
v8::String::AsciiValue arrayItemStr(arrayItem);
classPath << *arrayItemStr;
}
-
+
// get other options
v8::Local<v8::Value> optionsValue = handle_->Get(v8::String::New("options"));
if(!optionsValue->IsArray()) {
return ThrowException(v8::Exception::TypeError(v8::String::New("options must be an array")));
}
v8::Local<v8::Array> optionsArray = v8::Array::Cast(*optionsValue);
-
+
// create vm options
int vmOptionsCount = optionsArray->Length() + 1;
JavaVMOption* vmOptions = new JavaVMOption[vmOptionsCount];
@@ -200,6 +202,57 @@ v8::Handle<v8::Value> Java::createJVM(JavaVM** jvm, JNIEnv** env) {
return scope.Close(result);
}
+/*static*/ v8::Handle<v8::Value> Java::newDynamicProxy(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 argsStart = 0;
+ int argsEnd = args.Length();
+
+ ARGS_FRONT_STRING(interfaceName);
+ ARGS_FRONT_OBJECT(functions);
+
+ DynamicProxyData* dynamicProxyData = new DynamicProxyData();
+ dynamicProxyData->java = self;
+ dynamicProxyData->interfaceName = interfaceName;
+ dynamicProxyData->functions = v8::Persistent<v8::Object>::New(functions);
+
+ // find NodeDynamicProxyClass
+ std::string className = "node.NodeDynamicProxyClass";
+ jclass clazz = javaFindClass(env, className);
+ if(clazz == NULL) {
+ std::ostringstream errStr;
+ errStr << "Could not create class node/NodeDynamicProxyClass";
+ return ThrowException(javaExceptionToV8(env, errStr.str()));
+ }
+
+ // find constructor
+ jclass objectClazz = env->FindClass("java/lang/Integer");
+ jobjectArray methodArgs = env->NewObjectArray(1, objectClazz, NULL);
+ env->SetObjectArrayElement(methodArgs, 0, v8ToJava(env, v8::Integer::New((int)dynamicProxyData)));
+ jobject method = javaFindConstructor(env, clazz, methodArgs);
+ if(method == NULL) {
+ std::ostringstream errStr;
+ errStr << "Could not find constructor";
+ return ThrowException(javaExceptionToV8(env, errStr.str()));
+ }
+
+ // run constructor
+ v8::Handle<v8::Value> callback = v8::Object::New();
+ NewInstanceBaton* baton = new NewInstanceBaton(self, clazz, method, methodArgs, callback);
+ v8::Handle<v8::Value> result = baton->runSync();
+ delete baton;
+ if(result->IsNativeError()) {
+ return ThrowException(result);
+ }
+ return scope.Close(result);
+}
+
/*static*/ v8::Handle<v8::Value> Java::callStaticMethod(const v8::Arguments& args) {
v8::HandleScope scope;
Java* self = node::ObjectWrap::Unwrap<Java>(args.This());
@@ -511,3 +564,32 @@ v8::Handle<v8::Value> Java::createJVM(JavaVM** jvm, JNIEnv** env) {
return v8::Undefined();
}
+
+JNIEXPORT jobject JNICALL Java_node_NodeDynamicProxyClass_callJs(JNIEnv *env, jobject src, jint ptr, jobject method, jobjectArray args) {
+ v8::HandleScope scope;
+
+ DynamicProxyData* dynamicProxyData = (DynamicProxyData*)ptr;
+ jclass methodClazz = env->FindClass("java/lang/reflect/Method");
+ jmethodID method_getName = env->GetMethodID(methodClazz, "getName", "()Ljava/lang/String;");
+ std::string methodName = javaObjectToString(env, env->CallObjectMethod(method, method_getName));
+
+ v8::Local<v8::Value> fnObj = dynamicProxyData->functions->Get(v8::String::New(methodName.c_str()));
+ if(fnObj->IsUndefined() || fnObj->IsNull()) {
+ printf("ERROR: Could not find method %s", methodName.c_str());
+ }
+ if(!fnObj->IsFunction()) {
+ printf("ERROR: %s is not a function.", methodName.c_str());
+ }
+ v8::Function* fn = v8::Function::Cast(*fnObj);
+
+ v8::Array* v8Args = v8::Array::Cast(*javaArrayToV8(dynamicProxyData->java, env, args));
+ int argc = v8Args->Length();
+ v8::Handle<v8::Value>* argv = new v8::Handle<v8::Value>[argc];
+ for(int i=0; i<argc; i++) {
+ argv[i] = v8Args->Get(i);
+ }
+ v8::Local<v8::Value> result = fn->Call(dynamicProxyData->functions, argc, argv);
+ delete[] argv;
+
+ return v8ToJava(env, result);
+}
View
@@ -21,6 +21,7 @@ class Java : public node::ObjectWrap {
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> newDynamicProxy(const v8::Arguments& args);
static v8::Handle<v8::Value> callStaticMethod(const v8::Arguments& args);
static v8::Handle<v8::Value> callStaticMethodSync(const v8::Arguments& args);
static v8::Handle<v8::Value> findClassSync(const v8::Arguments& args);
View
@@ -82,7 +82,7 @@ JavaObject::~JavaObject() {
jobject method = javaFindMethod(env, self->m_class, methodNameStr, methodArgs);
if(method == NULL) {
- EXCEPTION_CALL_CALLBACK("Could not call method " << methodNameStr);
+ EXCEPTION_CALL_CALLBACK("Could not find method " << methodNameStr);
return v8::Undefined();
}
@@ -108,7 +108,9 @@ JavaObject::~JavaObject() {
jobject method = javaFindMethod(env, self->m_class, methodNameStr, methodArgs);
if(method == NULL) {
- return v8::Undefined();
+ std::ostringstream errStr;
+ errStr << "Could not find method " << methodNameStr;
+ return ThrowException(javaExceptionToV8(env, errStr.str()));
}
// run
Oops, something went wrong.
View
@@ -202,7 +202,27 @@ jobject v8ToJava(JNIEnv* env, v8::Local<v8::Value> arg) {
v8::String::AsciiValue constructorName(obj->GetConstructorName());
if(strcmp(*constructorName, "JavaObject") == 0) {
JavaObject* javaObject = node::ObjectWrap::Unwrap<JavaObject>(obj);
- return javaObject->getObject();
+ jobject jobj = javaObject->getObject();
+ jclass nodeDynamicProxyClass = env->FindClass("node/NodeDynamicProxyClass");
+
+ if(env->IsInstanceOf(jobj, nodeDynamicProxyClass)) {
+ jfieldID ptrField = env->GetFieldID(nodeDynamicProxyClass, "ptr", "I");
+ DynamicProxyData* proxyData = (DynamicProxyData*)(int)env->GetIntField(jobj, ptrField);
+
+ jclass dynamicInterface = javaFindClass(env, proxyData->interfaceName);
+ jclass classClazz = env->FindClass("java/lang/Class");
+ jobjectArray classArray = env->NewObjectArray(1, classClazz, NULL);
+ env->SetObjectArrayElement(classArray, 0, dynamicInterface);
+
+ jmethodID class_getClassLoader = env->GetMethodID(classClazz, "getClassLoader", "()Ljava/lang/ClassLoader;");
+ jobject classLoader = env->CallObjectMethod(dynamicInterface, class_getClassLoader);
+
+ jclass proxyClass = env->FindClass("java/lang/reflect/Proxy");
+ jmethodID proxy_newProxyInstance = env->GetStaticMethodID(proxyClass, "newProxyInstance", "(Ljava/lang/ClassLoader;[Ljava/lang/Class;Ljava/lang/reflect/InvocationHandler;)Ljava/lang/Object;");
+ jobj = env->CallStaticObjectMethod(proxyClass, proxy_newProxyInstance, classLoader, classArray, jobj);
+ }
+
+ return jobj;
}
}
View
@@ -23,6 +23,12 @@ typedef enum _jvalueType {
TYPE_ARRAY = 9
} jvalueType;
+struct DynamicProxyData {
+ Java* java;
+ std::string interfaceName;
+ v8::Persistent<v8::Object> functions;
+};
+
std::list<jobject> javaReflectionGetMethods(JNIEnv *env, jclass clazz);
std::list<jobject> javaReflectionGetFields(JNIEnv *env, jclass clazz);
std::string javaToString(JNIEnv *env, jstring str);
@@ -45,6 +51,15 @@ jobject javaFindConstructor(JNIEnv *env, jclass clazz, jobjectArray methodArgs);
#define UNUSED_VARIABLE(var) var = var;
+#define ARGS_FRONT_OBJECT(ARGNAME) \
+ if(args.Length() < argsStart+1 || !args[argsStart]->IsObject()) { \
+ std::ostringstream errStr; \
+ errStr << "Argument " << (argsStart+1) << " must be an object"; \
+ return ThrowException(v8::Exception::TypeError(v8::String::New(errStr.str().c_str()))); \
+ } \
+ v8::Local<v8::Object> ARGNAME = v8::Local<v8::Object>::Cast(args[argsStart]); \
+ argsStart++;
+
#define ARGS_FRONT_STRING(ARGNAME) \
if(args.Length() < argsStart+1 || !args[argsStart]->IsString()) { \
std::ostringstream errStr; \
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,28 @@
+
+public class RunInterface {
+ public static interface Interface0Arg {
+ public void run();
+ }
+
+ public static interface Interface1Arg {
+ public void run(String arg1);
+ }
+
+ public static interface InterfaceWithReturn {
+ public int run(int arg1);
+ }
+
+ public void run0Args(Interface0Arg r) {
+ r.run();
+ r.run();
+ }
+
+ public void run1Args(Interface1Arg r) {
+ r.run("test1");
+ r.run("test1");
+ }
+
+ public int runWithReturn(InterfaceWithReturn r) {
+ return r.run(42);
+ }
+}
@@ -0,0 +1,56 @@
+'use strict';
+
+var java = require("../testHelpers").java;
+var nodeunit = require("nodeunit");
+var util = require("util");
+
+exports['Dynamic Proxy'] = nodeunit.testCase({
+ "0 Arguments": function (test) {
+ var callCount = 0;
+
+ var myProxy = java.newDynamicProxy('RunInterface$Interface0Arg', {
+ run: function () {
+ callCount++;
+ }
+ });
+
+ var runInterface = java.newInstanceSync("RunInterface");
+ runInterface.run0ArgsSync(myProxy);
+
+ test.equals(callCount, 2);
+
+ test.done();
+ },
+
+ "1 Arguments": function (test) {
+ var runData = '';
+
+ var myProxy = java.newDynamicProxy('RunInterface$Interface1Arg', {
+ run: function (str) {
+ runData += str;
+ }
+ });
+
+ var runInterface = java.newInstanceSync("RunInterface");
+ runInterface.run1ArgsSync(myProxy);
+
+ test.equals(runData, 'test1test1');
+
+ test.done();
+ },
+
+ "1 Arguments with return data": function (test) {
+ var myProxy = java.newDynamicProxy('RunInterface$InterfaceWithReturn', {
+ run: function (i) {
+ return i + 1;
+ }
+ });
+
+ var runInterface = java.newInstanceSync("RunInterface");
+ var result = runInterface.runWithReturnSync(myProxy);
+
+ test.equals(result, 43);
+
+ test.done();
+ }
+});

0 comments on commit da2fae0

Please sign in to comment.