Here is another solution that does not require modifying the nashorn jar:
- bundle
nashorn.jar
(*) as a resource file in your war
- use a child-first/parent-last class loader such as this one
- load the engine via this class loader
Example servlet that implements the approach above, and then tries to evaluate your script with both the JRE's Nashorn, and the bundled Nashorn:
@WebServlet("/nashorn")
public class NashornDemoServlet extends HttpServlet {
private static final ClassLoader CL;
static {
// In my case nashorn.jar is under WEB-INF/classes (resources root)
URL nashornURL = NashornDemoServlet.class.getClassLoader().getResource("nashorn.jar");
CL = new ParentLastURLClassLoader(Collections.singletonList(nashornURL));
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
PrintWriter out = response.getWriter();
String script = "var c = JSON.parse(\"{\\\"123\\\": \\\"a\\\", \\\"456\\\": \\\"b\\\"}\"); c[\"123\"]";
ScriptEngine nashorn = new ScriptEngineManager(getClass().getClassLoader()).getEngineByName("nashorn");
try {
Object result = nashorn.eval(script);
out.println("### JRE Nashorn result: " + result);
} catch (Exception e) {
out.println("### JRE Nashorn failed!");
e.printStackTrace(out);
}
try {
Class<?> clazz = CL.loadClass("jdk.nashorn.api.scripting.NashornScriptEngineFactory");
Object factory = clazz.newInstance();
ScriptEngine engine = (ScriptEngine) clazz.getMethod("getScriptEngine").invoke(factory);
Object result = engine.eval(script);
out.println("\n### Bundled Nashorn result: " + result);
} catch (Exception e) {
out.println("### Bundled Nashorn failed!");
e.printStackTrace(out);
}
}
}
Result using tomcat 8, on JRE 8u45:
### JRE Nashorn failed!
java.lang.ArrayIndexOutOfBoundsException: Array index out of range: 7
at java.util.Arrays.rangeCheck(Arrays.java:120)
at java.util.Arrays.fill(Arrays.java:2868)
at jdk.nashorn.internal.runtime.BitVector.setRange(BitVector.java:273)
...
at java.lang.Thread.run(Thread.java:745)
### Bundled Nashorn result: a
Web app project tree:

Before that I also tried simply bundling nashorn.jar
under WEB-INF/lib
without custom class-loader trick (hoping the usual child-first class loader of servlet container would be enough), but that did not work. I suppose this is because Tomcat follows this rule from the servlet spec:
Servlet containers that are part of a Java EE product should not allow the application to override Java SE or Java EE platform classes, such as those in java.*
and javax.*
namespaces, that either Java SE or Java EE do not allow to be modified.
"Such as", so it seems jdk.*
also falls in that category (in any case, Tomcat does seem to exclude Nashorn). So yeah, bring your own ClassLoader
(*) Make sure you can legally do that. Maybe consider using a jar from an OpenJDK build, not copied from an Oracle Java installation directory. Maybe consider not including yourself it but providing instructions to add the file to the war you distribute (it's just a zip file), etc.