JNI crate. How pass -XstartOnFirstThread option to jvm?

68 views Asked by At

I need to add the "-XstartOnFirstThread" option to the JavaVM arguments in order to display the GUI on MacOs (Without this option, the application starts without errors, but does not display the GUI). On Linux and Windows without this option everything works fine. It is only needed for MacOs. When I add it to the list of options I get the error:

Unrecognized option: -XstartOnFirstThread
thread 'main' panicked at src/main.rs:39:6:
Failed to create JavaVM: Create(JniCall(Unknown))

How can I add it to the JVM? After all, without it, my application does not work correctly on MacOs (the application window is not created)

The code I use to initialize and start the JVM:

let jvm_args = InitArgsBuilder::new()
        .version(JNIVersion::V8)
        .option("-XstartOnFirstThread") // This option need for run GUI on MacOs
        //Classpath with natives skipped 
        .build()
        .unwrap();
    let jvm = JavaVM::new(jvm_args)
        .expect("Failed to create JavaVM");
    let mut env = jvm
        .attach_current_thread()
        .expect("Failed to attach JNIEnv");
    //Java class find and invoke static method Main skipped.

I spent several hours searching for answers on google and reading the documentation, but I still couldn’t find an answer, has anyone encountered this problem or have any ideas?

1

There are 1 answers

2
Botje On

After some investigation, it turns out that -XstartOnFirstThread is implemented in something called "libjli", not libjvm. The java executable links against libjli, whereas rust-jni uses libjvm as it normally should.

I managed to hack together the following code that uses the JLI_Launch function to start a Java interpreter. It launches the JVM on the main thread as you requested. You can verify this by setting _JAVA_LAUNCHER_DEBUG=1.

fn main() {
    let args = ["java", "-cp", ".", "-XstartOnFirstThread", "Test"];
    unsafe {
        let str_libjli = OsStr::new("/Library/Java/JavaVirtualMachines/temurin-21.jdk/Contents/Home/lib/libjli.dylib");
        let libjli = Library::new(str_libjli).unwrap();
        let jli_launch: Symbol<unsafe extern fn(c_int, *const *mut i8, c_int, *const *mut i8, c_int, *const *mut i8, *const i8, *const i8, *const i8, *const i8, c_int, c_int, c_int, c_int) -> sys::jint> =
            libjli.get(b"JLI_Launch\0").unwrap();
        let args_as_cstr : Vec<*mut i8> = args.into_iter().map(|s| CString::new(s).unwrap().into_raw()).collect();
        let empty = CString::new("").unwrap();
        let java = CString::new("java").unwrap();
        jli_launch(args_as_cstr.len() as i32, args_as_cstr.as_ptr(), 0, null_mut(), 0, null_mut(), empty.as_ptr(), empty.as_ptr(), java.as_ptr(), java.as_ptr(), 0, 0, 0 ,0);
    }
    let args = ["java", "-cp", ".", "-XstartOnFirstThread", "Test"];
    unsafe {
        let str_libjli = OsStr::new("/Library/Java/JavaVirtualMachines/temurin-21.jdk/Contents/Home/lib/libjli.dylib");
        let libjli = Library::new(str_libjli).unwrap();
        let jli_launch: Symbol<unsafe extern fn(c_int, *const *mut i8, c_int, *const *mut i8, c_int, *const *mut i8, *const i8, *const i8, *const i8, *const i8, c_int, c_int, c_int, c_int) -> sys::jint> =
            libjli.get(b"JLI_Launch\0").unwrap();
        let args_as_cstr : Vec<*mut i8> = args.into_iter().map(|s| CString::new(s).unwrap().into_raw()).collect();
        let empty = CString::new("").unwrap();
        let java = CString::new("java").unwrap();
        jli_launch(args_as_cstr.len() as i32, args_as_cstr.as_ptr(), 0, null_mut(), 0, null_mut(), empty.as_ptr(), empty.as_ptr(), java.as_ptr(), java.as_ptr(), 0, 0, 0 ,0);
    }
    let args = ["java", "-cp", ".", "-XstartOnFirstThread", "Test"];
    unsafe {
        let str_libjli = OsStr::new("/Library/Java/JavaVirtualMachines/temurin-21.jdk/Contents/Home/lib/libjli.dylib");
        let libjli = Library::new(str_libjli).unwrap();
        let jli_launch: Symbol<unsafe extern fn(c_int, *const *mut i8, c_int, *const *mut i8, c_int, *const *mut i8, *const i8, *const i8, *const i8, *const i8, c_int, c_int, c_int, c_int) -> sys::jint> =
            libjli.get(b"JLI_Launch\0").unwrap();
        let args_as_cstr : Vec<*mut i8> = args.into_iter().map(|s| CString::new(s).unwrap().into_raw()).collect();
        let empty = CString::new("").unwrap();
        let java = CString::new("java").unwrap();
        jli_launch(args_as_cstr.len() as i32, args_as_cstr.as_ptr(), 0, null_mut(), 0, null_mut(), empty.as_ptr(), empty.as_ptr(), java.as_ptr(), java.as_ptr(), 0, 0, 0 ,0);
    }
    // Program never gets here
}