Latest content
Explore and discover our latest tutorials
Tech Articles from your friends at Oracle and the developer community.
The Nashorn engine has been deprecated in JDK 11 as part of JEP 335 and and has been removed from JDK15 as part of JEP 372. To learn more, please read Migration Guide from Nashorn to GraalVM JavaScript.
This series of articles introduces Nashorn, a blazing fast JavaScript runtime engine that shipped with Java SE 8 to provide a lightweight environment for extending, supplementing, and often even replacing Java code. Full Java Virtual Machine (JVM) capabilities and dynamic typing represent an effective tooling that will appeal to developers and admins alike.
Nashorn is a Java implementation of JavaScript that was developed under JSR 292:
Supporting Dynamically Typed Languages on the Java Platform, as part of a Da Vinci Machine
Project inside the JVM group. Dynamic typing is achieved through the use of invokedynamic
bytecode that delegates invocations on Java objects. This allows
a seamless integration between Java and JavaScript and requires only a minimal amount of boilerplate to
start programming on the JVM without the need to write or compile Java code.
The Nashorn engine is a direct successor to Mozilla Rhino, and it continues the open source model under the OpenJDK project. The current implementation of Nashorn conforms to the ECMAScript 5.1 specification with the addition of new ECMAScript 6 (ES6) features in subsequent JDK releases under JEP 292: Implement Selected ECMAScript 6 Features in Nashorn. Starting with Java SE 8, each Java runtime environment (JRE) and Java Development Kit (JDK) ships with Nashorn by default, which makes Nashorn a very easy and accessible tool for gluing together all sorts of applications, databases, and clouds.
Running NashornOn systems with Java SE 8 or newer installed, Nashorn is already available for use. Most of the Linux
distributions might simply choose to install the java-1.8.0-openjdk
package
to quickly deploy the JDK. An easy way to try out Nashorn is a Docker container with the latest stable
OpenJDK release, as shown in Listing 1 (the official enterprise-grade JRE container store/oracle/serverjre:8
can be used instead for production environments).
$ jjs -v
nashorn 1.8.0_141
jjs>
C:\> jjs.exe -v
nashorn 1.8.0_141
jjs>
$ docker run openjdk jjs -v
nashorn 1.8.0_141
jjs>
Listing 1. Running the Nashorn shell on Linux or macOS, on Windows, or on Docker.
The binary program jjs
is used to launch the internal Nashorn shell, which
simply instantiates the jdk.nashorn.tools.Shell
class in Java 8 or jdk.scripting.nashorn.shell
in Java 9. The interactive prompt is quite like the
interactive interpreters from Python or Ruby. To quit the prompt run the exit()
function or press Control-C, where exit can optionally take a status
code as a parameter. A commonly known DBA trick is to run interactive interpreters with the rlwrap wrapper to benefit from persistent history and
typeahead search, which makes a lot of sense for shells such as jjs
(rlwrap
package is part of the Fedora EPEL repo).
Because Nashorn is simply a Java program, all JVM-specific attributes can be provided on the command
line, the most important being the classpath and system properties, which are specified with the -cp
and -D
switches, respectively. Instead of the
built-in -cp
or -classpath
, it's often better
to use the -J-Djava.class.path
attribute to avoid class resolution
issues—for example, with JDBC drivers—caused by nesting execution context.
When JVM code comes into play, Nashorn will not throw the entire Java stack trace by default. To achieve
better traceability, jjs
must be invoked as jjs --dump-on-error
or simply jjs -doe
.
To quickly determine all running instances of Nashorn, jcmd
can be used to
list their process IDs and parameters. This can be used to quickly confirm correct classpath parameters,
ES6 compatibility, or the scripting mode, as shown in Listing 2.
$ jcmd
2660 jdk.nashorn.tools.Shell -scripting
7476 jdk.nashorn.tools.Shell --language=es6 -ot
15640 sun.tools.jcmd.JCmd
Listing 2. Viewing running Nashorn shell instances with jcmd.
ES6 and RhinoWhen Java 8 debuted in 2014, the latest ECMAScript specification available at the time was 5.1. That
version became Nashorn's baseline. Shortly thereafter, work on JEP 292 started and most important
ES6 features started shipping. The single largest addition was the introduction of JEP 203:
Nashorn: Lexically-Scoped Variable & Constant Declarations in Java 8 Update 40, which fixed
one of the biggest gripes of ES5 JavaScript: the lack of variable block scope. Nashorn's ES6 efforts
and JEP 203 have fixed this with the introduction of the let
and const
keywords, while preserving the var
keyword.
Listing 3 demonstrates the importance of using let
and const
for programmers coming to JavaScript from other backgrounds. Larger
Nashorn codebases will certainly benefit from running the ES6 mode by default to avoid unexpected
scoping behavior unless JavaScript 5 compatibility is intended.
var x = 1;
function es5(b) {
if (b) {
var x = 2;
}
print(x);
}
let y = 1;
function es6(b) {
let y = 2;
if (b) {
}
print(y);
}
jjs> es5(true);
2
jjs> es5(false);
undefined
jjs> es6(true);
1
jjs> es6(false);
1
Listing 3. Nashorn implementation of ES6 variable scope.
Mozilla Rhino–compatible programs can be safely executed on Nashorn by loading the compatibility
library, which implements the importClass
, importPackage
, and JavaAdapter
functions, among
others. To enable compatibility, execute load("nashorn:mozilla_compat.js")
as the first step in the code. Note
that this feature has been kept only for backwords-compatibility purposes; using Rhino extensions by
default is not recommended.
Due to the rapid development track that Nashorn has undergone across the Java 8 lifespan, make sure you
work with a recent Nashorn release. An easy way to ensure a consistent Nashorn version is being used is
by running jjs -v
, which displays version information as the first line of
output (jjs -fv
displays full version information).
JavaScript has proven itself to be a very easy language to comprehend and immediately put into use. However, it comes with a set of basic principles that could be baffling to newcomers:
undefined
by default which is a different from being null
. To check if variable is defined, the basic condition if (variable)
is a sufficient check.undefined
, null
, false
, -0
, +0
, NaN
, and ''
are truthy and evaluate to
true
during comparison.
3 + 3 + "3"
evaluates to 63
whereas
"3" + 3 + 3
yields 333
. This
implicit conversion stems from left-to-right arithmetic and when a string object is found, the
operation continues as a concatenation.
==
operator. To avoid coercing the type when comparing values, it's good to use the ===
operator, which compares both the value and the type, for example,
1 == true
but 1 !== true
.
parseInt
and parseFloat
functions or a shorter
+variable
expression syntax to perform number conversion.
&&
and ||
operators so that 1 == "1" && print("yes")
simulates the longer
if (1 == "1") { print("yes"); }
syntax.
undefined
. The predefined array-like arguments
variable exposes the index access with square brackets. The only ways to access this variable are
through arguments[i]
and arguments.length()
.
Node.js developers attempting to execute the require()
function to include an
external module will be immediately puzzled by receiving the ReferenceError: "require" is not defined
error. Nashorn itself
doesn't incorporate any module system built in and relies only on the load()
function to import dependencies. Nevertheless, require()
is totally possible with
the use of third-party scripts implementing
the CommonJS specification, such as Nodyn .
See Listing 4 for a demonstration of the popular JavaScript unit testing framework QUnit used against
Java code.
/* unittest.js */
load("jvm-npm.js");
var QUnit = require("qunit-2.4.0.js");
QUnit.done(function(details) {
print(JSON.stringify(details, null, 4));
});
QUnit.test("Running at least Java 8", function(assert) {
assert.ok(java.lang.System.getProperty("java.version") >= "1.8");
});
QUnit.test("Always failing", function(assert) {
assert.fail();
});
QUnit.load();
$ jjs unittest.js
{
"passed": 1,
"failed": 1,
"total": 2,
"runtime": 295
}
Listing 4. Using the JavaScript QUnit unit testing framework from Nashorn.
Even though JavaScript does not offer syntax for implementing modules, a commonly used pattern can be
used with Nashorn to leverage the built-in load()
function. For even fairly
complex code, modules are inevitable for readability and maintainability in the long run. Listing 5
shows a typical module implementation that covers object-oriented programming aspects—with the
possibility of addressing global scope, even in the absence of a browser's window
or console
object in the language
specification.
/* module.js */
(function(global) {
return function(moduleArg) {
var privateMethod = function(v) {
print("This is private");
global["v"] = v;
}
this.publicMethod = function() {
print("This is public");
}
__proto__.staticMethod = function() {
print("This is static");
}
{
print("This is constructor");
privateMethod(42);
}
}
})(this);
jjs> var Module = load('module.js');
jjs> Module.staticMethod()
This is static
jjs> var m = new Module();
This is constructor
This is private
jjs> Module.privateMethod()
<shell>:1 TypeError: Module.privateMethod is not a function
Listing 5. Nashorn module pattern.
SummaryJavaScript's exponential growth, fueled by progress in web development, positions the language high in the ranks. The work that was started by Mozilla and Sun continues to receive a lot attention at Oracle, with a growing number of tools and products leveraging Nashorn for extensibility and scripting. The language that stems from the web has successfully entered the server space and, thanks to ScriptEngine and JSR 223, it has become a first-class JVM citizen. Subsequent articles in this series demonstrate first-hand examples of Nashorn put into use.