6. Nashorn
Nashorn replaces Rhino as the default JavaScript engine for the Oracle JVM.
Nashorn is much faster since it uses the invokedynamic feature of the JVM.
It also includes a command line tool (jjs).
6.1 jjs
JDK 8 includes the command line tool jjs for running JavaScript.
You can run JavaScript files from the command line (assuming you have Java 8’s bin in your PATH):
1 $ jjs script.js
This can be useful for running scripts; for example, let’s say you wanted to quickly find the sum of some numbers:
1 var data = [1, 3, 5, 7, 11]
2 var sum = data.reduce(function(x, y) {return x + y}, 0)
3 print(sum)
Running the above code should print out 27.
6.2 Scripting
Running jjs with the -scripting option starts up an interactive shell where you can type and evaluate JavaScript.
You can also embed variables into strings and have them evaluate; for example:
1 jjs> var date = new Date()
2 jjs> print("${date}")
This would print out the current date and time.
6.3 ScriptEngine
You can also run JavaScript dynamically from Java.
First, you need to import the ScriptEngine:
1 import javax.script.ScriptEngine;
2 import javax.script.ScriptEngineManager;
Second, you use the ScriptEngineManager to get the Nashorn engine:
1 ScriptEngineManager engineManager = new ScriptEngineManager();
2 ScriptEngine engine = engineManager.getEngineByName("nashorn");
Now you can evaluate javascript at any point:
1 engine.eval("function p(s) { print(s) }");
2 engine.eval("p('Hello Nashorn');");
The eval method can also take a FileReader as input:
1 engine.eval(new FileReader('library.js'));
This way you can include and run any JavaScript. However, keep in mind that the typical variables available to you in the browser (window, document, etc.) are not available.
6.4 Importing
You can import and use Java classes and packages using the JavaImporter.
For example, import java.util, the IO, and NIO file packages:
1 var imports = new JavaImporter(java.util, java.io, java.nio.file);
2 with (imports) {
3 var paths = new LinkedList();
4 print(paths instanceof LinkedList); //true
5 paths.add(Paths.get("file1"));
6 paths.add(Paths.get("file2"));
7 paths.add(Paths.get("file3"));
8 print(paths) // [file1, file2, file3]
9 }
The above demonstrates that paths is an instance of LinkedList and prints out the list.
Later on you could add the following code to write text into the files:
1 for (var i=0; i < paths.size(); i++)
2 Files.newOutputStream(paths.get(i))
3 .write("test\n".getBytes());
We can use existing Java classes, but we can also create new ones.
6.5 Extending
You can extend Java classes and interfaces using
the Java.type and Java.extend functions.
For example, you can extend the Callable interface and implement the call method:
1 var concurrent = new JavaImporter(java.util, java.util.concurrent);
2 var Callable = Java.type("java.util.concurrent.Callable");
3 with (concurrent) {
4 var executor = Executors.newCachedThreadPool();
5 var tasks = new LinkedHashSet();
6 for (var i=0; i < 200; i++) {
7 var MyTask = Java.extend(Callable, {call: function() {print("task " + i)}})
8 var task = new MyTask();
9 tasks.add(task);
10 executor.submit(task);
11 }
12 }
6.6 Invocable
You can also invoke JavaScript functions directly from Java.
Firstly, you need to cast the engine to the Invocable interface:
1 Invocable inv = (Invocable) engine;
Then, to invoke any function, simple use the invokeFunction method, for example:
1 engine.eval("function p(s) { print(s) }");
2 inv.invokeFunction("p", "hello");
Lastly, you can use the getInterface method to implement any interface in JavaScript.
For example, if you have the following JPrinter interface, you can use it like so:
1 public static interface JPrinter {
2 void p(String s);
3 }
4 // later on...
5 JPrinter printer = inv.getInterface(JPrinter.class);
6 printer.p("Hello again!");