Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Javascript bridge / upcall to JavaFX (via JSObject.setMember() method) breaks when distributing

The Problem

I spent several hours trying to determine why my distributed code fails and yet my source code when debugging with the IDE (NetBeans) works without issue. I have found a solution and am posting to help others that might have similar issues. BTW: I'm a self-taught programmer and might be missing a few fundamental concepts -- feel free to educate me.

Background Information

Using a WebView control within JavaFX application I load a webpage from an html file. I want to use JavaScript to handle the HTML side of things but I also need to freely pass information between Java and JavaScript (both directions). Works great to use the WebEngine.executeScript() method for Java initiated transfers and to use JSObject.setMember() in Java to set up a way for JavaScript to initiate information transfer to Java.

Setting up the link (this way breaks later):

/*Simple example class that gives method for 
JavaScript to send text to Java debugger console*/
public static class JavaLink {
    public void showMsg(String msg) {
        System.out.println(msg);
    }
}

...

/*This can be added in the initialize() method of  
the FXML controller with a reference to the WebEngine*/
public void initialize(URL url, ResourceBundle rb) {
    webE = webView.getEngine();

    //Retrieve a reference to the JavaScript window object
    JSObject jsObj = (JSObject)webE.executeScript("window");
    jsObj.setMember("javaLink", new JavaLink());
    /*Now in our JavaScript code we can type javaLink.showMsg("Hello!");
    which will send 'Hello!' to the debugger console*/
}

The code above will work great until distributing it and attempting to run the JAR file. After hours of searching and testing different tweaks I finally narrowed the problem down to the JavaLink object itself (I eventually learned that you can use try-catch blocks in JavaScript which enabled me to catch the error: "TypeError: showMsg is not a function...").

like image 357
DatuPuti Avatar asked Feb 04 '23 22:02

DatuPuti


1 Answers

The Solution

I found that declaring a global variable to hold an instance of the JavaLink class and passing that as a parameter into the setMember() method fixes it so that the app now runs both in the IDE as well as a standalone JAR:

JavaLink jl;

...

    jl = new JavaLink();

    //replace anonymous instantiation of JavaLink with global variable
    jsObj.setMember("javaLink", jl); 

Why!?

I'm guessing this has to do with garbage collection and that the JVM does not keep a reference to JavaLink unless you force it to by declaring a global variable. Any other ideas?

like image 88
DatuPuti Avatar answered Feb 07 '23 12:02

DatuPuti