Skip to main content

Java + Nashorn

Nashorn is a JavaScript engine for Java. It shipped with Java distributions starting with Java 8 and was eventually removed in Java 15. The project was spun off and a compatible standalone release is available for Java 15+.

The Standalone scripts can be parsed and evaluated in a Nashorn context.

Integration Details

Initialize Nashorn

global must be created from a Nashorn engine:

import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Scanner;

/* initialize nashorn engine */
ScriptEngine engine = (new ScriptEngineManager()).getEngineByName("javascript");

/* create global */
engine.eval("var global = (function(){ return this; }).call(null);");

Load SheetJS Scripts

The main library can be loaded by reading the script from the file system and evaluating in the Nashorn context:

engine.eval(new Scanner(
SheetJS.class.getResourceAsStream("/xlsx.full.min.js")
).useDelimiter("\\Z").next());

To confirm the library is loaded, XLSX.version can be printed using the Nashorn print built-in:

engine.eval("print('SheetJS Version ' + XLSX.version);");

Reading Files

Nashorn does not properly project byte[] into a JS array or Int8Array. The recommended workaround is to copy the data in the JS context using the JS code:

function b2a(b) {
var out = typeof Uint8Array == 'function' ? new Uint8Array(b.length) : new Array(b.length);
/* `b` is similar to Int8Array (values in the range -128 .. 127 ) */
for(var i = 0; i < out.length; i++) out[i] = (b[i] + 256) & 0xFF;
return out;
}

This function should be embedded in the Java code:

/* read spreadsheet bytes */
engine.put("bytes", Files.readAllBytes(Paths.get(args[0])));

/* convert signed byte array to JS Uint8Array or unsigned byte array */
engine.eval(
"function b2a(b) {" +
"var out = typeof Uint8Array == 'function' ? new Uint8Array(b.length) : new Array(b.length);" +
"for(var i = 0; i < out.length; i++) out[i] = b[i] & 0xFF;" +
"return out;" +
"}" +
"var u8a = b2a(bytes)"
);

/* parse workbook */
engine.eval("var wb = XLSX.read(u8a, {type: 'array'})");

Complete Example

note

This demo was tested in the following deployments:

OpenJDKNashornDate
20.0.115.4 standalone2023-05-21
19.0.215.4 standalone2023-05-21
17.0.615.4 standalone2023-05-21
15.0.1015.4 standalone2023-05-21
11.0.19Built-in2023-05-21
1.8.0Built-in2023-05-21

Nashorn is available without additional dependencies

1) Download the standalone script, shim script, and the test file:

curl -LO https://cdn.sheetjs.com/xlsx-0.19.3/package/dist/xlsx.full.min.js
curl -LO https://cdn.sheetjs.com/xlsx-0.19.3/package/dist/shim.min.js
curl -LO https://sheetjs.com/pres.xlsx

2) Download SheetJSNashorn.java:

curl -LO https://docs.sheetjs.com/nashorn/SheetJSNashorn.java

3) Build the sample class:

javac SheetJSNashorn.java

This program tries to parse the file specified by the first argument

4) Run the command directly:

java SheetJSNashorn pres.xlsx

5) Assemble a Java Archive:

jar -cf SheetJSNashorn.jar SheetJSNashorn.class xlsx.full.min.js shim.min.js

6) Verify the Java Archive.

Create new directory and copy the archives and test file:

mkdir -p sheethorn
cp *.jar pres.xlsx sheethorn
cd sheethorn

Invoke the command in the archive:

java -cp .:SheetJSNashorn.jar SheetJSNashorn pres.xlsx