7 min read

Blog-Java8-Nashorn-Engine.jpgThis blog post is a concluding post in the series on the top most interesting features of Java 8. Here, we’ll discuss the power of Nashorn Engine and Java-to-JavaScript interoperability . The change from “can call JavaScript to Java and vice-versa” to the embedded JavaScript engine greatly improves the performance and makes its adoption worthwhile for the enterprises. The post unfolds and demonstrates how Java types can be implemented and extended from JavaScript, providing a seamless integration between the two languages.

Introduction

Nashorn is a JavaScript engine for the JVM that is released with Java 8. It has replaced the existing Rhino engine. The advantages Nashorn brings to the table are multiple, from support to full ECMAScript 5.1 specification along with some extensions, to 100% pure Java implementation and 100% complied to bytecode and not the interpreted mode as with Rhino. Nashorn comprises of an embedded JavaScript interpreter and a command line tool. It directly compiles the JavaScript code in-memory and passes the Java bytecode to the JVM, and uses invokedynamic which provides backward compatibility to JDK7. This results in better runtime performance. Nashorn is compliant with the ECMAScript standard for JavaScript.

As JavaScript is getting popular on server-side programming (like Node.js), Nashorn enables the integration of JavaScript and Java applications by accessing JavaScript functions in Java, Java classes, and methods in JavaScript. This gives you portability to run JavaScript from the command line using Nashorns’ JJS command.

Background

The Nashorn engine is a successor to the Rhino engine, an open source JavaScript interpreter that was written in Java. The Rhino project began at Netscape in 1997 and was released to the Mozilla Foundation in April 1998. The Rhino JavaScript engine was shipped with JDKs until Java SE 7.

The Rhino was not particularly fast and had a limited ECMAScript 3 feature set. This made the engineers at Oracle, to build an efficient JavaScript interpreter using the new JVM instruction and this resulted in Java 8 being shipped with a new JavaScript engine called Nashorn (means rhinoceros in German), which is based on JSR 292.

Using Nashorn

The Nashorn Javascript engine gives you the flexibility to be used either programmatically from Java programs or by utilizing the command line tool jjs which is located in %JAVA_HOME%/bin:

C:\> cd %JAVA_HOME%\bin
-- on MS windows
C:\Program Files\Java\jdk1.8.0_25\bin>jjs
jjs> print ('Hello World');


Output:
//Hello World
// Sample Java code to demonstrate how to use Nashorn Engine:

//Create instance of javax.script.ScriptEngine
ScriptEngine engine = new ScriptEngineManager ().getEngineByName ("nashorn");
try {
//execute the JavaScript code
engine.eval ("print ('Hello World')");
}
catch (Exception e) {}

Output:
//Hello World

You can include JavaScript code either by passing the code as a string as shown in above examples or by passing a file reader pointing to the .js JavaScript file as shown in below example.

// Sample Java code to invoke JavaScript code from a .js file
ScriptEngine engine = new ScriptEngineManager ().getEngineByName ("nashorn");

//evaluate the JavaScript code written in a file.
engine.eval (new FileReader ("script.js"));

Nashorn JavaScript is based on ECMAScript 5.1, but the future versions of Nashorn will have support for ECMAScript 6. Nashorn defines various language and API extensions to the ECMAScript standard.

Invoking JavaScript Functions from Java

As stated earlier, You can invoke the JavaScript functions defined in your JavaScript files directly from the Java code as shown in below examples. You have to pass the Java objects as the function arguments and return values from the function to the calling Java method.

/*
* File Name : script.js
*
*
@function - Print a given name
* @param {String} name
* @return String
/
var fun1 = function (name) {
print ('Hello from JS, ' + name);
return "Hi from JS";
};

To begin with, you have to cast the script engine to Invocable to be able to call a function. The NashornScriptEngine implements the Invocable interface and defines a method invokeFunction to call a JavaScript function for a given name.

// Java code to call JavaScript function defined above
// Create instance of javax.script.ScriptEngine
ScriptEngine engine = new ScriptEngineManager ().getEngineByName ("nashorn");

//evaluate the javascript file.
engine.eval (new FileReader ("script.js"));

//get handle of JavaScript file’s context
Invocable invocable = (Invocable) engine;

//invoke function defined in the file’s context and pass the argument.
Object result = invocable.invokeFunction ("fun1", "Mr. X");

//print the result and its type.
System.out.println (result);
System.out.println (result.getClass ());

Output:
Hello from JS, Mr. X
Hi from JS
class java.lang.String

Invoking Java Methods from JavaScript

You can also invoke methods defined in Java from JavaScript easily. You have to first define a static method in Java as shown in below example.

package com.test
class JavaScriptTest {
// Following Java function will be called from JavaScript code:
/**
*
Print a given name
* @param name
* @return string
*/
static String fun1 (String name) {
System.out.format ("Hello from Java, %s", name);
return "Hi from Java";
}
}

You can refer to the classes from JavaScript by the Java.type API extension. It is similar to importing classes in Java code. After the Java type is defined, you can call the static method and print the result to console. There is no need to create an instance first as the method is static. This is shown in the following example:

jjs> var MyJavaClass = Java.type ('com.test.JavaScriptTest’); /* load java class ‘'com.test.JavaScriptTest’ */
jjs> var result = MyJavaClass.fun1 ('Jack'); /* call the function */
jjs> print (result);

Output:
// Hello from Java, Jack
// Hi from Java

Invoking Java API from JavaScript

You can invoke a Java API by providing the fully qualified name (including the package name) as shown in below example. Nashorn has defined global objects for the Java APIs in the package name.

jjs> java.lang.String.join (" ","Hello","World")

Output:
// Hello World

Alternately, you can also use the predefined “Java” global object. It contains a function named “type”, using this function, you can get the Java Class for the argument passed.

jjs> var joinString=Java.type ("java.lang.String")
jjs> joinString.join (" ", "Hello", "World")

Output:
// Hello World

Language Extensions

Typed Arrays

Native JavaScript arrays are untyped. Nashorn enable us to use typed Java arrays in JavaScript. Java and JavaScript arrays are quite different. When you supply a JavaScript array where a Java array is expected, Nashorn will carry out the conversion.

jjs> var intArr = Java.type ("int []")
jjs> var arr = new intArr (5)
jjs> arr [1] = 12
jjs> arr [2] = 32
jjs> arr [1] + arr [2]

// Output:
44

jjs> arr [5]
// Output:
java.lang.ArrayIndexOutOfBoundsException: Array index out of range: 5

jjs> arr.length
// Output:
5

Collections and For Each

You can use any Java collection for arrays. You have to define the Java type using Java.type and create new instances on demand.

jjs> var arrList = Java.type ('java.util.ArrayList');
jjs> var lst = new arrList ();
jjs> lst.add ('a');
jjs> lst.add ('b');
jjs> lst.add ('c');
jjs> for each (var el in lst) print (el);

// Output:
a, b, c

Java Beans

Just use the simple property names for both, getting and setting values from a Java bean.

jjs> var Date = Java.type ('java.util.Date');
jjs> var date = new Date ();
jjs> date.year += 1900;
// Output:
2015

jjs> print (date.year);
// Output:
2015

Function Literals

Ignore the curly braces for simple one-line functions.

jjs> function sqr(x) x * x;
jjs> print (sqr (2));

// Output:
4

Exceptions

The exceptions from Java methods can be handled in JavaScript.

try {
var firstElement = list.get(0);
...
} catch (e) {
if (e instanceof java.lang.IndexOutOfBoundsException)
print(‘No elements in list');
}

Examples

Example 1: To sort a list of names in JavaScript using Nashorn:

// Sort a list of names in JavaScript
jjs> var Collections = Java.type ("java.util.Collections");
jjs> var ArrayList = Java.type ("java.util.ArrayList");
jjs> var names = new ArrayList ();
jjs> names.add ('Ram');
jjs> names.add ('Mohan');
jjs> names.add ('Shyam');
jjs> print ('Before sort: ', names);
jjs> Collections.sort (names);
jjs> print ('After sort: ', names);

Before sort:
[Ram, Mohan, Shyam]

After sort:
[Mohan, Ram, Shyam]

Example 2: To filter from list of names using Stream APIs in JavaScript using Nashorn:

// Filter a list of names in JavaScript
jjs> var arrayList = new java.util.ArrayList();
jjs> arrayList.add("Jack");
jjs> arrayList.add("Ram");
jjs> arrayList.add("Nick");
jjs> arrayList.add("Adam");
jjs> arrayList.stream().filter(function(el) {return el.startsWith("A")}).sorted().forEach(function(el) {print(el)});

// Output
Adam

Summary

Nashorn is a new JavaScript engine shipped with Java SE 8. The objective of Nashorn is to implement a high-performance JavaScript runtime in Java with a native JVM. Using Nashorn, a developer can embed JavaScript in a Java application and can also invoke Java methods and classes from the JavaScript code.