Search code examples
javac++jvmjava-native-interfacejvm-arguments

How do I get an error message when failing to load a JVM via JNI?


I would like to retrieve an error message that explains why the jvm failed to load. From the examples provided here:

http://java.sun.com/docs/books/jni/html/invoke.html

I extracted this example:

 /* Create the Java VM */
 res = JNI_CreateJavaVM(&jvm, (void**)&env, &vm_args);

 if (res < 0) {
     // retrieve verbose error here?
     fprintf(stderr, "Can't create Java VM\n");
     exit(1);
 }

In my specific case I'm providing invalid arguments in the vm_args and would expect to see what I get on the command line: "Unrecognized option: -foo=bar"

On further testing it looks like the jvm is putting the message I want to stdout or stderr. I believe I would need to capture stdout and stderr to get the error I'm looking for (unless of course there is a simpler way). I'm coding in C++ so if someone can show a way to capture the error into a stringstream that would be ideal.

Thanks, Randy


Solution

  • I was able to get what I needed by using the "vfprintf" option described here:

    https://web.archive.org/web/20111229234347/http://java.sun.com/products/jdk/faq/jnifaq-old.html

    although I used the jdk1.2 options. This code snippet summarizes my solution:

    static string jniErrors;
    
    static jint JNICALL my_vfprintf(FILE *fp, const char *format, va_list args)
    {
        char buf[1024];
        vsnprintf(buf, sizeof(buf), format, args);
        jniErrors += buf;
        return 0;
    }
    
    ...
    
    JavaVMOption options[1];
    options[0].optionString = "vfprintf";
    options[0].extraInfo = my_vfprintf;
    
    JavaVMInitArgs vm_args;
    memset(&vm_args, 0, sizeof(vm_args));
    vm_args.nOptions = 1;
    vm_args.options = options;
    vm_args.version = JNI_VERSION_1_4;
    vm_args.ignoreUnrecognized = JNI_FALSE;
    
    JNIEnv env;
    JavaVM jvm;
    
    jint res = JNI_CreateJavaVM(&jvm, (void**)&env, &vm_args);
    
    if (res != JNI_OK)
        setError(jniErrors);
    
    jniErrors.clear();
    

    Also interesting was that I could not for the life of me capture stdout or stderr correctly with any of the freopen or dup2 tricks. I could load my own dll and redirect correctly but not the jvm. Anyway, it's better this way because I wanted the errors in memory rather than in a file.