summaryrefslogtreecommitdiffstats
path: root/sca-cpp/trunk/modules/java/eval.hpp
diff options
context:
space:
mode:
authorjsdelfino <jsdelfino@13f79535-47bb-0310-9956-ffa450edef68>2010-01-11 08:29:39 +0000
committerjsdelfino <jsdelfino@13f79535-47bb-0310-9956-ffa450edef68>2010-01-11 08:29:39 +0000
commit81678737cc3682dd811660ca794851b72e2b1fbe (patch)
tree62d0ffd191130ed443c777fdad7a24eea157b157 /sca-cpp/trunk/modules/java/eval.hpp
parentf5202e9e875e5e6c797e97c4db53f1f4d7364c65 (diff)
Added utility functions to help call Java code using JNI and wrap Axis2/java in a component.
git-svn-id: http://svn.us.apache.org/repos/asf/tuscany@897789 13f79535-47bb-0310-9956-ffa450edef68
Diffstat (limited to '')
-rw-r--r--sca-cpp/trunk/modules/java/eval.hpp398
1 files changed, 398 insertions, 0 deletions
diff --git a/sca-cpp/trunk/modules/java/eval.hpp b/sca-cpp/trunk/modules/java/eval.hpp
new file mode 100644
index 0000000000..b51a57337e
--- /dev/null
+++ b/sca-cpp/trunk/modules/java/eval.hpp
@@ -0,0 +1,398 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/* $Rev$ $Date$ */
+
+#ifndef tuscany_java_eval_hpp
+#define tuscany_java_eval_hpp
+
+/**
+ * Java component implementation evaluation logic.
+ */
+#include <jni.h>
+
+#include "list.hpp"
+#include "value.hpp"
+#include "io.hpp"
+
+namespace tuscany {
+namespace java {
+
+/**
+ * Initialize a JVM.
+ */
+jobject JNICALL nativeInvoke(JNIEnv *env, jobject self, jobject proxy, jobject method, jobjectArray args);
+
+class JavaRuntime {
+public:
+ JavaRuntime() {
+
+ // Create a JVM
+ JavaVMInitArgs args;
+ args.version = JNI_VERSION_1_6;
+ args.ignoreUnrecognized = JNI_FALSE;
+ JavaVMOption options[1];
+ options[0].optionString = const_cast<char*>("-Djava.class.path=.");
+ args.options = options;
+ args.nOptions = 1;
+ JNI_CreateJavaVM(&jvm, (void**)&env, &args);
+
+ // Capture JVM standard IO
+ setupIO();
+
+ // Lookup the system classes and methods we need
+ classClass = env->FindClass("java/lang/Class");
+ methodClass = env->FindClass("java/lang/reflect/Method");
+ objectClass = env->FindClass("java/lang/Object");
+ doubleClass = env->FindClass("java/lang/Double");
+ booleanClass = env->FindClass("java/lang/Boolean");
+ stringClass = env->FindClass("java/lang/String");
+ objectArrayClass = env->FindClass("[Ljava/lang/Object;");
+ doubleValueOf = env->GetStaticMethodID(doubleClass, "valueOf", "(D)Ljava/lang/Double;");
+ doubleValue = env->GetMethodID(doubleClass, "doubleValue", "()D");
+ booleanValueOf = env->GetStaticMethodID(booleanClass, "valueOf", "(Z)Ljava/lang/Boolean;");
+ booleanValue = env->GetMethodID(booleanClass, "booleanValue", "()Z");
+ declaredMethods = env->GetMethodID(classClass, "getDeclaredMethods", "()[Ljava/lang/reflect/Method;");
+ methodName = env->GetMethodID(methodClass, "getName", "()Ljava/lang/String;");
+
+ // Register our native invocation handler native
+ invokerClass = env->FindClass("org/apache/tuscany/InvocationHandler");
+ invokerValueOf = env->GetStaticMethodID(invokerClass, "valueOf", "(Ljava/lang/Class;J)Ljava/lang/Object;");
+ invokerLambda = env->GetFieldID(invokerClass, "lambda", "J");
+ JNINativeMethod nm;
+ nm.name = const_cast<char*>("invoke");
+ nm.signature = const_cast<char*>("(Ljava/lang/Object;Ljava/lang/reflect/Method;[Ljava/lang/Object;)Ljava/lang/Object;");
+ nm.fnPtr = (void*)nativeInvoke;
+ env->RegisterNatives(invokerClass, &nm, 1);
+ }
+
+ ~JavaRuntime() {
+ if (jvm == NULL)
+ return;
+ jvm->DestroyJavaVM();
+ }
+
+ JavaVM* jvm;
+ JNIEnv* env;
+
+ jclass classClass;
+ jclass methodClass;
+ jclass objectClass;
+ jclass doubleClass;
+ jclass booleanClass;
+ jclass stringClass;
+ jclass objectArrayClass;
+ jmethodID doubleValueOf;
+ jmethodID doubleValue;
+ jmethodID booleanValueOf;
+ jmethodID booleanValue;
+ jmethodID declaredMethods;
+ jmethodID methodName;
+
+ jclass invokerClass;
+ jmethodID invokerValueOf;
+ jfieldID invokerLambda;
+
+} javaRuntime;
+
+/**
+ * Return the last exception that occurred in a JVM.
+ */
+string lastException(const JavaRuntime& jr) {
+ if (!jr.env->ExceptionCheck())
+ return "No Exception";
+ jr.env->ExceptionDescribe();
+ jr.env->ExceptionClear();
+ return "UnknownException";
+}
+
+/**
+ * Declare conversion functions.
+ */
+const jobject valueToJobject(const JavaRuntime& jr, const value& v);
+const value jobjectToValue(const JavaRuntime& jr, const jobject o);
+const jobjectArray valuesToJarray(const JavaRuntime& jr, const list<value>& v);
+const list<value> jarrayToValues(const JavaRuntime& jr, const jobjectArray o);
+
+/**
+ * Convert a Java class name to a JNI class name.
+ */
+const bool jniClassNameHelper(char* to, const char* from) {
+ if (*from == '\0') {
+ *to = '\0';
+ return true;
+ }
+ *to = *from == '.'? '/' : *from;
+ return jniClassNameHelper(to + 1, from + 1);
+}
+
+const string jniClassName(const string& from) {
+ char buf[length(from) + 1];
+ jniClassNameHelper(buf, c_str(from));
+ return string(buf);
+}
+
+/**
+ * Create a new Java object representing a lambda expression.
+ */
+class javaLambda {
+public:
+ javaLambda(const JavaRuntime& jr, const value& iface, const lambda<value(const list<value>&)>& func) : jr(jr), iface(iface), func(func) {
+ }
+
+ const JavaRuntime& jr;
+ const value iface;
+ const lambda<value(const list<value>&)> func;
+};
+
+/**
+ * Invocation handler invoke method, dispatches to the lambda function wrapped
+ * in the invocation handler.
+ */
+jobject JNICALL nativeInvoke(JNIEnv* env, jobject self, unused jobject proxy, jobject method, jobjectArray args) {
+
+ // Retrieve the lambda function from the invocation handler
+ jclass clazz = env->GetObjectClass(self);
+ jfieldID f = env->GetFieldID(clazz, "lambda", "J");
+ const javaLambda& jl = *(javaLambda*)(long)env->GetLongField(self, f);
+
+ // Retrieve the function name
+ const jstring s = (jstring)env->CallObjectMethod(method, jl.jr.methodName);
+ const char* c = env->GetStringUTFChars(s, NULL);
+ const value func(c);
+ env->ReleaseStringUTFChars(s, c);
+
+ // Build the expression to evaluate
+ const list<value> expr = cons<value>(func, jarrayToValues(jl.jr, args));
+
+ // Invoke the lambda function
+ value result = jl.func(expr);
+
+ // Convert result to a jobject
+ return valueToJobject(jl.jr, result);
+}
+
+const jobject mkJavaLambda(const JavaRuntime& jr, const lambda<value(const list<value>&)>& l) {
+
+ // The lambda function is given the opportunity to give us a
+ // Java interface name that it implements, and which will be
+ // used as the type of the Java proxy representing it. If the
+ // lambda function doesn't specify an interface, then the
+ // proxy implements javax.script.Invocable.
+ const value iface = l(mklist<value>("interface"));
+ const gc_ptr<javaLambda> jl = new (gc_new<javaLambda>()) javaLambda(jr, iface, l);
+
+ jclass jc = jr.env->FindClass(c_str(jniClassName(string(iface))));
+ const jobject obj = jr.env->CallStaticObjectMethod(jr.invokerClass, jr.invokerValueOf, jc, (long)(javaLambda*)jl);
+ return obj;
+}
+
+/**
+ * Convert a list of values to a Java jobjectArray.
+ */
+const jobjectArray valuesToJarrayHelper(const JavaRuntime& jr, jobjectArray a, const list<value>& v, const int i) {
+ if (isNil(v))
+ return a;
+ jr.env->SetObjectArrayElement(a, i, valueToJobject(jr, car(v)));
+ return valuesToJarrayHelper(jr, a, cdr(v), i + 1);
+}
+
+const jobjectArray valuesToJarray(const JavaRuntime& jr, const list<value>& v) {
+ jobjectArray a = jr.env->NewObjectArray(length(v), jr.objectClass, NULL);
+ return valuesToJarrayHelper(jr, a, v, 0);
+}
+
+/**
+ * Convert a value to a Java jobject.
+ */
+const jobject valueToJobject(const JavaRuntime& jr, const value& v) {
+ switch (type(v)) {
+ case value::List:
+ return valuesToJarray(jr, v);
+ case value::Lambda:
+ return mkJavaLambda(jr, v);
+ case value::Symbol:
+ return jr.env->NewStringUTF(c_str(string("'") + v));
+ case value::String:
+ return jr.env->NewStringUTF(c_str(v));
+ case value::Number:
+ return jr.env->CallStaticObjectMethod(jr.doubleClass, jr.doubleValueOf, (double)v);
+ case value::Bool:
+ return jr.env->CallStaticObjectMethod(jr.booleanClass, jr.booleanValueOf, (double)v);
+ default:
+ return NULL;
+ }
+}
+
+/**
+ * Convert a list of values to an array of jvalues.
+ */
+const jvalue* valuesToJvaluesHelper(const JavaRuntime& jr, jvalue* a, const list<value>& v) {
+ if (isNil(v))
+ return a;
+ a->l = valueToJobject(jr, car(v));
+ return valuesToJvaluesHelper(jr, a + 1, cdr(v));
+}
+
+const jvalue* valuesToJvalues(const JavaRuntime& jr, const list<value>& v) {
+ const int n = length(v);
+ jvalue* a = new (gc_anew<jvalue>(n)) jvalue[n];
+ valuesToJvaluesHelper(jr, a, v);
+ return a;
+}
+
+/**
+ * Convert a Java jobjectArray to a list of values.
+ */
+const list<value> jarrayToValuesHelper(const JavaRuntime& jr, jobjectArray a, const int i, const int size) {
+ if (i == size)
+ return list<value>();
+ return cons(jobjectToValue(jr, jr.env->GetObjectArrayElement(a, i)), jarrayToValuesHelper(jr, a, i + 1, size));
+}
+
+const list<value> jarrayToValues(const JavaRuntime& jr, jobjectArray o) {
+ return jarrayToValuesHelper(jr, o, 0, jr.env->GetArrayLength(o));
+}
+
+/**
+ * Lambda function used to represent a Java callable object.
+ */
+struct javaCallable {
+ const JavaRuntime& jr;
+ const jobject obj;
+
+ javaCallable(const JavaRuntime& jr, const jobject obj) : jr(jr), obj(obj) {
+ }
+
+ const value operator()(const list<value>& args) const {
+ jobjectArray jargs = valuesToJarray(jr, args);
+ jobject result = jargs; //CallObject(func, jargs);
+ return jobjectToValue(jr, result);
+ }
+};
+
+/**
+ * Convert a Java jobject to a value.
+ */
+const value jobjectToValue(const JavaRuntime& jr, const jobject o) {
+ if (o == NULL)
+ return value();
+ const jclass clazz = jr.env->GetObjectClass(o);
+ if ((jr.env->IsSameObject(clazz, jr.stringClass))) {
+ const char* s = jr.env->GetStringUTFChars((jstring)o, NULL);
+ if (*s == '\'') {
+ const value v(s + 1);
+ jr.env->ReleaseStringUTFChars((jstring)o, s);
+ return v;
+ }
+ const value v = string(s);
+ jr.env->ReleaseStringUTFChars((jstring)o, s);
+ return v;
+ }
+ if (jr.env->IsSameObject(clazz, jr.booleanClass))
+ return value((bool)jr.env->CallBooleanMethod(o, jr.booleanValue));
+ if (jr.env->IsSameObject(clazz, jr.doubleClass))
+ return value((double)jr.env->CallDoubleMethod(o, jr.doubleValue));
+ if ((jr.env->IsSameObject(clazz, jr.objectArrayClass)))
+ return jarrayToValues(jr, (jobjectArray)o);
+ return lambda<value(const list<value>&)>(javaCallable(jr, o));
+}
+
+/**
+ * Returns a balanced tree of a class' methods.
+ */
+const value methodToValue(const JavaRuntime& jr, const jobject m) {
+ const jobject s = jr.env->CallObjectMethod(m, jr.methodName);
+ const char* c = jr.env->GetStringUTFChars((jstring)s, NULL);
+ const string& name = string(c);
+ jr.env->ReleaseStringUTFChars((jstring)s, c);
+ const jmethodID mid = jr.env->FromReflectedMethod(m);
+ return mklist<value>(c_str(name), (double)(long)mid);
+}
+
+const list<value> methodsToValues(const JavaRuntime& jr, const jobjectArray m, const int i) {
+ if (i == 0)
+ return list<value>();
+ return cons<value>(methodToValue(jr, jr.env->GetObjectArrayElement(m, i - 1)), methodsToValues(jr, m, i - 1));
+}
+
+const list<value> methodsToValues(const JavaRuntime& jr, const jclass clazz) {
+ const jobjectArray m = (jobjectArray)jr.env->CallObjectMethod(clazz, jr.declaredMethods);
+ return methodsToValues(jr, m, jr.env->GetArrayLength(m));
+}
+
+/**
+ * Represents a Java Class.
+ */
+class JavaClass {
+public:
+ JavaClass() : clazz(NULL), obj(NULL) {
+ }
+
+ JavaClass(const jclass clazz, const jobject obj, const list<value> m) : clazz(clazz), obj(obj), m(m) {
+ }
+
+ const jclass clazz;
+ const jobject obj;
+ const list<value> m;
+};
+
+/**
+ * Read a class.
+ */
+const failable<JavaClass> readClass(const JavaRuntime& jr, const string& name) {
+ const string jname = jniClassName(name);
+ const jclass clazz = jr.env->FindClass(c_str(jname));
+ if (clazz == NULL)
+ return mkfailure<JavaClass>(string("Couldn't load class: ") + name + " : " + lastException(jr));
+ const jmethodID constr = jr.env->GetMethodID(clazz, "<init>", "()V");
+ if (constr == NULL)
+ return mkfailure<JavaClass>(string("Couldn't find constructor: ") + name + " : " + lastException(jr));
+ const jobject obj = jr.env->NewObject(clazz, constr);
+ if (obj == NULL)
+ return mkfailure<JavaClass>(string("Couldn't construct object: ") + name + " : " + lastException(jr));
+ return JavaClass(clazz, obj, methodsToValues(jr, clazz));
+}
+
+/**
+ * Evaluate an expression against a Java class.
+ */
+const failable<value> evalClass(const JavaRuntime& jr, const value& expr, const JavaClass jc) {
+
+ // Lookup the Java function named as the expression operand
+ const list<value> func = assoc<value>(car<value>(expr), jc.m);
+ if (isNil(func))
+ return mkfailure<value>(string("Couldn't find function: ") + car<value>(expr) + " : " + lastException(jr));
+ const jmethodID fid = (jmethodID)(long)(double)cadr(func);
+
+ // Convert args to Java jvalues
+ const jvalue *args = valuesToJvalues(jr, cdr<value>(expr));
+
+ // Call the Java function
+ const jobject result = jr.env->CallObjectMethodA(jc.obj, fid, args);
+ if (result == NULL)
+ return mkfailure<value>(string("Function call failed: ") + car<value>(expr) + " : " + lastException(jr));
+
+ // Convert Java result to a value
+ return jobjectToValue(jr, result);
+}
+
+}
+}
+#endif /* tuscany_java_eval_hpp */