You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jena.apache.org by an...@apache.org on 2018/01/13 10:52:47 UTC
[2/8] jena git commit: JENA-1461: Nashorn and threading
JENA-1461: Nashorn and threading
Project: http://git-wip-us.apache.org/repos/asf/jena/repo
Commit: http://git-wip-us.apache.org/repos/asf/jena/commit/a0108d77
Tree: http://git-wip-us.apache.org/repos/asf/jena/tree/a0108d77
Diff: http://git-wip-us.apache.org/repos/asf/jena/diff/a0108d77
Branch: refs/heads/master
Commit: a0108d77c46616e65285f410a2f0f6cdab842c4f
Parents: c4f79ee
Author: Andy Seaborne <an...@apache.org>
Authored: Wed Jan 10 11:21:15 2018 +0000
Committer: Andy Seaborne <an...@apache.org>
Committed: Wed Jan 10 11:58:26 2018 +0000
----------------------------------------------------------------------
.../jena/sparql/function/js/EnvJavaScript.java | 132 ++++++++++---------
.../sparql/function/js/FunctionJavaScript.java | 7 +-
.../jena/sparql/function/js/JSEngine.java | 109 +++++++++++++++
jena-arq/testing/ARQ/JS/data.ttl | 2 +-
4 files changed, 182 insertions(+), 68 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/jena/blob/a0108d77/jena-arq/src/main/java/org/apache/jena/sparql/function/js/EnvJavaScript.java
----------------------------------------------------------------------
diff --git a/jena-arq/src/main/java/org/apache/jena/sparql/function/js/EnvJavaScript.java b/jena-arq/src/main/java/org/apache/jena/sparql/function/js/EnvJavaScript.java
index f18d236..167848f 100644
--- a/jena-arq/src/main/java/org/apache/jena/sparql/function/js/EnvJavaScript.java
+++ b/jena-arq/src/main/java/org/apache/jena/sparql/function/js/EnvJavaScript.java
@@ -18,23 +18,13 @@
package org.apache.jena.sparql.function.js;
-import java.io.FileNotFoundException;
-import java.io.IOException;
-import java.io.Reader;
-import java.nio.charset.StandardCharsets;
-import java.nio.file.Files;
-import java.nio.file.NoSuchFileException;
-import java.nio.file.Paths;
+import javax.script.ScriptException;
-import javax.script.*;
-
-import org.apache.jena.atlas.io.IO;
+import org.apache.jena.atlas.lib.Pool;
+import org.apache.jena.atlas.lib.PoolBase;
+import org.apache.jena.atlas.lib.PoolSync;
import org.apache.jena.query.ARQ;
-import org.apache.jena.riot.RiotNotFoundException;
-import org.apache.jena.sparql.ARQConstants;
-import org.apache.jena.sparql.ARQException;
import org.apache.jena.sparql.SystemARQ;
-import org.apache.jena.sparql.sse.builders.ExprBuildException;
import org.apache.jena.sparql.util.Context;
import org.apache.jena.sparql.util.Symbol;
@@ -50,85 +40,99 @@ import org.apache.jena.sparql.util.Symbol;
* then the string from {@code EnvJavaScript.symJavaScriptLib}.
*/
public class EnvJavaScript {
- /** JavaScript as a string value which is evaluated */
+ /** JavaScript functions as a string value which is evaluated. */
public static Symbol symJavaScriptLib = SystemARQ.allocSymbol("js-functions");
- /** JavaScript library in a file as named */
+ /** JavaScript library of functions in a file. */
public static Symbol symJavaScriptLibFile = SystemARQ.allocSymbol("js-library");
private final String scriptLib;
private final String scriptLibFile;
- private final ScriptEngine scriptEngine;
- private CompiledScript compiledScript;
-
- private final Invocable invoc;
-
public static EnvJavaScript create(Context context) {
return new EnvJavaScript(context);
}
private static EnvJavaScript global = null;
- /** Return */
- public static EnvJavaScript get() {
+ /**
+ * Return the global {@code EnvJavaScript}.
+ * Returns null if no JavaScript has been provided.
+ */
+ public static EnvJavaScript get() {
if ( global == null ) {
- Context context = ARQ.getContext();
- if ( context.isDefined(symJavaScriptLib) || context.isDefined(symJavaScriptLibFile) )
- global = create(ARQ.getContext());
+ synchronized(EnvJavaScript.class) {
+ Context context = ARQ.getContext();
+ if ( context.isDefined(symJavaScriptLib) || context.isDefined(symJavaScriptLibFile) )
+ global = create(ARQ.getContext());
+ }
}
return global ;
}
-
- /** Reset the global EnvJavaScript */
+
+ /** Reset the global {@code EnvJavaScript} based on the system-wide context */
public static void reset() {
reset(ARQ.getContext());
}
- /** Reset the global EnvJavaScript */
+ /** Reset the global {@code EnvJavaScript} */
public static void reset(Context context) {
global = create(context);
}
+ // ---- EnvJavaScript Object
+
+ // One script engine per thread, here done by one per usage.
+ // Nashorn script engines are thread safe but the script Bindings} must not be shared.
+ // Direct use of Nashorn, via the protected APIs of jdk.nashorn.api.scripting
+ // are needed to utilize this and having one compiled form and many execution units.
+ // But in Java8 is saving up problems for Java9 and the
+ // Nashorn subsystem is imporved at Java9 for this use case.
+ // For now, in combination with the implementation of JSEngine,
+ // we keep separate Nashorn script engines.
+
+ private Pool<JSEngine> pool = PoolSync.create(new PoolBase<JSEngine>());
+
private EnvJavaScript(Context context) {
this.scriptLib = context.getAsString(symJavaScriptLib);
this.scriptLibFile = context.getAsString(symJavaScriptLibFile);
- if ( this.scriptLib == null && this.scriptLibFile == null )
- throw new ARQException("Both script string and script filename are null");
- ScriptEngineManager manager = new ScriptEngineManager();
- scriptEngine = manager.getEngineByName("nashorn");
- // Add function to script engine.
- invoc = (Invocable)scriptEngine;
- if ( scriptLibFile != null ) {
- try {
- Reader reader = Files.newBufferedReader(Paths.get(scriptLibFile), StandardCharsets.UTF_8);
- Object x = scriptEngine.eval(reader);
- }
- catch (NoSuchFileException | FileNotFoundException ex) {
- throw new RiotNotFoundException("File: "+scriptLibFile);
- }
- catch (IOException ex) { IO.exception(ex); }
- catch (ScriptException e) {
- throw new ExprBuildException("Failed to load Javascript", e);
- }
- }
- if ( scriptLib != null ) {
- try {
- Object x = scriptEngine.eval(scriptLib);
- }
- catch (ScriptException e) {
- throw new ExprBuildException("Failed to load Javascript", e);
- }
- }
- // Try to call the init function - ignore NoSuchMethodException
+ // Put one in the pool.
+ pool.put(build());
+ }
+
+ private JSEngine build() {
+ return new JSEngine(scriptLib, scriptLibFile);
+ }
+
+ private JSEngine getEngine() {
+ JSEngine engine = pool.get();
+ if ( engine == null )
+ // Which will go into the pool when finished with.
+ engine = new JSEngine(scriptLib, scriptLibFile);
+ return engine;
+ }
+
+ private EnvJavaScript(String functions, String functionLibFile) {
+ this.scriptLib = functions;
+ this.scriptLibFile = functionLibFile;
+ }
+
+ public Object call(String functionName, Object[] args) throws NoSuchMethodException, ScriptException {
+ JSEngine engine = getEngine();
try {
- invoc.invokeFunction(ARQConstants.JavaScriptInitFunction);
- } catch (NoSuchMethodException ex) {}
- catch (ScriptException ex) {
- throw new ARQException("Failed to call JavaScript initialization function", ex);
+ return engine.call(functionName, args);
+ } finally {
+ pool.put(engine);
}
}
- public Invocable invoc() {
- return invoc;
- }
+// ---- ThreadLocal version,
+// private ThreadLocal<JSEngine> invocable = ThreadLocal.withInitial(()->build());
+//
+// private JSEngine build() {
+// return new JSEngine(scriptLib, scriptLibFile);
+// }
+// public Object call(String functionName, Object[] args) throws NoSuchMethodException, ScriptException {
+// return invocable.get().call(functionName, args);
+// }
+
}
http://git-wip-us.apache.org/repos/asf/jena/blob/a0108d77/jena-arq/src/main/java/org/apache/jena/sparql/function/js/FunctionJavaScript.java
----------------------------------------------------------------------
diff --git a/jena-arq/src/main/java/org/apache/jena/sparql/function/js/FunctionJavaScript.java b/jena-arq/src/main/java/org/apache/jena/sparql/function/js/FunctionJavaScript.java
index 4cb1cc0..bd4c25e 100644
--- a/jena-arq/src/main/java/org/apache/jena/sparql/function/js/FunctionJavaScript.java
+++ b/jena-arq/src/main/java/org/apache/jena/sparql/function/js/FunctionJavaScript.java
@@ -48,7 +48,7 @@ import org.apache.jena.sparql.function.FunctionBase;
* Functions that return null or undefined will resutl in a {@link ExprEvalException}.
*
* @see EnvJavaScript
- * @see NV
+ * @see NV
*/
public class FunctionJavaScript extends FunctionBase {
@@ -76,9 +76,10 @@ public class FunctionJavaScript extends FunctionBase {
Object[] a = new Object[args.size()];
for ( int i = 0 ; i < args.size(); i++ )
a[i] = NV.fromNodeValue(args.get(i));
- Object r = envJS.invoc().invokeFunction(functionName, a);
+ Object r = envJS.call(functionName, a);
if ( r == null )
- // Or string "undefined".
+ // null is used used to signal an ExprEvalException.
+ // NV.throwExprEvalException(....);
throw new ExprEvalException(functionName);
NodeValue nv = NV.toNodeValue(r);
return nv;
http://git-wip-us.apache.org/repos/asf/jena/blob/a0108d77/jena-arq/src/main/java/org/apache/jena/sparql/function/js/JSEngine.java
----------------------------------------------------------------------
diff --git a/jena-arq/src/main/java/org/apache/jena/sparql/function/js/JSEngine.java b/jena-arq/src/main/java/org/apache/jena/sparql/function/js/JSEngine.java
new file mode 100644
index 0000000..9084beb
--- /dev/null
+++ b/jena-arq/src/main/java/org/apache/jena/sparql/function/js/JSEngine.java
@@ -0,0 +1,109 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jena.sparql.function.js;
+
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.Reader;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.NoSuchFileException;
+import java.nio.file.Paths;
+
+import javax.script.*;
+
+import org.apache.jena.atlas.io.IO;
+import org.apache.jena.riot.RiotNotFoundException;
+import org.apache.jena.sparql.ARQConstants;
+import org.apache.jena.sparql.ARQException;
+import org.apache.jena.sparql.sse.builders.ExprBuildException;
+
+/** Abstraction of a <em>per-thread</em> JavaScript execution system */
+public class JSEngine {
+ private ScriptEngine scriptEngine;
+ private CompiledScript compiledScript;
+
+ private final Invocable invoc;
+ private String functions;
+
+ private String functionLibFile;
+
+
+ /** Create a {@code JSEngine} from a string. */
+ public static JSEngine createFromString(String functions) {
+ return new JSEngine(functions, null);
+ }
+
+ /** Create a {@code JSEngine} from the contents of a file. */
+ public static JSEngine createFromFile(String functionLibFile) {
+ return new JSEngine(null, functionLibFile);
+ }
+
+ /*package*/ JSEngine(String functions, String functionLibFile) {
+ this.functions = functions;
+ this.functionLibFile = functionLibFile;
+ invoc = build(functions, functionLibFile);
+ }
+
+ private static Invocable build(String functions, String functionLibFile) {
+ if ( functions == null && functionLibFile == null )
+ throw new ARQException("Both script string and script filename are null");
+
+ ScriptEngineManager manager = new ScriptEngineManager();
+ ScriptEngine scriptEngine = manager.getEngineByName("nashorn");
+
+ Invocable invoc = (Invocable)scriptEngine;
+ if ( functionLibFile != null ) {
+ try {
+ Reader reader = Files.newBufferedReader(Paths.get(functionLibFile), StandardCharsets.UTF_8);
+ Object x = scriptEngine.eval(reader);
+ }
+ catch (NoSuchFileException | FileNotFoundException ex) {
+ throw new RiotNotFoundException("File: "+functionLibFile);
+ }
+ catch (IOException ex) { IO.exception(ex); }
+ catch (ScriptException e) {
+ throw new ExprBuildException("Failed to load Javascript", e);
+ }
+ }
+ if ( functions != null ) {
+ try {
+ Object x = scriptEngine.eval(functions);
+ }
+ catch (ScriptException e) {
+ throw new ExprBuildException("Failed to load Javascript", e);
+ }
+ }
+
+ // Try to call the init function - ignore NoSuchMethodException
+ try {
+ invoc.invokeFunction(ARQConstants.JavaScriptInitFunction);
+ } catch (NoSuchMethodException ex) {}
+ catch (ScriptException ex) {
+ throw new ARQException("Failed to call JavaScript initialization function", ex);
+ }
+ return invoc;
+ }
+
+ public Object call(String functionName, Object[] args) throws NoSuchMethodException, ScriptException {
+ return invoc.invokeFunction(functionName, args);
+ }
+
+
+}
http://git-wip-us.apache.org/repos/asf/jena/blob/a0108d77/jena-arq/testing/ARQ/JS/data.ttl
----------------------------------------------------------------------
diff --git a/jena-arq/testing/ARQ/JS/data.ttl b/jena-arq/testing/ARQ/JS/data.ttl
index 12303ce..f99776a 100644
--- a/jena-arq/testing/ARQ/JS/data.ttl
+++ b/jena-arq/testing/ARQ/JS/data.ttl
@@ -1,5 +1,5 @@
PREFIX : <http://example/>
:s1 :p1 1 .
-:s2 :p2 "one" .
+[] :p2 "one" .