Jena JavaScript 自定义函数 scriptEngine 空错误

Jena JavaScript custom functions scriptEngine null error

总结: 我正在尝试使用 JavaScript SPARQL Functions 上的文档来获取调用自定义 javascript 函数的最小工作示例,但结果在 scriptEngine 空错误中。

详情:我按照Docker instructions设置了一个fuseki环境,所以:

docker build --force-rm --build-arg JENA_VERSION=3.16.0 -t fuseki .

然后我根据 JavaScript SPARQL Functions 上的文档创建一个 functions.js 文件并启动 Docker 容器:

docker run -i --rm -p "3030:3030" --name sparql1 -v `pwd`/functions.js:/functions.js -t fuseki --update --set arq:js-library=/functions.js --mem /ds

到目前为止,还不错。我已经确认这适用于简单的更新和查询,所以容器看起来很正常。但是,如果我现在创建一个包含 example in the docs:

query.rq 文件
query=PREFIX js: <http://jena.apache.org/ARQ/jsFunction#>

SELECT ?input (js:toCamelCase(?input) AS ?X)
{
  VALUES ?input { "some woRDs to PROCESS" }
}

然后继续将此查询分派到我的容器:

curl -X POST -d @query.rq localhost:3030/ds/query

然后容器所在的控制台运行如下所示:

% docker run -i --rm -p "3030:3030" --name sparql1 -v `pwd`/functions.js:/functions.js -t fuseki --update --set arq:js-library=/functions.js --mem /ds
[2022-03-09 16:47:20] INFO  Server          :: Apache Jena Fuseki 3.16.0
[2022-03-09 16:47:20] INFO  Server          :: Dataset Type: in-memory
[2022-03-09 16:47:20] INFO  Server          :: Path = /ds
[2022-03-09 16:47:20] INFO  Server          :: System
[2022-03-09 16:47:20] INFO  Server          ::   Memory: 2.0 GiB
[2022-03-09 16:47:20] INFO  Server          ::   Java:   17-ea
[2022-03-09 16:47:20] INFO  Server          ::   OS:     Linux 5.10.76-linuxkit amd64
[2022-03-09 16:47:20] INFO  Server          ::   PID:    1
[2022-03-09 16:47:20] INFO  Server          :: Start Fuseki (port=3030)
[2022-03-09 16:47:23] INFO  Fuseki          :: [1] POST http://localhost:3030/ds/query
[2022-03-09 16:47:23] INFO  Fuseki          :: [1] Query = PREFIX js: <http://jena.apache.org/ARQ/jsFunction#>SELECT ?input (js:toCamelCase(?input) AS ?X){  VALUES ?input { "some woRDs to PROCESS" }}
[2022-03-09 16:47:24] WARN  Fuseki          :: [1] RC = 500 : Cannot invoke "javax.script.ScriptEngine.eval(java.io.Reader)" because "scriptEngine" is null
java.lang.NullPointerException: Cannot invoke "javax.script.ScriptEngine.eval(java.io.Reader)" because "scriptEngine" is null
    at org.apache.jena.sparql.function.js.JSEngine.build(JSEngine.java:75) ~[jena-fuseki-server-3.16.0.jar:3.16.0]
    at org.apache.jena.sparql.function.js.JSEngine.<init>(JSEngine.java:61) ~[jena-fuseki-server-3.16.0.jar:3.16.0]
    at org.apache.jena.sparql.function.js.EnvJavaScript.build(EnvJavaScript.java:103) ~[jena-fuseki-server-3.16.0.jar:3.16.0]
    at org.apache.jena.sparql.function.js.EnvJavaScript.<init>(EnvJavaScript.java:99) ~[jena-fuseki-server-3.16.0.jar:3.16.0]
    at org.apache.jena.sparql.function.js.EnvJavaScript.create(EnvJavaScript.java:52) ~[jena-fuseki-server-3.16.0.jar:3.16.0]
    at org.apache.jena.sparql.function.js.EnvJavaScript.get(EnvJavaScript.java:66) ~[jena-fuseki-server-3.16.0.jar:3.16.0]
    at org.apache.jena.sparql.expr.E_Function.evalSpecial(E_Function.java:66) ~[jena-fuseki-server-3.16.0.jar:3.16.0]
    at org.apache.jena.sparql.expr.ExprFunctionN.eval(ExprFunctionN.java:100) ~[jena-fuseki-server-3.16.0.jar:3.16.0]
    at org.apache.jena.sparql.core.VarExprList.get(VarExprList.java:106) ~[jena-fuseki-server-3.16.0.jar:3.16.0]
    at org.apache.jena.sparql.engine.iterator.QueryIterAssign.accept(QueryIterAssign.java:62) ~[jena-fuseki-server-3.16.0.jar:3.16.0]
    at org.apache.jena.sparql.engine.iterator.QueryIterProcessBinding.hasNextBinding(QueryIterProcessBinding.java:69) ~[jena-fuseki-server-3.16.0.jar:3.16.0]
    at org.apache.jena.sparql.engine.iterator.QueryIteratorBase.hasNext(QueryIteratorBase.java:114) ~[jena-fuseki-server-3.16.0.jar:3.16.0]
    at org.apache.jena.sparql.engine.iterator.QueryIterConvert.hasNextBinding(QueryIterConvert.java:58) ~[jena-fuseki-server-3.16.0.jar:3.16.0]
    at org.apache.jena.sparql.engine.iterator.QueryIteratorBase.hasNext(QueryIteratorBase.java:114) ~[jena-fuseki-server-3.16.0.jar:3.16.0]
    at org.apache.jena.sparql.engine.iterator.QueryIteratorWrapper.hasNextBinding(QueryIteratorWrapper.java:38) ~[jena-fuseki-server-3.16.0.jar:3.16.0]
    at org.apache.jena.sparql.engine.iterator.QueryIteratorBase.hasNext(QueryIteratorBase.java:114) ~[jena-fuseki-server-3.16.0.jar:3.16.0]
    at org.apache.jena.sparql.engine.iterator.QueryIteratorWrapper.hasNextBinding(QueryIteratorWrapper.java:38) ~[jena-fuseki-server-3.16.0.jar:3.16.0]
    at org.apache.jena.sparql.engine.iterator.QueryIteratorBase.hasNext(QueryIteratorBase.java:114) ~[jena-fuseki-server-3.16.0.jar:3.16.0]
    at org.apache.jena.sparql.engine.ResultSetStream.hasNext(ResultSetStream.java:74) ~[jena-fuseki-server-3.16.0.jar:3.16.0]
    at org.apache.jena.sparql.engine.ResultSetCheckCondition.hasNext(ResultSetCheckCondition.java:55) ~[jena-fuseki-server-3.16.0.jar:3.16.0]
    at org.apache.jena.fuseki.servlets.SPARQLQueryProcessor.executeQuery(SPARQLQueryProcessor.java:324) ~[jena-fuseki-server-3.16.0.jar:3.16.0]
    at org.apache.jena.fuseki.servlets.SPARQLQueryProcessor.execute(SPARQLQueryProcessor.java:273) ~[jena-fuseki-server-3.16.0.jar:3.16.0]
    at org.apache.jena.fuseki.servlets.SPARQLQueryProcessor.executeWithParameter(SPARQLQueryProcessor.java:222) ~[jena-fuseki-server-3.16.0.jar:3.16.0]
    at org.apache.jena.fuseki.servlets.SPARQLQueryProcessor.execute(SPARQLQueryProcessor.java:207) ~[jena-fuseki-server-3.16.0.jar:3.16.0]
    at org.apache.jena.fuseki.servlets.ActionService.executeLifecycle(ActionService.java:58) ~[jena-fuseki-server-3.16.0.jar:3.16.0]
    at org.apache.jena.fuseki.servlets.SPARQLQueryProcessor.execPost(SPARQLQueryProcessor.java:83) ~[jena-fuseki-server-3.16.0.jar:3.16.0]
    at org.apache.jena.fuseki.servlets.ActionProcessor.process(ActionProcessor.java:34) ~[jena-fuseki-server-3.16.0.jar:3.16.0]
    at org.apache.jena.fuseki.servlets.ActionBase.process(ActionBase.java:55) ~[jena-fuseki-server-3.16.0.jar:3.16.0]
    at org.apache.jena.fuseki.servlets.ActionExecLib.execAction(ActionExecLib.java:107) ~[jena-fuseki-server-3.16.0.jar:3.16.0]
    at org.apache.jena.fuseki.server.Dispatcher.dispatchAction(Dispatcher.java:115) ~[jena-fuseki-server-3.16.0.jar:3.16.0]
    at org.apache.jena.fuseki.server.Dispatcher.process(Dispatcher.java:107) ~[jena-fuseki-server-3.16.0.jar:3.16.0]
    at org.apache.jena.fuseki.server.Dispatcher.dispatch(Dispatcher.java:101) ~[jena-fuseki-server-3.16.0.jar:3.16.0]
    at org.apache.jena.fuseki.servlets.FusekiFilter.doFilter(FusekiFilter.java:51) ~[jena-fuseki-server-3.16.0.jar:3.16.0]
    at org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1604) ~[jena-fuseki-server-3.16.0.jar:3.16.0]
    at org.eclipse.jetty.servlet.ServletHandler.doHandle(ServletHandler.java:545) ~[jena-fuseki-server-3.16.0.jar:3.16.0]
    at org.eclipse.jetty.server.handler.ScopedHandler.nextHandle(ScopedHandler.java:233) ~[jena-fuseki-server-3.16.0.jar:3.16.0]
    at org.eclipse.jetty.server.handler.ContextHandler.doHandle(ContextHandler.java:1297) ~[jena-fuseki-server-3.16.0.jar:3.16.0]
    at org.eclipse.jetty.server.handler.ScopedHandler.nextScope(ScopedHandler.java:188) ~[jena-fuseki-server-3.16.0.jar:3.16.0]
    at org.eclipse.jetty.servlet.ServletHandler.doScope(ServletHandler.java:485) ~[jena-fuseki-server-3.16.0.jar:3.16.0]
    at org.eclipse.jetty.server.handler.ScopedHandler.nextScope(ScopedHandler.java:186) ~[jena-fuseki-server-3.16.0.jar:3.16.0]
    at org.eclipse.jetty.server.handler.ContextHandler.doScope(ContextHandler.java:1212) ~[jena-fuseki-server-3.16.0.jar:3.16.0]
    at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:141) ~[jena-fuseki-server-3.16.0.jar:3.16.0]
    at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:127) ~[jena-fuseki-server-3.16.0.jar:3.16.0]
    at org.eclipse.jetty.server.Server.handle(Server.java:500) ~[jena-fuseki-server-3.16.0.jar:3.16.0]
    at org.eclipse.jetty.server.HttpChannel.lambda$handle(HttpChannel.java:383) ~[jena-fuseki-server-3.16.0.jar:3.16.0]
    at org.eclipse.jetty.server.HttpChannel.dispatch(HttpChannel.java:547) [jena-fuseki-server-3.16.0.jar:3.16.0]
    at org.eclipse.jetty.server.HttpChannel.handle(HttpChannel.java:375) [jena-fuseki-server-3.16.0.jar:3.16.0]
    at org.eclipse.jetty.server.HttpConnection.onFillable(HttpConnection.java:270) [jena-fuseki-server-3.16.0.jar:3.16.0]
    at org.eclipse.jetty.io.AbstractConnection$ReadCallback.succeeded(AbstractConnection.java:311) [jena-fuseki-server-3.16.0.jar:3.16.0]
    at org.eclipse.jetty.io.FillInterest.fillable(FillInterest.java:103) [jena-fuseki-server-3.16.0.jar:3.16.0]
    at org.eclipse.jetty.io.ChannelEndPoint.run(ChannelEndPoint.java:117) [jena-fuseki-server-3.16.0.jar:3.16.0]
    at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:806) [jena-fuseki-server-3.16.0.jar:3.16.0]
    at org.eclipse.jetty.util.thread.QueuedThreadPool$Runner.run(QueuedThreadPool.java:938) [jena-fuseki-server-3.16.0.jar:3.16.0]
    at java.lang.Thread.run(Unknown Source) [?:?]
[2022-03-09 16:47:24] INFO  Fuseki          :: [1] 500 Cannot invoke "javax.script.ScriptEngine.eval(java.io.Reader)" because "scriptEngine" is null (212 ms)

此时我完全迷路了...为什么 scriptEngine 在这里为空?

在 Apache Jena 3.16.0 中,JS 引擎是 Nashorn。 Nashorn 在 Java 11 中被弃用并在 Java17 中被移除。

较早的 Java 版本将包含 Hashorn。

现在 Jena 版本 4.4.0 使用已添加到类路径中的 javascript 选择引擎,因此需要在 dockerfile 中。 Jena 测试使用 GraalVM。

我接受了上面@AndyS的回答,因为它帮助我解决了问题。对于遇到此问题并想了解完整详细信息的任何人,我将发布此附加详细信息。

基本上,我忽略了“官方”docker 撰写脚本,因为它使用 OpenJDK,而是基于 official GraalVM containers 之一创建了一个简单的 Dockerfile。我只需要添加 GraalVM nodejs 以便它有一个 JavaScript 解释器,给出完整的 Dockerfile 如下:

FROM ghcr.io/graalvm/graalvm-ce:ol8-java17-22.0.0.2-b2

RUN gu install nodejs

RUN curl -s -S --fail --location --max-redirs 3 https://repo1.maven.org/maven2/org/apache/jena/jena-fuseki-server/4.4.0/jena-fuseki-server-4.4.0.jar --output jena-fuseki-server-4.4.0.jar

RUN adduser -M fuseki

USER fuseki

EXPOSE 3030

ENTRYPOINT ["java", "-jar", "jena-fuseki-server-4.4.0.jar"]
CMD []

现在,您可以使用以下方法构建它:

docker build -t fuseki .

然后 运行 它并包含来自原始问题的 functions.js 文件:

docker run -it --rm -p "3030:3030" --name sparql1 -v $(pwd)/functions.js:/functions.js fuseki --update --set arq:js-library=/functions.js --mem /ds

现在,发布上述问题的查询给出了正确的输出:

% curl -X POST -d @query.rq localhost:3030/ds/query

{ "head": {
    "vars": [ "input" , "X" ]
  } ,
  "results": {
    "bindings": [
      { 
        "input": { "type": "literal" , "value": "some woRDs to PROCESS" } ,
        "X": { "type": "literal" , "value": "someWordsToProcess" }
      }
    ]
  }
}

再次感谢@AndyS,如果没有他的评论,这个完整的解决方案就会让我逃避!