Search code examples
javac++pluginsjava-native-interface

c++ Plugin -> JNI --> Java Classpath


i am trying to create a Plugin for an Application. The Plugin needs to be written in c++. I want to use the Plugin on Windows and on Mac, so it would be great to write the Plugin in Java.

My Problem, there is an other Plugin using Java. Since they are using the same main Application there is already an running JavaVM.

JavaVM *jvm = NULL;
jsize jvm_count = 0;
jint res=0;


res = JNI_GetCreatedJavaVMs (&jvm, 1, &jvm_count);

My Problem: How can i change / modify the Classpath of the existing JavaVM? Or how can i create a new / second JavaVM?

I've tryed to load my jar file via JNI:

/* URL CLASS */
jclass URLcls;
URLcls = env->FindClass("java/net/URL");

/* URL CLASS CONSTRUCTOR*/
jmethodID URLclsMid;
URLclsMid = env->GetMethodID(URLcls, "<init>","(Ljava/lang/String;)V");

/* URL OBJECT */
jobject URLobj;
jstr = env->NewStringUTF("file:/path/to/test/file/test.jar");
URLobj = env->NewObject(URLcls, URLclsMid, jstr);

/* URL Array */
jobjectArray URLArray;
URLArray = env->NewObjectArray(1, URLcls, URLobj);



/*Thread Class*/
jclass ThreadCLS;
ThreadCLS = env->FindClass("java/lang/Thread");

/*Static Method currentThread*/
jmethodID ThreadCLS_currentThread;
ThreadCLS_currentThread = env->GetStaticMethodID(ThreadCLS, "currentThread","()Ljava/lang/Thread;");

/*get current Thread Object*/
jobject currentThread;
currentThread = env->CallStaticObjectMethod(ThreadCLS, ThreadCLS_currentThread);

/* getContextClassLoader method */  
jmethodID currentThread_getContextClassLoader;
currentThread_getContextClassLoader = env->GetMethodID(ThreadCLS, "getContextClassLoader","()Ljava/lang/ClassLoader;");

/* ClassLoader Class */     
jclass ClassLoaderCLS;
ClassLoaderCLS = env->FindClass("java/lang/ClassLoader");

/* get ClassLoader Object */
jobject classLoader = env->CallObjectMethod(currentThread, currentThread_getContextClassLoader);


/* URLClassLoader Class */
jclass URLClassLoaderCLS;
URLClassLoaderCLS = env->FindClass("java/net/URLClassLoader");

/* Static Method newInstance */
jmethodID URLClassLoaderCLS_newInstance;
URLClassLoaderCLS_newInstance = env->GetStaticMethodID(URLClassLoaderCLS, "newInstance","([Ljava/net/URL;Ljava/lang/ClassLoader;)Ljava/net/URLClassLoader;");


/* get new URLClassLoader Instance */
jobject myURLClassLoaderInstance;
myURLClassLoaderInstance = env->CallStaticObjectMethod(URLClassLoaderCLS, URLClassLoaderCLS_newInstance, URLArray, classLoader);


/* get setContextClassLoader Method */
jmethodID currentThread_setContextClassLoader;
currentThread_setContextClassLoader = env->GetMethodID(ThreadCLS, "setContextClassLoader","(Ljava/lang/ClassLoader;)V");

/* trying to set the ClassLoader from the current Thread */    
env->CallVoidMethod(currentThread, currentThread_setContextClassLoader, myURLClassLoaderInstance);


/* get loadClass Method */
jmethodID loadClass;
loadClass = env->GetMethodID(URLClassLoaderCLS, "loadClass","(Ljava/lang/String;)Ljava/lang/Class;");


/* get a Class from my test.jar*/
jstring methodString = env->NewStringUTF("de.domain.sayHello");
jclass myClass = (jclass)env->CallObjectMethod(myURLClassLoaderInstance, loadClass, methodString);


/*working till here*/


jmethodID myClassMethod;
myClassMethod = env->GetMethodID(myClass, "doIt","()Ljava/lang/String;");

if (myClassMethod == NULL) {
    // Method of Class "de.domain.sayHello" not found
}

What can i do to change the Classpath? or to load my jar File?

Thanks Lisa


Solution

  • I've solved my Problem. I did not manage to set or influence the Classpath in a way so that i can use:

    jmethodID myClassMethod;
    myClassMethod = env->GetMethodID(myClass, "doIt","()Ljava/lang/String;");
    
    if (myClassMethod == NULL) { 
        // Method of Class "de.domain.sayHello" not found
    }
    

    Instead of this i've created an Object of my Class using Java Reflection.

    Find my Java Class: (after "/working till here/" from the code of my question)

    /* Class CLASS */
    jclass Classcls = env->FindClass("java/lang/Class");
    
    
    /* String CLASS */
    jclass StringCls = env->FindClass("java/lang/String");
    
    jstring methodString = env->NewStringUTF("de.domain.sayHello");
    
    jclass myJavaClass = (jclass)env->CallObjectMethod(MyURLClassLoaderInstance, URLClassLoaderCLS_loadClass, methodString);
    

    Get an Object from my Java Class:

    /* CLASS newInstance Method*/
    jmethodID ClassNewInstanceMid = env->GetMethodID(Classcls, "newInstance","()Ljava/lang/Object;");
    
    
    
    jobject myObject = env->CallObjectMethod(myJavaClass, ClassNewInstanceMid);
    

    with this i got an object from my Java Class. (The Default Constructor was called)

    With the jobject i was able to call a method of my object via java reflection. I passed a Sring with the Application path to my Java Method.

    On the Java side i loaded the Application:

    import java.io.File;
    
    public class sayHello {
    
    public static native void sayHi(String message, int times);
    
    public sayHello()
    {
        System.out.println("object created :) ");
    
    }
    
    public void doIt(String test)
    {
    
    
         File myfile = new File(test);
    
           if(myfile.exists())
               System.load(myfile.getAbsolutePath());
           else
               System.out.println("Something went wrong :( ");
    
    
           sayHi("C++ Funktion Call out of Java",5);
    }
    
    }
    

    In my c++ Plugin i implemented this function:

    JNIEXPORT void JNICALL Java_de_domain_sayHello_sayHi
    (JNIEnv *localEnv, jclass clazz, jstring message, jint times)
    {
    
        int myT = times;
    
        for(int i=0;i<myT;i++)
            fprintf(stderr,"%s\n", localEnv->GetStringUTFChars(message, NULL));
    
    }
    

    An Interessting thing: Inside the JNICALL Method (Java_de_domain_sayHello_sayHi) i am able to find my Call via JNI:

    jclass myCLS = env->FindClass("de/domain/sayHello");
    if(myCLS != NULL){
         //class found
    }
    

    Inside this function it dosen't matter whether i use the JNIEnv "localEnv" from the function or "env" (global variable)

    Thanks Lisa