I have an existing Spring Boot application that is non-modular and uses Nashorn. The application works well on Java 14.
After adding the Maven coordinates of the new Nashorn available for Java 15, the application fails while starting the script engine.
public static void main(String[] args) throws ScriptException {
ScriptEngineManager factory = new ScriptEngineManager();
ScriptEngine engine = factory.getEngineByName("nashorn");
engine.eval("print('Hello, World!');");
}
Error message:
Exception in thread "main" java.lang.NullPointerException: Cannot invoke "javax.script.ScriptEngine.eval(String)" because "engine" is null
at xxxxx.yyyy.service.JavaScriptServiceImpl.main(JavaScriptServiceImpl.java:52)
Is it required to modularize the whole project to make use of Nashorn?
The Nashorn engine has been deprecated in JDK 11 as part of JEP 335 and and has been removed from JDK15 as part of JEP 372. GraalVM can step in as a replacement for JavaScript code previously executed on the Nashorn engine. GraalVM provides all the features for JavaScript previously provided by Nashorn.
With the release of Java 11, Nashorn was deprecated citing challenges to maintenance, and has been removed from JDK 15 onwards. Nashorn development continues on GitHub as a standalone OpenJDK project and the separate release can be used in Java project from Java 11 and up.
Java Nashorn. Nashorn is a JavaScript engine. It is used to execute JavaScript code dynamically at JVM (Java Virtual Machine). Java provides a command-line tool jjs which is used to execute JavaScript code. You can execute JavaScript code by using jjs command-line tool and by embedding into Java source code.
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).
To learn more, please read Migration Guide from Nashorn to GraalVM JavaScript. This series of articles introduces Nashorn, a blazing fast JavaScript runtime engine that shipped with Java SE 8 to provide a lightweight environment for extending, supplementing, and often even replacing Java code.
This series of articles introduces Nashorn, a blazing fast JavaScript runtime engine that shipped with Java SE 8 to provide a lightweight environment for extending, supplementing, and often even replacing Java code.
According to JEP 372, Nashorn had been removed from JDK 15 but you can get latest nashorn from https://search.maven.org/artifact/org.openjdk.nashorn/nashorn-core/15.0/jar
For Maven, include the below dependency into your pom.xml
<dependency>
<groupId>org.openjdk.nashorn</groupId>
<artifactId>nashorn-core</artifactId>
<version>15.0</version>
</dependency>
For Gradle, include dependency below into your build.gradle
implementation 'org.openjdk.nashorn:nashorn-core:15.0'
Unfortunately, Standalone Nashorn is only usable as a JPMS module. So you might need to follow the solution stated in https://stackoverflow.com/a/46289257 to make it work with a non-modular application.
From the given class xxxxx.yyyy.service.JavaScriptServiceImpl
and based on feedback from @JornVernee and @AttilaSzegedi, the command line should look like
jdk-15.0.1/bin/java -classpath /home/nashorn-helloworld/target/classes --module-path /home/org/openjdk/nashorn/nashorn-core/15.0:/home/org/ow2/asm/asm/7.3.1:/home/org/ow2/asm/asm-analysis/7.3.1:/home/org/ow2/asm/asm-commons/7.3.1:/home/org/ow2/asm/asm-tree/7.3.1/home/org/ow2/asm/asm-util/7.3.1 --add-modules org.openjdk.nashorn xxxxx.yyyy.service.JavaScriptServiceImpl
Nashorn maintainer here.
It indeed seems to be an issue with Spring Boot not loading Nashorn as a JPMS module. Nashorn exports itself as a scripting engine to be found by the javax.script.ScriptEngineManager
through a "provides" entry in its module-info.java
. It does not use the older, non-modular export mechanism of declaring itself through a relevant META-INF/services/…
entry in its JAR file. This means that if the JAR is not loaded as a JPMS module, script engine manager will not discover it. (NB: even if it redundantly had that META-INF/services
entry, it wouldn't help because Nashorn relies on being loaded as a module; as code that used to ship with JDK, it has been a module since Java 9… it'd be somewhat hard to undo that now.)
I created a small test application that confirms this is the case. I'm trying to enlist some people who work on Boot to help me get to the bottom of this. It's complicated by the fact that Boot creates a fat JAR file and packages all of its dependencies into it and then manages their loading, so it's not like you can "just" modify the modulepath yourself on startup.
Hopefully there's a way to tell Boot to load a dependency as a module; my attempts to find it through Google haven't proved fruitful so far.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With