Tuesday, May 28, 2013

Taming the Nashorn (first impressions)...

Hi there,
JavaScript is everywhere these days and so it is also part of JDK8 dev. prev. with Project Nashorn which is a lightweight high-performance JavaScript runtime engine.
Because I never used Rhino (which is in principle the same just older and slower) I had no  idea how to use it and for what reason.
After I met Marcus Lagergren (@lagergren) at JAX in Mainz (Germany), Geecon in Krakow (Poland) and at Java Forum in Malmö (Sweden) I decided that I have to take a look at the Nashorn to get an idea on how to use it. Of course I'm especially interested on how to use Nashorn in combination with JavaFX. Lucky me that Nashorn is part of the weekly developer previews of JDK8 since a few weeks ago and because Jim Laskey (@wickund) blogged about this use case I was able to start with it yesterday.
After some starting problems I slowly begin to understand and would like to share it with you...
First of all I would like to see how I could use Nashorn with JavaScript in combination with my controls, so the idea is to visualize a Lcd control of my Enzo library with Nashorn.
For the following example you'll need the jdk8 developer preview (grab it here) and a current version of my Enzo library (grab it here).
And with a shell and a text editor you are good to go...really nice :)

So first of all we need to create a JavaScript file that should create a JavaFX application which should show the Lcd control and set's the value of the Lcd control every 3 seconds to a random value between 0 and 100.

This is the JavaFX code to realize that...

import eu.hansolo.enzo.lcd.Lcd;
import eu.hansolo.enzo.lcd.LcdBuilder;
import javafx.animation.AnimationTimer;
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;

import java.util.Random;


public class EnzoFX extends Application {
  private Lcd            lcd;
  private Random         random;
  private long           lastTimerCall;
  private double         charge;
  private AnimationTimer timer;

  @Override public void init() {
    // Initialize the AnimationTimer
    random        = new Random();
    lastTimerCall = System.nanoTime();
    charge        = 0;
    timer         = new AnimationTimer() {
      @Override public void handle(long now) {
        if (now > lastTimerCall + 3_000_000_000l) {
          lcd.setValue(random.nextDouble() * 100);
          lcd.setTrend(Lcd.Trend.values()[random.nextInt(5)]);
          charge += 0.02;
          if (charge > 1.0) charge = 0.0;
          lcd.setBatteryCharge(charge);
          lastTimerCall = now;
          System.out.println(lcd.getValue());
        }
      }
    };

    // Initialize the Enzo Lcd control
    lcd = LcdBuilder.create()
                    .styleClass(Lcd.STYLE_CLASS_STANDARD_GREEN)
                    .title("Room Temp")
                    .unit("°C")
                    .decimals(2)
                    .minMeasuredValueDecimals(2)
                    .maxMeasuredValueDecimals(2)
                    .unitVisible(true)
                    .batteryVisible(true)
                    .alarmVisible(true)
                    .minMeasuredValueVisible(true)
                    .maxMeasuredValueVisible(true)
                    .lowerRightTextVisible(true)
                    .formerValueVisible(true)
                    .trendVisible(true)
                    .lowerRightText("Info")
                    .valueAnimationEnabled(true)
                    .foregroundShadowVisible(true)
                    .crystalOverlayVisible(true)
                    .build();
  }

  @Override public void start(Stage stage) {
    // Prepare stage and add controls
    StackPane root = new StackPane();
    root.setPadding(new Insets(10, 10, 10, 10));
    root.getChildren().add(lcd);

    stage.setTitle("Enzo in JavaFX");
    stage.setScene(new Scene(root, 528, 192));
    stage.show();

    // Start the timer
    timer.start();
  }

  public static void main(String[] args) {
    launch(args);
  }
}

Ok, so now we know how to achieve this in JavaFX but now let's take a look at the JavaScript code that leads to the same result. Here you go...

var System         = java.lang.System;
var Random         = java.util.Random;
var StackPane      = javafx.scene.layout.StackPane;
var Scene          = javafx.scene.Scene;
var Insets         = javafx.geometry.Insets;
var AnimationTimer = javafx.animation.AnimationTimer;
var Lcd            = Packages.eu.hansolo.enzo.lcd.Lcd;


// Initialize the AnimationTimer
var random        = new Random();
var lastTimerCall = System.nanoTime();
var charge        = 0;
var timer         = new AnimationTimer() {
    handle: function(now) {
        if (now > lastTimerCall + 3000000000) {
            lcd.value = random.nextDouble() * 100;
            lcd.trend = Lcd.Trend.values()[random.nextInt(5)];
            charge += 0.02;
            if (charge > 1.0) charge = 0.0;
            lcd.batteryCharge = charge;
            lastTimerCall = now;
            print(lcd.value);
        }
    }
}


// Initialize the Enzo Lcd control
var lcd   = new Lcd();
lcd.styleClass.add(Lcd.STYLE_CLASS_STANDARD_GREEN);
lcd.title                    = "Room Temp";
lcd.unit                     = "°C";
lcd.decimals                 = 2;
lcd.minMeasuredValueDecimals = 2;
lcd.maxMeasuredValueDecimals = 2;
lcd.unitVisible              = true;
lcd.batteryVisible           = true;
lcd.alarmVisible             = true;
lcd.minMeasuredValueVisible  = true;
lcd.maxMeasuredValueVisible  = true;
lcd.lowerRightTextVisible    = true;
lcd.formerValueVisible       = true;
lcd.trendVisible             = true;
lcd.lowerRightText           = "Info";
lcd.valueAnimationEnabled    = true;
lcd.foregroundShadowVisible  = true;
lcd.crystalOverlayVisible    = true;


// Prepare the stage and add controls
var root     = new StackPane();
root.padding = new Insets(10, 10, 10, 10);
root.children.add(lcd);

$STAGE.title = "Enzo with Nashorn";
$STAGE.scene = new Scene(root, 528, 192);
$STAGE.show();

// Start the timer
timer.start();

The JavaScript code that I saved to a file "javafx.js" looks not that different from the JavaFX code except...it's JavaScript...and the resulting application will look like this...



You might ask yourself how you could run this code and here is the answer, all it takes is one call on the command line that looks like this:

/PATH/TO/JDK/jdk1.8.0.jdk/Contents/Home/jre/bin/jjs -cp /PATH/TO/LIBRARY/Enzo.jar -fx /PATH/TO/JS-FILE/javafx.js

Means you have to now the path to your jdk8 installation folder where you could find the jjs executable that is needed to start nashorn from the command line. In addition you have to add the Enzo.jar to the classpath so that you could use it within your JavaScript file and at last you have to call the JavaScript file with the -fx parameter which will start the application....BAM...that's all...sweet :)
I've put the commandline in a bash script so that I could call it by simple calling the bash script instead of typing the whole line over and over again.

If you start the javafx.js file you should see something like on this youtube video.

Ok you might say where is the advantage of using JavaScript to run a JavaFX application...just think about not starting an IDE, not compiling and building a JavaFX application just to make a short test. Isn't it be much nicer to simply have a small JavaScript, edit it with your default text editor and run it with from the command line with one single call...I really like that for testing things.

If you would like to know more about Nashorn you should definitely take a look at the Nashorn blog ,subscribe to the Nashorn mailing list and take a look at the Nashorn Wiki.

At this point I would say THANK YOU to Jim Laskey and Marcus Lagergren which helped me to getting this stuff done!!!

This is really a first test of using Nashorn with JavaFX but now that I know how to use it I will use more often...so stay tuned and keep coding...

2 comments:

  1. Nice post, thanks for sharing.

    One question is left open though: In your JavaScript code sample, ehere does the $STAGE variable come from?

    ReplyDelete
    Replies
    1. Please take a look here: https://blogs.oracle.com/nashorn/entry/jjs_fx :)

      Delete