Oracle Blog

JavaScript for the JVM.

Friday Oct 24, 2014

During the JavaOne Nashorn: JavaScript for the JVM session, I showed a couple examples of converting JavaScript browser examples to use Nashorn and JavaFX. The first example was using CKEditor, a rich text/HTML editor. This editor is written entirely in JavaScript using the browser DOM interface. In this case, it did not really make sense to convert all the DOM code over to FX. Instead, I used the JavaFX WebView which supports all the DOM needed.

So let's start with a simple example to bring up a WebView (run with jjs -fx -scripting example1.js);

For the next part, it gets a little confusing, but you should be able to catch on to the weirdness (after all, you're a JavaScript developer.) First, you have to be aware of the fact that there is a separate JavaScriptCore engine inside the WebView. The example shows interaction with this engine by asking it to load an URL.

Second, we want to be able to have JSObject objects passed back from the WebView engine and have them behave like Nashorn objects. To accomplish this we need to "wrap" the JSObject objects in Nashorn JSAdaptor objects.

Thursday Sep 26, 2013

With people porting HTML to Nashorn + JavaFX more frequently, I often get asked the question "how do you port functions like setInterval and setTimeout" (setInterval is used to have a function repeated at regular intervals, where setTimeout is used to have a function delayed.) The following code duplicates the functionality of the each of the setInterval family of functions, when used in a JavaFX application.

Monday Sep 09, 2013

I was reading an article written by Felix Bembrick where he took a Chrome canvas example and ported it to Nashorn with JavaFX. I ran the two tests myself and was surprised to see that Nashorn + FX ran at 350fps where Chrome ran at 120fps (iMac, 3.4 GHz Intel Core i7, AMD Radeon HD 6970M 2048M.) Much of the credit for that is JavaFX's rendering engine, but Nashorn allowed Felix to leverage that. The original article is at JavaFX with Nashorn Canvas example.

Wednesday Sep 04, 2013

I haven't been blogging for a while. JavaOne examples and slides are taking up all my time. Marcus and Attila leaked out that I was doing a demo of the Nashorn Debugger prototype, but I still have some surprises up my sleeve. I was reviewing examples from last year and felt that they needed some updating. The FX Fireworks example had all kinds of Java code to support applications, timers and callbacks. This year's Fireworks version is pure Nashorn. I posted source and backdrop here. Just run with jjs -fx -scripting fireworks.js Enjoy.

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;

privatelong lastTimerCall;

private double charge;

private AnimationTimer timer;

@Overridepublic void init() {

// Initialize the AnimationTimer

random = new Random();

lastTimerCall = System.nanoTime();

charge = 0;

timer = new AnimationTimer() {

@Overridepublic 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();

}

@Overridepublic 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:

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.

Tuesday May 07, 2013

So after some playing around and working with the JavaFX folks, I think we have a proposal for jjs that works with JavaFX. The -fx flag on jjs will bootstrap scripts using a javafx.application.Application. Thus, writing JavaFX scripts in Nashorn is very easy.

The basic command line is;

jjs -fx fxscript.js

You can mix and match other jjs options like -scripting and -- ;

jjs -fx -scripting fxscript.js -- my script args

The content of the script follows some of the examples I've posted before. The script may optionally contain JavaFX init, start and/or stop functions. What is new, is that you can leave them behind and just straight script. The original hello world example;

where the stage is now a global var $STAGE (instead of the start function argument.)

Also for convenience, we've predefined includes for all of the JavaFX classes. I would recommend using only the classes you need (only needed for new and for static field access), but for prototyping having includes really helps things move along.

Saturday Apr 06, 2013

I find myself facing a dilemma today. How should I use Java FX from Nashorn? So far, I have two approaches I could use, but each comes with some issues. Some background first.

Java FX, even though ships with the JDK, is on a different build cycle and has dependencies on elements of the JDK. This arraignment limits Nashorn, which is part of the JDK, from actually having dependencies into Java FX. But, there is a dependency requirement to implement a Java FX application. Java FX applications begin with a subclass instance of javafx.application.Application. Therefore, whatever choice is made, it has to be independent of the JDK (at some point should be part of Java FX.)

The first approach, in general terms, is the easiest to use. It involves using a predefined shell that is similar to jjs but handles the overrides of Application methods init, start and finish. The source of this shell is currently checked into the Nashorn repo under nashorn/tools/fxshell.

To built it you can (cd make ; ant build-fxshell) from within the nashorn repo. The result is in nashorn/dist/nashornfx.jar. To use just java -cp dist/nashornfx.jar jdk.nashorn.tools.FXShell <myscript.js> … . For the JDK savvy you can create a launcher by modelling an entry in jdk/makefiles/CompileLaunchers.gmk after the jjs entry.

The big plus for this approach is that it handles almost everything for you. You just have to define a start method with a few class declarations and that is it. The down side is that ideally you would want this implemented as a jjsfx launcher embedded in the JDK. But then we run into the chicken and egg dependency on Java FX.

The second approach only relies on jjs. With a recent modification to Java.extend (currently only in the nashorn forest), it is now possible to subclass javafx.application.Application. and thus launch from within a script. This sounds like all pluses except for the fact you have to wrap your brain around the fact that FX applications take control of execution and has static init dependencies that require careful use in your program.

I prototyped a simple fxinit.js include that shows how we could implement such a scheme. Ignore the implementation quirks. It's simpler than it seems.

Note the placement of the load("fxinit.js");. Since this is where the FX Application takes control, anything after the load will not complete until the application exits.

One other quirk. Since you can not static init some of Java FX classes until after the application launches. You can not globally (script level) declare any uses of these classes. Uses can be embedded in methods used after the launch, but no where else. This is a style cramp for me.

There is a third approach I have been considering. It involves some argument trickery, but may play out as a better way of doing things. Imagine jjs fxinit.js -- myscript.js -- my scripts args . The -- indicates the beginning of arguments passed to the script. The notion here is that fxinit.js launches the application and then evals myscript.js. This cleanses my script of any quirks, while putting the onus on getting the command line right.

Saturday Dec 01, 2012

After doing the Twitter example, it seemed reasonable to try graphing the result with JavaFX. At this time the Nashorn project doesn't have a JavaFX shell, so we have to go through some hoops to create an JavaFX application. I thought showing you some of those hoops might give you some idea about what you can do mixing Nashorn and Java (we'll add a JavaFX shell to the todo list.)

First, let's look at the meat of the application. Here is the repackaged version of the original twitter example.

var twitter4j = Packages.twitter4j;

var TwitterFactory = twitter4j.TwitterFactory;

var Query = twitter4j.Query;

function getTrendingData() {

var twitter = new TwitterFactory().instance;

var query = new Query("nashorn OR nashornjs");

query.since("2012-11-21");

query.count = 100;

var data = {};

do {

var result = twitter.search(query);

var tweets = result.tweets;

for each (var tweet in tweets) {

var date = tweet.createdAt;

var key = (1900 + date.year) + "/" +

(1 + date.month) + "/" +

date.date;

data[key] = (data[key] || 0) + 1;

}

} while (query = result.nextQuery());

return data;

}

Instead of just printing out tweets, getTrendingData tallies "tweets per date" during the sample period (since "2012-11-21", the date "New Project: Nashorn" was posted.) getTrendingData then returns the resulting tally object.

Next, use JavaFX BarChart to display that data.

var javafx = Packages.javafx;

var Stage = javafx.stage.Stage

var Scene = javafx.scene.Scene;

var Group = javafx.scene.Group;

var Chart = javafx.scene.chart.Chart;

var FXCollections = javafx.collections.FXCollections;

var ObservableList = javafx.collections.ObservableList;

var CategoryAxis = javafx.scene.chart.CategoryAxis;

var NumberAxis = javafx.scene.chart.NumberAxis;

var BarChart = javafx.scene.chart.BarChart;

var XYChart = javafx.scene.chart.XYChart;

var Series = javafx.scene.chart.XYChart.Series;

var Data = javafx.scene.chart.XYChart.Data;

function graph(stage, data) {

var root = new Group();

stage.scene = new Scene(root);

var dates = Object.keys(data);

var xAxis = new CategoryAxis();

xAxis.categories = FXCollections.observableArrayList(dates);

var yAxis = new NumberAxis("Tweets", 0.0, 200.0, 50.0);

var series = FXCollections.observableArrayList();

for (var date in data) {

series.add(new Data(date, data[date]));

}

var tweets = new Series("Tweets", series);

var barChartData = FXCollections.observableArrayList(tweets);

var chart = new BarChart(xAxis, yAxis, barChartData, 25.0);

root.children.add(chart);

}

I should point out that there is a lot of subtlety in this sample. For example;

stage.scene = new Scene(root) is equivalent to stage.setScene(new Scene(root)). If Nashorn can't find a property (scene), then it searches (via Dynalink) for the Java Beans equivalent (setScene.) Also note, that Nashorn is magically handling the generic class FXCollections. Finally, with the call to observableArrayList(dates), Nashorn is automatically converting the JavaScript array dates to a Java collection. It really is hard to identify which objects are JavaScript and which are Java. Does it really matter?

Okay, with the meat out of the way, let's talk about the hoops.

When working with JavaFX, you start with a main subclass of javafx.application.Application. This class handles the initialization of the JavaFX libraries and the event processing. This is what I used for this example;

This code sets up an instance of the Nashorn engine for evaluating scripts.

The load method reads a script into memory and then gets engine to eval that script. Note, that load also returns the result of the eval.

Now for the fun part. There are several different approaches we could use to communicate between the Java main and the script. In this example we'll use a Java interface. The JavaFX main needs to do at least start and stop, so the following will suffice as an interface;

public interface Trending {

public void start(Stage stage) throws Exception;

public void stop() throws Exception;

}

At the end of the example's script we add;

function newTrending() {

return new Packages.Trending() {

start: function(stage) {

var data = getTrendingData();

graph(stage, data);

stage.show();

},

stop: function() {

}

}

}

newTrending();

which instantiates a new subclass instance of Trending and overrides the start and stop methods. The result of this function call is what is returned to main via the eval.

trending = (Trending) load("Trending.js");

To recap, the script Trending.js contains functions getTrendingData, graph and newTrending, plus the call at the end to newTrending. Back in the Java code, we cast the result of the eval (call to newTrending) to Trending, thus, we end up with an object that we can then use to call back into the script.