Nashron in a Nutshell

Coding JavaScript on the JVM

Sathya Bandara
5 min readJan 29, 2018

What is Nashorn?

Nashorn is a JavaScript engine for the JVM that is released with Java 8. Nashorn comprises of an embedded JavaScript interpreter and a command line tool. 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 providing a seamless integration between the two languages.

Role of Nashorn in Server Side Scripting

writing server-side JS is nowadays a common requirement. As JavaScript is getting popular on server-side programming — e.g. Node.js, Nashorn enables the integration of JavaScript and Java applications by accessing JavaScript functions in Java and vice versa. It directly compiles the JavaScript code in-memory and passes the Java bytecode to the JVM. Adding support for server side scripting to your products using Nashorn we can easily customize the product to suit various requirements without having the necessity to recompile every-time.

Now lets see some of Nashorn’s functionalities.

Invoking JavaScript Functions from Java

Simple hello world example.

ScriptEngine nashorn = new ScriptEngineManager().getEngineByName
("nashorn");
nashorn.eval("print('Hello, world')");

We can feed an entire .js file into the .eval() method for more serious usecases as follows.

ScriptEngine engine = new ScriptEngineManager().getEngineByName ("nashorn");
engine.eval(new FileReader("script.js"));

Passing the Java objects as the function arguments and return values from the JS function to the calling Java method

  • cast the script engine to Invocable to be able to call a function.
  • NashornScriptEngine defines a method invokeFunction to call a JavaScript function for a given name.
String script = "function var printHello = function(name) {
print('Hello, ' + name);
return 'hello from Javascript';
};";
ScriptEngine engine = new ScriptEngineManager().getEngineByName ("nashorn");
Invocable invocable = (Invocable) engine;
Object result = invocable.invokeFunction("printHello", "Sathya");

Invoking Java Methods from JavaScript

We ca refer to the Java classes from JavaScript using the Java.type API extension. It is similar to importing classes in Java code.

var MyJavaClass = Java.type (‘com.test.JavaScriptTest’);
var result = MyJavaClass.func1 (‘Sathya’); //call a function named 'func1'
print (result);

Invoking Java APIs from JavaScript

Option 1 — provide the fully qualified name i.e. the package name

var joinString=Java.type (“java.lang.String”)
joinString.join (“ “, “Hello”, “World”)

Option 2 — Access global objects defined for the Java APIs for the package name.

java.lang.String.join (“ “,”Hello”,”World”)

ScriptObjectMirror

The ScriptObjectMirror is part of the jdk.nashorn.api and is intended to be used in client-code instead of the internal classes.

This mirror object is a representation of the underlying JavaScript object and provides access to it and its methods and properties.

The ScriptObjectMirror implements the Map interface.

Facilitates calling member functions on JavaScript objects from Java. An example would be as follows.

/*Person is a JavaScript type with some properties and a function.*/
function Person(firstName, lastName) {
this.firstName = firstName;
this.lastName = lastName;
this.getFullName = function() {
return this.firstName + ' ' + this.lastName;
}
}

The function getFullName() can be called on ScriptObjectMirror via callMember()

public static void getFullName(ScriptObjectMirror person) {
System.out.println("Full name is: " + person.callMember("getFullName"));
}

Bindings / Context

A ScriptContext contains one or more bindings, each associated to a scope. By default, there are two scopes:
ENGINE_SCOPE
GLOBAL_SCOPE

When a Nashorn engine is created, it creates a default context:
ScriptContext defaultContext = engine.getContext();

The default context’s ENGINE_SCOPE is the scope where "global" objects and functions (Object, Function, Math, RegExp, parseInt, etc.) are stored.

The GLOBAL_SCOPE is shared between all engines created by the same ScriptEngineManager.

You can store variables in the context (use of the scope is optional, default is ENGINE_SCOPE):

ScriptContext context = engine.getContext();
// stores an object under the key `myKey` in the (engine scoped) context
context.setAttribute("myKey", object, ScriptContext.ENGINE_SCOPE);
// retrieves the object with key `myKey` from (engine scoped) context
context.getAttribute("myKey", ScriptContext.ENGINE_SCOPE);
Bindings b = context.getBindings(ScriptContext.ENGINE_SCOPE);
b.get("Object"); // gets ECMAScript "Object" constructor
b.get("undefined"); // ECMAScript 'undefined' value

If a variable is not found in ENGINE_SCOPE, GLOBAL_SCOPE bindings are being searched.

Java Beans

With Nashorn, you get direct access to the properties of a Java bean, no need to use getters and setters any more:

var Date = Java.type('java.util.Date');
var date = new Date();
date.year += 1900; // -> no setter!
print(date.year); /

Binding Properties

You can bind properties from one object to another object:

var o1 = {};
var o2 = { foo: 'bar' };
Object.bindProperties(o1, o2);print(o1.foo);
o1.foo = 'John';
print(o2.foo);

To get a full list of Nashorn’s capabilities you can refer [1].

Sample Usecase of Nashorn

Suppose we have an IAM solution providing an adaptive authentication scheme purely written in Java.

In an adaptive authentication scheme, we bind the authentication process to external parameters. For example contextual facts — how many times the user tried to log into the system, user specific attributes — user’s IP, location, device, set of previleges etc. In this scheme, administrators should have the capability to modify login and authentication flows based on this dynamic data.

Now assume you want to sell this software to multiple client. Each client may have different requirements and may demand customizations on your solution. If the whole solution is implemented in Java we may have a hard time changing the business logic to suit each customers requirements and it will involve heavy code level changes and recompilation. Along with this comes comes extensive testing cycles on the code level changes. This can be a burden to the software developer and will surely consume most of his resources — time, effort, hardware etc.

As a solution, instead of writing the software solely in Java we can add support for scripting using Nashorn in the product. With Nashorn scripts, we provide the capability to the clients for whom we are writing this software to be able to customize the business logic for each client’s requirement without recompiling the code.

Now we can easily sell the software to multiple clients and give details on the functionalities. Clients can implement their own logic engage custom scripts by writing small piece of JavaScript code with customer-specific business rules and their requirements will be satified with minimized solution developer involvement. At runtime Nashorn will execute these scripts by including dynamic parameters.

Pros

  • Add support for scripting to your products that lets you or your users quickly develop and modify your product to suit various needs without needing to recompile or learn the inner workings of the product.
  • Provides capability to modify your software’s business logic on the fly without changing Java code and recompiling the code to suit each client’s requirement.
  • Enhance customizability of the applications. ability to implement core parts of business logic and extension points as JavaScript facilitates easy changeability at runtime.
  • Divorcing parts of the application in such a way can allow for much quicker bug fixes and additional features.
  • Reduces overall DTAP cycle of software
  • Provides much faster execution and multi-threading of JavaScript while running in Java’s JRE.
  • Providing access to all of the benefits of Java — extensive libraries, code optimization, vast support networks, etc.
  • Ability to use in a flexible, dynamically-typed language that many programmers are already familiar with.
  • Significant performance improvements compared to Mozilla’s Rhino.
  • Nashorn can be very easily integrated and used with JavaFX. Users can very easily interpret a JavaFX script application with Nashorn using the jjs command line tool.

--

--

Sathya Bandara

A Software Engineer on a Quest for Knowledge, Eager to Learn, Share, and Lift Others Along the Way