BT

Facilitating the Spread of Knowledge and Innovation in Professional Software Development

Write for InfoQ

Topics

Choose your language

InfoQ Homepage Articles Nashorn - The Combined Power of Java and JavaScript in JDK 8

Nashorn - The Combined Power of Java and JavaScript in JDK 8

Leia em Português

Bookmarks

Ever since JDK 6, Java has shipped with a bundled JavaScript engine based on Mozilla's Rhino. This feature allowed you to embed JavaScript code into Java and even call into Java from the embedded JavaScript. Additionally it provided the capability to run JavaScript from the command line using jrunscript. That was pretty good provided you didn’t require great performance and you could live with the limited ECMAScript 3 feature set.

Starting with the JDK 8 Nashorn replaces Rhino as Java’s embedded JavaScript engine. Nashorn supports the full ECMAScript 5.1 specification plus some extensions. It compiles JavaScript to Java bytecode using new language features based on JSR 292, including invokedynamic, that were introduced in JDK 7.

This brings a 2 to 10x performance boost over the former Rhino implementation, although it is still somewhat short of V8, the engine inside Chrome and Node.js. If you are interested in details of the implementation you can have a look at these slides from the 2013 JVM Language Summit.

As Nashorn comes with JDK 8, it also adds very neat support for functional interfaces, as we shall see in more detail shortly.

Let’s kick it off with a very small example. First you might want to install the JDK 8 and NetBeans, IntelliJ IDEA, or Eclipse. All of them provide at least basic support for integrated JavaScript development. Let’s create a simple Java project consisting of the following two example files and let the program run:

(Click on the image to enlarge it)

In line 12 we use the engine’s "eval" method to evaluate any JavaScript code. In this case we just load the top JavaScript file and evaluate it. You might find “print” to be unfamiliar. It is not a built-in function for JavaScript, but Nashorn provides this and other convenience functions that come in handy in a scripting environment. You could have also embedded the printing of “hello world” directly into the string passed into the "eval" method, but having your JavaScript in a file of its own opens up a whole world of tooling for it.

Eclipse currently has no dedicated Nashorn support via its JavaScript Development Tools (JSDT) project, however, basic tooling and editing for JavaScript are supported:

(Click on the image to enlarge it)

IntelliJ IDEA 13.1 (Community and Ultimate Editions) provides outstanding JavaScript and Nashorn support. There is a full feature debugger and it even allows refactoring to be synchronized between Java and JavaScript; so for example if you rename a Java class that is referenced from JavaScript or if you rename a JavaScript file sourced in from Java, the IDE will modify the corresponding references across languages.

Here is an example of how you can debug the JavaScript called from Java (note that NetBeans also provides JavaScript debugging as shown in the screenshot below):

(Click on the image to enlarge it)

You may say the tooling looks nice and the new implementation fixes the performance as well as the compliance problem, but why should I use it? One reason would be general scripting. Sometimes it comes in handy to be able to throw in any kind of string and just let it be interpreted. Sometimes it might just be nice to not have a compiler in the way, or not to worry about static typing. Or maybe you are interested in the Node.js programming model, which can be used with Java as we will see at the end of this article. There is also a case to be made that developing JavaFX will be much faster using JavaScript as opposed to Java.

Shell scripts

The Nashorn engine can be called from the command line using the jjs command. You can call it without any argument, which will bring you into an interactive mode, or you can pass the name of a JavaScript file you want executed, or you can use it as a replacement for a shell script, like this:

#!/usr/bin/env jjs 

var name = $ARG[0];
print(name ? "Hello, ${name}!" : "Hello, world!");

To pass in program arguments to jjs, prefix them with “--”. So for example you might call:

./hello-script.js -- Joe 

Without the “--” prefix, the parameter would be interpreted as a file name.

Passing data to and from Java

As I indicated above you can call JavaScript directly from your Java code; just obtain an engine and call its "eval" method. You can pass in data explicitly as strings ...

ScriptEngineManager scriptEngineManager = 
new ScriptEngineManager();
ScriptEngine nashorn =
scriptEngineManager.getEngineByName("nashorn");
String name = "Olli";
nashorn.eval("print('" + name + "')");

… or you can pass bindings from Java that can be accessed as global variables from inside the JavaScript engine:

int valueIn = 10; 
SimpleBindings simpleBindings = new SimpleBindings();
simpleBindings.put("globalValue", valueIn);
nashorn.eval("print (globalValue)", simpleBindings);

The results of a JavaScript eval computation will be returned from the engine’s "eval" method:

Integer result = (Integer) nashorn.eval("1 + 2"); 
assert(result == 3);

Using Java classes in Nashorn

As mentioned before, one of the most powerful features of Nashorn comes from calling Java classes from inside JavaScript. You can not only access classes and create instances, but you can also subclass them, call their static members, and do virtually anything you could do from Java.

As an example let’s take a look at threads. JavaScript does not have any language features for concurrency and all common runtimes are single-threaded or at least without any shared state. It is interesting to see that in the Nashorn environment JavaScript could in fact run concurrently and with shared state, just like in Java:

// this is how we get access to Java class Thread 
var Thread = Java.type("java.lang.Thread");
// subclass with our run method
var MyThread = Java.extend(Thread, {
run: function() {
print("Run in separate thread");
}
});
var th = new MyThread();
th.start();
th.join();

Note that the canonical way to access a class from Nashorn is by using Java.type and you can extend a class using Java.extend.

Functional delight

By all counts, with the release of JDK 8, Java has - at least to a certain extent - become a functional language. You can now use higher-order functions on collections, for example to iterate over their elements. A higher-order function is a function that takes another function as a parameter and does something meaningful with it. Have a look at this example in Java

List<Integer> list = Arrays.asList(3, 4, 1, 2); 
list.forEach(new Consumer() {
@Override
public void accept(Object o) {
System.out.println(o); } });

In this example, instead of iterating over the elements using an “external” loop as we would traditionally have done, we now pass a "Consumer" function to the "forEach" operation, a higher-order “internal-looping” operation that executes the consumer’s "accept" method by passing in each element of the collection, one by one.

As already mentioned, the functional language approach for such a higher-order function would rather accept a function parameter rather than an object. While passing around references to functions per se is not traditionally Java’s province, JDK 8 now has some syntactic sugar for expressing just that using lambda expressions (aka “closures”) . For example:

List<Integer> list = Arrays.asList(3, 4, 1, 2); 
list.forEach(el -> System.out.println(el));

In this case the parameter to "forEach" has the form of such a function reference. This is possible, because Consumer is a functional interface, (sometimes called a Single Abstract Method type, or “SAM”).

So why are we talking about lambdas in a discussion of Nashorn? Because in JavaScript you can write code like this as well and Nashorn is especially well prepared to bridge the gap between Java and JavaScript in this case. In particular, it allows you to even pass plain JavaScript functions as implementations of functional interfaces (SAM types).

Let us have a look at some plain JavaScript code that does the same thing as our Java code above. Note that there is no built-in list type in JavaScript, just arrays; but those arrays are dynamically sized and have methods comparable to the ones of a Java list. So, in this example we are calling the "forEach" method of a JavaScript array:

var jsArray = [4,1,3,2]; 
jsArray.forEach(function(el) { print(el) } );

The similarity is obvious; but that isn’t all. You can also convert such a JavaScript array to a Java list:

var list = java.util.Arrays.asList(jsArray); 

See? And yes, this is JavaScript running inside Nashorn. As this is now a Java list, you can call its "forEach" method. Note that this is not the same "forEach" method that we called on the JavaScript array, but rather Java’s "forEach" method defined on collections. Still, we are passing in a plain JavaScript function here:

list.forEach(function(el) { print(el) } ); 

Nashorn allows us to provide plain JavaScript function references where a functional interface (SAM type) is expected. This is thus not only possible from Java, but also from JavaScript.

The next version of ECMAScript - which is expected to become final this year - will include a short syntax for functions that will allow them to be written nearly as Java lambdas, except that it uses a double arrow =>. This will drive the alignment even further.

Special Nashorn JavaScript Dialect

As I mentioned in the introduction, Nashorn supports JavaScript in the ECMAScript 5.1 version plus some extensions. I do not necessarily recommend using those extensions, because being neither Java nor JavaScript, they can feel unnatural to either developer. On the other hand there are two extensions that are used throughout Oracle’s documentation, and so we should become familiar with them.

First let us set the stage for the first extension. As you have seen before, you can extend a Java class from JavaScript using Java.extend. If you want to subclass an abstract Java class or implement an interface, you can use a more convenient syntax. In this case you can virtually call the constructor of the abstract class or the interface and pass in a JavaScript object literal that describes the implemented methods. JavaScript object literals are just name / value pairs, similar to what you may know from the JSON format. This allows us to implement the Runnable interface like this:

var r = new java.lang.Runnable({
run: function() {
print("running...\n");
}
});

In this example we are virtually calling the constructor of Runnable with an object literal that specifies the implementation of the run method. Note that this is something the Nashorn implementation is giving us, it would otherwise not be possible in JavaScript.

The code of this example already looks similar to how we would implement an interface as an anonymous inner class in Java, but not quite. This brings us to the first extension, which lets you pass the last parameter after the closing “)” when you make a constructor call. Doing this, our code looks like this ...

var r = new java.lang.Runnable() {
run: function() {
print("running...\n");
}
};

… which does exactly the same thing, but has an even greater resemblance to Java.

The second frequently used extension is a shortcut for functions that allows you to omit both the curly braces as well as the return statement for the method body in a single line function. Thus our example from the previous section:

list.forEach(function(el) { print(el) } );

could be expressed as the slightly more terse:

list.forEach(function(el) print(el)); 

Avatar.js

We have seen that with Nashorn we have a premium JavaScript engine embedded into Java. We have also seen that from Nashorn we can access any Java class. Avatar.js goes one step further and brings “the Node programming model, APIs and module ecosystem to the Java platform”. To understand what this means and why it is exciting, we first have to understand what Node is. Node basically extracts Chrome's V8 JavaScript engine to make it work from the command line without the need for a browser. It thus makes JavaScript executable not only in the browser, but also on the server side. To execute JavaScript on a server in any meaningful way you will at least need to access the file system and the network. To achieve this, Node embeds a library called libuv that does this in an asynchronous way. Practically this means that your calls to the operating system never block even if they take a while to return. Instead of blocking, you provide a callback function that will be triggered once the call is done, delivering the results if there are any.

There are several companies using Node for serious applications, among them Walmart and Paypal.

Let’s take a look at a small JavaScript example that I have adapted from Node’s website:

// load module 'http' (this is blocking) to handle http requests 
var http = require('http'); 

// when there is a request we return 'Hello, World\n' function handleRequest(req, res) { res.writeHead(200, {'Content-Type': 'text/plain'}); res.end('Hello, World\n'); }
// we listen on localhost, port 1337
// and give handleRequest as call back
// you see the non-blocking / asynchronous nature here
http.createServer(handleRequest).listen(1337, '127.0.0.1');
// logs to the console to reassure that we are on our way
console.log('Get your hello at http://127.0.0.1:1337/');

To run this code you would need to install Node, then save the above JavaScript code into a file, and finally, call Node with this file as a parameter.

The goal of Avatar.js is to provide the same core API as Node by binding libuv to Java classes and then making them accessible to JavaScript. Even though this may sound cumbersome, it works surprisingly well. Avatar.js supports a large number of Node modules and the support of "express" - the mainstream web framework for Node - indicates that this could indeed work with a large number of existing projects.

Unfortunately, at the time of this writing, there is no binary distribution for Avatar.js. There is a readme that explains how to build it from source, but if you are not so much into building from scratch, you can also get the binaries without building yourself. Both approaches do work, but I recommend the second one for quicker results.

Once you have set up your binaries and put them into a lib folder, you would then call the Avatar.js framework using something like:

java -Djava.library.path=lib -jar lib/avatar-js.jar helloWorld.js 

We assume that the demo server (the code above) is saved in a file called “helloWorld.js”.

Again, let us ask, why is this useful? The good people at Oracle (slide 10) see a couple of use cases for such a library. I mainly concur with two of them, namely

  1. you have a Node application and want to use certain Java libraries to complement the Node API
  2. you want to switch to JavaScript and the Node API, but need to embed legacy Java code either partially or completely

Both use cases work by using Avatar.js and calling any required Java classes from the JavaScript code, which is supported by Nashorn, as we have seen.

Let me give you an example of the first use case. JavaScript currently has just a single type for expressing numbers called “number”. This would be equivalent to the Java “double” precision, with the same limitations; JavaScript’s number, like Java’s double is not able to express arbitrary range and precision, for example when dealing with money.

In Java you could use BigDecimal, which supports exactly that. But JavaScript has no built-in equivalent, so you could just access the BigDecimal class from your JavaScript code and have a safe handling of monetary values.

Let us look at an example web service that calculates the percentage of some amount. First we need to have a function that does the actual calculation:

var BigDecimal = Java.type('java.math.BigDecimal'); 

function calculatePercentage(amount, percentage) {
var result = new BigDecimal(amount).multiply(
new BigDecimal(percentage)).divide(
new BigDecimal("100"), 2, BigDecimal.ROUND_HALF_EVEN);
return result.toPlainString(); }

In JavaScript there are no declared types, but apart from that the code looks pretty similar to the Java code I have written for this task:

public static String calculate(String amount, String percentage) { 
BigDecimal result = new BigDecimal(amount).multiply(
new BigDecimal(percentage)).divide(
new BigDecimal("100"), 2, BigDecimal.ROUND_HALF_EVEN);
return result.toPlainString();
}

We just need to replace the handleRequest function of the Node example above to complete our code. It goes like this

// load utility module 'url' to parse url 
var url = require('url');
function handleRequest(req, res) {
// '/calculate' is the path of our web service
if (url.parse(req.url).pathname === '/calculate') {
var query = url.parse(req.url, true).query;
// amount and percentage are passed in as query parameters
var result = calculatePercentage(query.amount,
query.percentage);
res.writeHead(200, {'Content-Type': 'text/plain'});
res.end(result + '\n');
}
}

We use a second core module of Node to process the URL of the request to parse out the query parameters for amount and percentage.

When I start the server (as shown above) and issue a request like this

http://localhost:1337/calculate?amount=99700000000000000086958613&percentage=7.59 

using the web browser, I get the correct answer “7567230000000000006600158.73” which would have been impossible using JavaScript’s plain “number” type.

The second use case would make sense when you decide to migrate your existing JEE application to JavaScript and Node. In this case you can easily access all your existing services from within JavaScript. Another related use case would be to have a new piece of server functionality built using JavaScript and Node that still can benefit from existing JEE services.

Going in the same direction there is also Project Avatar which is based on Avatar.js. Details are beyond the scope of this article, but to get a quick overview, have a look at this Oracle announcement. The basic idea is to write your application in JavaScript and access JEE services. Project Avatar comes with a combined binary distribution for Avatar.js, but requires Glassfish for installation and development.

Wrap-up

Project Nashorn has enhanced the original JDK 6 Rhino implementation by greatly improving performance for longer running applications, for example when used inside a web server. Nashorn integrates Java with JavaScript and even takes the new lambdas of JDK 8 into account. A real innovation comes with Avatar.js, which builds on those features and provides for integration of enterprise Java and JavaScript code while being largely compatible with the de-facto standard for JavaScript server programming.

Complete examples including Avatar.js binaries for Mac OS X can be found on Github.

About the Author

Oliver Zeigermann is a self-employed Software Architect/Developer, Consultant, and Coach from Hamburg, Germany. He is currently focused on using JavaScript in enterprise applications.

Rate this Article

Adoption
Style

Hello stranger!

You need to Register an InfoQ account or or login to post comments. But there's so much more behind being registered.

Get the most out of the InfoQ experience.

Allowed html: a,b,br,blockquote,i,li,pre,u,ul,p

Community comments

  • I don't get it

    by C Curl,

    Your message is awaiting moderation. Thank you for participating in the discussion.

    Neat, but Why?

  • Questionable performance

    by Ivo Houbrechts,

    Your message is awaiting moderation. Thank you for participating in the discussion.

    "... greatly improving performance ..."
    I have at least one use case where Rhino (with optimization turned on) outperforms Nashorn by far. See houbie.blogspot.co.uk/2014/03/follow-up-javascr...

  • Mixed Java/JavaScript debugging in action

    by Jaroslav Tulach,

    Your message is awaiting moderation. Thank you for participating in the discussion.

    Video showing the power of mixed Java/Nashorn debugging.

  • Different benchmarks show test different things...

    by Michael Ahern,

    Your message is awaiting moderation. Thank you for participating in the discussion.

    I think the question is answered by the benchmark provided in the original article:
    ariya.ofilabs.com/2014/03/nashorn-the-new-rhino...

    In the benchmark sited "Cold" (or interpreted / first-time) runs of JS with Nashorn is slower than Rhino. However, with "warm" (once the JVM has time to optimize the bytecode) performance the performance is dramatically faster.

    -----------

    The test you are citing would always hit the cold case since it is doing a "static" compile of a LESS to CSS. This is a one-time (or once at app-startup) operation. Given the original benchmark - one would expect THIS to be SLOWER than Rhino.

    However, if you are running a server like Vert.X or NodeJS (where the same code is hit over-and-over again) you should see dramatically better performance.

    ----------

    That said, where I would love to use Nashorn is for JS uglification or CSS compilation utilizing Node.JS stack tools for my legacy JEE applications. Given the benchmarks, it is interesting (as you point out) that performance would be SLOWER than Rhino - as these are one time operations.

    It looks like a bit of work is still needed to optimize Nashorn for these use cases.

  • Re: Different benchmarks show test different things...

    by Michael Ahern,

    Your message is awaiting moderation. Thank you for participating in the discussion.

    While I am thinking about it - this probably does point some serious immaturity in Nashorn.

    If you think about V8 or other mature JS engines they will make multiple passes through the JS code. On a first pass they would run it in interpreted mode to minimize the startup cost, while performing further optimization in the background.

    Nashorn probably needs a first-pass run mode where it just interprets the JS to get the cold run bencharks back in line with or ahead of Rhino.

  • Re: Mixed Java/JavaScript debugging in action

    by Oliver Zeigermann,

    Your message is awaiting moderation. Thank you for participating in the discussion.

    Pretty cool stuff. Maybe it is worth mentioning that the example uses HTML4J

    bits.netbeans.org/html4j

  • Nashorn has error when running React.js code. Any clues?

    by alota takada,

    Your message is awaiting moderation. Thank you for participating in the discussion.

Allowed html: a,b,br,blockquote,i,li,pre,u,ul,p

Allowed html: a,b,br,blockquote,i,li,pre,u,ul,p

BT