Skip to Main Content

Java User Groups

Announcement

Testing banner

Can I get failure details when JLI_Launch fails due to a class load failure?

Jeff HoltApr 13 2021 — edited Apr 13 2021

TLDR; Can I use JLI_Launch or JNI_CreateJavaVM to launch a Java Swing app and get diagnostics via some API in case the launch fails (e.g., class load failure)? Can I do this without duplicating much code?
The appbundler project, for launching macOS Java applications, launches an app using JLI_Launch. If the created VM cannot load the main class, say, due to a class loader problem, then the app bundler has no way to notify the user. That's because JLI_Launch eventually calls exit.
I quickly discovered there is actually only one documented API and it does not include JLI_Launch.
In my attempts to use the only documented (i.e., in jni.h) vm creation code, I was able to launch only non-swing applications. I believe this has something to do with event loops and multiple threads.
I downloaded the source for JDK 16 and it includes the source for the code that calls JLI_Launch on macOS. It confirmed the calling of the CRT exit function and it exposed the code that I think I'd have to write to make proper use of JNI_CreateJavaVM for a Swing application.
I believe that I could duplicate most of JLI_Launch and its dependencies. Then prevent it from calling exit so that I can interrogate the created virtual machines and see why they failed. But I do not want to do that because surely someone has anticipated that and the code exists someone. Surely.
Below is a listing of filename and function name involved in this "problem", all arranged in a call stack fashion:

src/java.base/macosx/native/libjli/java_md_macosx.m: apple_main
src/java.base/macosx/native/libjli/java_md_macosx.m: MacOSXStartup
src/java.base/macosx/native/libjli/java_md_macosx.m: CreateExecutionEnvironment
src/java.base/share/native/libjli/java.c: JLI_Launch

The Minimal, Complete, Verifiable example that I have to demonstrate the failing of the documented API when launching a Swing (but probably any toolkit) is attached as test.zip.
test.zip (1.79 KB)When the MCV runs Test2 (console hello world), all is well but when it runs Test1 (mcv swing class), it fails in the FindClass function. In fact, FindClass never returns.

$ gcc -o test.exe -DMACOS -g -I $JAVA_HOME/include -I $JAVA_HOME/include/darwin test.c
$ javac Test1.java Test2.java
$ ./test.exe Test2 # this is the console hello world app
returned ok from dlopen /Library/Java/JavaVirtualMachines/jdk1.8.0_271.jdk/Contents/Home/../MacOS/libjli.dylib
returned ok from dlsym
returned ok from JNI_CreateJavaVM
returned ok from FindClass(Test2)
returned ok from GetStaticMethodID
Hello, world.
returned ok from CallStaticVoidMethod
$ ./test.exe Test1 # this is the swing app
returned ok from dlopen /Library/Java/JavaVirtualMachines/jdk1.8.0_271.jdk/Contents/Home/../MacOS/libjli.dylib
returned ok from dlsym
returned ok from JNI_CreateJavaVM

At the moment test.exe calls FindClass, it enters a seemingly infinite loop. It partially starts the app but only the part of the app that sets the macOS app menu at the top of the current display. You have to press ^C and then click on the app icon to make it stop.
Proof that JLI_Launch is undocumented in the installed JDK's:

$ find /Library/Java/JavaVirtualMachines -name '*.h' -exec grep JLI_Launch {} \;
$ find . /Library/Java/JavaVirtualMachines -name '*.h' -exec grep -l JLI_Launch {} \;
./src/java.base/share/native/launcher/defines.h
./src/java.base/share/native/libjli/java.h
./src/jdk.jpackage/share/native/applauncher/JvmLauncher.h
$ find . /Library/Java/JavaVirtualMachines -name java.h
./src/java.base/share/native/libjli/java.h
$ find /Library/Java/JavaVirtualMachines -name java.h
$

JLI_Launch is documented only in the JDK source.

Comments
Post Details
Added on Apr 13 2021
0 comments
42 views