Nashorn - Working with the Java Platform - Java in a Nutshell, 6th Edition (2015)

Java in a Nutshell, 6th Edition (2015)

Part II. Working with the Java Platform

Chapter 12. Nashorn

With Java 8, Oracle has included Nashorn, a new JavaScript implementation that runs on the JVM. Nashorn is designed to replace the original JavaScript-on-the-JVM project—which was called Rhino (Nashorn is the German word for “rhino”).

Nashorn is a completely rewritten implementation and strives for easy interoperability with Java, high performance, and precise conformance to the JavaScript ECMA specifications. Nashorn was the first implementation of JavaScript to hit a perfect 100% on spec compliance and is already at least 20 times faster than Rhino on most workloads.

Introduction to Nashorn

In this chapter, we will assume some basic understanding of JavaScript. If you aren’t already familiar with basic JavaScript concepts, then Head First JavaScript by Michael Morrison (O’Reilly) is a good place to start.

If you recall the differences between Java and JavaScript outlined in “Java Compared to JavaScript”, you know that we can see that the two languages are very different. It may, therefore, seem surprising that JavaScript should be able to run on top of the same virtual machine as Java.

Non-Java Languages on the JVM

In fact, there are a very large number of non-Java languages that run on the JVM—and some of them are a lot more unlike Java than JavaScript is. This is made possible by the fact that the Java language and JVM are only very loosely coupled, and only really interact via the definition of the class file format. This can be accomplished in two different ways:

§ The source language has an interpreter that has been implemented in Java.

The interpreter runs on the JVM and executes programs written in the source language.

§ The source language ships with a compiler that produces class files from units of source language code.

The resulting compiled class files are then directly executed on the JVM, usually with some additional language-specific runtime support.

Nashorn takes the second approach—but with the added refinement that the compiler is inside the runtime, so that JavaScript source code is never compiled before program execution begins. This means that JavaScript that was not specifically written for Nashorn can still be easily deployed on the platform.

NOTE

Nashorn is unlike many other JVM languages (such as JRuby) in that it does not implement any form of interpreter. Nashorn always compiles JavaScript to JVM bytecode and executes the bytecode directly.

This is interesting, from a technical perspective, but many developers are curious as to what role Nashorn is intended to play in the mature and well-established Java ecosystem. Let’s look at that role next.

Motivation

Nashorn serves several purposes within the Java and JVM ecosystem. Firstly, it provides a viable environment for JavaScript developers to discover the power of the JVM. Second, it enables companies to continue to leverage their existing investment in Java technologies while additionally adopting JavaScript as a development language. Last, it provides a great engineering showcase for the advanced virtual machine technology present in the HotSpot Java Virtual Machine.

With the continued growth and adoption of JavaScript, broadening out from its traditional home in the browser to more general-purpose computing and the server side, Nashorn represents a great bridge between the existing rock-solid Java ecosystem and a promising wave of new technologies.

For now, let’s move on to discuss the mechanics of how Nashorn works, and how to get started with the platform. There are several different ways in which JavaScript code can be executed on Nashorn, and in the next section we’ll look at two of the most commonly used.

Executing JavaScript with Nashorn

In this section, we’ll be introducing the Nashorn environment, and discuss two different ways of executing JavaScript (both of which are present in the bin subdirectory of $JAVA_HOME):

jrunscript

A simple script runner for executing JavaScript as .js files.

jjs

A more full-featured shell—suitable for both running scripts and use as an interactive, read-eval-print-loop (REPL) environment for exploring Nashorn and its features.

Let’s start by looking at the basic runner, which is suitable for the majority of simple JavaScript applications.

Running from the Command Line

To run a JavaScript file called my_script.js with Nashorn, just use the jrunscript command:

jrunscript my_script.js

jrunscript can also be used with different script engines than Nashorn (see “Nashorn and javax.script” for more details on script engines) and it provides a -l switch to specify them if needed:

jrunscript –l nashorn my_script.js

NOTE

With this switch, jrunscript can even run scripts in languages other than JavaScript, provided a suitable script engine is available.

The basic runner is perfectly suitable for simple use cases but it has limitations and so for serious use we need a more capable execution environment. This is provided by jjs, the Nashorn shell.

Using the Nashorn Shell

The Nashorn shell command is jjs. This can be used either interactively, or non-interactively, as a drop-in replacement for jrunscript.

The simplest JavaScript example is, of course, the classic “Hello World,” so let’s look at how we would achieve this in the interactive shell:

$ jjs

jjs> print("Hello World!");

Hello World!

jjs>

Nashorn interoperability with Java can be easily handled from the shell. We’ll discuss this in full detail in “Calling Java from Nashorn”, but to give a first example, we can directly access Java classes and methods from JavaScript by using the fully qualified class name. As a concrete example, let’s access Java’s builtin regular expression support:

jjs> var pattern = java.util.regex.Pattern.compile("\\d+");

jjs> var myNums = pattern.split("a1b2c3d4e5f6");

jjs> print(myNums);

[Ljava.lang.String;@10b48321

jjs> print(myNums[0]);

a

NOTE

When we used the REPL to print out the JavaScript variable myNums, we got the result [Ljava.lang.String;@10b48321—this is a tell-tale sign that despite being represented in a JavaScript variable, myNums is really a Java array of strings.

We’ll have a great deal more to say about interoperation between Nashorn and Java later on, but first let’s discuss some of the additional features of jjs. The general form of the jjs command is:

jjs [<options>] <files> [-- <arguments>]

There are a number of options that can be passed to jjs—some of the most common are:

§ -cp or -classpath indicates where additional Java classes can be found (to be used via the Java.type mechanism, as we’ll see later).

§ -doe or -dump-on-error will produce a full error dump if Nashorn is forced to exit.

§ -J is used to pass options to the JVM. For example, if we want to increase the maximum memory available to the JVM:

§ $ jjs -J-Xmx4g

§ jjs> java.lang.Runtime.getRuntime().maxMemory()

3817799680

§ -strict causes all script and functions to be run in JavaScript strict mode. This is a feature of JavaScript that was introduced with ECMAScript version 5, and is intended to reduce bugs and errors. Strict mode is recommended for all new development in JavaScript, and if you’re not familiar with it you should read up on it.

§ -D allows the developer to pass key-value pairs to Nashorn as system properties, in the usual way for the JVM. For example:

§ $ jjs –DmyKey=myValue

§ jjs> java.lang.System.getProperty("myKey");

myValue

§ -v or -version is the standard Nashorn version string.

§ -fv or -fullversion prints the full Nashorn version string.

§ -fx is used to execute a script as a JavaFX GUI application. This allows a JavaFX programmer to write a lot less boilerplate by making use of Nashorn.15

§ -h is the standard help switch.

§ -scripting can be used to enable Nashorn-specific scripting extensions. This is the subject of the next subsection.

Scripting with jjs

The jjs shell can be a good way to test out some basic JavaScript, or to work interactively with an unfamiliar JavaScript package (e.g., when learning it). However, it is slightly hampered by lacking multiline input and other more advanced features that are often expected when developing with languages that make heavy use of a REPL.

Instead, jjs is very suitable for noninteractive use, such as bringing up a daemon process written in JavaScript. For use cases like this, we invoke jjs like this:

$ jjs -scripting my_script.js

This enables us to make use of the enhanced features of jjs. These include some useful extensions, many of which make using Nashorn slightly more familiar to the script programmer.

Scripting comments

In traditional Unix scripting, the # character is used to indicate a comment that runs until the end of the line. JavaScript, of course, uses C/C++ style comments that include // to indicate a comment that runs to the end of the line. Nashorn conforms to this as well, but in scripting mode also accepts the Unix scripting style, so that this code is perfectly legal:

#!/usr/bin/jjs

# A perfectly legal comment in scripting mode

print("After the comment");

Inline command execution

This feature is usually referred to as “backticks” by seasoned Unix programmers. So, just as we could write this bit of bash to download content from Google by using the Unix curl command:

echo "Google says: " `curl http://www.google.co.uk`

we can also use the ` backtick quotes to enclose a Unix shell command that we want to run from within a Nashorn script. Like this:

print("Google says: "+ `curl http://www.google.co.uk`);

String interpolation

String interpolation is a special bit of syntax that allows the programmer to directly include the contents of a variable without using string concatenation. In Nashorn scripting, we can use the syntax ${<variable name>} to interpolate variables within strings. For example, the previous example of downloading a web page can be rewritten using interpolation like this:

var url = "www.google.co.uk";

var pageContents = `curl http://${url}`;

print("Google says: ${pageContents}");

Special variables

Nashorn also provides several special global variables and functions that are specifically helpful for scripting and are not normally available in JavaScript. For example, the arguments to a script can be accessed via the variable $ARG. The arguments must be passed using the -- convention, like this:

jjs test1.jjs -- aa bbb cccc

Then the arguments can be accessed as shown in this example:

print($ARG);

for(var i=0; i < $ARG.length; i++) {

print("${i}: "+ $ARG[i]);

}

NOTE

The variable $ARG is a JavaScript array (as we can see from how it behaves when it’s passed to print()) and needs to be treated as one. This syntax may be a little confusing for programmers coming from other languages, where the $ symbol often indicates a scalar variable.

The next special global variable that we’ll meet is $ENV, that provides an interface to the current environment variables. For example, to print out the current user’s home directory:

print("HOME = "+ $ENV.HOME); # Prints /home/ben for me

Nashorn also provides access to a special global function called $EXEC(). This works like the backticks we met just now, as this example shows:

var execOutput = $EXEC("echo Print this on stdout");

print(execOutput);

You may have noticed that when we use the backtick or $EXEC() then the output of the executed command is not printed—but instead ends up as the return value of the function. This is to prevent the printed output of executed commands from corrupting the output of the main script.

Nashorn provides two other special variables that can help the programmer to work with the output of commands that are executed from within a script: $OUT and $ERR. These are used to capture the output and any error messages from a command that is executed from within a script. For example:

$EXEC("echo Print this on stdout");

// Code that doesn't change stdout

var saveOut = $OUT;

print("- - - - - - -");

print(saveOut);

The contents of $OUT and $ERR persist until they are overwritten by subsequent code in the main script that can also affect the values held there (such as another command execution).

Inline documents

JavaScript, like Java, does not support strings where the opening quote is on one line and the closing quote on another (known as multiline strings). However, Nashorn in scripting mode supports this as an extension. This feature is also known as an inline document or a heredoc and is a common feature of scripting languages.

To use a heredoc, use the syntax <<END_TOKEN to indicate that the heredoc starts on the next line. Then, everything until the end token (which can be any string, but is usually all capitals—strings like END, END_DOC, END_STR, EOF, and EOSTR are all quite common) is part of the multiline string. After the end token, the script resumes as normal. Let’s look at an example:

var hw = "Hello World!";

var output = <<EOSTR;

This is a multiline string

It can interpolate too - ${hw}

EOSTR

print(output);

Nashorn helper functions

Nashorn also provides some helper functions to make it easier for developers to accomplish common tasks that shell scripts often want to perform:

print() / echo()

We’ve been using print() throughout many of our examples, and these functions behave exactly as expected. They print the string they’ve been passed, followed by a newline character.

quit() / exit()

These two functions are completely equivalent—they both cause the script to exit. They can take an integer parameter that will be used as the return code of the script’s process. If no argument is supplied, they will default to using 0, as is customary for Unix processes.

readLine()

Reads a single line of input from standard input (usually the keyboard). By default, it will print the line out on standard output, but if the return value of readLine() is assigned to a variable, the entered data will end up there instead, as in this example:

print("Please enter your name: ");

var name = readLine();

print("Please enter your age: ");

var age = readLine();

print(<<EOREC);

Student Record

-+-+-+-+-+-+-+-

Name: ${name}

Age: ${age}

EOREC

readFully()

Instead of reading from standard input, readFully() loads the entire contents of a file. As with readLine(), the contents are either printed to standard output, or assigned to a variable:

var contents = readFully("input.txt");

load()

This function is used to load and evaluate (via JavaScript’s eval) a script. The script can be located by a local path, or a URL. Alternatively, it may be defined as a string using JavaScript’s script object notation.

NOTE

When using load() to evaluate other scripts, unexpected errors may occur. JavaScript supports a form of exception handling using try-catch blocks, so you should use it when loading code.

Here’s a quick example of how to load the D3 graphics visualization library from Nashorn:

try {

load("http://d3js.org/d3.v3.min.js");

} catch (e) {

print("Something went wrong, probably that we're not a web browser");

}

loadWithNewGlobal()

When we use load(), it evaluates the script based on the current JavaScript context. Sometimes we want to put the script into its own, clean context. In these cases, use loadWithNewGlobal() instead, as this starts off the script with a fresh, global context.

Shebang syntax

All the features in this section help to make jjs a good alternative language that can easily be used to write shell scripts as bash, Perl, or other scripting languages. One final feature helps to round out this support—the availability of the “shebang” syntax for starting up scripts written in Nashorn.

NOTE

If the first line of an executable script starts with #! followed by a path to an executable, then a Unix operating system will assume the path points at an interpreter that is able to handle this type of script. If the script is executed, the OS will execute the interpreter and pass it the script file to be handled.

In the case of Nashorn, it is good practice to symlink (possibly needing sudo access) so that there is a link from /usr/bin/jjs (or /usr/local/bin/jjs) to the actual location of the jjs binary (usually $JAVA_HOME/bin/jjs). The Nashorn shell scripts can then be written like this:

#!/usr/bin/jjs

# ... rest of script

For more advanced use cases (e.g., long-running daemons) Nashorn can even provide compatibility with Node.js. This is achieved by the Avatar.js portion of Project Avatar, that is discussed in “Project Avatar”.

The tools we’ve seen in this section easily enable JavaScript code to be run directly from the command line, but in many cases we will want to go the other way. That is, we will want to call out to Nashorn and execute JavaScript code from within a Java program. The API that enables us to do this is contained in the Java package javax.script, so let’s move on to examine that package next, and discuss how Java interacts with engines for interpreting scripting languages.

Nashorn and javax.script

Nashorn is not the first scripting language to ship with the Java platform. The story starts with the inclusion of javax.script in Java 6, which provided a general interface for engines for scripting languages to interoperate with Java.

This general interface included concepts fundamental to scripting languages, such as execution and compilation of scripting code (whether a full script or just a single scripting statement in an already existing context). In addition, a notion of binding between scripting entities and Java was introduced, as well as script engine discovery. Finally, javax.script provides optional support for invocation (distinct from execution, as it allows intermediate code to be exported from a scripting language’s runtime and used by the JVM runtime).

The example language provided was Rhino, but many other scripting languages were created to take advantage of the support provided. With Java 8, Rhino has been removed, and Nashorn is now the default scripting language supplied with the Java platform.

Introducing javax.script with Nashorn

Let’s look at a very simple example of how to use Nashorn to run JavaScript from Java:

import javax.script.*;

ScriptEngineManager m = new ScriptEngineManager();

ScriptEngine e = m.getEngineByName("nashorn");

try {

e.eval("print('Hello World!');");

} catch (final ScriptException se) {

// ...

}

The key concept here is ScriptEngine, which is obtained from a ScriptEngineManager. This provides an empty scripting environment, to which we can add code via the eval() method.

The Nashorn engine provides a single global JavaScript object, so all calls to eval() will execute on the same environment. This means that we can make a series of eval() calls and build up JavaScript state in the script engine. For example:

e.eval("i = 27;");

e.put("j", 15);

e.eval("var z = i + j;");

System.out.println(((Number) e.get("z")).intValue()); // prints 42

Note that one of the problems with interacting with a scripting engine directly from Java is that we don’t normally have any information about what the types of values are.

Nashorn has a fairly close binding to much of the Java type system, so we need to be somewhat careful, however. When dealing with the JavaScript equivalents of primitive types, these will typically be converted to the appropriate (boxed) types when they are made visible to Java. For example, if we add the following line to our previous example:

System.out.println(e.get("z").getClass());

we can easily see that the value returned by e.get("z") is of type java.lang.Integer. If we change the code very slightly, like this:

e.eval("i = 27.1;");

e.put("j", 15);

e.eval("var z = i + j;");

System.out.println(e.get("z").getClass());

then this is sufficient to alter the type of the return value of e.get("z") to type java.lang.Double, which marks out the distinction between the two type systems. In other implementations of JavaScript, these would both be treated as the numeric type (as JavaScript does not define integer types). Nashorn, however, is more aware of the actual type of the data.

NOTE

When dealing wih JavaScript that the Java programmer must be consciously aware of the difference between Java’s static typing and the dynamic nature of JavaScript types. Bugs can easily creep in if this awareness is lost.

In our examples, we have made use of the get() and put() methods on the ScriptEngine. These allow us to directly get and set objects within the global scope of the script being executed by a Nashorn engine, without having to write or eval JavaScript code directly.

The javax.script API

Let’s round out this section with a brief description of some key clases and interfaces in the javax.script API. This is a fairly small API (six interfaces, five classes, and one exception) that has not changed since its introduction in Java 6.

ScriptEngineManager

The entry point into the scripting support. It maintains a list of available scripting implementations in this process. This is achieved via Java’s service provider mechanism, which is a very general way of managing extensions to the platform that may have wildly different implementations. By default, the only scripting extension available is Nashorn, although other scripting environments (such as Groovy or JRuby) can also be made available.

ScriptEngine

This class represents the script engine responsible for maintaining the environment in which our scripts will be interpreted.

Bindings

This interface extends Map and provides a mapping between strings (the names of variables or other symbols) and scripting objects. Nashorn uses this to implement the ScriptObjectMirror mechanism for interoperability.

In practice, most applications will deal with the relatively opaque interface offered by methods on ScriptEngine such as eval(), get(), and put(), but it’s useful to understand the basics of how this interface plugs in to the overall scripting API.

Advanced Nashorn

Nashorn is a sophisticated programming environment, which has been engineered to be a robust platform for deploying applications, and to have great interoperability with Java. Let’s look at some more advanced use cases for JavaScript to Java integration, and examine how this is achieved by looking inside Nashorn at some implementation details.

Calling Java from Nashorn

As each JavaScript object is compiled into an instance of a Java class, it’s perhaps not surprising that Nashorn has seamless integration with Java—despite the major difference in type systems and language features. However, there are still mechanisms that need to be in place to get the most out of this integration.

We’ve already seen that we can directly access Java classes and methods from Nashorn, for example:

$ jjs -Dkey=value

jjs> print(java.lang.System.getProperty("key"));

value

Let’s take a closer look at the syntax and see how to achieve this support in Nashorn.

JavaClass and JavaPackage

From a Java perspective, the expression java.lang.System.getProperty("key") reads as fully qualified access to the static method getProperty() on java.lang.System. However, as JavaScript syntax, this reads like a chain of property accesses, starting from the symbol java—so let’s investigate how this symbol behaves in the jjs shell:

jjs> print(java);

[JavaPackage java]

jjs> print(java.lang.System);

[JavaClass java.lang.System]

So java is a special Nashorn object that gives access to the Java system packages, which are given the JavaScript type JavaPackage, and Java classes are represented by the JavaScript type JavaClass. Any top-level package can be directly used as a package navigation object, and subpackages can be assigned to a JavaScript object. This allows syntax that gives concise access to Java classes:

jjs> var juc = java.util.concurrent;

jjs> var chm = new juc.ConcurrentHashMap;

In addition to navigation by package objects, there is another object, called Java, which has a number of useful methods on it. One of the most important is the Java.type() method. This allows the user to query the Java type system, and get access to Java classes. For example:

jjs> var clz = Java.type("java.lang.System");

jjs> print(clz);

[JavaClass java.lang.System]

If the class is not present on the classpath (e.g., specified using the -cp option to jjs), then a ClassNotFoundException is thrown (jjs will wrap this in a Java RuntimeException):

jjs> var klz = Java.type("Java.lang.Zystem");

java.lang.RuntimeException: java.lang.ClassNotFoundException:

Java.lang.Zystem

The JavaScript JavaClass objects can be used like Java class objects in most cases (they are a slightly different type—but just think of them as the Nashorn-level mirror of a class object). For example, we can use a JavaClass to create a new Java object directly from Nashorn:

jjs> var clz = Java.type("java.lang.Object");

jjs> var obj = new clz;

jjs> print(obj);

java.lang.Object@73d4cc9e

jjs> print(obj.hashCode());

1943325854

// Note that this syntax does not work

jjs> var obj = clz.new;

jjs> print(obj);

undefined

However, you should be slightly careful. The jjs environment automatically prints out the results of expressions—which can lead to some unexpected 'margin-top:0cm;margin-right:0cm;margin-bottom:0cm; margin-left:20.0pt;margin-bottom:.0001pt;line-height:normal;vertical-align: baseline'>jjs> var clz = Java.type("java.lang.System");

jjs> clz.out.println("Baz!");

Baz!

null

The point here is that java.lang.System.out.println() has a return type of void (i.e., it does not return a value). However, jjs expects expressions to have a value and, in the absence of a variable assignment, it will print it out. So the nonexistent return value of println() is mapped to the JavaScript value null, and printed out.

NOTE

Java programmers who are not familiar with JavaScript should be aware that the handling of null and missing values in JavaScript is subtle, and in particular that null != undefined.

JavaScript functions and Java lambda expressions

The interoperability between JavaScript and Java goes to a very deep level. We can even use JavaScript functions as anonymous implementations of Java interfaces (or as lambda expressions). For example, let’s use a JavaScript function as an instance of the Callable interface (which represents a block of code to be called later). This has only a single method, call(), which takes no parameters and returns void. In Nashorn, we can use a JavaScript function as a lambda expression instead:

jjs> var clz = Java.type("java.util.concurrent.Callable");

jjs> print(clz);

[JavaClass java.util.concurrent.Callable]

jjs> var obj = new clz(function () { print("Foo"); } );

jjs> obj.call();

Foo

The basic fact that is being demonstrated is that, in Nashorn, there is no distinction between a JavaScript function and a Java lambda expression. Just as we saw in Java, the function is being automatically converted to an object of the appropriate type. Let’s look at how we might use a JavaExecutorService to execute some Nashorn JavaScript on a Java thread pool:

jjs> var juc = java.util.concurrent;

jjs> var exc = juc.Executors.newSingleThreadExecutor();

jjs> var clbl = new juc.Callable(function (){

\java.lang.Thread.sleep(10000); return 1; });

jjs> var fut = exc.submit(clbl);

jjs> fut.isDone();

false

jjs> fut.isDone();

true

The reduction in boilerplate compared to the equivalent Java code (even with Java 8 lambdas) is quite staggering. However, there are some limitations caused by the manner in which lambdas have been implemented. For example:

jjs> var fut=exc.submit(function (){\

java.lang.Thread.sleep(10000); return 1;});

java.lang.RuntimeException: java.lang.NoSuchMethodException: Can't

unambiguously select between fixed arity signatures

[(java.lang.Runnable), (java.util.concurrent.Callable)] of the method

java.util.concurrent.Executors.FinalizableDelegatedExecutorService↵

.submit for argument types

[jdk.nashorn.internal.objects.ScriptFunctionImpl]

The problem here is that the thread pool has an overloaded submit() method. One version will accept a Callable and the other will accept a Runnable. Unfortunately, the JavaScript function is eligible (as a lambda expression) for conversion to both types. This is where the error message about not being able to “unambiguously select” comes from. The runtime could choose either, and can’t choose between them.

Nashorn’s JavaScript Language Extensions

As we’ve discussed, Nashorn is a completely conformant implementation of ECMAScript 5.1 (as JavaScript is known to the standards body). In addition, however, Nashorn also implements a number of JavaScript language syntax extensions, to make life easier for the developer. These extensions should be familiar to developers used to working with JavaScript, and quite a few of them duplicate extensions present in the Mozilla dialect of JavaScript. Let’s take a look at a few of the most common, and useful, extensions.

Foreach loops

Standard JavaScript does not have an equivalent of Java’s foreach loop, but Nashorn implements the Mozilla syntax for for each in loops, like this:

var jsEngs = [ "Nashorn", "Rhino", "V8", "IonMonkey", "Nitro" ];

for each (js in jsEngs) {

print(js);

}

Single expression functions

Nashorn also supports another small syntax enhancement, designed to make one-line functions that comprise a single expression easier to read. If a function (named or anonymous) comprises just a single expression, then the braces and return statements can be omitted. In the example that follows, cube() and cube2() are completely equivalent functions, but cube() is not normally legal JavaScript syntax:

function cube(x) x*x*x;

function cube2(x) {

return x*x*x;

}

print(cube(3));

print(cube2(3));

Multiple catch clauses

JavaScript supports try, catch, and throw in a similar way to Java.

WARNING

JavaScript has no support for checked exceptions—all JavaScript exceptions are unchecked.

However, standard JavaScript only allows a single catch clause following a try block. There is no support for different catch clauses handling different types of exception. Fortunately, there is already an existing Mozilla syntax extension to offer this feature, and Nashorn implements it as well, as shown in this example:

function fnThatMightThrow() {

if (Math.random() < 0.5) {

throw new TypeError();

} else {

throw new Error();

}

}

try {

fnThatMightThrow();

} catch (e if e instanceof TypeError) {

print("Caught TypeError");

} catch (e) {

print("Caught some other error");

}

Nashorn implements a few other nonstandard syntax extensions (and when we met scripting mode for jjs we saw some other useful syntax innovations), but these are likely to be the most familiar and widely used.

Under the Hood

As we have previously discussed, Nashorn works by compiling JavaScript programs directly to JVM bytecode, and then runs them just like any other class. It is this functionality that enables, for example, the straightforward representation of JavaScript functions as lambda expressions and their easy interoperability.

Let’s take a closer look at an earlier example, and see how we’re able to use a function as an anonymous implementation of a Java interface:

jjs> var clz = Java.type("java.util.concurrent.Callable");

jjs> var obj = new clz(function () { print("Foo"); } );

jjs> print(obj);

jdk.nashorn.javaadapters.java.util.concurrent.Callable@290dbf45

This means that the actual type of the JavaScript object implementing Callable is jdk.nashorn.javaadapters.java.util.concurrent.Callable. This class is not shipped with Nashorn, of course. Instead, Nashorn spins up dynamic bytecode to implement whatever interface is required and just maintains the original name as part of the package structure for readability.

NOTE

Remember that dynamic code generation is an essential part of Nashorn, and that all JavaScript code is compiled by Nashorn in Java bytecode and never interpreted.

One final note is that Nashorn’s insistence on 100% compliance with the spec does sometimes restrict the capabilities of the implementation. For example, consider printing out an object, like this:

jjs> var obj = {foo:"bar",cat:2};

jjs> print(obj);

[object Object]

The ECMAScript specification requires the output to be [object Object]—conformant implementations are not allowed to give more useful detail (such as a complete list of the properties and values contained in obj).

Conclusion

In this chapter, we’ve met Nashorn, the JavaScript implementation on top of the JVM that ships with Oracle’s Java 8. We’ve seen how to use it to execute scripts and even replace bash and Perl scripts with enhanced JavaScript scripts that can leverage the full power of Java and the JVM. We’ve met the JavaScript engine API and seen how the bridge between Java and scripting languages is implemented.

We’ve seen the tight integration between JavaScript and Java that Nashorn provides, and some of the small language syntax extensions that Nashorn provides to make programming a little bit easier. Finally, we’ve had a brief peek under the hood at how Nashorn implements all of this functionality. To conclude, let’s take a quick look into the future and meet Project Avatar, which could be the future of Java/JavaScript web applications.

Project Avatar

One of the most successful movements in the JavaScript community in recent years has been Node.js. This is a simple server-side JavaScript implementation developed by Ryan Dahl and now curated by Joyent. Node.js provides a programming model that is heavily asynchronous—designed around callbacks, nonblocking I/O, and a simple, single-threaded event loop model.

While it is not suitable for developing complex enterprise applications (due to limitations of the callback model in larger codebases), Node.js (often referred to simply as Node) has nonetheless become an interesting option for developing prototypes, simple “glue” servers, and single-purpose HTTP and TCP server applications of low to moderate complexity.

The Node ecosystem has also prospered by promoting reusable units of code, known as Node packages. Similar to the Maven archives (and to earlier systems, such as the Perl CPAN), Node packages allow the easy creation and redistribution of code, although they suffer from the relative immaturity of JavaScript, which is missing many modularity and deployment features.

The original implementation of Node is composed of several basic components—a JavaScript execution engine (the V8 engine developed by Google for their Chrome browser), a thin abstraction layer, and a standard library (of mostly JavaScript code).

In September 2013, Oracle announced Project Avatar. This is an effort by Oracle to produce a future-state architecture for web applications and to marry JavaScript (and Node) to the mature ecosystem that already exists for Java web apps.

As part of Project Avatar, Oracle open sourced their implementation of the Node API, which runs on top of Nashorn and the JVM. This implementation, known as Avatar.js, is a faithful implementation of most of the Node API. It is currently (April 2014) capable of running a large number of Node modules—essentially anything that does not depend on native code.

The future is, of course, unknown, but Avatar points the way towards a possible world where the JVM is the foundation of a new generation of web applications that combine JavaScript with Java and hopefully provide the best of both worlds.

15 JavaFX is a standard Java technology used for making GUIs—but it is outside the scope of this book.