Skip to content

Commit

Permalink
System property to enable scripting
Browse files Browse the repository at this point in the history
  • Loading branch information
afs committed Mar 23, 2023
1 parent 7e91271 commit 795233e
Show file tree
Hide file tree
Showing 7 changed files with 168 additions and 16 deletions.
12 changes: 10 additions & 2 deletions jena-arq/src/main/java/org/apache/jena/query/ARQ.java
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
import org.apache.jena.sparql.exec.http.QuerySendMode;
import org.apache.jena.sparql.expr.aggregate.AggregateRegistry;
import org.apache.jena.sparql.function.FunctionRegistry;
import org.apache.jena.sparql.function.scripting.ScriptLangSymbols;
import org.apache.jena.sparql.mgt.ARQMgt;
import org.apache.jena.sparql.mgt.Explain;
import org.apache.jena.sparql.mgt.Explain.InfoLevel;
Expand Down Expand Up @@ -522,15 +523,22 @@ public static void enableOptimizer(Context context, boolean state) {
*/
public static final Symbol extensionValueTypes = SystemARQ.allocSymbol("extensionValueTypesExpr");

/**
* Java system property to enable JavaScript functions
*/
public static final String systemPropertyScripting = "jena:scripting";

/**
* Context symbol for JavaScript functions as a string value which is evaluated.
* {@code arq:js-functions}.
*/
public static Symbol symJavaScriptFunctions = SystemARQ.allocSymbol("js-functions");
public static Symbol symJavaScriptFunctions = ScriptLangSymbols.scriptFunctions("js");

/**
* Context symbol for JavaScript library of functions defined in a file.
* {@code arq:js-library}.
*/
public static Symbol symJavaScriptLibFile = SystemARQ.allocSymbol("js-library");
public static Symbol symJavaScriptLibFile = ScriptLangSymbols.scriptLibrary("js");

/**
* Generate the ToList operation in the algebra (as ARQ is stream based, ToList is a non-op).
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,10 +40,18 @@
import org.apache.jena.sparql.function.FunctionBase;

public class ScriptFunction extends FunctionBase {
static {

static {
System.setProperty("polyglot.engine.WarnInterpreterOnly", "false");
}

private static void checkScriptingEnabled() {
String x = System.getProperty(ARQ.systemPropertyScripting);
boolean scriptingEnabled = "true".equals(x);
if ( !scriptingEnabled )
throw new ExprException("Scripting not enabled");
}

private static final ScriptEngineManager scriptEngineManager = new ScriptEngineManager();

// The URI is structured: http://jena.apache.org/ARQ/jsFunction#fn
Expand Down Expand Up @@ -78,6 +86,7 @@ public static boolean isScriptFunction(String uri) {

@Override
public void checkBuild(String uri, ExprList args) {
checkScriptingEnabled();
if (!isScriptFunction(uri))
throw new ExprException("Invalid URI: " + uri);
String localPart = uri.substring(ARQ_NS.length());
Expand All @@ -99,6 +108,7 @@ public void checkBuild(String uri, ExprList args) {

@Override
public NodeValue exec(List<NodeValue> args) {
checkScriptingEnabled();
Invocable engine = getEngine();

try {
Expand Down Expand Up @@ -149,7 +159,7 @@ private Invocable createEngine() {
if (!(engine instanceof Invocable))
throw new ExprException("Script engine " + engine.getFactory().getEngineName() + " doesn't implement Invocable");

String functionLibFile = ARQ.getContext().getAsString(LanguageSymbols.scriptLibrary(lang));
String functionLibFile = ARQ.getContext().getAsString(ScriptLangSymbols.scriptLibrary(lang));
if (functionLibFile != null) {
try (Reader reader = Files.newBufferedReader(Path.of(functionLibFile), StandardCharsets.UTF_8)) {
engine.eval(reader);
Expand All @@ -162,7 +172,7 @@ private Invocable createEngine() {
}
}

String functions = ARQ.getContext().getAsString(LanguageSymbols.scriptFunctions(lang));
String functions = ARQ.getContext().getAsString(ScriptLangSymbols.scriptFunctions(lang));
if (functions != null) {
try {
engine.eval(functions);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
import org.apache.jena.sparql.SystemARQ;
import org.apache.jena.sparql.util.Symbol;

public class LanguageSymbols {
public class ScriptLangSymbols {
public static Symbol scriptLibrary(String lang) {
return SystemARQ.allocSymbol(lang + "-library");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@
@Suite.SuiteClasses( {
TestNV.class,
TestScriptFunction.class,
// Between two classes with scripting enabled.
TestNoScripting.class,
TestSPARQL_Scripting.class
})
public class TS_FunctionScripting {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
/*
* 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.scripting;

import static org.junit.Assert.assertTrue;

import java.util.Arrays;
import java.util.Collection;

import org.apache.jena.query.ARQ;
import org.apache.jena.sparql.expr.ExprException;
import org.apache.jena.sparql.expr.NodeValue;
import org.apache.jena.sparql.sse.SSE;
import org.apache.jena.sparql.util.Context;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;

@RunWith(Parameterized.class)
public class TestNoScripting {
private static Context ctx = ARQ.getContext();

private String language;
private String library;
private String functions;

// @BeforeClass public static void enableScripting() {
// System.setProperty(ScriptFunction.systemPropertyScripting, "true");
// }
//
// @AfterClass public static void disbleScripting() {
// System.clearProperty(ScriptFunction.systemPropertyScripting);
// }

@Parameterized.Parameters
public static Collection<Object[]> data() {
return Arrays.asList(new Object[][] {
{ "js", "testing/ARQ/Scripting/test-library.js",
"function toCamelCase(str) { return str.split(' ').map(cc).join('');}\n"
+ "function ucFirst(word) { return word.charAt(0).toUpperCase() + word.slice(1).toLowerCase();}\n"
+ "function lcFirst(word) { return word.toLowerCase(); }\n"
+ "function cc(word,index) { return (index == 0) ? lcFirst(word) : ucFirst(word); }\n" }
, {"python", "testing/ARQ/Scripting/test-library.py",
"def toCamelCase(str):\n" +
" return ''.join([cc(word, index) for index, word in enumerate(str.split(' '))])\n" +
"def ucFirst(word):\n" +
" return word[0].upper() + word[1:].lower()\n" +
"def lcFirst(word):\n" +
" return word.lower()\n" +
"def cc(word,index):\n" +
" if index == 0:\n" +
" return lcFirst(word)\n" +
" return ucFirst(word)\n" }
});
}


public TestNoScripting(String language, String library, String functions) {
this.language = language;
this.library = library;
this.functions = functions;
}

@Before
public void setup() {
ctx.set(ScriptLangSymbols.scriptLibrary(language), library);
ctx.set(ScriptLangSymbols.scriptFunctions(language), functions);
}

@After
public void teardown() {
ctx.unset(ScriptLangSymbols.scriptFunctions(language));
ctx.unset(ScriptLangSymbols.scriptLibrary(language));

ScriptFunction.clearEngineCache();
}

@Test(expected = ExprException.class)
public void script_dt_boolean() {
// Scripting not enabled.
NodeValue nv = eval("rtnBoolean");
assertTrue(nv.isBoolean());
}

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]);
}
ScriptFunction f = new ScriptFunction();
f.build( "http://jena.apache.org/ARQ/" + language + "Function#" + fn, null);
return f.exec(Arrays.asList(nvs));
}

private static NodeValue nv(String str) {
return NodeValue.makeNode(SSE.parseNode(str));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,18 +32,28 @@
@Manifests({
"testing/ARQ/Scripting/manifest.ttl"
})
// TODO: Add more languages



public class TestSPARQL_Scripting {
static final String JS_LIB_FILE = "testing/ARQ/Scripting/test-library.js";


@BeforeClass public static void enableScripting() {
System.setProperty(ARQ.systemPropertyScripting, "true");
}

@AfterClass public static void disbleScripting() {
System.clearProperty(ARQ.systemPropertyScripting);
}

@BeforeClass
public static void setupJS() {
Context cxt = ARQ.getContext();
cxt.set(ARQ.symJavaScriptLibFile, JS_LIB_FILE);
cxt.set(ARQ.symJavaScriptFunctions, "function inc(x) { return x+1 }");
ScriptFunction.clearEngineCache();
}

@AfterClass
public static void unsetupJS() {
Context cxt = ARQ.getContext();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,7 @@
import org.apache.jena.sparql.expr.NodeValue;
import org.apache.jena.sparql.sse.SSE;
import org.apache.jena.sparql.util.Context;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.*;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;

Expand All @@ -43,6 +41,14 @@ public class TestScriptFunction {
private String library;
private String functions;

@BeforeClass public static void enableScripting() {
System.setProperty(ARQ.systemPropertyScripting, "true");
}

@AfterClass public static void disbleScripting() {
System.clearProperty(ARQ.systemPropertyScripting);
}

@Parameterized.Parameters
public static Collection<Object[]> data() {
return Arrays.asList(new Object[][] {
Expand Down Expand Up @@ -74,14 +80,14 @@ public TestScriptFunction(String language, String library, String functions) {

@Before
public void setup() {
ctx.set(LanguageSymbols.scriptLibrary(language), library);
ctx.set(LanguageSymbols.scriptFunctions(language), functions);
ctx.set(ScriptLangSymbols.scriptLibrary(language), library);
ctx.set(ScriptLangSymbols.scriptFunctions(language), functions);
}

@After
public void teardown() {
ctx.unset(LanguageSymbols.scriptFunctions(language));
ctx.unset(LanguageSymbols.scriptLibrary(language));
ctx.unset(ScriptLangSymbols.scriptFunctions(language));
ctx.unset(ScriptLangSymbols.scriptLibrary(language));

ScriptFunction.clearEngineCache();
}
Expand Down

0 comments on commit 795233e

Please sign in to comment.