Nashorn, the rhino in the room
- by costlow
Nashorn is a new runtime within JDK 8 that allows developers to run code written in JavaScript and call back and forth with Java. One advantage to the Nashorn scripting engine is that is allows for quick prototyping of functionality or basic shell scripts that use Java libraries. The previous JavaScript runtime, named Rhino, was introduced in JDK 6 (released 2006, end of public updates Feb 2013). Keeping tradition amongst the global developer community, "Nashorn" is the German word for rhino.
The Java platform and runtime is an intentional home to many languages beyond the Java language itself. OpenJDK’s Da Vinci Machine helps coordinate work amongst language developers and tool designers and has helped different languages by introducing the Invoke Dynamic instruction in Java 7 (2011), which resulted in two major benefits: speeding up execution of dynamic code, and providing the groundwork for Java 8’s lambda executions. Many of these improvements are discussed at the JVM Language Summit, where language and tool designers get together to discuss experiences and issues related to building these complex components.
There are a number of benefits to running JavaScript applications on JDK 8’s Nashorn technology beyond writing scripts quickly:
Interoperability with Java and JavaScript libraries.
Scripts do not need to be compiled.
Fast execution and multi-threading of JavaScript running in Java’s JRE.
The ability to remotely debug applications using an IDE like NetBeans, Eclipse, or IntelliJ (instructions on the Nashorn blog).
Automatic integration with Java monitoring tools, such as performance, health, and SIEM.
In the remainder of this blog post, I will explain how to use Nashorn and the benefit from those features.
Nashorn execution environment
The Nashorn scripting engine is included in all versions of Java SE 8, both the JDK and the JRE. Unlike Java code, scripts written in nashorn are interpreted and do not need to be compiled before execution.
Developers and users can access it in two ways:
Users running JavaScript applications can call the binary directly:jre8/bin/jjs
This mechanism can also be used in shell scripts by specifying a shebang like #!/usr/bin/jjs
Developers can use the API and obtain a ScriptEngine through:ScriptEngine engine = new ScriptEngineManager().getEngineByName("nashorn");
When using a ScriptEngine, please understand that they execute code. Avoid running untrusted scripts or passing in untrusted/unvalidated inputs. During compilation, consider isolating access to the ScriptEngine and using Type Annotations to only allow @Untainted String arguments.
One noteworthy difference between JavaScript executed in or outside of a web browser is that certain objects will not be available. For example when run outside a browser, there is no access to a document object or DOM tree. Other than that, all syntax, semantics, and capabilities are present.
Examples of Java and JavaScript
The Nashorn script engine allows developers of all experience levels the ability to write and run code that takes advantage of both languages. The specific dialect is ECMAScript 5.1 as identified by the User Guide and its standards definition through ECMA international.
In addition to the example below, Benjamin Winterberg has a very well written Java 8 Nashorn Tutorial that provides a large number of code samples in both languages.
Basic Operations
A basic Hello World application written to run on Nashorn would look like this:
#!/usr/bin/jjs
print("Hello World");
The first line is a standard script indication, so that Linux or Unix systems can run the script through Nashorn. On Windows where scripts are not as common, you would run the script like: jjs helloWorld.js.
Receiving Arguments
In order to receive program arguments your jjs invocation needs to use the -scripting flag and a double-dash to separate which arguments are for jjs and which are for the script itself:jjs -scripting print.js -- "This will print"
#!/usr/bin/jjs
var whatYouSaid = $ARG.length==0 ? "You did not say anything" : $ARG[0]
print(whatYouSaid);
Interoperability with Java libraries (including 3rd party dependencies)
Another goal of Nashorn was to allow for quick scriptable prototypes, allowing access into Java types and any libraries. Resources operate in the context of the script (either in-line with the script or as separate threads) so if you open network sockets and your script terminates, those sockets will be released and available for your next run.
Your code can access Java types the same as regular Java classes. The “import statements” are written somewhat differently to accommodate for language. There is a choice of two styles:
For standard classes, just name the class: var ServerSocket = java.net.ServerSocket
For arrays or other items, use Java.type: var ByteArray = Java.type("byte[]")You could technically do this for all.
The same technique will allow your script to use Java types from any library or 3rd party component and quickly prototype items.
Building a user interface
One major difference between JavaScript inside and outside of a web browser is the availability of a DOM object for rendering views. When run outside of the browser, JavaScript has full control to construct the entire user interface with pre-fabricated UI controls, charts, or components. The example below is a variation from the Nashorn and JavaFX guide to show how items work together.
Nashorn has a -fx flag to make the user interface components available. With the example script below, just specify: jjs -fx -scripting fx.js -- "My title"
#!/usr/bin/jjs -fx
var Button = javafx.scene.control.Button;
var StackPane = javafx.scene.layout.StackPane;
var Scene = javafx.scene.Scene;
var clickCounter=0;
$STAGE.title = $ARG.length>0 ? $ARG[0] : "You didn't provide a title";
var button = new Button();
button.text = "Say 'Hello World'";
button.onAction = myFunctionForButtonClicking;
var root = new StackPane();
root.children.add(button);
$STAGE.scene = new Scene(root, 300, 250);
$STAGE.show();
function myFunctionForButtonClicking(){
var text = "Click Counter: " + clickCounter;
button.setText(text);
clickCounter++;
print(text);
}
For a more advanced post on using Nashorn to build a high-performing UI, see JavaFX with Nashorn Canvas example.
Interoperable with frameworks like Node, Backbone, or Facebook React
The major benefit of any language is the interoperability gained by people and systems that can read, write, and use it for interactions. Because Nashorn is built for the ECMAScript specification, developers familiar with JavaScript frameworks can write their code and then have system administrators deploy and monitor the applications the same as any other Java application.
A number of projects are also running Node applications on Nashorn through Project Avatar and the supported modules.
In addition to the previously mentioned Nashorn tutorial, Benjamin has also written a post about Using Backbone.js with Nashorn.
To show the multi-language power of the Java Runtime, there is another interesting example that unites Facebook React and Clojure on JDK 8’s Nashorn.
Summary
Nashorn provides a simple and fast way of executing JavaScript applications and bridging between the best of each language. By making the full range of Java libraries to JavaScript applications, and the quick prototyping style of JavaScript to Java applications, developers are free to work as they see fit.
Software Architects and System Administrators can take advantage of one runtime and leverage any work that they have done to tune, monitor, and certify their systems.
Additional information is available within:
The Nashorn Users’ Guide
Java Magazine’s article "Next Generation JavaScript Engine for the JVM."
The Nashorn team’s primary blog or a very helpful collection of Nashorn links.