我可以在战争中捆绑更新版本的Nashorn吗?

时间:2017-08-21 19:29:05

标签: java nashorn

除非我在这里遗漏了一些东西,否则这个版本的Nashorn似乎有一些错误:

$ jjs -v
nashorn 1.8.0_45

它使用3位或更多位数的多个积分作为属性键扼要:

$ echo 'var c = JSON.parse("{\"123\": \"a\", \"456\": \"b\"}"); print(c["123"])' | jjs; echo
jjs> java.lang.ArrayIndexOutOfBoundsException: Array index out of range: 7

2位数工作正常:

$ echo 'var c = JSON.parse("{\"12\": \"a\", \"45\": \"b\"}"); print(c["12"])' | jjs; echo
jjs> a

3位数和2位数给出了不同的错误:

$ echo 'var c = JSON.parse("{\"123\": \"a\", \"45\": \"b\"}"); print(c["123"])' | jjs; echo
jjs> undefined

一个3位数和一个字符串工作正常:

$ echo 'var c = JSON.parse("{\"123\": \"a\", \"foo\": \"b\"}"); print(c["123"])' | jjs; echo
jjs> a

使用此版本一切正常:

$ jjs -v
nashorn 1.8.0_121

$ echo 'var c = JSON.parse("{\"123\": \"a\", \"456\": \"b\"}"); print(c["123"])' | jjs; echo
jjs> a

无论如何,上面的代码片段只是展示我在我的网络应用中遇到的问题的一种方式。我的问题是 - 有没有办法在我的webapp中捆绑这个较新版本的nashorn,以便我不需要在服务器上请求java升级?

2 个答案:

答案 0 :(得分:7)

这是另一个不需要修改nashorn jar的解决方案:

  • 捆绑nashorn.jar(*)作为战争中的资源文件
  • 使用child-first / parent-last类加载器,例如this one
  • 通过此类加载器加载引擎

实现上述方法的示例servlet,然后尝试使用JRE的Nashorn和捆绑的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);
        }
    }
}

在JRE 8u45上使用tomcat 8的结果:

### 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

网络应用项目树:

enter image description here

在此之前,我还尝试简单地在nashorn.jar下绑定WEB-INF/lib而不使用自定义类加载器技巧(希望servlet容器的通常的子级优先级加载器就足够了),但这不起作用。我想这是因为Tomcat遵循servlet规范中的这条规则:

  

作为Java EE产品一部分的Servlet容器不应允许应用程序覆盖Java SE或Java SE或Java EE平台类,例如java.*javax.*命名空间中的Java SE或Java EE平台类Java EE不允许修改。

&#34; 例如&#34;,所以似乎jdk.*也属于该类别(无论如何,Tomcat似乎确实排除了Nashorn)。所以是的,带上你自己的ClassLoader

(*)确保您可以合法地执行此操作。也许考虑使用OpenJDK构建中的jar,而不是从Oracle Java安装目录中复制。也许可以考虑包括你自己,但提供将文件添加到你分发的战争的说明(它只是一个zip文件)等。

答案 1 :(得分:2)

原则上,我希望您能够从较新的JRE的\ lib \ ext文件夹中获取nashorn.jar并通过类似maven-shade-plugin(取决于您的构建系统)的方式运行它到{ {3}}使用不同的名称(以免与您希望它在底层JVM中的现有名称冲突)。您可能还想更改相应的新jar中的META-INF/services/javax.script.ScriptEngineFactory,并更改NashornScriptEngineFactory.class中的值,以便它具有不同的脚本引擎名称。然后,您可以将其作为库添加到war文件中,然后使用new ScriptEngineManager().getEngineByName("my-nashorn");或您为其提供的任何名称创建脚本引擎的版本。

或者,您可以尝试从relocate the packages自己编译Nashorn,也可以稍微修改它以给它自己的引擎名称。我不确定这是否更容易,因为我没有尝试过任何一种方法。

这两种方法都假定:

  1. 您需要修复的错误是在nashorn代码本身中修复的内容,而不是在底层JVM中实际修复的内容。
  2. 这样做符合许可协议。我没有调查它,虽然看起来its source code。但我不是开源专家。
  3. 我希望只更新服务器上的Java版本以修复已知错误(和source code is GPL 2!)必须要简单得多,但如果你需要运行特定旧版Java的某些特定代码的某些特定版本,您需要以某种方式自己提供该特定代码。