diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 607426145..724584f0f 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -9,7 +9,7 @@ jobs:
runs-on: ${{ matrix.os }}
strategy:
matrix:
- os: [ 'ubuntu-latest', 'macos-latest', 'windows-latest' ]
+ os: [ 'ubuntu-latest', 'macos-12', 'windows-latest' ]
java: [ '8', '11', '17', '21' ]
fail-fast: false
steps:
diff --git a/commons/rest/api-descriptor/pom.xml b/commons/rest/api-descriptor/pom.xml
index e92903518..b3d26e8cb 100644
--- a/commons/rest/api-descriptor/pom.xml
+++ b/commons/rest/api-descriptor/pom.xml
@@ -28,7 +28,7 @@
bundle
- 1.7.12
+
diff --git a/pom.xml b/pom.xml
index b5afef99f..642260087 100644
--- a/pom.xml
+++ b/pom.xml
@@ -190,6 +190,7 @@
2.14.3
1.7.36
1.6.11
+ 1.7.14
diff --git a/script/common/src/main/java/org/forgerock/script/exception/ScriptCompilationException.java b/script/common/src/main/java/org/forgerock/script/exception/ScriptCompilationException.java
index bf6c17c1d..c131d772d 100644
--- a/script/common/src/main/java/org/forgerock/script/exception/ScriptCompilationException.java
+++ b/script/common/src/main/java/org/forgerock/script/exception/ScriptCompilationException.java
@@ -1,8 +1,4 @@
/*
- * DO NOT REMOVE COPYRIGHT NOTICES OR THIS HEADER.
- *
- * Copyright (c) 2015 ForgeRock AS. All rights reserved.
- *
* The contents of this file are subject to the terms
* of the Common Development and Distribution License
* (the License). You may not use this file except in
@@ -20,6 +16,8 @@
* with the fields enclosed by brackets [] replaced by
* your own identifying information:
* "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * Copyright 2015-2016 ForgeRock AS.
*/
package org.forgerock.script.exception;
@@ -35,6 +33,13 @@ public class ScriptCompilationException extends ScriptException {
/** Serializable class a version number. */
static final long serialVersionUID = 1L;
+ /**
+ * Constructs a new exception with the specified detail message.
+ */
+ public ScriptCompilationException(String message) {
+ super(message);
+ }
+
/**
* Constructs a new exception with the specified detail message and cause.
*/
@@ -50,4 +55,4 @@ public ScriptCompilationException(String message, Exception cause, String fileNa
super(message, fileName, lineNumber, columnNumber);
initCause(cause);
}
-}
+}
\ No newline at end of file
diff --git a/script/javascript/pom.xml b/script/javascript/pom.xml
index 0185b9817..2743abe2d 100644
--- a/script/javascript/pom.xml
+++ b/script/javascript/pom.xml
@@ -67,7 +67,7 @@
org.apache.servicemix.bundles
org.apache.servicemix.bundles.rhino
- 1.7R4_1
+ ${rhino.version}_2
org.eclipse.wst.jsdt.debug
@@ -130,12 +130,12 @@
test
-
+
org.slf4j
slf4j-simple
diff --git a/script/javascript/src/main/java/org/forgerock/script/javascript/RhinoScript.java b/script/javascript/src/main/java/org/forgerock/script/javascript/RhinoScript.java
index c14ad0a74..3411a1f68 100644
--- a/script/javascript/src/main/java/org/forgerock/script/javascript/RhinoScript.java
+++ b/script/javascript/src/main/java/org/forgerock/script/javascript/RhinoScript.java
@@ -1,8 +1,4 @@
/*
- * DO NOT REMOVE COPYRIGHT NOTICES OR THIS HEADER.
- *
- * Copyright (c) 2012-2014 ForgeRock AS. All rights reserved.
- *
* The contents of this file are subject to the terms
* of the Common Development and Distribution License
* (the License). You may not use this file except in
@@ -20,6 +16,8 @@
* with the fields enclosed by brackets [] replaced by
* your own identifying information:
* "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * Copyright 2012-2016 ForgeRock AS.
*/
package org.forgerock.script.javascript;
@@ -97,6 +95,9 @@ public class RhinoScript implements CompiledScript {
/** The CommonJS module builder for Require instances. */
private final RequireBuilder requireBuilder;
+ /** Indicates if this script instance should use the shared scope. */
+ private final boolean sharedScope;
+
public static final Global GLOBAL = new Global();
static {
@@ -112,62 +113,43 @@ public void quit(Context cx, int exitCode) {
}
}
- /** Indicates if this script instance should use the shared scope. */
- private final boolean sharedScope;
-
/**
- * Compiles the JavaScript source code into an executable script. If
- * {@code useSharedScope} is {@code true}, then a sealed shared scope
- * containing standard JavaScript objects (Object, String, Number, Date,
- * etc.) will be used for script execution; otherwise a new unsealed scope
- * will be allocated for each execution.
*
- *
- * @param compiledScript
- * the source code of the JavaScript script.
- * @param sharedScope
- * if {@code true}, uses the shared scope, otherwise allocates
- * new scope.
- * @throws ScriptException
- * if there was an exception encountered while compiling the
- * script.
+ * @param name the name of the script
+ * @param compiledScript the to-be-executed compiled script
+ * @param engine the parent script engine
+ * @param requireBuilder The CommonJS module builder for Require instances.
+ * @param sharedScope Indicates if this script instance should use the shared scope. If {@code useSharedScope} is
+ * {@code true}, then a sealed shared scope containing standard JavaScript objects (Object, String, Number, Date,
+ * etc.) will be used for script execution; otherwise a new unsealed scope will be allocated for each execution.
*/
public RhinoScript(String name, Script compiledScript, final RhinoScriptEngine engine, RequireBuilder requireBuilder,
- boolean sharedScope) throws ScriptException {
+ boolean sharedScope) {
this.scriptName = name;
this.sharedScope = sharedScope;
this.engine = engine;
this.requireBuilder = requireBuilder;
Context cx = Context.enter();
+ cx.setLanguageVersion(Context.VERSION_ES6);
try {
scriptScope = getScriptScope(cx);
script = compiledScript;
- // script = cx.compileString(source, name, 1, null);
- } catch (RhinoException re) {
- throw new ScriptException(re.getMessage());
} finally {
Context.exit();
}
}
/**
- * TEMPORARY
+ *
+ * @param name the name of the script
+ * @param engine the parent script engine
+ * @param requireBuilder The CommonJS module builder for Require instances.
+ * @param sharedScope Indicates if this script instance should use the shared scope. If {@code useSharedScope} is
+ * {@code true}, then a sealed shared scope containing standard JavaScript objects (Object, String, Number, Date,
+ * etc.) will be used for script execution; otherwise a new unsealed scope will be allocated for each execution.
*/
- public RhinoScript(String name, final RhinoScriptEngine engine, RequireBuilder requireBuilder, boolean sharedScope)
- throws ScriptException {
- this.scriptName = name;
- this.sharedScope = sharedScope;
- this.engine = engine;
- this.requireBuilder = requireBuilder;
- Context cx = Context.enter();
- try {
- scriptScope = getScriptScope(cx);
- script = null;// cx.compileReader(reader, name, 1, null);
- } catch (RhinoException re) {
- throw new ScriptException(re);
- } finally {
- Context.exit();
- }
+ public RhinoScript(String name, final RhinoScriptEngine engine, RequireBuilder requireBuilder, boolean sharedScope) {
+ this(name, null, engine, requireBuilder, sharedScope);
}
/**
@@ -184,7 +166,6 @@ private ScriptableObject getStandardObjects(final Context context) {
if (!sharedScope) {
// somewhat expensive
ScriptableObject scope = context.initStandardObjects();
- installRequire(context, scope);
return scope;
}
// lazy initialization race condition is harmless
@@ -203,7 +184,6 @@ private ScriptableObject getStandardObjects(final Context context) {
}
}
addLoggerProperty(scope);
- installRequire(context, scope);
// seal the whole scope (not just standard objects)
scope.sealObject();
topSharedScope = scope;
@@ -211,12 +191,6 @@ private ScriptableObject getStandardObjects(final Context context) {
return topSharedScope;
}
- // install require function per unofficial CommonJS author documentation
- // https://groups.google.com/d/msg/mozilla-rhino/HCMh_lAKiI4/P1MA3sFsNKQJ
- private void installRequire(final Context context, final ScriptableObject scope) {
- requireBuilder.createRequire(context, scope).install(scope);
- }
-
/**
* Get the scope scriptable re-used for this script Holds common
* functionality such as the logger
@@ -260,6 +234,7 @@ public Object eval(final org.forgerock.services.context.Context ctx, Bindings re
throws ScriptException {
Context context = Context.enter();
+ context.setLanguageVersion(Context.VERSION_ES6);
try {
Scriptable outer = context.newObject(getStandardObjects(context));
@@ -303,15 +278,19 @@ public Object eval(final org.forgerock.services.context.Context ctx, Bindings re
}
outer.setPrototype(scriptScope); // script level context and
- // standard objects included with
- // every box
+ // standard objects included with
+ // every box
outer.setParentScope(null);
Scriptable inner = context.newObject(outer); // inner transient
- // scope for new
- // properties
+ // scope for new
+ // properties
inner.setPrototype(outer);
inner.setParentScope(null);
+ // install require function per unofficial CommonJS author documentation
+ // https://groups.google.com/d/msg/mozilla-rhino/HCMh_lAKiI4/P1MA3sFsNKQJ
+ requireBuilder.createRequire(context, inner).install(inner);
+
final Script scriptInstance = null != script ? script : engine.createScript(scriptName);
Object result = Converter.convert(scriptInstance.exec(context, inner));
return result; // Context.jsToJava(result, Object.class);
@@ -319,27 +298,24 @@ public Object eval(final org.forgerock.services.context.Context ctx, Bindings re
throw e;
} catch (WrappedException e) {
if (e.getWrappedException() instanceof ResourceException) {
- throw new ScriptThrownException(e.getMessage(), e.sourceName(), e.lineNumber(), e.columnNumber(),
- ((ResourceException) e.getWrappedException()).toJsonValue().getObject());
+ throw getScriptExecutionGenerator().newScriptThrownException(e,
+ ((ResourceException)e.getWrappedException()).toJsonValue().getObject());
} else {
- ScriptException exception =
- new ScriptThrownException(e.getMessage(), e.sourceName(), e.lineNumber(), e.columnNumber(),
- e.getWrappedException());
+ ScriptException exception = getScriptExecutionGenerator().newScriptThrownException(e,
+ e.getWrappedException());
exception.initCause(e.getWrappedException());
throw exception;
}
} catch (JavaScriptException e) {
logger.debug("Failed to evaluate {} script.", scriptName, e);
- ScriptThrownException exception =
- new ScriptThrownException(e.getMessage(), e.sourceName(), e.lineNumber(), e.columnNumber(),
- Converter.convert(e.getValue()));
+ ScriptThrownException exception = getScriptExecutionGenerator().newScriptThrownException(e,
+ Converter.convert(e.getValue()));
exception.initCause(e);
throw exception;
} catch (RhinoException e) {
logger.debug("Failed to evaluate {} script.", scriptName, e);
// some other runtime exception encountered
- final ScriptException exception =
- new ScriptException(e.getMessage(), e.sourceName(), e.lineNumber(), e.columnNumber());
+ final ScriptException exception = getScriptExecutionGenerator().newScriptException(e);
exception.initCause(e);
throw exception;
} catch (Exception e) {
@@ -351,6 +327,10 @@ public Object eval(final org.forgerock.services.context.Context ctx, Bindings re
}
}
+ private RhinoScriptEngine.ScriptExceptionGenerator getScriptExecutionGenerator() {
+ return engine.getScriptExceptionGenerator();
+ }
+
private static class InnerClassLoader extends SecureClassLoader {
public InnerClassLoader(ClassLoader parent) {
@@ -372,4 +352,4 @@ public Class> loadClass(String name) throws ClassNotFoundException {
}
}
-}
+}
\ No newline at end of file
diff --git a/script/javascript/src/main/java/org/forgerock/script/javascript/RhinoScriptEngine.java b/script/javascript/src/main/java/org/forgerock/script/javascript/RhinoScriptEngine.java
index fea928d88..fdef9e780 100644
--- a/script/javascript/src/main/java/org/forgerock/script/javascript/RhinoScriptEngine.java
+++ b/script/javascript/src/main/java/org/forgerock/script/javascript/RhinoScriptEngine.java
@@ -1,8 +1,4 @@
/*
- * DO NOT REMOVE COPYRIGHT NOTICES OR THIS HEADER.
- *
- * Copyright (c) 2012-2014 ForgeRock AS. All rights reserved.
- *
* The contents of this file are subject to the terms
* of the Common Development and Distribution License
* (the License). You may not use this file except in
@@ -20,15 +16,19 @@
* with the fields enclosed by brackets [] replaced by
* your own identifying information:
* "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * Copyright 2012-2016 ForgeRock AS.
*/
package org.forgerock.script.javascript;
+import org.forgerock.json.JsonValue;
import org.forgerock.json.resource.ResourceException;
import org.forgerock.script.engine.AbstractScriptEngine;
import org.forgerock.script.engine.CompilationHandler;
import org.forgerock.script.engine.ScriptEngineFactory;
import org.forgerock.script.exception.ScriptCompilationException;
+import org.forgerock.script.exception.ScriptThrownException;
import org.forgerock.script.scope.OperationParameter;
import org.forgerock.script.source.ScriptSource;
import org.forgerock.script.source.SourceContainer;
@@ -57,6 +57,7 @@
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
+import java.util.regex.Pattern;
/**
* A NAME does ...
@@ -64,6 +65,12 @@
* @author Laszlo Hordos
*/
public class RhinoScriptEngine extends AbstractScriptEngine {
+ public static final String CONFIG_DEBUG_PROPERTY = "javascript.debug";
+ public static final String CONFIG_RECOMPILE_MINIMUM_INTERVAL_PROPERTY =
+ "javascript.recompile.minimumInterval";
+ public static final String CONFIG_EXCEPTION_DEBUG_INFO = "javascript.exception.debug.info";
+
+ static final Pattern RHINO_EXCEPTION_FILE_INFO_PATTERN = Pattern.compile("[ ][(].+[)]$");
/**
* Setup logging for the {@link RhinoScriptEngine}.
@@ -75,25 +82,24 @@ public class RhinoScriptEngine extends AbstractScriptEngine {
private final ConcurrentMap scriptCache =
new ConcurrentHashMap();
- private long minimumRecompilationInterval = -1;
+ private final long minimumRecompilationInterval;
private RequireBuilder requireBuilder;
private ClassLoader classLoader;
+ private final ScriptExceptionGenerator scriptExceptionGenerator;
+
RhinoScriptEngine(final Map configuration, final ScriptEngineFactory factory,
- final Collection sourceContainers, ClassLoader registryLevelClassLoader) {
+ final Collection sourceContainers, ClassLoader registryLevelClassLoader) {
this.factory = factory;
-
- Object debugProperty = configuration.get(CONFIG_DEBUG_PROPERTY);
- if (debugProperty instanceof String) {
- initDebugListener((String) debugProperty);
- }
- Object recompile = configuration.get(CONFIG_RECOMPILE_MINIMUM_INTERVAL_PROPERTY);
- if (recompile instanceof String) {
- minimumRecompilationInterval = Long.valueOf((String) recompile);
- }
-
+ final JsonValue jsonValueConfig = new JsonValue(configuration);
+ initDebugListener(jsonValueConfig.get(CONFIG_DEBUG_PROPERTY).defaultTo(null).asString());
+ minimumRecompilationInterval = Long.valueOf(
+ jsonValueConfig.get(CONFIG_RECOMPILE_MINIMUM_INTERVAL_PROPERTY).defaultTo("-1").asString());
+ scriptExceptionGenerator = jsonValueConfig.get(CONFIG_EXCEPTION_DEBUG_INFO).defaultTo(true).asBoolean()
+ ? DEBUG_SCRIPT_EXCEPTION_GENERATOR
+ : NON_DEBUG_SCRIPT_EXCEPTION_GENERATOR;
// Use an Iterable over the SourceContainer collection--that way if it
// changes (adds, removes, changes)--the new collection is reflected in
// the UrlModuleSourceProvider.
@@ -145,7 +151,7 @@ private static final class ScriptCacheEntry {
private final long lastCheck;
private ScriptCacheEntry(final Script compiledScript, final ScriptSource scriptSource,
- long lastModified, long lastCheck) {
+ long lastModified, long lastCheck) {
this.compiledScript = compiledScript;
this.scriptSource = scriptSource;
this.lastModified = lastModified;
@@ -231,12 +237,13 @@ public void compileScript(CompilationHandler handler) throws ScriptException {
private Script compileScript(String name, Reader scriptReader) throws ScriptCompilationException {
Context cx = Context.enter();
+ cx.setLanguageVersion(Context.VERSION_ES6);
try {
return cx.compileReader(scriptReader, name, 1, null);
} catch (IOException ioe) {
throw new ScriptCompilationException(ioe.getMessage(), ioe);
} catch (RhinoException re) {
- throw new ScriptCompilationException(re.getMessage(), re, re.sourceName(), re.lineNumber(), re.columnNumber());
+ throw getScriptExceptionGenerator().newScriptCompilationException(re);
} finally {
Context.exit();
if (scriptReader != null) {
@@ -261,10 +268,6 @@ public OperationParameter getOperationParameter(final org.forgerock.services.con
private volatile Boolean debugInitialised = null;
- public static final String CONFIG_DEBUG_PROPERTY = "javascript.debug";
- public static final String CONFIG_RECOMPILE_MINIMUM_INTERVAL_PROPERTY =
- "javascript.recompile.minimumInterval";
-
private synchronized void initDebugListener(String configString) {
if (null == debugInitialised) {
// Get here only once when the first factory initialised.
@@ -364,4 +367,77 @@ public Bindings compileBindings(org.forgerock.services.context.Context context,
// return new RhinoScript(name, source, sharedScope);
// }
-}
+ /**
+ * Interface defining factory for exceptions resulting from script compilation or execution.
+ */
+ interface ScriptExceptionGenerator {
+ /**
+ * @param e the RhinoException representing the thrown exception
+ * @param scriptThrownExceptionValue the actual exception value thrown by the script, converted to a java object
+ * @return a ScriptThrownException representing this exception, and encapsulating the correct level of debug information
+ */
+ ScriptThrownException newScriptThrownException(RhinoException e, Object scriptThrownExceptionValue);
+
+ /**
+ *
+ * @param e the RhinoException generated by the script compilation failure
+ * @return a ScriptCompilationException representing this failure, and encapsulating the correct level of debug information
+ */
+ ScriptCompilationException newScriptCompilationException(RhinoException e);
+
+ /**
+ *
+ * @param e the RhinoException thrown by the RhinoEngine during script execution
+ * @return a ScriptException representing this exception, and encapsulating the correct level of debug information.
+ */
+ ScriptException newScriptException(RhinoException e);
+ }
+
+ ScriptExceptionGenerator getScriptExceptionGenerator() {
+ return scriptExceptionGenerator;
+ }
+
+ private static final ScriptExceptionGenerator DEBUG_SCRIPT_EXCEPTION_GENERATOR = new ScriptExceptionGenerator() {
+ @Override
+ public ScriptThrownException newScriptThrownException(RhinoException e, Object scriptThrownExceptionValue) {
+ return new ScriptThrownException(e.getMessage(), e.sourceName(), e.lineNumber(), e.columnNumber(),
+ scriptThrownExceptionValue);
+ }
+
+ @Override
+ public ScriptCompilationException newScriptCompilationException(RhinoException e) {
+ return new ScriptCompilationException(e.getMessage(), e, e.sourceName(), e.lineNumber(), e.columnNumber());
+ }
+
+ @Override
+ public ScriptException newScriptException(RhinoException e) {
+ return new ScriptException(e.getMessage(), e.sourceName(), e.lineNumber(), e.columnNumber());
+ }
+ };
+
+ private static final ScriptExceptionGenerator NON_DEBUG_SCRIPT_EXCEPTION_GENERATOR = new ScriptExceptionGenerator() {
+ @Override
+ public ScriptThrownException newScriptThrownException(RhinoException e, Object scriptThrownExceptionValue) {
+ return new ScriptThrownException(stripDebugInformationFromRhinoExceptionMessage(e), scriptThrownExceptionValue);
+ }
+
+ @Override
+ public ScriptCompilationException newScriptCompilationException(RhinoException e) {
+ return new ScriptCompilationException(stripDebugInformationFromRhinoExceptionMessage(e));
+ }
+
+ @Override
+ public ScriptException newScriptException(RhinoException e) {
+ return new ScriptException(stripDebugInformationFromRhinoExceptionMessage(e));
+ }
+
+ /*
+ The Rhino runtime can include script debug information in the exception message. The format of this included information
+ can be found in RhinoException#getMessage. The pattern below will match this information, if present, and return only
+ the preceeding message state.
+ */
+ private String stripDebugInformationFromRhinoExceptionMessage(RhinoException e) {
+ return RHINO_EXCEPTION_FILE_INFO_PATTERN.split(e.getMessage())[0];
+ }
+ };
+}
\ No newline at end of file