|
@@ -1,166 +0,0 @@
|
|
|
-/* Example of calling java from ECL code via embedded C++
|
|
|
-*
|
|
|
-* This example uses the following code in JavaCat.java:
|
|
|
-*
|
|
|
-* public class JavaCat
|
|
|
-* {
|
|
|
-* public static String cat(String a, String b)
|
|
|
-* {
|
|
|
-* System.out.println("In java");
|
|
|
-* System.out.println(a+b);
|
|
|
-* return a + b;
|
|
|
-* }
|
|
|
-* }
|
|
|
-*
|
|
|
-* Compile it using
|
|
|
-*
|
|
|
-* javac javacat
|
|
|
-*
|
|
|
-* You can also generate the signature for the function to be called using
|
|
|
-*
|
|
|
-* javap -s -p javacat
|
|
|
-*
|
|
|
-* Note that a JVM - once loaded - cannot be successfully unloaded. Therefore this code will work
|
|
|
-* for repeated workunits on hthor, but on Thor and Roxie you will only be able to run once before
|
|
|
-* needing to restart the Thor/Roxie cluster. Moving the code into a plugin will help resolve that.
|
|
|
-*
|
|
|
-*/
|
|
|
-
|
|
|
-// Set the compiler/linker options needed to ensure that the jvm is linked
|
|
|
-
|
|
|
-#option ('compileOptions', '-I/usr/lib/jvm/java-6-openjdk/include/');
|
|
|
-#option ('linkOptions', '-L/usr/lib/jvm/java-6-openjdk/jre/lib/amd64/server/ -L/usr/lib/jvm/java-6-openjdk/jre/lib/amd64/ -Wl,-rpath,/usr/lib/jvm/java-6-openjdk/jre/lib/amd64/ -Wl,-rpath,/usr/lib/jvm/java-6-openjdk/jre/lib/amd64/server/');
|
|
|
-
|
|
|
-// Embedded C++ that makes a JNI call
|
|
|
-
|
|
|
-string cat(varstring a, varstring b) := BEGINC++
|
|
|
-
|
|
|
-// This section of the code should probably move to a plugin, or somesuch
|
|
|
-
|
|
|
-#include <jni.h>
|
|
|
-#include <assert.h>
|
|
|
-#option library jawt
|
|
|
-#option library jvm
|
|
|
-
|
|
|
-static JavaVM *javaVM; /* denotes a Java VM */
|
|
|
-static pthread_once_t jni_init_flag = PTHREAD_ONCE_INIT; /* Ensures initialized just once */
|
|
|
-
|
|
|
-static void initJNI()
|
|
|
-{
|
|
|
- assert (!javaVM);
|
|
|
- JavaVMInitArgs vm_args; /* JDK/JRE 6 VM initialization arguments */
|
|
|
- JavaVMOption* options = new JavaVMOption[2];
|
|
|
- char classPath[2048];
|
|
|
- strcpy(classPath,"-Djava.class.path=.:/opt/HPCCSystems/examples/jni");
|
|
|
- const char *oldPath = getenv("CLASSPATH");
|
|
|
- if (oldPath && *oldPath)
|
|
|
- {
|
|
|
- strcat(classPath, ":");
|
|
|
- strcat(classPath, oldPath);
|
|
|
- }
|
|
|
- options[0].optionString = classPath;
|
|
|
- options[1].optionString = (char *) "-verbose:jni";
|
|
|
- vm_args.version = JNI_VERSION_1_6;
|
|
|
- vm_args.nOptions = 1; // set to 2 if you want the verbose...
|
|
|
- vm_args.options = options;
|
|
|
- vm_args.ignoreUnrecognized = false;
|
|
|
- /* load and initialize a Java VM, return a JNI interface pointer in env */
|
|
|
- JNIEnv *env; /* receives pointer to native method interface */
|
|
|
- jint res =JNI_CreateJavaVM(&javaVM, (void**)&env, &vm_args);
|
|
|
- delete options;
|
|
|
- if (res < 0)
|
|
|
- {
|
|
|
- rtlFail(res, "Couldn't create JVM");
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-static JNIEnv *getJNIEnvironment()
|
|
|
-{
|
|
|
- pthread_once(&jni_init_flag, initJNI);
|
|
|
- // Make sure we attach the current thread to the JVM env...
|
|
|
- // MORE - not sure how efficient it is to call AttachCurrentThread every time
|
|
|
- // - We could probably avoid doing so if we add a hook into jthread
|
|
|
- // - we are also never calling DetachCurrentThread
|
|
|
- JNIEnv *JNIenv; /* receives pointer to native method interface */
|
|
|
- jint res = javaVM->AttachCurrentThread((void **) &JNIenv, NULL);
|
|
|
- if (!res < 0)
|
|
|
- printf("Thread attach failed");
|
|
|
- return JNIenv;
|
|
|
-}
|
|
|
-
|
|
|
-// MORE - as coded, the JavaVM is never released.
|
|
|
-// This is not too big a deal for a standalone ECL test program for POC,
|
|
|
-// and isn't that much of a problem in a hthor program, as the process terminates
|
|
|
-// at the end of of the workunit execution, but might be an issue if using from thor or roxie.
|
|
|
-//
|
|
|
-// Also should consider whether the java VM should be shared between queries.
|
|
|
-//
|
|
|
-// There is a known bug in DestroyJavaVM, which means you can't actually create more than one Java VM per process.
|
|
|
-// This bug has been around forever, and is not going to get fixed. So the options here are limited...
|
|
|
-
|
|
|
-//--------------------------------------------------------
|
|
|
-
|
|
|
-// This section of code should ideally be generated by the ECL compiler in response to a method being marked as ,jni
|
|
|
-// You would need similar code for each java method you want to call
|
|
|
-
|
|
|
-static jclass JavaCat;
|
|
|
-static jmethodID JavaCat_cat;
|
|
|
-static pthread_once_t jni_resolve_flag = PTHREAD_ONCE_INIT; /* Ensures called just once */
|
|
|
-
|
|
|
-static void resolveJNIMethods()
|
|
|
-{
|
|
|
- /* Do all the function resolution just the once... */
|
|
|
- /* MORE - this bit should be generated too */
|
|
|
- JNIEnv *env = getJNIEnvironment();
|
|
|
- JavaCat = env->FindClass("JavaCat");
|
|
|
- JavaCat_cat = env->GetStaticMethodID(JavaCat, "cat", "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;");
|
|
|
-}
|
|
|
-
|
|
|
-//--------------------------------------------------------
|
|
|
-
|
|
|
-// This section of code should ideally be generated by the ECL compiler inline when calling a ,jni method
|
|
|
-
|
|
|
-#body
|
|
|
-
|
|
|
-// extern void user1(size32_t & __lenResult,char * & __result,const char * a,const char * b) {
|
|
|
- pthread_once(&jni_resolve_flag, resolveJNIMethods);
|
|
|
-
|
|
|
- JNIEnv *env = getJNIEnvironment();
|
|
|
- if (!env || !JavaCat || !JavaCat_cat)
|
|
|
- rtlFail(0, "Failed to resolve JNI functions");
|
|
|
- jstring jstrA = env->NewStringUTF(a);
|
|
|
- jstring jstrB = env->NewStringUTF(b);
|
|
|
- jstring result = (jstring) env->CallStaticObjectMethod(JavaCat, JavaCat_cat, jstrA, jstrB);
|
|
|
- // MORE - should consider marshalling java exceptions into c++ ones here...
|
|
|
-
|
|
|
- __lenResult = env->GetStringUTFLength(result);
|
|
|
- const char * chars = env->GetStringUTFChars(result, NULL);
|
|
|
- __result = (char *)rtlMalloc(__lenResult);
|
|
|
- memcpy(__result, chars, __lenResult);
|
|
|
- env->ReleaseStringUTFChars(result, chars);
|
|
|
-// }
|
|
|
-ENDC++;
|
|
|
-
|
|
|
-//--------------------------------------------------------
|
|
|
-
|
|
|
-// ECL code - an input dataset with 2 records, each containing 2 strings
|
|
|
-
|
|
|
-inrec := RECORD
|
|
|
- string f1;
|
|
|
- string f2;
|
|
|
- END;
|
|
|
-infile1 := DATASET([{'a', 'b'}, {'c', 'd'}], inrec);
|
|
|
-infile2 := DATASET([{'e', 'f'}, {'g', 'h'}], inrec);
|
|
|
-
|
|
|
-// Output record has just one string, filled in from the result of the java function
|
|
|
-outrec := RECORD
|
|
|
- string c;
|
|
|
- END;
|
|
|
-
|
|
|
-outrec t(inrec L) := TRANSFORM
|
|
|
- SELF.c := cat(L.f1, L.f2) // Calls Java function
|
|
|
-END;
|
|
|
-
|
|
|
-outfile := project(infile1, t(LEFT))+project(infile2, t(LEFT)); // threaded concat operation
|
|
|
-
|
|
|
-outfile;
|