23

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?

Lii
  • 11,553
  • 8
  • 64
  • 88
Aswath Murugan
  • 505
  • 2
  • 4
  • 12
  • 3
    Java Nashorn has been removed in Java 15 (see [JEP/732](https://openjdk.java.net/jeps/372)). You should have gotten a warning about it since Java 11. – Codo Dec 12 '20 at 14:23

3 Answers3

18

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
MK Tan
  • 586
  • 1
  • 4
  • 12
  • @JornVernee I tried the --add-modules as args but it didn't work – Aswath Murugan Dec 13 '20 at 07:17
  • jdk-15.0.1/bin/java -classpath /home/nashorn-helloworld/target/classes HelloWorld --module-path /home/org/openjdk/nashorn/nashorn-core/15.0/nashorn-core-15.0.jar:/home/org/ow2/asm/asm/7.3.1/asm-7.3.1.jar:/home/org/ow2/asm/asm-commons/7.3.1/asm-commons-7.3.1.jar:/home/org/ow2/asm/asm-tree/7.3.1/asm-tree-7.3.1.jar:/home/org/ow2/asm/asm-util/7.3.1/asm-util-7.3.1.jar --add-modules org.openjdk.nashorn --add-modules org.ow2.asm --add-modules org.ow2.asm.asm-commons --add-modules org.ow2.asm.asm-tree --add-modules org.ow2.asm.asm-util – Aswath Murugan Dec 13 '20 at 07:21
  • 3
    @AswathMurugan You only need `--add-modules org.openjdk.nashorn` but you're passing the `--module-path` argument as program argument, not as VM option, the `HelloWorld`, which I assume is your main class, goes all the way at the end of the command. – Jorn Vernee Dec 13 '20 at 13:07
  • 2
    Furthermore, `--module-path` is a list of directories; you just need to list the directories where the modular JAR files are, not the JAR files themselves. – Attila Szegedi Dec 13 '20 at 16:58
17

I just released Nashorn 15.1 which makes it possible for Nashorn to function when loaded through classpath instead of through modulepath. I tested it with a small Spring Boot application of my own, and it works.

Attila Szegedi
  • 4,405
  • 26
  • 24
  • 1
    Sorry for being a bit daft, but I just upgraded to JDK 15 and I would have thought that just adding the dependency to org.openjdk.nashorn:nashorn-core:15.2 would suffice. However I keep getting the NoClassDefFound error on the ClassFilter class. Am I not right in assuming that I don't need the add-modules option in that case? – StFS Feb 15 '21 at 10:37
  • 1
    Seems to work like a charm, thanks Attila. First I tried to use standalone Nashorn 15.2 without module too, it seemed `getScriptEngine()` was stuck in infinite wait/loop with no error. `ScriptEngineFactory sef = new org.openjdk.nashorn.api.scripting.NashornScriptEngineFactory(); ScriptEngine nashornEngine = sef.getScriptEngine();` In fact Maven Shade plugin appeared to remove mandatory dependencies on packaging. All works fine with all dependencies packaged. Tested on win/lin/mac jdks. – ron190 Feb 21 '21 at 11:51
15

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.

Attila Szegedi
  • 4,405
  • 26
  • 24
  • Hi @attila , do we have a alternative for javax.script.invocable in the open jdk impl ? – Ayush v Dec 16 '21 at 08:04
  • Hi @Attila , do we have an alternative for javax.script.invocable in the open jdk impl ?In my current implementation with JAVA 8, i am using javax.script.Invocable . How can we migrate this ? How can we migrate this to Open JDK Impl – Ayush v Dec 16 '21 at 08:10