2020-04-11

Java with Nashorn engine

In this document we will discuss about how to use Java program with Nashorn engine for scripting features provided by Java SE.

Introduction to the Nashorn Engine


It was fully developed in the Java language as part of the Nashorn Project. The code is based on the new features of the Da Vinci Machine, which is the reference implementation of Java Specification Request (JSR) 292: Supporting Dynamically Typed Languages on the Java Platform.

The Nashorn engine is included in the Java SE Development Kit (JDK). You can invoke Nashorn from a Java application using the Java Scripting API to interpret embedded scripts, or you can pass the script to the jjs or jrunscript tool.


The Nashorn engine implements many new features introduced in ECMAScript 6 including template strings, let, const, and block scope, iterators and for..of loops, Map, Set, WeakMap, and WeakSet data types, symbols, and binary and octal literals.

Invoking Nashorn from Java Code


To invoke Nashorn in your Java application, create an instance of the Nashorn engine using the Java Scripting API.


To get an instance of the Nashorn engine:

1. Import the javax.script package.
The Java Scripting API is composed of classes and interfaces in this package.
2. Create a ScriptEngineManager object.
The ScriptEngineManager class is the starting point for the Java Scripting API. A
ScriptEngineManager object is used to instantiate ScriptEngine objects and
maintain global variable values shared by them.
3. Get a ScriptEngine object from the manager using the getEngineByName()
method.
This method takes one String argument with the name of the script engine. To
get an instance of the Nashorn engine, pass in "nashorn". Alternatively, you can
use any of the following: "Nashorn", "javascript", "JavaScript", "js", "JS",
"ecmascript", "ECMAScript".

After you have the Nashorn engine instance, you can use it to evaluate statements and script files, set variables, and so on. The following example, EvalScript.java, provides simple Java application code that evaluates a print("Hello, World!"); statement using Nashorn.
import javax.script.*;
public class EvalScript {
 public static void main(String[] args) throws Exception {
 // create a script engine manager
 ScriptEngineManager factory = new ScriptEngineManager();
 // create a Nashorn script engine
 ScriptEngine engine = factory.getEngineByName("nashorn");
 // evaluate JavaScript statement
 try {
 engine.eval("print('Hello, World!');");
 } catch (final ScriptException se) { se.printStackTrace(); }
 }
}

Invoking Nashorn from the Command Line

There are two command-line tools that can be used to invoke the Nashorn engine:
• jrunscript: This is a generic command that invokes any available script engine compliant with JSR 223. By default, without any options, jrunscript invokes the Nashorn engine, because it is the default script engine in the JDK.
• jjs: This is the recommended tool, created specifically for Nashorn. To evaluate a script file using Nashorn, pass the name of the script file to the jjs tool. To launch an interactive shell that interprets statements passed in using standard input, start the jjs tool without specifying any script files.

Nashorn Parser API

The Nashorn parser API enables applications, in particular IDEs and server-side frameworks, to parse and analyze ECMAScript code.

The Nashorn Java API

Accessing Java Classes

There are two approaches to access packages and classes using Nashorn: the traditional approach is to use the Packages global object, and the recommended approach is to use the Java global object. This section describes both approaches. The predefined top-level Packages object enables you to access Java packages and classes using their fully qualified names, as if they are properties of the Packages object. The following example shows how you can access the MyPackage package and
its MyClass class if MyPackage.jar is in your class path:
jjs> Packages.MyPackage
[JavaPackage MyPackage]
jjs> Packages.MyPackage.MyClass
[JavaClass MyPackage.MyClass]
Accessing standard Java packages and classes is more straightforward than accessing custom packages and classes. For your convenience, there are global objects defined for each of the standard Java packages: com, edu, java, javax, and org. They have aliases that correspond to properties of the Packages object.

The following example shows how you can access the java.lang package and the
java.lang.System class:
jjs> java.lang
[JavaPackage java.lang]
jjs> typeof java.lang
object
jjs> java.lang.System
[JavaClass java.lang.System]
jjs> typeof java.lang.System
function
As you can see from the previous example, Nashorn interprets Java packages as JavaPackage objects, and Java classes as JavaClass function objects, which can be used as constructors for the classes. Creating Java Objects describes how to instantiate a class.
The traditional approach for accessing Java packages and classes is intuitive and straightforward, but at the same time, it can be inefficient, limited, and error-prone for the following reasons:
• Each property access has a cost, so accessing a package or class in a deep hierarchy can be slow.
• There is no special syntax for creating Java arrays. You must use the java.lang.reflect.Array class as a workaround.
• If you misspell a class name, Nashorn assumes that you provided a package name, and interprets it as a JavaPackage object instead of a JavaClass function object. You might not be aware of this until an error is thrown when you attempt to use it as a class. To avoid this, use the typeof operator to conditionally test that the construct you are trying to access is interpreted as a function object.

The following example shows how this conditional check works:
jjs> typeof java.lang.System == "function"
true
jjs> typeof java.lang.Zyztem == "function"
false
To avoid the disadvantages of the approach previously described, Nashorn defines the Java global object that has several functions for working with Java classes. The Java.type() function takes a string with the fully qualified Java class name, and returns the corresponding JavaClass function object.

The following example shows how you can access the java.lang.System class:
jjs> Java.type("java.lang.System")
[JavaClass java.lang.System]
Similar to importing classes in Java, it is a good practice to declare variables of
JavaClass type at the beginning of a script. The following example shows how you can
declare the System variable and give it a value of the java.lang.System class:
jjs> var System = Java.type("java.lang.System")
jjs> System
[JavaClass java.lang.System]

Creating Java Objects

To instantiate a class, pass the JavaClass function object to the new operator. Nashorn invokes the corresponding constructor based on the arguments passed to the function.

The following example shows how you can instantiate the java.util.HashMap class with the default initial capacity and with the initial capacity set to 100:
jjs> var HashMap = Java.type("java.util.HashMap")
jjs> var mapDef = new HashMap()
jjs> var map100 = new HashMap(100)
Accessing Class and Instance Members You can use the standard dot notation to access static fields, methods, and inner classes as follows.
jjs> Java.type("java.lang.Math").PI
3.141592653589793
jjs> Java.type("java.lang.System").currentTimeMillis()
1375813353330
jjs> Java.type("java.util.Map").Entry
[JavaClass java.util.Map$Entry]
An inner class can also be accessed using internal representation with the dollar sign
($) as the separator, or a dot, which is consistent with Java:
jjs> Java.type("java.util.Map$Entry")
[JavaClass java.util.Map$Entry]
jjs> Java.type("java.util.Map.Entry")
[JavaClass java.util.Map$Entry]

To invoke an instance method or access an instance field of an object, use the dot operator, similar to how it is done in Java. The following example shows how you can call the toUpperCase() method on a String object:
jjs> var String = Java.type("java.lang.String")
jjs> var str = new String("Hello")
jjs> str
Hello
jjs> var upper = str.toUpperCase()
jjs> upper
HELLO
Nashorn also supports member access using the bracket notation, where you specify the name of the member as a string between brackets ([]) that immediately follow the class (in case of a static member) or object (in case of an instance member). This method is defined by the ECMAScript as an alternative to the dot notation, and is not intuitive for Java developers. However, it can be used to resolve method overload ambiguity. By default, Nashorn uses the overloaded method that best matches the arguments, and this is not always what you expect. For example, if you want to print a double value, you must use the java.lang.System.out.println(double) method overload, as shown in the following example:
jjs> Java.type("java.lang.System").out.println(10)
10
jjs> Java.type("java.lang.System").out["println(double)"](10)
10.0


Using JavaBeans

Nashorn enables you to treat accessor and mutator methods in JavaBeans as equivalent JavaScript properties. The name of the property is the name of the JavaBean method without the get or set suffix, and starts with a lowecase letter.

For example you can call the getYear() and setYear() methods in a java.util.Date object using the year property as follows:
jjs> var Date = Java.type("java.util.Date")
jjs> var date = new Date()
jjs> date.year + 1900
2013
jjs> date.year = 2014 - 1900
114
jjs> date.year + 1900
2014

Working with Java Arrays

To access a Java array class, pass to the Java.type() function the type of objects that comprise the array followed by a pair of brackets (similar to Java syntax).

The following example shows how you can access a Java array of integers and a Java array of String objects:
jjs> Java.type("int[]")
[JavaClass [I]
jjs> Java.type("java.lang.String[]")
[JavaClass [Ljava.lang.String;]
After you have the array type object, you can use it to instantiate an array as you do any other class. You can access array entries by their indexes, and use the dot or bracket notation to access members (similar to Java syntax), as shown in the following example:
jjs> var IntArrayType = Java.type("int[]")
jjs> var arr = new IntArrayType(10)
jjs> arr[1] = 123
123
jjs> arr[2] = 321
321
jjs> arr[1] + arr[2]
444
jjs> arr[10]
java.lang.ArrayIndexOutOfBoundsException: Array index out of range: 10
jjs> arr.length
10
If you have an existing JavaScript array, you can convert it to a Java array using the Java.to() function.

The following example shows how you can convert a JavaScript array of strings "a", "b", and "c", to a java.lang.String[] array with the same values:
jjs> var jsArr = ["a","b","c"]
jjs> var strArrType = Java.type("java.lang.String[]")
jjs> var javaArr = Java.to(jsArr, strArrType)
jjs> javaArr.class
class [Ljava.lang.String;
jjs> javaArr[0]
a
You can iterate through a Java array's indexes and values using the for and for each statements as follows:
jjs> for (var i in javaArr) print(i)
0
1
2

No comments:

Post a Comment