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:46 UTC

[1/8] jena git commit: JENA-1461: JavaScript custom functions

Repository: jena
Updated Branches:
  refs/heads/master 67898ffc0 -> 58ac14b7e


JENA-1461: JavaScript custom functions


Project: http://git-wip-us.apache.org/repos/asf/jena/repo
Commit: http://git-wip-us.apache.org/repos/asf/jena/commit/c4f79ee9
Tree: http://git-wip-us.apache.org/repos/asf/jena/tree/c4f79ee9
Diff: http://git-wip-us.apache.org/repos/asf/jena/diff/c4f79ee9

Branch: refs/heads/master
Commit: c4f79ee9f908f301028a4e13dfc7c2fc2de89cbf
Parents: 2933d71
Author: Andy Seaborne <an...@apache.org>
Authored: Sun Jan 7 19:50:12 2018 +0000
Committer: Andy Seaborne <an...@apache.org>
Committed: Sun Jan 7 21:16:48 2018 +0000

----------------------------------------------------------------------
 .../org/apache/jena/sparql/ARQConstants.java    |   7 +
 .../org/apache/jena/sparql/expr/E_Function.java |  40 +++-
 .../jena/sparql/expr/ExprUndefFunction.java     |  32 +++
 .../jena/sparql/function/FunctionBase.java      |   2 +-
 .../jena/sparql/function/js/EnvJavaScript.java  | 134 ++++++++++++
 .../sparql/function/js/FunctionJavaScript.java  |  93 ++++++++
 .../org/apache/jena/sparql/function/js/NV.java  | 217 +++++++++++++++++++
 .../apache/jena/sparql/function/js/RDFJS.java   |  35 +++
 .../jena/riot/langsuite/FactoryTestRiot.java    |   1 -
 .../java/org/apache/jena/sparql/TC_General.java |   2 +
 .../jena/sparql/function/js/TS_FunctionJS.java  |  31 +++
 .../function/js/TestJavaScriptFunctions.java    | 198 +++++++++++++++++
 .../apache/jena/sparql/function/js/TestNV.java  |  71 ++++++
 .../jena/sparql/function/js/TestSPARQL_JS.java  |  49 +++++
 .../function/library/TS_LibraryFunctions.java   |   2 +-
 jena-arq/testing/ARQ/JS/data.ttl                |   5 +
 jena-arq/testing/ARQ/JS/js-query-1.rq           |   4 +
 jena-arq/testing/ARQ/JS/js-query-1.srj          |  12 +
 jena-arq/testing/ARQ/JS/js-query-2.rq           |   4 +
 jena-arq/testing/ARQ/JS/js-query-2.srj          |  12 +
 jena-arq/testing/ARQ/JS/js-query-3.rq           |   4 +
 jena-arq/testing/ARQ/JS/js-query-3.srj          |  12 +
 jena-arq/testing/ARQ/JS/js-query-4.rq           |   9 +
 jena-arq/testing/ARQ/JS/js-query-4.srj          |  14 ++
 jena-arq/testing/ARQ/JS/manifest.ttl            |  50 +++++
 jena-arq/testing/ARQ/JS/test-library.js         |  14 ++
 26 files changed, 1044 insertions(+), 10 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/jena/blob/c4f79ee9/jena-arq/src/main/java/org/apache/jena/sparql/ARQConstants.java
----------------------------------------------------------------------
diff --git a/jena-arq/src/main/java/org/apache/jena/sparql/ARQConstants.java b/jena-arq/src/main/java/org/apache/jena/sparql/ARQConstants.java
index 322f3e6..2cf4e4d 100644
--- a/jena-arq/src/main/java/org/apache/jena/sparql/ARQConstants.java
+++ b/jena-arq/src/main/java/org/apache/jena/sparql/ARQConstants.java
@@ -67,6 +67,13 @@ public class ARQConstants
     /** XML Schema namespace */
     public static final String XML_SCHEMA_NS = "http://www.w3.org/2001/XMLSchema#" ;
     
+    /** The URI prefix that triggers JavaScript functions */ 
+    public static final String JavaScriptURI = "http://jena/apache.org/ARQ/jsFunction#" ;
+    
+    /** Function called when JaavScript is initialized. */ 
+    public static final String JavaScriptInitFunction = "arqJSinit";
+
+    /** URI scheme that triggers the loader to load a java class */ 
     public static final String javaClassURIScheme = "java:" ;
     
     /** The ARQ function library URI space */

http://git-wip-us.apache.org/repos/asf/jena/blob/c4f79ee9/jena-arq/src/main/java/org/apache/jena/sparql/expr/E_Function.java
----------------------------------------------------------------------
diff --git a/jena-arq/src/main/java/org/apache/jena/sparql/expr/E_Function.java b/jena-arq/src/main/java/org/apache/jena/sparql/expr/E_Function.java
index 7b1501e..2d097a1 100644
--- a/jena-arq/src/main/java/org/apache/jena/sparql/expr/E_Function.java
+++ b/jena-arq/src/main/java/org/apache/jena/sparql/expr/E_Function.java
@@ -21,12 +21,15 @@ package org.apache.jena.sparql.expr;
 import java.util.List ;
 
 import org.apache.jena.query.ARQ ;
+import org.apache.jena.sparql.ARQConstants;
 import org.apache.jena.sparql.ARQInternalErrorException ;
 import org.apache.jena.sparql.engine.binding.Binding ;
 import org.apache.jena.sparql.function.Function ;
 import org.apache.jena.sparql.function.FunctionEnv ;
 import org.apache.jena.sparql.function.FunctionFactory ;
 import org.apache.jena.sparql.function.FunctionRegistry ;
+import org.apache.jena.sparql.function.js.EnvJavaScript;
+import org.apache.jena.sparql.function.js.FunctionJavaScript;
 import org.apache.jena.sparql.serializer.SerializationContext ;
 import org.apache.jena.sparql.util.Context ;
 import org.apache.jena.sparql.util.FmtUtils ;
@@ -38,6 +41,7 @@ public class E_Function extends ExprFunctionN
     private static final String name = "function" ;
     public static boolean WarnOnUnknownFunction = true ;
     private String functionIRI ;
+    private String javaScriptFunction = null; 
     
     private Function function = null ;
     private boolean functionBound = false ;
@@ -45,16 +49,33 @@ public class E_Function extends ExprFunctionN
     public E_Function(String functionIRI, ExprList args) {
         super(name, args) ;
         this.functionIRI = functionIRI ;
+        if ( functionIRI.startsWith(ARQConstants.JavaScriptURI) )
+            javaScriptFunction = functionIRI.substring(ARQConstants.JavaScriptURI.length());
     }
 
     @Override
     public String getFunctionIRI() { return functionIRI ; }
     
     // The Function subsystem takes over evaluation via SpecialForms.
-    // This allows a "function" to behave as special forms (this is discouraged).
+    // This allows a "function" to behave as a special form (this is discouraged).
     
     @Override
     public NodeValue evalSpecial(Binding binding, FunctionEnv env) {
+        if ( javaScriptFunction != null ) {
+            // JavaScript 
+            EnvJavaScript jsEnv = EnvJavaScript.get();
+            // FunctionJavaScript : functions only : no special forms.
+            function = new FunctionJavaScript(javaScriptFunction, jsEnv);
+            try {
+                return function.exec(binding, args, getFunctionIRI(), env);
+            } catch (ExprUndefFunction ex) {
+                // Or check if there is a Java function registered - allows java replacement.
+                if ( WarnOnUnknownFunction )
+                    ARQ.getExecLogger().warn("JavaScript function not found: "+ex.getFunctionName());
+                throw ex;
+            }
+        }
+        
         // Only needed because some tests call straight in.
         // Otherwise, the buildFunction() calls should have done everything
         if ( !functionBound )
@@ -72,23 +93,28 @@ public class E_Function extends ExprFunctionN
     }
 
     public void buildFunction(Context cxt) {
+        if ( javaScriptFunction != null )
+            return;
         try { bindFunction(cxt) ; }
-        catch (ExprException ex) {
+        catch (ExprUndefFunction ex) {
             if ( WarnOnUnknownFunction )
                 ARQ.getExecLogger().warn("URI <"+functionIRI+"> has no registered function factory") ;
         }
     }
     
+    private FunctionFactory functionFactory(Context cxt) {
+        FunctionRegistry registry = chooseRegistry(cxt) ;
+        FunctionFactory ff = registry.get(functionIRI) ;
+        return ff;
+    }
+    
     private void bindFunction(Context cxt) {
         if ( functionBound )
             return ;
-
-        FunctionRegistry registry = chooseRegistry(cxt) ;
-        FunctionFactory ff = registry.get(functionIRI) ;
-
+        FunctionFactory ff = functionFactory(cxt);
         if ( ff == null ) {
             functionBound = true ;
-            throw new ExprEvalException("URI <" + functionIRI + "> not found as a function") ;
+            throw new ExprUndefFunction("URI <" + functionIRI + "> not found as a function", functionIRI) ;
         }
         function = ff.create(functionIRI) ;
         function.build(functionIRI, args) ;

http://git-wip-us.apache.org/repos/asf/jena/blob/c4f79ee9/jena-arq/src/main/java/org/apache/jena/sparql/expr/ExprUndefFunction.java
----------------------------------------------------------------------
diff --git a/jena-arq/src/main/java/org/apache/jena/sparql/expr/ExprUndefFunction.java b/jena-arq/src/main/java/org/apache/jena/sparql/expr/ExprUndefFunction.java
new file mode 100644
index 0000000..64467d1
--- /dev/null
+++ b/jena-arq/src/main/java/org/apache/jena/sparql/expr/ExprUndefFunction.java
@@ -0,0 +1,32 @@
+/*
+ * 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.expr;
+
+/** Exception for an undefined function. */
+
+public class ExprUndefFunction extends ExprEvalException
+{
+    private final String fnName;
+
+    public ExprUndefFunction(String msg, String fnName) { super(msg) ; this.fnName = fnName;}
+    
+    public String getFunctionName() {
+        return fnName;
+    }
+}

http://git-wip-us.apache.org/repos/asf/jena/blob/c4f79ee9/jena-arq/src/main/java/org/apache/jena/sparql/function/FunctionBase.java
----------------------------------------------------------------------
diff --git a/jena-arq/src/main/java/org/apache/jena/sparql/function/FunctionBase.java b/jena-arq/src/main/java/org/apache/jena/sparql/function/FunctionBase.java
index 53177ff..4139f53 100644
--- a/jena-arq/src/main/java/org/apache/jena/sparql/function/FunctionBase.java
+++ b/jena-arq/src/main/java/org/apache/jena/sparql/function/FunctionBase.java
@@ -60,7 +60,7 @@ public abstract class FunctionBase implements Function {
         try {
             this.functionEnv = env ;
             this.binding = binding;
-            NodeValue nv =  exec(evalArgs) ;
+            NodeValue nv = exec(evalArgs) ;
             return nv ;
         } finally {
             this.functionEnv = null ;

http://git-wip-us.apache.org/repos/asf/jena/blob/c4f79ee9/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
new file mode 100644
index 0000000..f18d236
--- /dev/null
+++ b/jena-arq/src/main/java/org/apache/jena/sparql/function/js/EnvJavaScript.java
@@ -0,0 +1,134 @@
+/*
+ * 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.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;
+
+/** Environment for executing a JavaScript function.
+ * <p>
+ * Functions are loaded from the file named in context setting
+ * {@link EnvJavaScript#symJavaScriptLibFile}.
+ * <p>
+ * Function are loaded from a string value in context setting
+ * {@link EnvJavaScript#symJavaScriptLib}.
+ * <p>
+ * If both are present, the file named by {@code EnvJavaScript.symJavaScriptLibFile} is loaded
+ * then the string from {@code EnvJavaScript.symJavaScriptLib}.
+ */
+public class EnvJavaScript {
+    /** JavaScript as a string value which is evaluated */ 
+    public static Symbol symJavaScriptLib = SystemARQ.allocSymbol("js-functions");
+    /** JavaScript library in a file as named */ 
+    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() { 
+        if ( global == null ) {
+            Context context = ARQ.getContext();
+            if ( context.isDefined(symJavaScriptLib) || context.isDefined(symJavaScriptLibFile) )
+                global = create(ARQ.getContext());
+        }
+        return global ;
+    }
+    
+    /** Reset the global EnvJavaScript */
+    public static void reset() {
+        reset(ARQ.getContext());
+    }
+
+    /** Reset the global EnvJavaScript */
+    public static void reset(Context context) {
+        global = create(context);
+    }
+    
+    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 
+        try {
+            invoc.invokeFunction(ARQConstants.JavaScriptInitFunction);
+        } catch (NoSuchMethodException ex) {}
+        catch (ScriptException ex) {
+            throw new ARQException("Failed to call JavaScript initialization function", ex);
+        }
+    }
+
+    public Invocable invoc() {
+        return invoc;
+    }
+}

http://git-wip-us.apache.org/repos/asf/jena/blob/c4f79ee9/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
new file mode 100644
index 0000000..4cb1cc0
--- /dev/null
+++ b/jena-arq/src/main/java/org/apache/jena/sparql/function/js/FunctionJavaScript.java
@@ -0,0 +1,93 @@
+/*
+ * 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.util.List;
+
+import javax.script.ScriptException;
+
+import org.apache.jena.sparql.expr.ExprEvalException;
+import org.apache.jena.sparql.expr.ExprList;
+import org.apache.jena.sparql.expr.ExprUndefFunction;
+import org.apache.jena.sparql.expr.NodeValue;
+import org.apache.jena.sparql.function.FunctionBase;
+
+/**
+ * Javascript implemented SPARQL custom functions for ARQ. The JavaScript function is
+ * called with arguments which are mapped so that XSD strings, numbers and booleans become the
+ * equivalent native JavaScript object, and anything else becomes a {@link NV}, a
+ * JavaScript object providing access to the RDF features such as datatype.
+ * {@link NV#toString} returns a string so a function working with URIs can treat URIs as
+ * strings which is natural in JavaScript and aligned to
+ * <a href="https://github.com/rdfjs/representation-task-force/"
+ * >rdfjs/representation-task-force</a>.
+ * <p>
+ * Functions are executed in {@link EnvJavaScript}. There is a global
+ * {@link EnvJavaScript} and it can also be set specifically for a query execution.
+ * See {@link EnvJavaScript} for details of configuration.
+ * <p>
+ * Note: there is an
+ * attempt to reconstruct the datatype of the result of the function into
+ * {@code xsd:integer} and {@code xsd:double}.
+ * <p>
+ * Functions that return null or undefined will resutl in a {@link ExprEvalException}.
+ * 
+ * @see EnvJavaScript
+ * @see NV 
+ */
+public class FunctionJavaScript extends FunctionBase {
+
+    private final EnvJavaScript envJS;
+    private final String functionName;
+
+    private boolean initialized = false;
+    
+    public FunctionJavaScript(String functionName, EnvJavaScript env) {
+        this.functionName = functionName;
+        this.envJS = env;
+    }
+
+    @Override
+    public void checkBuild(String uri, ExprList args) {}
+
+    @Override
+    public NodeValue exec(List<NodeValue> args) {
+        if ( ! initialized )
+            checkBuild(null, null);
+        
+        try {
+            // Convert NodeValues to types more akin to Javascript. 
+            // Pass strings as string, and numbers as Number.
+            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);
+            if ( r == null )
+                // Or string "undefined". 
+                throw new ExprEvalException(functionName);
+            NodeValue nv = NV.toNodeValue(r);
+            return nv;
+        }
+        catch (NoSuchMethodException ex) {
+            throw new ExprUndefFunction("No such JavaScript function '"+functionName+"'", functionName);
+        }
+        catch (ScriptException e) {
+            throw new ExprEvalException("Failed to evaluate javascript function '"+functionName+"'", e);
+        }
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/jena/blob/c4f79ee9/jena-arq/src/main/java/org/apache/jena/sparql/function/js/NV.java
----------------------------------------------------------------------
diff --git a/jena-arq/src/main/java/org/apache/jena/sparql/function/js/NV.java b/jena-arq/src/main/java/org/apache/jena/sparql/function/js/NV.java
new file mode 100644
index 0000000..9ea418b
--- /dev/null
+++ b/jena-arq/src/main/java/org/apache/jena/sparql/function/js/NV.java
@@ -0,0 +1,217 @@
+/*
+ * 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.math.BigDecimal;
+import java.math.BigInteger;
+import java.net.URI;
+
+import org.apache.jena.graph.Node;
+import org.apache.jena.graph.NodeFactory;
+import org.apache.jena.sparql.expr.ExprEvalException;
+import org.apache.jena.sparql.expr.NodeValue;
+
+/**
+ * General representation of an {@link NodeValue} for JavaScript. Conversion is to native
+ * types where possible, otherwise {@code NV}. {@code NV.toString} of a URI returns the
+ * uri as a string so {@code NV} works naturally in Java/Nashorn. 
+ * 
+ * @see #fromNodeValue
+ * @see #toNodeValue
+ */
+
+public class NV implements RDFJS {
+    //  Six data types that are primitives in JavaScript:
+    //           Boolean
+    //           Null
+    //           Undefined
+    //           Number
+    //           String
+    //           Symbol (new in ECMAScript 6; not in Nashorn/Java8).
+    //       and Object
+    
+    private NodeValue nv;
+    /** 
+     * Enable restoring integer from doubles.
+     */
+    private final static boolean narrowDoubles = true;
+    /**
+     * Map an ARQ {@link NodeValue} to java/Nashorn representation of a JavaScript object.
+     * Native JaavScript types supported are null, string, number and boolean.
+     * Otherwise a {@link NV} is returned.
+     */
+    public static Object fromNodeValue(NodeValue nv) {
+        if ( nv == null )
+            return null;
+        if ( nv.isString() )
+            return nv.getString();
+        if ( nv.isNumber() ) {
+            if ( nv.isInteger())
+                return nv.getInteger();
+            if ( nv.isDecimal() )
+                return nv.getDecimal();
+            if ( nv.isDouble() )
+                return nv.getDouble();
+        }
+        if ( nv.isBoolean() )
+            return nv.getBoolean();
+        return new NV(nv);
+    }
+    /**
+     * Map a java/Nashorn representation of a JavaScript object to an ARQ
+     * {@link NodeValue}. Identified types are null, string, number and boolean and also
+     * {@code NV} returned by the JavaScript code.
+     */
+    public static NodeValue toNodeValue(Object r) {
+        if ( r == null )
+            return null;
+        if ( r instanceof NV )
+            return ((NV)r).nv();
+        if ( r instanceof NodeValue )
+            return (NodeValue)r;
+        // May not be a String.  String.toString is very efficient!
+        // https://docs.oracle.com/javase/8/docs/technotes/guides/scripting/prog_guide/javascript.html#A1147390
+        if ( r instanceof CharSequence )
+            return NodeValue.makeString(((CharSequence)r).toString());
+        if ( r instanceof Number )
+            return number2value((Number)r);
+        if ( r instanceof Boolean ) {
+            return NodeValue.makeBoolean((Boolean)r); 
+        }
+        if ( r instanceof URI ) {
+            Node n = NodeFactory.createURI(((URI)r).toString());
+            return NodeValue.makeNode(n); 
+        }
+        throw new ExprEvalException("Can't convert '"+r+"' to a NodeValue.  r is of class "+r.getClass().getName());  
+    }
+    
+    // Convert the numeric values that Nashorn can return.
+    static NodeValue number2value(Number r) {
+        if ( r instanceof Integer )
+            return NodeValue.makeInteger((Integer)r);
+        if ( r instanceof Long )
+            return NodeValue.makeInteger((Long)r);
+        if ( r instanceof Double ) {
+            double d = (Double)r;
+            if ( narrowDoubles && Double.isFinite(d)) {
+                try {
+                    long x = (long)d;
+                    // Check for loss in conversion
+                    if ( x == d )
+                        return NodeValue.makeInteger(x);
+                    // If is a very large integer (larger than long)?
+                    if ( d == Math.floor(d) && Double.isFinite(d) ) {
+                        BigInteger big = new BigInteger(Double.toString(x));
+                        return NodeValue.makeInteger(big); 
+                    }
+                    if ( false ) {
+                        // This always works but it is confusing decimal and double.
+                        // Leave doubles as doubles.
+                        return NodeValue.makeDecimal(d);
+                    }
+                } catch (NumberFormatException ex) {
+                    // Shouldn't happen.
+                    throw new ExprEvalException("Bad number format", ex);
+                }
+            }
+            return NodeValue.makeDouble(d);
+        }
+        // These are carried NV.value returns.
+        if ( r instanceof BigInteger )
+            return NodeValue.makeInteger((BigInteger)r); 
+        if ( r instanceof BigDecimal )
+            return NodeValue.makeDecimal((BigDecimal)r); 
+        
+        throw new ExprEvalException("Unknown return type for number: "+r); 
+    }
+    
+    // ------ Object NV
+    
+    // Javascript names and RDF extras.
+    public boolean isURI() { return nv.isIRI(); }
+    public boolean isBlank() { return nv.isBlank(); }
+    public boolean isNumber() { return nv.isNumber(); }
+    public boolean isLiteral() { return nv.isLiteral(); }
+
+    // -- rdfjs
+    @Override
+    public String getTermType() {
+        if ( isURI() )
+            return "NamedNode";
+        if ( isBlank() )
+            return "BlankNode";
+        if ( isLiteral() )
+            return "Literal";
+        return null; 
+    }
+
+    @Override
+    public String getValue() { 
+        if ( isURI() )
+            return getUri();
+        if ( isBlank() )
+            return getLabel();
+        if ( isLiteral() )
+            return getLex();
+        return null; 
+    }
+    // -- rdfjs
+
+    public String getLabel()    { return nv.asNode().getBlankNodeLabel(); }
+    public String getDT()       { return nv.getDatatypeURI(); }
+    public String getDatatype() { return nv.getDatatypeURI(); }
+    public String getLanguage() { return nv.getLang(); }
+    public String getLang()     { return nv.getLang().toLowerCase(); }
+    public String getLex()      { return nv.getNode().getLiteralLexicalForm(); }
+    public String getUri()      { return nv.getNode().getURI(); }
+
+    public NV(NodeValue nv) {
+        this.nv = nv;
+    }
+    public NodeValue nv() {
+        return nv;
+    }
+
+    @Override
+    public String toString() { return nv.asUnquotedString(); }
+
+    @Override
+    public int hashCode() {
+        final int prime = 31;
+        int result = 1;
+        result = prime * result + ((nv == null) ? 0 : nv.hashCode());
+        return result;
+    }
+    @Override
+    public boolean equals(Object obj) {
+        if ( this == obj )
+            return true;
+        if ( obj == null )
+            return false;
+        if ( getClass() != obj.getClass() )
+            return false;
+        NV other = (NV)obj;
+        if ( nv == null ) {
+            if ( other.nv != null )
+                return false;
+        } else if ( !nv.equals(other.nv) )
+            return false;
+        return true;
+    } 
+    }
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/jena/blob/c4f79ee9/jena-arq/src/main/java/org/apache/jena/sparql/function/js/RDFJS.java
----------------------------------------------------------------------
diff --git a/jena-arq/src/main/java/org/apache/jena/sparql/function/js/RDFJS.java b/jena-arq/src/main/java/org/apache/jena/sparql/function/js/RDFJS.java
new file mode 100644
index 0000000..a4fdf92
--- /dev/null
+++ b/jena-arq/src/main/java/org/apache/jena/sparql/function/js/RDFJS.java
@@ -0,0 +1,35 @@
+/*
+ * 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;
+
+/**
+ * The <a href="https://github.com/rdfjs/representation-task-force/">rdfjs/representation-task-force</a>
+ * API for RDF terms.
+ * 
+ */
+
+
+public interface RDFJS {
+    public String getTermType();
+    public String getValue();
+    
+//    public interface RDFJS_NamedNode {}
+//    public interface RDFJS_BlankNode {}
+//    public interface RDFJS_Literal {}
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/jena/blob/c4f79ee9/jena-arq/src/test/java/org/apache/jena/riot/langsuite/FactoryTestRiot.java
----------------------------------------------------------------------
diff --git a/jena-arq/src/test/java/org/apache/jena/riot/langsuite/FactoryTestRiot.java b/jena-arq/src/test/java/org/apache/jena/riot/langsuite/FactoryTestRiot.java
index 5e8bb2c..3c946ec 100644
--- a/jena-arq/src/test/java/org/apache/jena/riot/langsuite/FactoryTestRiot.java
+++ b/jena-arq/src/test/java/org/apache/jena/riot/langsuite/FactoryTestRiot.java
@@ -150,5 +150,4 @@ public class FactoryTestRiot extends TestFactoryManifest
         baseIRI = baseIRI+x ;
         return baseIRI ;
     }
-    
 }

http://git-wip-us.apache.org/repos/asf/jena/blob/c4f79ee9/jena-arq/src/test/java/org/apache/jena/sparql/TC_General.java
----------------------------------------------------------------------
diff --git a/jena-arq/src/test/java/org/apache/jena/sparql/TC_General.java b/jena-arq/src/test/java/org/apache/jena/sparql/TC_General.java
index e4542bc..594ba60 100644
--- a/jena-arq/src/test/java/org/apache/jena/sparql/TC_General.java
+++ b/jena-arq/src/test/java/org/apache/jena/sparql/TC_General.java
@@ -31,6 +31,7 @@ import org.apache.jena.sparql.engine.join.TS_Join ;
 import org.apache.jena.sparql.expr.E_Function ;
 import org.apache.jena.sparql.expr.NodeValue ;
 import org.apache.jena.sparql.expr.TS_Expr ;
+import org.apache.jena.sparql.function.js.TS_FunctionJS;
 import org.apache.jena.sparql.function.library.TS_LibraryFunctions ;
 import org.apache.jena.sparql.function.user.TS_UserFunctions ;
 import org.apache.jena.sparql.graph.TS_Graph ;
@@ -60,6 +61,7 @@ import org.junit.runners.Suite ;
     , TS_Expr.class
     , TS_LibraryFunctions.class
     , TS_UserFunctions.class
+    , TS_FunctionJS.class
     , TS_PFunction.class
     
     , TS_ResultSet.class

http://git-wip-us.apache.org/repos/asf/jena/blob/c4f79ee9/jena-arq/src/test/java/org/apache/jena/sparql/function/js/TS_FunctionJS.java
----------------------------------------------------------------------
diff --git a/jena-arq/src/test/java/org/apache/jena/sparql/function/js/TS_FunctionJS.java b/jena-arq/src/test/java/org/apache/jena/sparql/function/js/TS_FunctionJS.java
new file mode 100644
index 0000000..59e8b48
--- /dev/null
+++ b/jena-arq/src/test/java/org/apache/jena/sparql/function/js/TS_FunctionJS.java
@@ -0,0 +1,31 @@
+/*
+ * 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 org.junit.runner.RunWith;
+import org.junit.runners.Suite;
+import org.junit.runners.Suite.SuiteClasses;
+
+@RunWith(Suite.class)
+@SuiteClasses( {
+    TestNV.class
+    , TestJavaScriptFunctions.class
+    , TestSPARQL_JS.class
+})
+public class TS_FunctionJS {}

http://git-wip-us.apache.org/repos/asf/jena/blob/c4f79ee9/jena-arq/src/test/java/org/apache/jena/sparql/function/js/TestJavaScriptFunctions.java
----------------------------------------------------------------------
diff --git a/jena-arq/src/test/java/org/apache/jena/sparql/function/js/TestJavaScriptFunctions.java b/jena-arq/src/test/java/org/apache/jena/sparql/function/js/TestJavaScriptFunctions.java
new file mode 100644
index 0000000..7f456ff
--- /dev/null
+++ b/jena-arq/src/test/java/org/apache/jena/sparql/function/js/TestJavaScriptFunctions.java
@@ -0,0 +1,198 @@
+/*
+ * 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 static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import java.util.Arrays;
+
+import org.apache.jena.atlas.lib.StrUtils;
+import org.apache.jena.datatypes.xsd.XSDDatatype;
+import org.apache.jena.query.ARQ;
+import org.apache.jena.sparql.expr.ExprEvalException;
+import org.apache.jena.sparql.expr.NodeValue;
+import org.apache.jena.sparql.sse.SSE;
+import org.apache.jena.sparql.util.Context;
+import org.junit.Test;
+
+public class TestJavaScriptFunctions {
+    static final String JS_LIB_FILE = "testing/ARQ/JS/test-library.js";
+    
+    static final String CamelCaseJS = 
+        StrUtils.strjoinNL
+         ("function toCamelCase(str) { return str.split(' ').map(cc).join('');}"
+         ,"function ucFirst(word)    { return word.charAt(0).toUpperCase() + word.slice(1).toLowerCase();}"
+         ,"function lcFirst(word)    { return word.toLowerCase(); }"
+         ,"function cc(word,index)   { return (index == 0) ? lcFirst(word) : ucFirst(word); }"
+         );
+
+    private static EnvJavaScript setupJS() {
+        Context cxt = ARQ.getContext().copy();
+        cxt.set(EnvJavaScript.symJavaScriptLib, CamelCaseJS);
+        cxt.set(EnvJavaScript.symJavaScriptLibFile, JS_LIB_FILE);
+        return EnvJavaScript.create(cxt);
+    }
+
+    private EnvJavaScript envJS = setupJS();
+
+    @Test public void js_dt_boolean() {
+        NodeValue nv = eval("rtnBoolean");
+        assertTrue(nv.isBoolean());
+    }
+    
+    @Test public void js_dt_string() {
+        NodeValue nv = eval("rtnString");
+        assertTrue(nv.isString());
+    }
+
+    //Note: NodeValue isDouble etc means "can be used as" so NodeValue integer isDouble
+    // Use assertDatatype.
+    
+    @Test public void js_dt_integer() {
+        NodeValue nv = eval("rtnInteger");
+        assertDatatype(nv, XSDDatatype.XSDinteger);
+    }
+    
+    @Test public void js_dt_double() {
+        NodeValue nv = eval("rtnDouble");
+        assertDatatype(nv, XSDDatatype.XSDdouble);
+    }
+    
+    @Test(expected=ExprEvalException.class)
+    public void js_dt_undef() {
+        NodeValue nv = eval("rtnUndef");
+    }
+    
+    @Test(expected=ExprEvalException.class) 
+    public void js_dt_null() {
+        NodeValue nv = eval("rtnNull");
+    }
+    
+//    @Test public void js_dt_symbol() {
+//        NodeValue nv = eval("rtnSymbol");
+//    }
+    
+    @Test public void js_dt_1() { 
+        NodeValue nv = eval("identity", "'2018-01-06T17:56:41.293+00:00'^^xsd:dateTime");
+    }
+
+    @Test public void js_1() {
+        NodeValue nv = eval("identity", "1");
+        assertNotNull(nv);
+    }
+
+    @Test public void js_2() {
+        NodeValue nv = eval("value", "<http://example/xyz>");
+        assertNotNull(nv);
+        assertTrue(nv.isString());
+    }
+
+    //combine dynamics.
+
+    @Test public void js_3() {
+        NodeValue nv = eval("combine", "1", "2");
+        NodeValue nvx = nv("3");
+        assertTrue(nv.isNumber());
+        assertDatatype(nv, XSDDatatype.XSDinteger);
+        assertEquals(nvx, nv);
+    }
+
+    @Test public void js_4() {
+        NodeValue nv = eval("combine", "'a'", "2");
+        NodeValue nvx = nv("'a2'");
+        assertTrue(nv.isString());
+        assertEquals(nvx, nv);
+    }
+
+    @Test public void js_5() {
+        NodeValue nv = eval("combine", "2", "'a'");
+        NodeValue nvx = nv("'2a'");
+        assertTrue(nv.isString());
+        assertEquals(nvx, nv);
+    }
+
+    @Test public void js_6() {
+        NodeValue nv = eval("combine", "2", "2.5");
+        NodeValue nvx = nv("4.5e0");
+        assertDatatype(nv, XSDDatatype.XSDdouble);
+        assertEquals(nvx, nv);
+    }
+
+    // Narrow to integer
+    @Test public void js_7() {
+        NodeValue nv = eval("combine", "2.5", "3.5");
+        NodeValue nvx = nv("6");
+        assertDatatype(nv, XSDDatatype.XSDinteger);
+        assertEquals(nvx, nv);
+    }
+    
+    @Test public void js_8() {
+        NodeValue nv = eval("toCamelCase", "'abc def ghi'");
+        NodeValue nvx = nv("'abcDefGhi'");
+        assertDatatype(nv, XSDDatatype.XSDstring);
+        assertEquals(nvx, nv);
+    }
+    
+    private void assertDatatype(NodeValue nv, XSDDatatype xsdDatatype) {
+        assertEquals(nv.asNode().getLiteralDatatype(), xsdDatatype);
+    }
+
+    @Test(expected=ExprEvalException.class)
+    public void js_err_1() {
+        NodeValue nv = eval("no_such_function()");
+    }
+    
+    // Wrong number of argument is OK in JavaScript- except "null" return becomes ExprEvalException.   
+    @Test(expected=ExprEvalException.class)
+    public void js_err_2() {
+        NodeValue nv = eval("identity");
+    }
+
+    @Test
+    public void js_err_3() {
+        // Legal.
+        NodeValue nv = eval("combine", "'a'");
+        assertNotNull(nv);
+    }
+
+    @Test
+    public void js_err_4() {
+        // Legal.
+        NodeValue nv = eval("identity", "3", "2");
+        NodeValue nvx = nv("3");
+        assertEquals(nvx, nv);
+    }
+
+
+    private NodeValue eval(String fn, String ...args) {
+        NodeValue[] nvs = new NodeValue[args.length];
+        for ( int i = 0 ; i < args.length ; i++ ) {
+            nvs[i] = nv(args[i]);
+        }
+        FunctionJavaScript x = new FunctionJavaScript(fn, envJS);
+        NodeValue nvr = x.exec(Arrays.asList(nvs));
+        return nvr;
+    }
+    
+    private static NodeValue nv(String str) {
+        return NodeValue.makeNode(SSE.parseNode(str)); 
+    }
+}

http://git-wip-us.apache.org/repos/asf/jena/blob/c4f79ee9/jena-arq/src/test/java/org/apache/jena/sparql/function/js/TestNV.java
----------------------------------------------------------------------
diff --git a/jena-arq/src/test/java/org/apache/jena/sparql/function/js/TestNV.java b/jena-arq/src/test/java/org/apache/jena/sparql/function/js/TestNV.java
new file mode 100644
index 0000000..728172d
--- /dev/null
+++ b/jena-arq/src/test/java/org/apache/jena/sparql/function/js/TestNV.java
@@ -0,0 +1,71 @@
+/*
+ * 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 org.apache.jena.sparql.expr.NodeValue;
+import org.apache.jena.sparql.sse.SSE;
+import org.junit.Test;
+import static org.junit.Assert.*;
+
+public class TestNV {
+
+    @Test public void nv_1() { test("'abc'"); }
+    @Test public void nv_2() { test("true"); }
+    @Test public void nv_3() { test("123"); }
+    @Test public void nv_4() { test("123.5"); }
+    
+    // No conversion to JS - becomes an NV.
+    @Test public void nv_5() { test("'2018-01-06T17:56:41.293+00:00'^^xsd:dateTime"); }
+    @Test public void nv_6() { test("<http://jena/apche.org/>"); }
+    @Test public void nv_7() { test("_:abc123"); }
+    
+    @Test public void nv_10() {
+        NodeValue nodeValue = nv("'abc'");
+        NV nv = new NV(nodeValue);
+        assertEquals("abc", nv.getLex());
+        assertEquals("abc", nv.getValue());
+        assertEquals("Literal", nv.getTermType());
+    }
+
+    @Test public void nv_12() {
+        NodeValue nodeValue = nv("<http://jena/apche.org/>");
+        NV nv = new NV(nodeValue);
+        assertEquals("http://jena/apche.org/", nv.getUri());
+        assertEquals("http://jena/apche.org/", nv.getValue());
+        assertEquals("NamedNode", nv.getTermType());
+    }
+
+    @Test public void nv_13() {
+        NodeValue nodeValue = nv("_:a");
+        NV nv = new NV(nodeValue);
+        assertEquals(nv.getLabel(), nv.getValue());
+        assertEquals("BlankNode", nv.getTermType());
+    }
+    
+    private void test(String str) {
+        NodeValue nv = nv(str);
+        Object x = NV.fromNodeValue(nv);
+        NodeValue nv2 = NV.toNodeValue(x);
+        assertEquals(nv,nv2);
+    }
+
+    private static NodeValue nv(String str) {
+        return NodeValue.makeNode(SSE.parseNode(str)); 
+    }
+}

http://git-wip-us.apache.org/repos/asf/jena/blob/c4f79ee9/jena-arq/src/test/java/org/apache/jena/sparql/function/js/TestSPARQL_JS.java
----------------------------------------------------------------------
diff --git a/jena-arq/src/test/java/org/apache/jena/sparql/function/js/TestSPARQL_JS.java b/jena-arq/src/test/java/org/apache/jena/sparql/function/js/TestSPARQL_JS.java
new file mode 100644
index 0000000..309e367
--- /dev/null
+++ b/jena-arq/src/test/java/org/apache/jena/sparql/function/js/TestSPARQL_JS.java
@@ -0,0 +1,49 @@
+/*
+ * 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 junit.framework.TestSuite;
+import org.apache.jena.query.ARQ;
+import org.apache.jena.sparql.junit.ScriptTestSuiteFactory;
+import org.apache.jena.sparql.util.Context;
+import org.apache.jena.system.JenaSystem;
+import org.junit.runner.RunWith;
+import org.junit.runners.AllTests;
+
+@RunWith(AllTests.class)
+public class TestSPARQL_JS {
+    static final String MANIFEST = "testing/ARQ/JS/manifest.ttl";
+    static final String JS_LIB_FILE = "testing/ARQ/JS/test-library.js";
+    
+    private static void setupJS() {
+        Context cxt = ARQ.getContext();
+        cxt.set(EnvJavaScript.symJavaScriptLibFile, JS_LIB_FILE);
+        cxt.set(EnvJavaScript.symJavaScriptLib, "function inc(x) { return x+1 }");
+        EnvJavaScript.reset();
+    }
+    
+    static public TestSuite suite() {
+        JenaSystem.init();
+        setupJS();
+        TestSuite ts = new TestSuite(TestSPARQL_JS.class.getName());
+        TestSuite ts2 = ScriptTestSuiteFactory.make(MANIFEST);
+        ts.addTest(ts2);
+        return ts;
+    }
+}

http://git-wip-us.apache.org/repos/asf/jena/blob/c4f79ee9/jena-arq/src/test/java/org/apache/jena/sparql/function/library/TS_LibraryFunctions.java
----------------------------------------------------------------------
diff --git a/jena-arq/src/test/java/org/apache/jena/sparql/function/library/TS_LibraryFunctions.java b/jena-arq/src/test/java/org/apache/jena/sparql/function/library/TS_LibraryFunctions.java
index 132b313..7d0830f 100644
--- a/jena-arq/src/test/java/org/apache/jena/sparql/function/library/TS_LibraryFunctions.java
+++ b/jena-arq/src/test/java/org/apache/jena/sparql/function/library/TS_LibraryFunctions.java
@@ -24,7 +24,7 @@ import org.junit.runners.Suite.SuiteClasses ;
 
 @RunWith(Suite.class)
 @SuiteClasses( {
-    // A lot of the test ate in TestFunctions and TestFunctions2.
+    // A lot of the test are in TestFunctions and TestFunctions2.
     TestFunctionCollation.class
     , TestFnFunctions.class
 })

http://git-wip-us.apache.org/repos/asf/jena/blob/c4f79ee9/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
new file mode 100644
index 0000000..12303ce
--- /dev/null
+++ b/jena-arq/testing/ARQ/JS/data.ttl
@@ -0,0 +1,5 @@
+PREFIX : <http://example/>
+
+:s1 :p1 1 .
+:s2 :p2 "one" .
+

http://git-wip-us.apache.org/repos/asf/jena/blob/c4f79ee9/jena-arq/testing/ARQ/JS/js-query-1.rq
----------------------------------------------------------------------
diff --git a/jena-arq/testing/ARQ/JS/js-query-1.rq b/jena-arq/testing/ARQ/JS/js-query-1.rq
new file mode 100644
index 0000000..ad90720
--- /dev/null
+++ b/jena-arq/testing/ARQ/JS/js-query-1.rq
@@ -0,0 +1,4 @@
+PREFIX js: <http://jena/apache.org/ARQ/jsFunction#>
+PREFIX : <http://example/>
+
+SELECT (js:identity(8) AS ?X) {}

http://git-wip-us.apache.org/repos/asf/jena/blob/c4f79ee9/jena-arq/testing/ARQ/JS/js-query-1.srj
----------------------------------------------------------------------
diff --git a/jena-arq/testing/ARQ/JS/js-query-1.srj b/jena-arq/testing/ARQ/JS/js-query-1.srj
new file mode 100644
index 0000000..04b98bc
--- /dev/null
+++ b/jena-arq/testing/ARQ/JS/js-query-1.srj
@@ -0,0 +1,12 @@
+{
+  "head": {
+    "vars": [ "X" ]
+  } ,
+  "results": {
+    "bindings": [
+      {
+        "X": { "type": "literal" , "datatype": "http://www.w3.org/2001/XMLSchema#integer" , "value": "8" }
+      }
+    ]
+  }
+}

http://git-wip-us.apache.org/repos/asf/jena/blob/c4f79ee9/jena-arq/testing/ARQ/JS/js-query-2.rq
----------------------------------------------------------------------
diff --git a/jena-arq/testing/ARQ/JS/js-query-2.rq b/jena-arq/testing/ARQ/JS/js-query-2.rq
new file mode 100644
index 0000000..bf5cc35
--- /dev/null
+++ b/jena-arq/testing/ARQ/JS/js-query-2.rq
@@ -0,0 +1,4 @@
+PREFIX js: <http://jena/apache.org/ARQ/jsFunction#>
+PREFIX : <http://example/>
+
+SELECT (js:inc(8) AS ?X) {}

http://git-wip-us.apache.org/repos/asf/jena/blob/c4f79ee9/jena-arq/testing/ARQ/JS/js-query-2.srj
----------------------------------------------------------------------
diff --git a/jena-arq/testing/ARQ/JS/js-query-2.srj b/jena-arq/testing/ARQ/JS/js-query-2.srj
new file mode 100644
index 0000000..54e9a3e
--- /dev/null
+++ b/jena-arq/testing/ARQ/JS/js-query-2.srj
@@ -0,0 +1,12 @@
+{
+  "head": {
+    "vars": [ "X" ]
+  } ,
+  "results": {
+    "bindings": [
+      {
+        "X": { "type": "literal" , "datatype": "http://www.w3.org/2001/XMLSchema#integer" , "value": "9" }
+      }
+    ]
+  }
+}

http://git-wip-us.apache.org/repos/asf/jena/blob/c4f79ee9/jena-arq/testing/ARQ/JS/js-query-3.rq
----------------------------------------------------------------------
diff --git a/jena-arq/testing/ARQ/JS/js-query-3.rq b/jena-arq/testing/ARQ/JS/js-query-3.rq
new file mode 100644
index 0000000..55bf329
--- /dev/null
+++ b/jena-arq/testing/ARQ/JS/js-query-3.rq
@@ -0,0 +1,4 @@
+PREFIX js: <http://jena/apache.org/ARQ/jsFunction#>
+PREFIX : <http://example/>
+
+SELECT (js:inc(js:inc(8)) AS ?X) {}

http://git-wip-us.apache.org/repos/asf/jena/blob/c4f79ee9/jena-arq/testing/ARQ/JS/js-query-3.srj
----------------------------------------------------------------------
diff --git a/jena-arq/testing/ARQ/JS/js-query-3.srj b/jena-arq/testing/ARQ/JS/js-query-3.srj
new file mode 100644
index 0000000..19f24bd
--- /dev/null
+++ b/jena-arq/testing/ARQ/JS/js-query-3.srj
@@ -0,0 +1,12 @@
+{
+  "head": {
+    "vars": [ "X" ]
+  } ,
+  "results": {
+    "bindings": [
+      {
+        "X": { "type": "literal" , "datatype": "http://www.w3.org/2001/XMLSchema#integer" , "value": "10" }
+      }
+    ]
+  }
+}

http://git-wip-us.apache.org/repos/asf/jena/blob/c4f79ee9/jena-arq/testing/ARQ/JS/js-query-4.rq
----------------------------------------------------------------------
diff --git a/jena-arq/testing/ARQ/JS/js-query-4.rq b/jena-arq/testing/ARQ/JS/js-query-4.rq
new file mode 100644
index 0000000..6e466c3
--- /dev/null
+++ b/jena-arq/testing/ARQ/JS/js-query-4.rq
@@ -0,0 +1,9 @@
+PREFIX js: <http://jena/apache.org/ARQ/jsFunction#>
+PREFIX : <http://example/>
+
+SELECT * {
+   ?s ?p ?o
+   FILTER(js:identity(?s) = ?s)
+   FILTER(js:identity(?p) = :p1 )
+   FILTER(sameTerm(js:identity(?o),1) )
+}

http://git-wip-us.apache.org/repos/asf/jena/blob/c4f79ee9/jena-arq/testing/ARQ/JS/js-query-4.srj
----------------------------------------------------------------------
diff --git a/jena-arq/testing/ARQ/JS/js-query-4.srj b/jena-arq/testing/ARQ/JS/js-query-4.srj
new file mode 100644
index 0000000..be95897
--- /dev/null
+++ b/jena-arq/testing/ARQ/JS/js-query-4.srj
@@ -0,0 +1,14 @@
+{
+  "head": {
+    "vars": [ "s" , "p" , "o" ]
+  } ,
+  "results": {
+    "bindings": [
+      {
+        "s": { "type": "uri" , "value": "http://example/s1" } ,
+        "p": { "type": "uri" , "value": "http://example/p1" } ,
+        "o": { "type": "literal" , "datatype": "http://www.w3.org/2001/XMLSchema#integer" , "value": "1" }
+      }
+    ]
+  }
+}

http://git-wip-us.apache.org/repos/asf/jena/blob/c4f79ee9/jena-arq/testing/ARQ/JS/manifest.ttl
----------------------------------------------------------------------
diff --git a/jena-arq/testing/ARQ/JS/manifest.ttl b/jena-arq/testing/ARQ/JS/manifest.ttl
new file mode 100644
index 0000000..7dd64ae
--- /dev/null
+++ b/jena-arq/testing/ARQ/JS/manifest.ttl
@@ -0,0 +1,50 @@
+#  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.
+
+@prefix rdf:    <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
+@prefix rdfs:	<http://www.w3.org/2000/01/rdf-schema#> .
+@prefix mf:     <http://www.w3.org/2001/sw/DataAccess/tests/test-manifest#> .
+@prefix qt:     <http://www.w3.org/2001/sw/DataAccess/tests/test-query#> .
+
+<>  rdf:type mf:Manifest ;
+    rdfs:label    "JavaScript function" ;
+    rdfs:comment  "JavaScript function" ;
+    mf:entries
+    ( 
+      [  mf:name    "JS Call: file-defined function" ;
+         mf:action
+            [ qt:query  <js-query-1.rq> ;
+              qt:data   <data.ttl> ] ;
+         mf:result  <js-query-1.srj>
+      ]
+      [  mf:name    "JS Call: string-defined function" ;
+         mf:action
+            [ qt:query  <js-query-2.rq> ;
+              qt:data   <data.ttl> ] ;
+         mf:result  <js-query-2.srj>
+      ]
+      [  mf:name    "JS Call: nested functions" ;
+         mf:action
+            [ qt:query  <js-query-3.rq> ;
+              qt:data   <data.ttl> ] ;
+         mf:result  <js-query-3.srj>
+      ]
+      [  mf:name    "JS Call: FILTER functions" ;
+         mf:action
+            [ qt:query  <js-query-4.rq> ;
+              qt:data   <data.ttl> ] ;
+         mf:result  <js-query-4.srj>
+      ]
+    ).

http://git-wip-us.apache.org/repos/asf/jena/blob/c4f79ee9/jena-arq/testing/ARQ/JS/test-library.js
----------------------------------------------------------------------
diff --git a/jena-arq/testing/ARQ/JS/test-library.js b/jena-arq/testing/ARQ/JS/test-library.js
new file mode 100644
index 0000000..60d5fad
--- /dev/null
+++ b/jena-arq/testing/ARQ/JS/test-library.js
@@ -0,0 +1,14 @@
+var barx = '||';
+function bar(x,y)       { return barx+combine(x,y)+barx } 
+function value(n)       { return n.value ; }
+function combine(x,y)   { return x+y }
+function identity(x)    { return x }
+
+// The JavaScript types (number split).
+function rtnBoolean()   { return true; }
+function rtnString()    { return "foo"; }
+function rtnInteger()   { return 57 }
+function rtnDouble()    { return 5.7 }
+function rtnUndef()     { return undefined }
+function rtnNull()      { return null; }
+//function rtnSymbol()    { return Symbol('foo') }


[2/8] jena git commit: JENA-1461: Nashorn and threading

Posted by an...@apache.org.
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" .
 


[5/8] jena git commit: Fix javadoc

Posted by an...@apache.org.
Fix javadoc


Project: http://git-wip-us.apache.org/repos/asf/jena/repo
Commit: http://git-wip-us.apache.org/repos/asf/jena/commit/65d42959
Tree: http://git-wip-us.apache.org/repos/asf/jena/tree/65d42959
Diff: http://git-wip-us.apache.org/repos/asf/jena/diff/65d42959

Branch: refs/heads/master
Commit: 65d42959b46897bd7037da1d4a0977b7847ffa71
Parents: be160bc
Author: Andy Seaborne <an...@apache.org>
Authored: Wed Jan 10 16:24:34 2018 +0000
Committer: Andy Seaborne <an...@apache.org>
Committed: Wed Jan 10 16:24:34 2018 +0000

----------------------------------------------------------------------
 jena-arq/src/main/java/org/apache/jena/sparql/ARQConstants.java   | 2 +-
 jena-arq/src/main/java/org/apache/jena/sparql/function/js/NV.java | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/jena/blob/65d42959/jena-arq/src/main/java/org/apache/jena/sparql/ARQConstants.java
----------------------------------------------------------------------
diff --git a/jena-arq/src/main/java/org/apache/jena/sparql/ARQConstants.java b/jena-arq/src/main/java/org/apache/jena/sparql/ARQConstants.java
index 652306f..d6792df 100644
--- a/jena-arq/src/main/java/org/apache/jena/sparql/ARQConstants.java
+++ b/jena-arq/src/main/java/org/apache/jena/sparql/ARQConstants.java
@@ -70,7 +70,7 @@ public class ARQConstants
     /** The URI prefix that triggers JavaScript functions */ 
     public static final String JavaScriptURI = "http://jena.apache.org/ARQ/jsFunction#" ;
     
-    /** Function called when JaavScript is initialized. */ 
+    /** Function called when JavaScript is initialized. */ 
     public static final String JavaScriptInitFunction = "arqJSinit";
 
     /** URI scheme that triggers the loader to load a java class */ 

http://git-wip-us.apache.org/repos/asf/jena/blob/65d42959/jena-arq/src/main/java/org/apache/jena/sparql/function/js/NV.java
----------------------------------------------------------------------
diff --git a/jena-arq/src/main/java/org/apache/jena/sparql/function/js/NV.java b/jena-arq/src/main/java/org/apache/jena/sparql/function/js/NV.java
index 9ea418b..2abcad1 100644
--- a/jena-arq/src/main/java/org/apache/jena/sparql/function/js/NV.java
+++ b/jena-arq/src/main/java/org/apache/jena/sparql/function/js/NV.java
@@ -53,7 +53,7 @@ public class NV implements RDFJS {
     private final static boolean narrowDoubles = true;
     /**
      * Map an ARQ {@link NodeValue} to java/Nashorn representation of a JavaScript object.
-     * Native JaavScript types supported are null, string, number and boolean.
+     * Native JavaScript types supported are null, string, number and boolean.
      * Otherwise a {@link NV} is returned.
      */
     public static Object fromNodeValue(NodeValue nv) {


[3/8] jena git commit: Fix namespace

Posted by an...@apache.org.
Fix namespace


Project: http://git-wip-us.apache.org/repos/asf/jena/repo
Commit: http://git-wip-us.apache.org/repos/asf/jena/commit/4d5faef3
Tree: http://git-wip-us.apache.org/repos/asf/jena/tree/4d5faef3
Diff: http://git-wip-us.apache.org/repos/asf/jena/diff/4d5faef3

Branch: refs/heads/master
Commit: 4d5faef30d9966d70256c6c73ea40fb2a8255cfe
Parents: a0108d7
Author: Andy Seaborne <an...@apache.org>
Authored: Wed Jan 10 16:20:05 2018 +0000
Committer: Andy Seaborne <an...@apache.org>
Committed: Wed Jan 10 16:20:30 2018 +0000

----------------------------------------------------------------------
 jena-arq/src/main/java/org/apache/jena/sparql/ARQConstants.java | 2 +-
 jena-arq/testing/ARQ/JS/js-query-1.rq                           | 2 +-
 jena-arq/testing/ARQ/JS/js-query-2.rq                           | 2 +-
 jena-arq/testing/ARQ/JS/js-query-3.rq                           | 2 +-
 jena-arq/testing/ARQ/JS/js-query-4.rq                           | 2 +-
 5 files changed, 5 insertions(+), 5 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/jena/blob/4d5faef3/jena-arq/src/main/java/org/apache/jena/sparql/ARQConstants.java
----------------------------------------------------------------------
diff --git a/jena-arq/src/main/java/org/apache/jena/sparql/ARQConstants.java b/jena-arq/src/main/java/org/apache/jena/sparql/ARQConstants.java
index 2cf4e4d..652306f 100644
--- a/jena-arq/src/main/java/org/apache/jena/sparql/ARQConstants.java
+++ b/jena-arq/src/main/java/org/apache/jena/sparql/ARQConstants.java
@@ -68,7 +68,7 @@ public class ARQConstants
     public static final String XML_SCHEMA_NS = "http://www.w3.org/2001/XMLSchema#" ;
     
     /** The URI prefix that triggers JavaScript functions */ 
-    public static final String JavaScriptURI = "http://jena/apache.org/ARQ/jsFunction#" ;
+    public static final String JavaScriptURI = "http://jena.apache.org/ARQ/jsFunction#" ;
     
     /** Function called when JaavScript is initialized. */ 
     public static final String JavaScriptInitFunction = "arqJSinit";

http://git-wip-us.apache.org/repos/asf/jena/blob/4d5faef3/jena-arq/testing/ARQ/JS/js-query-1.rq
----------------------------------------------------------------------
diff --git a/jena-arq/testing/ARQ/JS/js-query-1.rq b/jena-arq/testing/ARQ/JS/js-query-1.rq
index ad90720..51ad595 100644
--- a/jena-arq/testing/ARQ/JS/js-query-1.rq
+++ b/jena-arq/testing/ARQ/JS/js-query-1.rq
@@ -1,4 +1,4 @@
-PREFIX js: <http://jena/apache.org/ARQ/jsFunction#>
+PREFIX js: <http://jena.apache.org/ARQ/jsFunction#>
 PREFIX : <http://example/>
 
 SELECT (js:identity(8) AS ?X) {}

http://git-wip-us.apache.org/repos/asf/jena/blob/4d5faef3/jena-arq/testing/ARQ/JS/js-query-2.rq
----------------------------------------------------------------------
diff --git a/jena-arq/testing/ARQ/JS/js-query-2.rq b/jena-arq/testing/ARQ/JS/js-query-2.rq
index bf5cc35..bc70e57 100644
--- a/jena-arq/testing/ARQ/JS/js-query-2.rq
+++ b/jena-arq/testing/ARQ/JS/js-query-2.rq
@@ -1,4 +1,4 @@
-PREFIX js: <http://jena/apache.org/ARQ/jsFunction#>
+PREFIX js: <http://jena.apache.org/ARQ/jsFunction#>
 PREFIX : <http://example/>
 
 SELECT (js:inc(8) AS ?X) {}

http://git-wip-us.apache.org/repos/asf/jena/blob/4d5faef3/jena-arq/testing/ARQ/JS/js-query-3.rq
----------------------------------------------------------------------
diff --git a/jena-arq/testing/ARQ/JS/js-query-3.rq b/jena-arq/testing/ARQ/JS/js-query-3.rq
index 55bf329..0b9a663 100644
--- a/jena-arq/testing/ARQ/JS/js-query-3.rq
+++ b/jena-arq/testing/ARQ/JS/js-query-3.rq
@@ -1,4 +1,4 @@
-PREFIX js: <http://jena/apache.org/ARQ/jsFunction#>
+PREFIX js: <http://jena.apache.org/ARQ/jsFunction#>
 PREFIX : <http://example/>
 
 SELECT (js:inc(js:inc(8)) AS ?X) {}

http://git-wip-us.apache.org/repos/asf/jena/blob/4d5faef3/jena-arq/testing/ARQ/JS/js-query-4.rq
----------------------------------------------------------------------
diff --git a/jena-arq/testing/ARQ/JS/js-query-4.rq b/jena-arq/testing/ARQ/JS/js-query-4.rq
index 6e466c3..c10eb74 100644
--- a/jena-arq/testing/ARQ/JS/js-query-4.rq
+++ b/jena-arq/testing/ARQ/JS/js-query-4.rq
@@ -1,4 +1,4 @@
-PREFIX js: <http://jena/apache.org/ARQ/jsFunction#>
+PREFIX js: <http://jena.apache.org/ARQ/jsFunction#>
 PREFIX : <http://example/>
 
 SELECT * {


[7/8] jena git commit: JENA-1461: Put contants into a public place.

Posted by an...@apache.org.
JENA-1461: Put contants into a public place.

Improve and add tests


Project: http://git-wip-us.apache.org/repos/asf/jena/repo
Commit: http://git-wip-us.apache.org/repos/asf/jena/commit/2eb211f8
Tree: http://git-wip-us.apache.org/repos/asf/jena/tree/2eb211f8
Diff: http://git-wip-us.apache.org/repos/asf/jena/diff/2eb211f8

Branch: refs/heads/master
Commit: 2eb211f80aebe37f54df246ef626cffd7f5b9fc8
Parents: 64fe8bd
Author: Andy Seaborne <an...@apache.org>
Authored: Fri Jan 12 16:05:16 2018 +0000
Committer: Andy Seaborne <an...@apache.org>
Committed: Fri Jan 12 16:05:16 2018 +0000

----------------------------------------------------------------------
 .../main/java/org/apache/jena/query/ARQ.java    | 10 ++++++++++
 .../org/apache/jena/sparql/ARQConstants.java    |  2 +-
 .../org/apache/jena/sparql/expr/E_Function.java |  4 ++++
 .../jena/sparql/function/js/EnvJavaScript.java  | 11 +++++------
 .../sparql/function/js/FunctionJavaScript.java  |  3 ---
 .../jena/sparql/function/js/TS_FunctionJS.java  | 20 +++++++++++++++++++-
 .../function/js/TestJavaScriptFunctions.java    |  4 ++--
 .../jena/sparql/function/js/TestSPARQL_JS.java  |  4 ++--
 jena-arq/testing/ARQ/JS/js-query-5.rq           |  7 +++++++
 jena-arq/testing/ARQ/JS/js-query-5.srj          |  8 ++++++++
 jena-arq/testing/ARQ/JS/manifest.ttl            |  6 ++++++
 11 files changed, 64 insertions(+), 15 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/jena/blob/2eb211f8/jena-arq/src/main/java/org/apache/jena/query/ARQ.java
----------------------------------------------------------------------
diff --git a/jena-arq/src/main/java/org/apache/jena/query/ARQ.java b/jena-arq/src/main/java/org/apache/jena/query/ARQ.java
index b023a1b..e319bc8 100644
--- a/jena-arq/src/main/java/org/apache/jena/query/ARQ.java
+++ b/jena-arq/src/main/java/org/apache/jena/query/ARQ.java
@@ -485,6 +485,16 @@ public class ARQ
      * Expression evaluation without extension types (e.g. xsd:date, language tags)
      */
     public static final Symbol extensionValueTypes = SystemARQ.allocSymbol("extensionValueTypesExpr") ;
+    
+    /** 
+     * Context symbol for JavaScript functions as a string value which is evaluated.
+     */ 
+    public static Symbol symJavaScriptFunctions = SystemARQ.allocSymbol("js-functions");
+    
+    /**
+     * Context symbol for JavaScript library of functions defined in a file.
+     */
+    public static Symbol symJavaScriptLibFile = SystemARQ.allocSymbol("js-library");
 
     /**
      * Generate the ToList operation in the algebra (as ARQ is stream based, ToList is a non-op).

http://git-wip-us.apache.org/repos/asf/jena/blob/2eb211f8/jena-arq/src/main/java/org/apache/jena/sparql/ARQConstants.java
----------------------------------------------------------------------
diff --git a/jena-arq/src/main/java/org/apache/jena/sparql/ARQConstants.java b/jena-arq/src/main/java/org/apache/jena/sparql/ARQConstants.java
index d6792df..1cfafb4 100644
--- a/jena-arq/src/main/java/org/apache/jena/sparql/ARQConstants.java
+++ b/jena-arq/src/main/java/org/apache/jena/sparql/ARQConstants.java
@@ -67,7 +67,7 @@ public class ARQConstants
     /** XML Schema namespace */
     public static final String XML_SCHEMA_NS = "http://www.w3.org/2001/XMLSchema#" ;
     
-    /** The URI prefix that triggers JavaScript functions */ 
+    /** The URI prefix that triggers JavaScript functions */
     public static final String JavaScriptURI = "http://jena.apache.org/ARQ/jsFunction#" ;
     
     /** Function called when JavaScript is initialized. */ 

http://git-wip-us.apache.org/repos/asf/jena/blob/2eb211f8/jena-arq/src/main/java/org/apache/jena/sparql/expr/E_Function.java
----------------------------------------------------------------------
diff --git a/jena-arq/src/main/java/org/apache/jena/sparql/expr/E_Function.java b/jena-arq/src/main/java/org/apache/jena/sparql/expr/E_Function.java
index 2d097a1..558a174 100644
--- a/jena-arq/src/main/java/org/apache/jena/sparql/expr/E_Function.java
+++ b/jena-arq/src/main/java/org/apache/jena/sparql/expr/E_Function.java
@@ -64,6 +64,10 @@ public class E_Function extends ExprFunctionN
         if ( javaScriptFunction != null ) {
             // JavaScript 
             EnvJavaScript jsEnv = EnvJavaScript.get();
+            if ( jsEnv == null ) {
+                ARQ.getExecLogger().warn("JavaScript function found '"+javaScriptFunction+"' but no JavaScript library installed");
+                throw new ExprUndefFunction("No JavaScript function library", javaScriptFunction) ;
+            }
             // FunctionJavaScript : functions only : no special forms.
             function = new FunctionJavaScript(javaScriptFunction, jsEnv);
             try {

http://git-wip-us.apache.org/repos/asf/jena/blob/2eb211f8/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 148fde4..259ba83 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
@@ -24,7 +24,6 @@ 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.sparql.SystemARQ;
 import org.apache.jena.sparql.util.Context;
 import org.apache.jena.sparql.util.Symbol;
 
@@ -35,16 +34,16 @@ import org.apache.jena.sparql.util.Symbol;
  * The file must be UTF-8 encoded.
  * <p>
  * Function are loaded from a string value in context setting
- * {@link EnvJavaScript#symJavaScriptLib}.
+ * {@link EnvJavaScript#symJavaScriptFunctions}.
  * <p>
  * If both are present, the file named by {@code EnvJavaScript.symJavaScriptLibFile} is loaded
  * then the string from {@code EnvJavaScript.symJavaScriptLib}.
  */
 public class EnvJavaScript {
     /** JavaScript functions as a string value which is evaluated. */ 
-    public static Symbol symJavaScriptLib = SystemARQ.allocSymbol("js-functions");
+    public static Symbol symJavaScriptFunctions = ARQ.symJavaScriptFunctions;
     /** JavaScript library of functions in a file. */ 
-    public static Symbol symJavaScriptLibFile = SystemARQ.allocSymbol("js-library");
+    public static Symbol symJavaScriptLibFile = ARQ.symJavaScriptLibFile;
     
     private final String scriptLib;
     private final String scriptLibFile;
@@ -63,7 +62,7 @@ public class EnvJavaScript {
         if ( global == null ) {
             synchronized(EnvJavaScript.class) {
                 Context context = ARQ.getContext();
-                if ( context.isDefined(symJavaScriptLib) || context.isDefined(symJavaScriptLibFile) )
+                if ( context.isDefined(symJavaScriptFunctions) || context.isDefined(symJavaScriptLibFile) )
                     global = create(ARQ.getContext());
             }
         }
@@ -94,7 +93,7 @@ public class EnvJavaScript {
     private Pool<JSEngine> pool = PoolSync.create(new PoolBase<JSEngine>());
     
     private EnvJavaScript(Context context) {
-        this.scriptLib = context.getAsString(symJavaScriptLib);
+        this.scriptLib = context.getAsString(symJavaScriptFunctions);
         this.scriptLibFile = context.getAsString(symJavaScriptLibFile);
         // Put one in the pool.
         pool.put(build());

http://git-wip-us.apache.org/repos/asf/jena/blob/2eb211f8/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 cda6d31..cacef8e 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
@@ -67,9 +67,6 @@ public class FunctionJavaScript extends FunctionBase {
 
     @Override
     public NodeValue exec(List<NodeValue> args) {
-        if ( ! initialized )
-            checkBuild(null, null);
-        
         try {
             // Convert NodeValues to types more akin to Javascript. 
             // Pass strings as string, and numbers as Number.

http://git-wip-us.apache.org/repos/asf/jena/blob/2eb211f8/jena-arq/src/test/java/org/apache/jena/sparql/function/js/TS_FunctionJS.java
----------------------------------------------------------------------
diff --git a/jena-arq/src/test/java/org/apache/jena/sparql/function/js/TS_FunctionJS.java b/jena-arq/src/test/java/org/apache/jena/sparql/function/js/TS_FunctionJS.java
index 59e8b48..9683082 100644
--- a/jena-arq/src/test/java/org/apache/jena/sparql/function/js/TS_FunctionJS.java
+++ b/jena-arq/src/test/java/org/apache/jena/sparql/function/js/TS_FunctionJS.java
@@ -18,6 +18,10 @@
 
 package org.apache.jena.sparql.function.js;
 
+import org.apache.jena.query.ARQ;
+import org.apache.jena.sparql.expr.E_Function;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
 import org.junit.runner.RunWith;
 import org.junit.runners.Suite;
 import org.junit.runners.Suite.SuiteClasses;
@@ -28,4 +32,18 @@ import org.junit.runners.Suite.SuiteClasses;
     , TestJavaScriptFunctions.class
     , TestSPARQL_JS.class
 })
-public class TS_FunctionJS {}
+public class TS_FunctionJS {
+    static boolean b = false;
+    
+    @BeforeClass public static void beforeClass() {
+        b = E_Function.WarnOnUnknownFunction;
+        E_Function.WarnOnUnknownFunction = false ;
+    }
+    
+    @AfterClass public static void afterClass() {
+        ARQ.getContext().unset(ARQ.symJavaScriptFunctions);
+        ARQ.getContext().unset(ARQ.symJavaScriptLibFile);
+        E_Function.WarnOnUnknownFunction = b ;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/jena/blob/2eb211f8/jena-arq/src/test/java/org/apache/jena/sparql/function/js/TestJavaScriptFunctions.java
----------------------------------------------------------------------
diff --git a/jena-arq/src/test/java/org/apache/jena/sparql/function/js/TestJavaScriptFunctions.java b/jena-arq/src/test/java/org/apache/jena/sparql/function/js/TestJavaScriptFunctions.java
index 7f456ff..12d8799 100644
--- a/jena-arq/src/test/java/org/apache/jena/sparql/function/js/TestJavaScriptFunctions.java
+++ b/jena-arq/src/test/java/org/apache/jena/sparql/function/js/TestJavaScriptFunctions.java
@@ -46,7 +46,7 @@ public class TestJavaScriptFunctions {
 
     private static EnvJavaScript setupJS() {
         Context cxt = ARQ.getContext().copy();
-        cxt.set(EnvJavaScript.symJavaScriptLib, CamelCaseJS);
+        cxt.set(EnvJavaScript.symJavaScriptFunctions, CamelCaseJS);
         cxt.set(EnvJavaScript.symJavaScriptLibFile, JS_LIB_FILE);
         return EnvJavaScript.create(cxt);
     }
@@ -160,7 +160,7 @@ public class TestJavaScriptFunctions {
         NodeValue nv = eval("no_such_function()");
     }
     
-    // Wrong number of argument is OK in JavaScript- except "null" return becomes ExprEvalException.   
+    // Wrong number of argument is OK in JavaScript - "null" return becomes ExprEvalException.   
     @Test(expected=ExprEvalException.class)
     public void js_err_2() {
         NodeValue nv = eval("identity");

http://git-wip-us.apache.org/repos/asf/jena/blob/2eb211f8/jena-arq/src/test/java/org/apache/jena/sparql/function/js/TestSPARQL_JS.java
----------------------------------------------------------------------
diff --git a/jena-arq/src/test/java/org/apache/jena/sparql/function/js/TestSPARQL_JS.java b/jena-arq/src/test/java/org/apache/jena/sparql/function/js/TestSPARQL_JS.java
index 309e367..0e9e85b 100644
--- a/jena-arq/src/test/java/org/apache/jena/sparql/function/js/TestSPARQL_JS.java
+++ b/jena-arq/src/test/java/org/apache/jena/sparql/function/js/TestSPARQL_JS.java
@@ -33,8 +33,8 @@ public class TestSPARQL_JS {
     
     private static void setupJS() {
         Context cxt = ARQ.getContext();
-        cxt.set(EnvJavaScript.symJavaScriptLibFile, JS_LIB_FILE);
-        cxt.set(EnvJavaScript.symJavaScriptLib, "function inc(x) { return x+1 }");
+        cxt.set(ARQ.symJavaScriptLibFile, JS_LIB_FILE);
+        cxt.set(ARQ.symJavaScriptFunctions, "function inc(x) { return x+1 }");
         EnvJavaScript.reset();
     }
     

http://git-wip-us.apache.org/repos/asf/jena/blob/2eb211f8/jena-arq/testing/ARQ/JS/js-query-5.rq
----------------------------------------------------------------------
diff --git a/jena-arq/testing/ARQ/JS/js-query-5.rq b/jena-arq/testing/ARQ/JS/js-query-5.rq
new file mode 100644
index 0000000..0757be0
--- /dev/null
+++ b/jena-arq/testing/ARQ/JS/js-query-5.rq
@@ -0,0 +1,7 @@
+PREFIX js: <http://jena.apache.org/ARQ/jsFunction#>
+PREFIX : <http://example/>
+
+SELECT * {
+   ?s ?p ?o
+   FILTER(js:nosuchFunction())
+}

http://git-wip-us.apache.org/repos/asf/jena/blob/2eb211f8/jena-arq/testing/ARQ/JS/js-query-5.srj
----------------------------------------------------------------------
diff --git a/jena-arq/testing/ARQ/JS/js-query-5.srj b/jena-arq/testing/ARQ/JS/js-query-5.srj
new file mode 100644
index 0000000..0fe0186
--- /dev/null
+++ b/jena-arq/testing/ARQ/JS/js-query-5.srj
@@ -0,0 +1,8 @@
+{
+  "head": {
+    "vars": [ "s" , "p" , "o" ]
+  } ,
+  "results": {
+    "bindings": []
+  }
+}

http://git-wip-us.apache.org/repos/asf/jena/blob/2eb211f8/jena-arq/testing/ARQ/JS/manifest.ttl
----------------------------------------------------------------------
diff --git a/jena-arq/testing/ARQ/JS/manifest.ttl b/jena-arq/testing/ARQ/JS/manifest.ttl
index 7dd64ae..9381ebb 100644
--- a/jena-arq/testing/ARQ/JS/manifest.ttl
+++ b/jena-arq/testing/ARQ/JS/manifest.ttl
@@ -47,4 +47,10 @@
               qt:data   <data.ttl> ] ;
          mf:result  <js-query-4.srj>
       ]
+      [  mf:name    "JS Call: FILTER functions" ;
+         mf:action
+            [ qt:query  <js-query-5.rq> ;
+              qt:data   <data.ttl> ] ;
+         mf:result  <js-query-5.srj>
+      ]
     ).


[4/8] jena git commit: Note UTF-8 required

Posted by an...@apache.org.
Note UTF-8 required


Project: http://git-wip-us.apache.org/repos/asf/jena/repo
Commit: http://git-wip-us.apache.org/repos/asf/jena/commit/be160bcd
Tree: http://git-wip-us.apache.org/repos/asf/jena/tree/be160bcd
Diff: http://git-wip-us.apache.org/repos/asf/jena/diff/be160bcd

Branch: refs/heads/master
Commit: be160bcda6e4baef3c82f799e790a26e40f30739
Parents: 4d5faef
Author: Andy Seaborne <an...@apache.org>
Authored: Wed Jan 10 16:22:54 2018 +0000
Committer: Andy Seaborne <an...@apache.org>
Committed: Wed Jan 10 16:22:54 2018 +0000

----------------------------------------------------------------------
 .../java/org/apache/jena/sparql/function/js/EnvJavaScript.java    | 3 ++-
 .../org/apache/jena/sparql/function/js/FunctionJavaScript.java    | 2 +-
 2 files changed, 3 insertions(+), 2 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/jena/blob/be160bcd/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 167848f..148fde4 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
@@ -31,7 +31,8 @@ import org.apache.jena.sparql.util.Symbol;
 /** Environment for executing a JavaScript function.
  * <p>
  * Functions are loaded from the file named in context setting
- * {@link EnvJavaScript#symJavaScriptLibFile}.
+ * {@link EnvJavaScript#symJavaScriptLibFile}. 
+ * The file must be UTF-8 encoded.
  * <p>
  * Function are loaded from a string value in context setting
  * {@link EnvJavaScript#symJavaScriptLib}.

http://git-wip-us.apache.org/repos/asf/jena/blob/be160bcd/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 bd4c25e..cda6d31 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
@@ -45,7 +45,7 @@ import org.apache.jena.sparql.function.FunctionBase;
  * attempt to reconstruct the datatype of the result of the function into
  * {@code xsd:integer} and {@code xsd:double}.
  * <p>
- * Functions that return null or undefined will resutl in a {@link ExprEvalException}.
+ * Functions that return null or undefined will result in a {@link ExprEvalException}.
  * 
  * @see EnvJavaScript
  * @see NV


[8/8] jena git commit: Merge commit 'refs/pull/340/head' of github.com:apache/jena

Posted by an...@apache.org.
Merge commit 'refs/pull/340/head' of github.com:apache/jena

This closes #340.


Project: http://git-wip-us.apache.org/repos/asf/jena/repo
Commit: http://git-wip-us.apache.org/repos/asf/jena/commit/58ac14b7
Tree: http://git-wip-us.apache.org/repos/asf/jena/tree/58ac14b7
Diff: http://git-wip-us.apache.org/repos/asf/jena/diff/58ac14b7

Branch: refs/heads/master
Commit: 58ac14b7ed1414d8465ab0e12b13edae23315149
Parents: 67898ff 2eb211f
Author: Andy Seaborne <an...@apache.org>
Authored: Sat Jan 13 10:20:00 2018 +0000
Committer: Andy Seaborne <an...@apache.org>
Committed: Sat Jan 13 10:20:00 2018 +0000

----------------------------------------------------------------------
 .../main/java/org/apache/jena/query/ARQ.java    |  10 +
 .../org/apache/jena/sparql/ARQConstants.java    |   7 +
 .../org/apache/jena/sparql/expr/E_Function.java |  44 +++-
 .../jena/sparql/expr/ExprUndefFunction.java     |  32 +++
 .../jena/sparql/function/FunctionBase.java      |   2 +-
 .../jena/sparql/function/js/EnvJavaScript.java  | 138 ++++++++++++
 .../sparql/function/js/FunctionJavaScript.java  |  91 ++++++++
 .../jena/sparql/function/js/JSEngine.java       | 109 ++++++++++
 .../org/apache/jena/sparql/function/js/NV.java  | 217 +++++++++++++++++++
 .../apache/jena/sparql/function/js/RDFJS.java   |  35 +++
 .../jena/sparql/util/graph/GraphUtils.java      |   9 +-
 .../jena/riot/langsuite/FactoryTestRiot.java    |   1 -
 .../java/org/apache/jena/sparql/TC_General.java |   2 +
 .../jena/sparql/function/js/TS_FunctionJS.java  |  49 +++++
 .../function/js/TestJavaScriptFunctions.java    | 198 +++++++++++++++++
 .../apache/jena/sparql/function/js/TestNV.java  |  71 ++++++
 .../jena/sparql/function/js/TestSPARQL_JS.java  |  49 +++++
 .../function/library/TS_LibraryFunctions.java   |   2 +-
 jena-arq/testing/ARQ/JS/data.ttl                |   5 +
 jena-arq/testing/ARQ/JS/js-query-1.rq           |   4 +
 jena-arq/testing/ARQ/JS/js-query-1.srj          |  12 +
 jena-arq/testing/ARQ/JS/js-query-2.rq           |   4 +
 jena-arq/testing/ARQ/JS/js-query-2.srj          |  12 +
 jena-arq/testing/ARQ/JS/js-query-3.rq           |   4 +
 jena-arq/testing/ARQ/JS/js-query-3.srj          |  12 +
 jena-arq/testing/ARQ/JS/js-query-4.rq           |   9 +
 jena-arq/testing/ARQ/JS/js-query-4.srj          |  14 ++
 jena-arq/testing/ARQ/JS/js-query-5.rq           |   7 +
 jena-arq/testing/ARQ/JS/js-query-5.srj          |   8 +
 jena-arq/testing/ARQ/JS/manifest.ttl            |  56 +++++
 jena-arq/testing/ARQ/JS/test-library.js         |  14 ++
 .../apache/jena/fuseki/build/FusekiConfig.java  |  51 ++---
 .../apache/jena/fuseki/mgt/ActionDatasets.java  |   2 +-
 .../apache/jena/fuseki/server/FusekiSystem.java |  29 +--
 .../jena/fuseki/embedded/FusekiServer.java      |  28 ++-
 35 files changed, 1259 insertions(+), 78 deletions(-)
----------------------------------------------------------------------



[6/8] jena git commit: JENA-1464: Refactor Fuseki configuration processing.

Posted by an...@apache.org.
JENA-1464: Refactor Fuseki configuration processing.


Project: http://git-wip-us.apache.org/repos/asf/jena/repo
Commit: http://git-wip-us.apache.org/repos/asf/jena/commit/64fe8bd9
Tree: http://git-wip-us.apache.org/repos/asf/jena/tree/64fe8bd9
Diff: http://git-wip-us.apache.org/repos/asf/jena/diff/64fe8bd9

Branch: refs/heads/master
Commit: 64fe8bd9161bed88150e330d8808ed45ce35d957
Parents: 65d4295
Author: Andy Seaborne <an...@apache.org>
Authored: Fri Jan 12 15:18:43 2018 +0000
Committer: Andy Seaborne <an...@apache.org>
Committed: Fri Jan 12 16:01:38 2018 +0000

----------------------------------------------------------------------
 .../jena/sparql/util/graph/GraphUtils.java      |  9 ++--
 .../apache/jena/fuseki/build/FusekiConfig.java  | 51 ++++++--------------
 .../apache/jena/fuseki/mgt/ActionDatasets.java  |  2 +-
 .../apache/jena/fuseki/server/FusekiSystem.java | 29 +++--------
 .../jena/fuseki/embedded/FusekiServer.java      | 28 ++++++++---
 5 files changed, 51 insertions(+), 68 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/jena/blob/64fe8bd9/jena-arq/src/main/java/org/apache/jena/sparql/util/graph/GraphUtils.java
----------------------------------------------------------------------
diff --git a/jena-arq/src/main/java/org/apache/jena/sparql/util/graph/GraphUtils.java b/jena-arq/src/main/java/org/apache/jena/sparql/util/graph/GraphUtils.java
index d82dfeb..ce0243e 100644
--- a/jena-arq/src/main/java/org/apache/jena/sparql/util/graph/GraphUtils.java
+++ b/jena-arq/src/main/java/org/apache/jena/sparql/util/graph/GraphUtils.java
@@ -173,13 +173,16 @@ public class GraphUtils {
             return null ;
         return s.getResource() ;
     }
+    
+    public static List<Resource> listResourcesByType(Model model, Resource type) {
+        return Iter.toList(model.listSubjectsWithProperty(RDF.type, type)) ;
+    }
 
     public static Resource getResourceByType(Model model, Resource type) {
-        // See also
-        StmtIterator sIter = model.listStatements(null, RDF.type, type) ;
+        ResIterator sIter = model.listSubjectsWithProperty(RDF.type, type) ;
         if ( !sIter.hasNext() )
             return null ;
-        Resource r = sIter.nextStatement().getSubject() ;
+        Resource r = sIter.next();
         if ( sIter.hasNext() )
             throw new TypeNotUniqueException(r) ;
         return r ;

http://git-wip-us.apache.org/repos/asf/jena/blob/64fe8bd9/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/build/FusekiConfig.java
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/build/FusekiConfig.java b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/build/FusekiConfig.java
index 6e64632..614b721 100644
--- a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/build/FusekiConfig.java
+++ b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/build/FusekiConfig.java
@@ -33,7 +33,6 @@ import java.util.Collections ;
 import java.util.List ;
 
 import org.apache.jena.assembler.JA ;
-import org.apache.jena.atlas.iterator.Iter ;
 import org.apache.jena.atlas.lib.IRILib ;
 import org.apache.jena.atlas.lib.StrUtils ;
 import org.apache.jena.fuseki.Fuseki ;
@@ -47,7 +46,7 @@ import org.apache.jena.query.ResultSet ;
 import org.apache.jena.rdf.model.* ;
 import org.apache.jena.riot.Lang;
 import org.apache.jena.sparql.core.assembler.AssemblerUtils ;
-import org.apache.jena.vocabulary.RDF ;
+import org.apache.jena.sparql.util.graph.GraphUtils;
 import org.slf4j.Logger ;
 
 public class FusekiConfig {
@@ -55,19 +54,13 @@ public class FusekiConfig {
     
     private static Logger log = Fuseki.configLog ;
     
-    /** Has side effects in server setup */
-    public static List<DataAccessPoint> readServerConfigFile(String filename) {
-        // Old-style config file.
-        Model model = readAssemblerFile(filename) ;
-        if ( model.size() == 0 )
-            return Collections.emptyList() ;
-        server(model) ;
-        return servicesAndDatasets(model) ;
-    }
-
-    private static void server(Model model) {
+    /** Process the server section, if any, of a configuration file.
+     * This includes setting global context and ja:loadClass.
+     * It does not include services - see {@link #servicesAndDatasets}
+     */ 
+    public static void processServerConfig(Model model) {
         // Find one server.
-        List<Resource> servers = getByType(FusekiVocab.tServer, model) ;
+        List<Resource> servers = GraphUtils.listResourcesByType(model, FusekiVocab.tServer) ;
         if ( servers.size() == 0 )
             return ; 
         if ( servers.size() > 1 )
@@ -106,9 +99,13 @@ public class FusekiConfig {
         }
     }
     
-    private static List<DataAccessPoint> servicesAndDatasets(Model model) {
+    /** Find and process datasets and services in a configuration file.
+     * This can be a Fuseki server configuration file or a services-only configuration file.
+     * It looks {@code fuseki:services ( .... )} then, if not found, all {@code rtdf:type fuseki:services}. 
+     */
+    public static List<DataAccessPoint> servicesAndDatasets(Model model) {
         // Old style configuration file : server to services.
-        DatasetDescriptionRegistry dsDescMap = FusekiSystem.registryForBuild() ;
+        DatasetDescriptionRegistry dsDescMap = new DatasetDescriptionRegistry();
         // ---- Services
         ResultSet rs = FusekiLib.query("SELECT * { ?s fu:services [ list:member ?service ] }", model) ;
         List<DataAccessPoint> accessPoints = new ArrayList<>() ;
@@ -124,7 +121,6 @@ public class FusekiConfig {
             DataAccessPoint acc = FusekiBuilder.buildDataAccessPoint(svc, dsDescMap) ;
             accessPoints.add(acc) ;
         }
-        
         return accessPoints ;
     }
     
@@ -147,12 +143,6 @@ public class FusekiConfig {
         return AssemblerUtils.readAssemblerFile(filename) ;
     }
     
-    // XXX Move to a library
-    private static List<Resource> getByType(Resource type, Model m) {
-        ResIterator rIter = m.listSubjectsWithProperty(RDF.type, type) ;
-        return Iter.toList(rIter) ;
-    }
-    
     // ---- Directory of assemblers
     
     /** Read service descriptions in the given directory */ 
@@ -177,7 +167,7 @@ public class FusekiConfig {
         List<DataAccessPoint> dataServiceRef = new ArrayList<>() ;
         try (DirectoryStream<Path> stream = Files.newDirectoryStream(pDir, filter)) {
             for ( Path p : stream ) {
-                DatasetDescriptionRegistry dsDescMap = FusekiSystem.registryForBuild() ;
+                DatasetDescriptionRegistry dsDescMap = new DatasetDescriptionRegistry() ;
                 String fn = IRILib.filenameToIRI(p.toString()) ;
                 log.info("Load configuration: "+fn);
                 Model m = readAssemblerFile(fn) ;
@@ -189,21 +179,12 @@ public class FusekiConfig {
         return dataServiceRef ;
     }
 
-    /** Read and process one file */ 
-    public static  List<DataAccessPoint> readConfigurationFile(String fn) {
-        List<DataAccessPoint> acc = new ArrayList<>() ;
-        Model m = readAssemblerFile(fn) ;
-        DatasetDescriptionRegistry dsDescMap = new DatasetDescriptionRegistry() ;
-        readConfiguration(m, dsDescMap, acc) ;
-        return acc ;
-    }
-    
     /** Read a configuration in a model.
      * Allow dataset descriptions to be carried over from another place.
      * Add to a list. 
      */
     private static void readConfiguration(Model m, DatasetDescriptionRegistry dsDescMap, List<DataAccessPoint> dataServiceRef) {
-        List<Resource> services = getByType(FusekiVocab.fusekiService, m) ; 
+        List<Resource> services = GraphUtils.listResourcesByType(m, FusekiVocab.fusekiService) ; 
 
         if ( services.size() == 0 ) {
             log.error("No services found") ;
@@ -219,7 +200,7 @@ public class FusekiConfig {
     // ---- System database
     /** Read the system database */
     public static List<DataAccessPoint> readSystemDatabase(Dataset ds) {
-        DatasetDescriptionRegistry dsDescMap = FusekiSystem.registryForBuild() ;
+        DatasetDescriptionRegistry dsDescMap = new DatasetDescriptionRegistry() ;
         String qs = StrUtils.strjoinNL
             (SystemState.PREFIXES ,
              "SELECT * {" ,

http://git-wip-us.apache.org/repos/asf/jena/blob/64fe8bd9/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/mgt/ActionDatasets.java
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/mgt/ActionDatasets.java b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/mgt/ActionDatasets.java
index d59ea66..85e84af 100644
--- a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/mgt/ActionDatasets.java
+++ b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/mgt/ActionDatasets.java
@@ -111,7 +111,7 @@ public class ActionDatasets extends ActionContainerItem {
     @Override
     protected JsonValue execPostContainer(HttpAction action) {
         JenaUUID uuid = JenaUUID.generate() ;
-        DatasetDescriptionRegistry registry = FusekiSystem.registryForBuild() ;
+        DatasetDescriptionRegistry registry = new DatasetDescriptionRegistry() ;
         
         ContentType ct = FusekiLib.getContentType(action) ;
         

http://git-wip-us.apache.org/repos/asf/jena/blob/64fe8bd9/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/server/FusekiSystem.java
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/server/FusekiSystem.java b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/server/FusekiSystem.java
index 78f6490..105f20a 100644
--- a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/server/FusekiSystem.java
+++ b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/server/FusekiSystem.java
@@ -45,6 +45,7 @@ import org.apache.jena.riot.Lang ;
 import org.apache.jena.riot.RDFDataMgr ;
 import org.apache.jena.riot.RDFLanguages ;
 import org.apache.jena.sparql.core.DatasetGraph ;
+import org.apache.jena.sparql.core.assembler.AssemblerUtils;
 import org.apache.jena.tdb.sys.Names ;
 
 public class FusekiSystem
@@ -257,12 +258,17 @@ public class FusekiSystem
             return Collections.emptyList(); 
         }
         Fuseki.configLog.info("Configuration file: " + configFilename) ;
-        return FusekiConfig.readServerConfigFile(configFilename) ;
+        //return FusekiConfig.readServerConfigFile(configFilename);
+        Model model = AssemblerUtils.readAssemblerFile(configFilename) ;
+        if ( model.size() == 0 )
+            return Collections.emptyList() ;
+        FusekiConfig.processServerConfig(model);
+        return FusekiConfig.servicesAndDatasets(model) ;
     }
     
     private static DataAccessPoint configFromTemplate(String templateFile, String datasetPath, 
                                                       boolean allowUpdate, Map<String, String> params) {
-        DatasetDescriptionRegistry registry = FusekiSystem.registryForBuild() ; 
+        DatasetDescriptionRegistry registry = new DatasetDescriptionRegistry() ; 
         // ---- Setup
         if ( params == null ) {
             params = new HashMap<>() ;
@@ -404,25 +410,6 @@ public class FusekiSystem
     }
 
     /**
-     * <ul>
-     * <li>GLOBAL: sharing across all descriptions
-     * <li>FILE: sharing within files but not across files.
-     * </ul>
-     */
-    enum DatasetDescriptionScope { GLOBAL, FILE }
-    private static DatasetDescriptionRegistry globalDatasets = new DatasetDescriptionRegistry() ;
-    private static DatasetDescriptionScope policyDatasetDescriptionScope = DatasetDescriptionScope.FILE ;
-    
-    /** Call this once per configuration file. */
-    public static DatasetDescriptionRegistry registryForBuild() {
-        switch (policyDatasetDescriptionScope) {
-            case FILE :     return new DatasetDescriptionRegistry() ;
-            case GLOBAL :   return globalDatasets ;
-            default:        throw new InternalErrorException() ;
-        }
-    }
-    
-    /**
      * Dataset set name to configuration file name. Return a configuration file name -
      * existing one or ".ttl" form if new
      */

http://git-wip-us.apache.org/repos/asf/jena/blob/64fe8bd9/jena-fuseki2/jena-fuseki-embedded/src/main/java/org/apache/jena/fuseki/embedded/FusekiServer.java
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-embedded/src/main/java/org/apache/jena/fuseki/embedded/FusekiServer.java b/jena-fuseki2/jena-fuseki-embedded/src/main/java/org/apache/jena/fuseki/embedded/FusekiServer.java
index 5bc9102..c88509e 100644
--- a/jena-fuseki2/jena-fuseki-embedded/src/main/java/org/apache/jena/fuseki/embedded/FusekiServer.java
+++ b/jena-fuseki2/jena-fuseki-embedded/src/main/java/org/apache/jena/fuseki/embedded/FusekiServer.java
@@ -18,9 +18,12 @@
 
 package org.apache.jena.fuseki.embedded;
 
-import java.util.*;
 import static java.util.Objects.requireNonNull;
 
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+
 import javax.servlet.ServletContext;
 import javax.servlet.http.HttpServlet;
 
@@ -32,16 +35,17 @@ import org.apache.jena.fuseki.build.FusekiBuilder;
 import org.apache.jena.fuseki.build.FusekiConfig;
 import org.apache.jena.fuseki.jetty.FusekiErrorHandler1;
 import org.apache.jena.fuseki.mgt.ActionStats;
-import org.apache.jena.fuseki.server.DataAccessPoint;
-import org.apache.jena.fuseki.server.DataAccessPointRegistry;
-import org.apache.jena.fuseki.server.DataService;
-import org.apache.jena.fuseki.server.Operation;
-import org.apache.jena.fuseki.servlets.ServiceDispatchRegistry;
+import org.apache.jena.fuseki.server.*;
 import org.apache.jena.fuseki.servlets.ActionService;
 import org.apache.jena.fuseki.servlets.FusekiFilter;
+import org.apache.jena.fuseki.servlets.ServiceDispatchRegistry;
 import org.apache.jena.query.Dataset;
+import org.apache.jena.rdf.model.Model;
+import org.apache.jena.rdf.model.Resource;
 import org.apache.jena.riot.WebContent;
 import org.apache.jena.sparql.core.DatasetGraph;
+import org.apache.jena.sparql.core.assembler.AssemblerUtils;
+import org.apache.jena.sparql.util.graph.GraphUtils;
 import org.eclipse.jetty.http.MimeTypes;
 import org.eclipse.jetty.security.SecurityHandler;
 import org.eclipse.jetty.server.HttpConnectionFactory;
@@ -297,9 +301,17 @@ public class FusekiServer {
          */
         public Builder parseConfigFile(String filename) {
             requireNonNull(filename, "filename");
-            List<DataAccessPoint> x = FusekiConfig.readConfigurationFile(filename);
+            Model model = AssemblerUtils.readAssemblerFile(filename);
+            
+            // Process server context
+            Resource server = GraphUtils.getResourceByType(model, FusekiVocab.tServer);
+            if ( server != null )
+                AssemblerUtils.setContext(server, Fuseki.getContext()) ;
+
+            // Process services, whether via server ja:services or, if absent, by finding by type. 
+            List<DataAccessPoint> x = FusekiConfig.servicesAndDatasets(model);
             // Unbundle so that they accumulate.
-            x.forEach(dap-> add(dap.getName(), dap.getDataService()));
+            x.forEach(dap->add(dap.getName(), dap.getDataService()));
             return this;
         }