Search code examples
javajavafx-8nashorn

Passing command line switch to Nashorn JavaScript engine


I am trying to run the the following code in Java environment using the nashorn.Code Doc

load("fx:base.js");
load("fx:controls.js");
load("fx:graphics.js");

var material = new PhongMaterial();
material.diffuseColor = Color.LIGHTGREEN;
material.specularColor = Color.rgb(30, 30, 30);

var meshView = Java.to([
    new Box(200, 200, 200),
    new Sphere(100),
    new Cylinder(100, 200)
], "javafx.scene.shape.Shape3D[]");

for (var i = 0; i != 3; i++) {
    meshView[i].material = material;
    meshView[i].translateX = (i + 1) * 220;
    meshView[i].translateY = 200;
    meshView[i].translateZ = 20;
    meshView[i].drawMode = DrawMode.FILL;
    meshView[i].cullFace = CullFace.BACK;
};

var pointLight = new PointLight(Color.WHITE);
pointLight.translateX = 800;
pointLight.translateY = -200;
pointLight.translateZ = -1000;

var root = new Group(meshView);
root.children.add(pointLight);

var scene = new Scene(root, 800, 400, true);
scene.fill = Color.rgb(127, 127, 127);
scene.camera = new PerspectiveCamera(false);
$STAGE.scene = scene;
$STAGE.show();

It uses JavaScript APIs to create a JavaFx scene.

If you use the jjs command line utility with -fx switch it works as expected, but if you execute the same file with nashorn script engine via Java code, it throws the following exception

Exception in thread "main" java.lang.ExceptionInInitializerError
    at java.lang.Class.forName0(Native Method)
    at java.lang.Class.forName(Class.java:348)
    at jdk.nashorn.internal.runtime.Context.findClass(Context.java:983)
    at jdk.nashorn.internal.objects.NativeJava.simpleType(NativeJava.java:489)
    at jdk.nashorn.internal.objects.NativeJava.type(NativeJava.java:320)
    at jdk.nashorn.internal.objects.NativeJava.type(NativeJava.java:312)
    at jdk.nashorn.internal.objects.NativeJava.type(NativeJava.java:308)
    at jdk.nashorn.internal.scripts.Script$Recompilation$3$500A$\=fx\!base.LOAD_FX_CLASSES(fx:base.js:38)
    at jdk.nashorn.internal.scripts.Script$4$\=fx\!controls.:program(fx:controls.js:30)
    at jdk.nashorn.internal.runtime.ScriptFunctionData.invoke(ScriptFunctionData.java:636)
    at jdk.nashorn.internal.runtime.ScriptFunction.invoke(ScriptFunction.java:229)
    at jdk.nashorn.internal.runtime.ScriptRuntime.apply(ScriptRuntime.java:387)
    at jdk.nashorn.internal.runtime.Context.evaluateSource(Context.java:1150)
    at jdk.nashorn.internal.runtime.Context.load(Context.java:799)
    at jdk.nashorn.internal.objects.Global.load(Global.java:995)
    at jdk.nashorn.internal.scripts.Script$\^eval\_.:program(<eval>:2)
    at jdk.nashorn.internal.runtime.ScriptFunctionData.invoke(ScriptFunctionData.java:636)
    at jdk.nashorn.internal.runtime.ScriptFunction.invoke(ScriptFunction.java:229)
    at jdk.nashorn.internal.runtime.ScriptRuntime.apply(ScriptRuntime.java:387)
    at jdk.nashorn.api.scripting.NashornScriptEngine.evalImpl(NashornScriptEngine.java:437)
    at jdk.nashorn.api.scripting.NashornScriptEngine.evalImpl(NashornScriptEngine.java:401)
    at jdk.nashorn.api.scripting.NashornScriptEngine.evalImpl(NashornScriptEngine.java:397)
    at jdk.nashorn.api.scripting.NashornScriptEngine.eval(NashornScriptEngine.java:147)
    at javax.script.AbstractScriptEngine.eval(AbstractScriptEngine.java:212)
    at com.metalop.nashorn.javafx.GettingStarted.main(GettingStarted.java:23)
Caused by: java.lang.IllegalStateException: Toolkit not initialized
    at com.sun.javafx.application.PlatformImpl.runLater(PlatformImpl.java:273)
    at com.sun.javafx.application.PlatformImpl.runLater(PlatformImpl.java:268)
    at com.sun.javafx.application.PlatformImpl.setPlatformUserAgentStylesheet(PlatformImpl.java:550)
    at com.sun.javafx.application.PlatformImpl.setDefaultPlatformUserAgentStylesheet(PlatformImpl.java:512)
    at javafx.scene.control.Control.<clinit>(Control.java:87)
    ... 25 more

So how do I pass the -fx switch or initilize javafx within java code for nashorn?


Solution

  • There are couple of things that I did to get the JavaScript working as passing the -fx nashorn arg didn't work for me.

    1. As mentioned in the comment by Nikos the class has to extend from javafx.application.‌​Application and the main function should then call the launch() which has the code for Nashorn initialization and JS script execution.

    2. The start method has start(Stage primaryStage) signature. The Stage object had to be bound to the JavaScript while evaluating.

    The following code is a sample how I was able to achieve it.

    import java.io.BufferedReader;
    import java.io.InputStream;
    import java.io.InputStreamReader;
    
    import javax.script.ScriptEngine;
    import javax.script.ScriptEngineManager;
    import javax.script.SimpleBindings;
    
    import javafx.application.Application;
    import javafx.stage.Stage;
    
    public class GettingStarted extends Application {
    
        public static void main(String args[]) {
    
            launch(args);
        }
    
        @Override
        public void start(Stage primaryStage) throws Exception {
            ScriptEngine engine = new ScriptEngineManager().getEngineByName("nashorn");
    
            try (InputStream is = GettingStarted.class.getResourceAsStream("getting-started.js")) {
    
                BufferedReader reader = new BufferedReader(new InputStreamReader(is));
                SimpleBindings bindings = new SimpleBindings();
                bindings.put("$STAGE", primaryStage);
                engine.eval(reader, bindings);
    
    
            } catch (Exception e) {
                e.printStackTrace();
            }
    
        }
    
    }