Introduction to scripting API
Scripting for the Java Platform (JSR-223) is a framework specification for embedding scripts in Java source code. This is a very powerful tool that allows you to integrate scripting languages into the application such as: Ruby, Groovy, Scala, Python, JavaScript (All list of JVM languages).
Let’s consider the known implementations for JavaScript:
- Nashorn is a default JavaScript engine built into Java.
- Rhino is a JavaScript engine managed by the Mozilla Foundation.
- Graal.js is a high performance implementation of the JavaScript engine, built on the GraalVM by Oracle Labs.
Integration of one separate script will look simple enough:
However, JavaScript, like many other languages, has support for a module ecosystem. The JavaScript modules implementation is CommonJS. Therefore, this code will not work:
To integrate a JavaScript module (such as a library for Node.js) we need a CommonJS implementation in Java.
CommonJS Modules Support for Nashorn, Rhino and Graal.js
Library jsr223-commonjs-modules adds support for CommonJS modules (aka require
) inside a Nashorn, Rhino and Graal.js script engines. It is based on the specification for NodeJS modules and it supports loading modules from the node_modules
folder just as Node does. Of course, it doesn't provide an implementation for Node's APIs, so any module that depends on those won't work.
Supported features:
- Ready for use in scripting engines Nashorn, Rhino and Graal.js.
- Displays the file name and line number in the error stacktrace.
- Compatible with JSR-223 standard.
- Implementation on pure Java.
- No dependency on third-party libraries.
Getting the library using Maven
Add this dependency to your pom.xml
to reference the library:
Usage
Enabling require
in Nashorn script engine:
Enabling require
in Rhino script engine:
Enabling require
in Graal.js script engine:
This will expose a new global require
function at the engine scope. Any code that is then run using this engine can make use of require
.
The second argument specifies the root Folder
from which modules are made available. Folder
is an interface exposing a few calls that need to be implemented by backing providers to enable loading files and accessing subfolders. Out-of-the-box, the library supports loading modules from the filesystem and from Java resources.
Loading modules from the filesystem
Use the FilesystemFolder.create
method to create an implementation of Folder
rooted at a particular location in the filesystem:
You need to specify the encoding of the files. Most of the time UTF-8 will be a reasonable choice.
The resulting folder is rooted at the path you specified, and JavaScript code won’t be able to “escape” that root by using ../../..
. In other words, it behaves as is the root folder was the root of the filesystem.
Loading modules from Java resources
Use the ResourceFolder.create
method to create an implementation of Folder
backed by Java resources:
As for ResourceFolder
, you need to specify the encoding for the files that are read.
More usage examples
See in my GitHub repository https://github.com/a-langer/jsr223-commonjs-modules.