Skip to content

Commit 030a810

Browse files
committed
Nashorn Chapter added
1 parent a934af3 commit 030a810

File tree

6 files changed

+303
-2
lines changed

6 files changed

+303
-2
lines changed

10-nashorn.md

Lines changed: 234 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,11 @@ In this chapter, we will cover the following:
1414

1515
* Working with Nashorn command-line
1616
* Accessing Java classes and methods
17-
* Working with Java collections
1817
* Using external JavaScript libraries
1918
* Writing scripts
2019
* Using Nashorn from Java code
2120
* Using Java 8 features like Streams and Lambdas inside JavaScript code
21+
* Turning off Java language access
2222

2323

2424
## Working with Nashorn command-line
@@ -94,6 +94,23 @@ jjs> userAndAge["shekhar"]
9494
32
9595
```
9696

97+
Similarly, you can work with other Java collections. To use an `ArrayList` you will write code as shown below.
98+
99+
```bash
100+
jjs> var List = Java.type("java.util.ArrayList")
101+
jjs> var names = new List()
102+
jjs> names.add("shekhar")
103+
true
104+
jjs> names.add("rahul")
105+
true
106+
jjs> names.add("sameer")
107+
true
108+
jjs> names.get(0)
109+
shekhar
110+
jjs> names[1]
111+
rahul
112+
```
113+
97114
### Accessing static methods
98115

99116
To access static methods you have to first get the Java type using `Java.type` method and then calling method on `JavaClass` function object.
@@ -104,3 +121,219 @@ jjs>
104121
jjs> UUID.randomUUID().toString()
105122
e4242b89-0e94-458e-b501-2fc4344d5498
106123
```
124+
125+
You can sort list using `Collections.sort` method as shown below.
126+
127+
```bash
128+
jjs> var Collections = Java.type("java.util.Collections")
129+
jjs>
130+
jjs> Collections.sort(names)
131+
jjs> names
132+
[rahul, sameer, shekhar]
133+
jjs>
134+
```
135+
136+
## Using external JavaScript libraries
137+
138+
Let's suppose we want to use an external JavaScript library in our JavaScript code. Nashorn comes up with a built-in function -- `load` that loads and evaluates a script from a path, URL, or script object. To use `lodash` library we can write code as shown below.
139+
140+
```
141+
jjs> load("https://raw.github.com/lodash/lodash/3.10.1/lodash.js")
142+
143+
jjs> _.map([1, 2, 3], function(n) { return n * 3; });
144+
3,6,9
145+
```
146+
147+
## Writing scripts
148+
149+
You can use Nashorn extensions that enable users to write scripts that can use Unix shell scripting features. To enable shell scripting features, you have to start `jjs` with `-scripting` option as shown below.
150+
151+
```bash
152+
jjs -scripting
153+
jjs>
154+
```
155+
156+
Now you have access to Nashorn shell scripting global objects.
157+
158+
**$ARG:** This global object can be used to access the arguments passed to the script
159+
```
160+
$ jjs -scripting -- hello hey
161+
jjs>
162+
jjs> $ARG
163+
hello,hey
164+
```
165+
166+
**$ENV:** A map containing all the current environment variables
167+
168+
```bash
169+
jjs> $ENV["HOME"]
170+
/Users/shekhargulati
171+
172+
jjs> $ENV.JAVA_HOME
173+
/Library/Java/JavaVirtualMachines/jdk1.8.0_60.jdk/Contents/Home
174+
```
175+
176+
**$EXEC:** launches processes to run commands
177+
178+
```bash
179+
jjs> $EXEC("pwd")
180+
/Users/shekhargulati/java8-the-missing-tutorial
181+
```
182+
183+
### Writing executable scripts
184+
185+
You can use shebang(#!) at the beginning of the script to make a script file run as shell executable. Let's write a simple script that reads content of a file. We will use Java's `Files` and `Paths` API.
186+
187+
```javascript
188+
#!/usr/bin/jjs
189+
190+
var Paths = Java.type("java.nio.file.Paths");
191+
var Files = Java.type("java.nio.file.Files");
192+
193+
Files.lines(Paths.get($ARG[0])).forEach(function(line){print(line);})
194+
```
195+
196+
We will invoke it as
197+
198+
```bash
199+
$ jjs ch10/lines.js -- README.md
200+
```
201+
202+
## Using Nashorn from Java code
203+
204+
To use Nashorn from inside Java code, you have to create an instance of ScriptEngine from `ScriptEngineManager` as shown below. Once you have `ScriptEngine` you can evaluate expressions.
205+
206+
```java
207+
import javax.script.ScriptEngine;
208+
import javax.script.ScriptEngineManager;
209+
import javax.script.ScriptException;
210+
211+
public class NashornExample1 {
212+
213+
public static void main(String[] args) throws ScriptException {
214+
ScriptEngineManager manager = new ScriptEngineManager();
215+
ScriptEngine nashorn = manager.getEngineByName("nashorn");
216+
Integer eval = (Integer) nashorn.eval("10 + 20");
217+
System.out.println(eval);
218+
}
219+
}
220+
```
221+
222+
223+
Using bindings
224+
225+
```java
226+
import javax.script.*;
227+
import java.util.AbstractMap.SimpleEntry;
228+
import java.util.stream.Stream;
229+
230+
import static java.util.stream.Collectors.toMap;
231+
232+
public class NashornExample2 {
233+
234+
public static void main(String[] args) throws ScriptException {
235+
ScriptEngineManager manager = new ScriptEngineManager();
236+
ScriptEngine nashorn = manager.getEngineByName("nashorn");
237+
238+
Bindings bindings = new SimpleBindings(Stream.of(
239+
new SimpleEntry<>("a", 10),
240+
new SimpleEntry<>("b", 20))
241+
.collect(toMap(SimpleEntry::getKey, SimpleEntry::getValue)));
242+
Double eval = (Double) nashorn.eval("a + b", bindings);
243+
System.out.println(eval);
244+
}
245+
}
246+
```
247+
248+
## Using Java 8 features like Streams and Lambdas inside JavaScript code
249+
250+
Java 8 supports lambdas and many API in JDK make use of them. Every collection in Java has `forEach` method that accepts a consumer. Consumer is an interface with one method. In Java, you can write following:
251+
252+
```java
253+
Arrays.asList("shekhar","rahul","sameer").forEach(name -> System.out.println(name));
254+
255+
// shekhar
256+
// rahul
257+
// sameer
258+
```
259+
260+
In Nashorn, you can use them same API but you will pass JavaScript function instead as shown below.
261+
262+
```javascript
263+
jjs> var Arrays = Java.type("java.util.Arrays")
264+
jjs> Arrays.asList("shekhar","rahul","sameer")
265+
[shekhar, rahul, sameer]
266+
jjs> var names = Arrays.asList("shekhar","rahul","sameer")
267+
jjs> names.forEach(function(name){print(name);})
268+
shekhar
269+
rahul
270+
sameer
271+
```
272+
273+
You can also use Stream API with Nashorn as shown below.
274+
275+
```
276+
jjs> names
277+
.stream().filter(function(name){return name.startsWith("s");})
278+
.forEach(function(name){print(name);})
279+
280+
shekhar
281+
sameer
282+
```
283+
284+
## Turning off Java language access
285+
286+
In case you need to disallow Java usage, you can very easily turn off by passing `--no-java` option to `jjs` as shown below.
287+
288+
```
289+
→ jjs --no-java
290+
jjs>
291+
jjs> var HashMap = Java.type("java.util.HashMap")
292+
<shell>:1 TypeError: null has no such function "type"
293+
```
294+
295+
Now when you will try to use `java.util.HashMap` you will get `TypeError`.
296+
297+
You can do the same with Java code as well.
298+
299+
```java
300+
301+
import jdk.nashorn.api.scripting.ClassFilter;
302+
import jdk.nashorn.api.scripting.NashornScriptEngineFactory;
303+
304+
import javax.script.ScriptEngine;
305+
import javax.script.ScriptException;
306+
307+
public class NashornExample3 {
308+
309+
public static void main(String[] args) throws ScriptException {
310+
NashornScriptEngineFactory factory = new NashornScriptEngineFactory();
311+
ScriptEngine nashorn = factory.getScriptEngine(new NoJavaFilter());
312+
Integer eval = (Integer) nashorn.eval("var HashMap = Java.type('java.util.HashMap')");
313+
System.out.println(eval);
314+
}
315+
316+
private static class NoJavaFilter implements ClassFilter{
317+
318+
@Override
319+
public boolean exposeToScripts(String s) {
320+
return false;
321+
}
322+
}
323+
}
324+
```
325+
326+
You will get following exception when you run this program.
327+
328+
```
329+
Caused by: java.lang.ClassNotFoundException: java.util.HashMap
330+
at jdk.nashorn.internal.runtime.Context.findClass(Context.java:1032)
331+
at jdk.nashorn.internal.objects.NativeJava.simpleType(NativeJava.java:493)
332+
at jdk.nashorn.internal.objects.NativeJava.type(NativeJava.java:322)
333+
at jdk.nashorn.internal.objects.NativeJava.type(NativeJava.java:314)
334+
at jdk.nashorn.internal.objects.NativeJava.type(NativeJava.java:310)
335+
at jdk.nashorn.internal.scripts.Script$\^eval\_.:program(<eval>:1)
336+
at jdk.nashorn.internal.runtime.ScriptFunctionData.invoke(ScriptFunctionData.java:640)
337+
at jdk.nashorn.internal.runtime.ScriptFunction.invoke(ScriptFunction.java:228)
338+
at jdk.nashorn.internal.runtime.ScriptRuntime.apply(ScriptRuntime.java:393)
339+
```

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ Java 8 is not a new topic anymore. There are many good books published on it. St
1414
* [Building functional programs](./07-building-functional-programs.md): Not written yet
1515
* [Date Time API](./08-date-time-api.md)
1616
* [Completable Futures](./09-completable-futures.md): Not written yet
17-
* [Nashorn](./10-nashorn.md): Not written yet
17+
* [Nashorn](./10-nashorn.md)
1818
* [Tools](./11-tools.md): Not written yet
1919
* [Annotation improvements](./12-annotations.md)
2020

ch10/lines.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
#!/usr/bin/jjs
2+
3+
var Paths = Java.type("java.nio.file.Paths");
4+
var Files = Java.type("java.nio.file.Files");
5+
6+
Files.lines(Paths.get($ARG[0])).forEach(function(line){print(line);})
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package com.shekhargulati.java8_tutorial.ch10;
2+
3+
import javax.script.ScriptEngine;
4+
import javax.script.ScriptEngineManager;
5+
import javax.script.ScriptException;
6+
7+
public class NashornExample1 {
8+
9+
public static void main(String[] args) throws ScriptException {
10+
ScriptEngineManager manager = new ScriptEngineManager();
11+
ScriptEngine nashorn = manager.getEngineByName("nashorn");
12+
Integer eval = (Integer) nashorn.eval("10 + 20");
13+
System.out.println(eval);
14+
}
15+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
package com.shekhargulati.java8_tutorial.ch10;
2+
3+
import javax.script.*;
4+
import java.util.AbstractMap.SimpleEntry;
5+
import java.util.stream.Stream;
6+
7+
import static java.util.stream.Collectors.toMap;
8+
9+
public class NashornExample2 {
10+
11+
public static void main(String[] args) throws ScriptException {
12+
ScriptEngineManager manager = new ScriptEngineManager();
13+
ScriptEngine nashorn = manager.getEngineByName("nashorn");
14+
15+
Bindings bindings = new SimpleBindings(Stream.of(
16+
new SimpleEntry<>("a", 10),
17+
new SimpleEntry<>("b", 20))
18+
.collect(toMap(SimpleEntry::getKey, SimpleEntry::getValue)));
19+
Double eval = (Double) nashorn.eval("a + b", bindings);
20+
System.out.println(eval);
21+
}
22+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
package com.shekhargulati.java8_tutorial.ch10;
2+
3+
import jdk.nashorn.api.scripting.ClassFilter;
4+
import jdk.nashorn.api.scripting.NashornScriptEngineFactory;
5+
6+
import javax.script.ScriptEngine;
7+
import javax.script.ScriptException;
8+
9+
public class NashornExample3 {
10+
11+
public static void main(String[] args) throws ScriptException {
12+
NashornScriptEngineFactory factory = new NashornScriptEngineFactory();
13+
ScriptEngine nashorn = factory.getScriptEngine(new NoJavaFilter());
14+
Integer eval = (Integer) nashorn.eval("var HashMap = Java.type('java.util.HashMap')");
15+
System.out.println(eval);
16+
}
17+
18+
private static class NoJavaFilter implements ClassFilter{
19+
20+
@Override
21+
public boolean exposeToScripts(String s) {
22+
return false;
23+
}
24+
}
25+
}

0 commit comments

Comments
 (0)