[java-metadata-aggregator] 03/03: MDA-116 - Bring ScriptletStage up to snuff
Ian Young
ian at iay.org.uk
Thu Aug 27 13:29:30 UTC 2020
This is an automated email from the git hooks/post-receive script.
iay pushed a commit to branch main
in repository java-metadata-aggregator.
View the commit online:
http://git.shibboleth.net/view/?p=java-metadata-aggregator.git;a=commit;h=d6f6b158bef0bf60899b16b11356c6584a486984
commit d6f6b158bef0bf60899b16b11356c6584a486984
Author: Ian Young <ian at iay.org.uk>
AuthorDate: Thu Aug 27 14:27:50 2020 +0100
MDA-116 - Bring ScriptletStage up to snuff
https://issues.shibboleth.net/jira/browse/MDA-116
---
aggregator-pipeline/pom.xml | 45 ++++-
.../metadata/pipeline/ScriptletStage.java | 39 ++++-
.../metadata/pipeline/ScriptletStageTest.java | 182 +++++++++++++++++++++
.../metadata/pipeline/ScriptletStage-bad.js | 13 ++
.../metadata/pipeline/ScriptletStage-script.js | 9 +
.../metadata/pipeline/ScriptletStage-script.py | 10 ++
.../metadata/pipeline/ScriptletStage-script.rb | 11 ++
7 files changed, 301 insertions(+), 8 deletions(-)
diff --git a/aggregator-pipeline/pom.xml b/aggregator-pipeline/pom.xml
index cb3a4d1..384a42e 100644
--- a/aggregator-pipeline/pom.xml
+++ b/aggregator-pipeline/pom.xml
@@ -77,11 +77,23 @@
<type>test-jar</type>
<scope>test</scope>
</dependency>
+ <dependency>
+ <groupId>org.jruby</groupId>
+ <artifactId>jruby</artifactId>
+ <version>9.2.13.0</version>
+ <scope>test</scope>
+ </dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<scope>test</scope>
</dependency>
+ <dependency>
+ <groupId>org.python</groupId>
+ <artifactId>jython-slim</artifactId>
+ <version>2.7.2</version>
+ <scope>test</scope>
+ </dependency>
<dependency>
<groupId>${spring.groupId}</groupId>
<artifactId>spring-test</artifactId>
@@ -97,7 +109,7 @@
<artifactId>spring-context</artifactId>
<scope>test</scope>
</dependency>
-
+
</dependencies>
<build>
@@ -247,6 +259,37 @@
</build>
</profile>
+ <!--
+ Pull in a Javascript engine for testing in Java
+ versions where the JDK doesn't provide one.
+ -->
+ <profile>
+ <id>get-graal-nashorn</id>
+ <activation>
+ <jdk>[15,</jdk>
+ </activation>
+ <dependencies>
+ <dependency>
+ <groupId>org.graalvm.sdk</groupId>
+ <artifactId>graal-sdk</artifactId>
+ <version>${graalvm.version}</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.graalvm.js</groupId>
+ <artifactId>js</artifactId>
+ <version>${graalvm.version}</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>net.shibboleth.idp.plugin.scripting</groupId>
+ <artifactId>idp-plugin-nashorn-impl</artifactId>
+ <version>${nashorn.engine.version}</version>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+ </profile>
+
</profiles>
<distributionManagement>
diff --git a/aggregator-pipeline/src/main/java/net/shibboleth/metadata/pipeline/ScriptletStage.java b/aggregator-pipeline/src/main/java/net/shibboleth/metadata/pipeline/ScriptletStage.java
index c2acf46..6d899ef 100644
--- a/aggregator-pipeline/src/main/java/net/shibboleth/metadata/pipeline/ScriptletStage.java
+++ b/aggregator-pipeline/src/main/java/net/shibboleth/metadata/pipeline/ScriptletStage.java
@@ -23,6 +23,7 @@ import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.annotation.concurrent.GuardedBy;
import javax.annotation.concurrent.ThreadSafe;
+import javax.script.ScriptContext;
import javax.script.ScriptException;
import javax.script.SimpleScriptContext;
@@ -32,8 +33,10 @@ import org.slf4j.LoggerFactory;
import net.shibboleth.metadata.Item;
import net.shibboleth.utilities.java.support.annotation.constraint.NonnullAfterInit;
import net.shibboleth.utilities.java.support.annotation.constraint.NonnullElements;
+import net.shibboleth.utilities.java.support.annotation.constraint.NotEmpty;
import net.shibboleth.utilities.java.support.component.ComponentInitializationException;
import net.shibboleth.utilities.java.support.logic.Constraint;
+import net.shibboleth.utilities.java.support.primitive.StringSupport;
import net.shibboleth.utilities.java.support.scripting.EvaluableScript;
/**
@@ -55,16 +58,40 @@ import net.shibboleth.utilities.java.support.scripting.EvaluableScript;
@ThreadSafe
public class ScriptletStage<T> extends AbstractStage<T> {
- /** Name of the scriptlet attribute, {@value} , containing the Item collection to be transformed. */
- public static final String ITEMS = "items";
-
/** Class logger. */
private final Logger log = LoggerFactory.getLogger(ScriptletStage.class);
+ /**
+ * Name of the scriptlet attribute containing the {@link Item} collection to be transformed.
+ *
+ * <p>Defaults to <code>items</code>.</p>
+ */
+ @GuardedBy("this") @Nonnull @NotEmpty private String variableName = "items";
+
/** Script executed by this stage. */
@NonnullAfterInit @GuardedBy("this")
private EvaluableScript script;
+ /**
+ * Gets the variable name to contain the list of items.
+ *
+ * @return the variable name
+ */
+ public final synchronized String getVariableName() {
+ return variableName;
+ }
+
+ /**
+ * Sets the variable name to contain the list of items.
+ *
+ * @param name the variable name
+ */
+ public final synchronized void setVariableName(@Nonnull @NotEmpty final String name) {
+ throwSetterPreconditionExceptions();
+ variableName = Constraint.isNotNull(StringSupport.trimOrNull(name),
+ "variable name may not be null or empty");
+ }
+
/**
* Gets the script executed by this stage.
*
@@ -88,14 +115,12 @@ public class ScriptletStage<T> extends AbstractStage<T> {
protected void doExecute(@Nonnull @NonnullElements final List<Item<T>> items)
throws StageProcessingException {
final SimpleScriptContext context = new SimpleScriptContext();
- context.setAttribute(ITEMS, items, SimpleScriptContext.ENGINE_SCOPE);
+ context.setAttribute(getVariableName(), items, ScriptContext.ENGINE_SCOPE);
try {
getScript().eval(context);
} catch (final ScriptException e) {
- final String errMsg = getId() + " pipeline stage unable to execute script";
- log.error(errMsg, e);
- throw new StageProcessingException(errMsg, e);
+ throw new StageProcessingException("unable to execute script", e);
}
}
diff --git a/aggregator-pipeline/src/test/java/net/shibboleth/metadata/pipeline/ScriptletStageTest.java b/aggregator-pipeline/src/test/java/net/shibboleth/metadata/pipeline/ScriptletStageTest.java
new file mode 100644
index 0000000..11ee1c4
--- /dev/null
+++ b/aggregator-pipeline/src/test/java/net/shibboleth/metadata/pipeline/ScriptletStageTest.java
@@ -0,0 +1,182 @@
+
+package net.shibboleth.metadata.pipeline;
+
+import java.util.List;
+
+import javax.script.Compilable;
+import javax.script.ScriptEngineManager;
+
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+import net.shibboleth.ext.spring.resource.ResourceHelper;
+import net.shibboleth.metadata.BaseTest;
+import net.shibboleth.metadata.Item;
+import net.shibboleth.metadata.MockItem;
+import net.shibboleth.metadata.TestMarker;
+import net.shibboleth.utilities.java.support.component.ComponentInitializationException;
+import net.shibboleth.utilities.java.support.scripting.EvaluableScript;
+
+public class ScriptletStageTest extends BaseTest {
+
+ protected ScriptletStageTest() {
+ super(ScriptletStage.class);
+ }
+
+ @Test(expectedExceptions = {ComponentInitializationException.class})
+ public void testNoScript() throws Exception {
+ final var stage = new ScriptletStage<String>();
+ stage.setId("test");
+ stage.initialize();
+ }
+
+ @Test
+ public void testEngineFactories() throws Exception {
+ // Enumerates the engines available and some of their attributes
+ final var manager = new ScriptEngineManager();
+ final var factories = manager.getEngineFactories();
+ for (var factory : factories) {
+ System.out.println(factory.getEngineName() + ": " + factory.getLanguageName());
+ final var engine = factory.getScriptEngine();
+ System.out.println(" Compilable: " + (engine instanceof Compilable));
+ for (String name : factory.getNames()) {
+ System.out.println(" " + name);
+ }
+ }
+ }
+
+ @Test
+ public void testJavascript() throws Exception {
+ // pick up the script
+ final var scriptResource = getClasspathResource("script.js");
+
+ final var script = new EvaluableScript();
+ script.setScript(ResourceHelper.of(scriptResource));
+ script.initialize();
+
+ final var stage = new ScriptletStage<String>();
+ stage.setId("test");
+ stage.setScript(script);
+ stage.initialize();
+
+ final var items = List.<Item<String>>of(new MockItem("one"), new MockItem("two"));
+ stage.execute(items);
+
+ // The script should have added a TestMarker to each item.
+
+ var item1 = items.get(0);
+ var marks1 = item1.getItemMetadata().get(TestMarker.class);
+ Assert.assertEquals(marks1.size(), 1);
+ var mark1 = marks1.get(0).getMarker();
+ Assert.assertEquals(mark1, "foo 0");
+
+ var item2 = items.get(1);
+ var marks2 = item2.getItemMetadata().get(TestMarker.class);
+ Assert.assertEquals(marks2.size(), 1);
+ var mark2 = marks2.get(0).getMarker();
+ Assert.assertEquals(mark2, "foo 1");
+ }
+
+ @Test
+ public void testBadJavascript() throws Exception {
+ // pick up the script
+ final var scriptResource = getClasspathResource("bad.js");
+
+ final var script = new EvaluableScript();
+ script.setScript(ResourceHelper.of(scriptResource));
+ script.initialize();
+
+ final var stage = new ScriptletStage<String>();
+ stage.setId("test");
+ stage.setScript(script);
+ stage.initialize();
+
+ final var items = List.<Item<String>>of(new MockItem("one"), new MockItem("two"));
+
+ try {
+ stage.execute(items);
+ Assert.fail("expected an exception");
+ } catch (StageProcessingException e) {
+ // expected
+ }
+
+ // The script should have added a TestMarker to the first item, not to the second.
+
+ var item1 = items.get(0);
+ var marks1 = item1.getItemMetadata().get(TestMarker.class);
+ Assert.assertEquals(marks1.size(), 1);
+ var mark1 = marks1.get(0).getMarker();
+ Assert.assertEquals(mark1, "foo 0");
+
+ var item2 = items.get(1);
+ var marks2 = item2.getItemMetadata().get(TestMarker.class);
+ Assert.assertEquals(marks2.size(), 0);
+ }
+
+ @Test
+ public void testRuby() throws Exception {
+ // pick up the script
+ final var scriptResource = getClasspathResource("script.rb");
+
+ final var script = new EvaluableScript();
+ script.setScript(ResourceHelper.of(scriptResource));
+ script.setEngineName("ruby");
+ script.initialize();
+
+ final var stage = new ScriptletStage<String>();
+ stage.setId("test");
+ stage.setScript(script);
+ stage.setVariableName("$items");
+ stage.initialize();
+
+ final var items = List.<Item<String>>of(new MockItem("one"), new MockItem("two"));
+ stage.execute(items);
+
+ // The script should have added a TestMarker to each item.
+
+ var item1 = items.get(0);
+ var marks1 = item1.getItemMetadata().get(TestMarker.class);
+ Assert.assertEquals(marks1.size(), 1);
+ var mark1 = marks1.get(0).getMarker();
+ Assert.assertEquals(mark1, "foo 0");
+
+ var item2 = items.get(1);
+ var marks2 = item2.getItemMetadata().get(TestMarker.class);
+ Assert.assertEquals(marks2.size(), 1);
+ var mark2 = marks2.get(0).getMarker();
+ Assert.assertEquals(mark2, "foo 1");
+ }
+
+ @Test
+ public void testPython() throws Exception {
+ // pick up the script
+ final var scriptResource = getClasspathResource("script.py");
+
+ final var script = new EvaluableScript();
+ script.setScript(ResourceHelper.of(scriptResource));
+ script.setEngineName("python");
+ script.initialize();
+
+ final var stage = new ScriptletStage<String>();
+ stage.setId("test");
+ stage.setScript(script);
+ stage.initialize();
+
+ final var items = List.<Item<String>>of(new MockItem("one"), new MockItem("two"));
+ stage.execute(items);
+
+ // The script should have added a TestMarker to each item.
+
+ var item1 = items.get(0);
+ var marks1 = item1.getItemMetadata().get(TestMarker.class);
+ Assert.assertEquals(marks1.size(), 1);
+ var mark1 = marks1.get(0).getMarker();
+ Assert.assertEquals(mark1, "foo 0");
+
+ var item2 = items.get(1);
+ var marks2 = item2.getItemMetadata().get(TestMarker.class);
+ Assert.assertEquals(marks2.size(), 1);
+ var mark2 = marks2.get(0).getMarker();
+ Assert.assertEquals(mark2, "foo 1");
+ }
+}
diff --git a/aggregator-pipeline/src/test/resources/net/shibboleth/metadata/pipeline/ScriptletStage-bad.js b/aggregator-pipeline/src/test/resources/net/shibboleth/metadata/pipeline/ScriptletStage-bad.js
new file mode 100644
index 0000000..d13b0f9
--- /dev/null
+++ b/aggregator-pipeline/src/test/resources/net/shibboleth/metadata/pipeline/ScriptletStage-bad.js
@@ -0,0 +1,13 @@
+// Deliberately broken script.
+// Add a TestMarker to each item's item metadata
+// "foo 0" for the first, "foo 1" for the second, etc.
+// BUT throw an exception after the first one.
+
+var TestMarker = Java.type('net.shibboleth.metadata.TestMarker')
+for (i=0; i<items.length; i++) {
+ item = items[i]
+ marker = new TestMarker('foo ' + i);
+ item.getItemMetadata().put(marker);
+
+ throw 'this is an exception'
+}
diff --git a/aggregator-pipeline/src/test/resources/net/shibboleth/metadata/pipeline/ScriptletStage-script.js b/aggregator-pipeline/src/test/resources/net/shibboleth/metadata/pipeline/ScriptletStage-script.js
new file mode 100644
index 0000000..5f3772d
--- /dev/null
+++ b/aggregator-pipeline/src/test/resources/net/shibboleth/metadata/pipeline/ScriptletStage-script.js
@@ -0,0 +1,9 @@
+// Add a TestMarker to each item's item metadata
+// "foo 0" for the first, "foo 1" for the second, etc.
+
+var TestMarker = Java.type('net.shibboleth.metadata.TestMarker')
+for (i=0; i<items.length; i++) {
+ item = items[i]
+ marker = new TestMarker('foo ' + i);
+ item.getItemMetadata().put(marker);
+}
diff --git a/aggregator-pipeline/src/test/resources/net/shibboleth/metadata/pipeline/ScriptletStage-script.py b/aggregator-pipeline/src/test/resources/net/shibboleth/metadata/pipeline/ScriptletStage-script.py
new file mode 100644
index 0000000..971f258
--- /dev/null
+++ b/aggregator-pipeline/src/test/resources/net/shibboleth/metadata/pipeline/ScriptletStage-script.py
@@ -0,0 +1,10 @@
+# Add a TestMarker to each item's item metadata
+# "foo 0" for the first, "foo 1" for the second, etc.
+
+from net.shibboleth.metadata import TestMarker;
+
+count = 0
+for item in items:
+ marker = TestMarker('foo {}'.format(count))
+ item.getItemMetadata().put(marker)
+ count += 1
diff --git a/aggregator-pipeline/src/test/resources/net/shibboleth/metadata/pipeline/ScriptletStage-script.rb b/aggregator-pipeline/src/test/resources/net/shibboleth/metadata/pipeline/ScriptletStage-script.rb
new file mode 100644
index 0000000..2d4942a
--- /dev/null
+++ b/aggregator-pipeline/src/test/resources/net/shibboleth/metadata/pipeline/ScriptletStage-script.rb
@@ -0,0 +1,11 @@
+# Add a TestMarker to each item's item metadata
+# "foo 0" for the first, "foo 1" for the second, etc.
+require 'java'
+
+count = 0
+
+$items.each do |item|
+ marker = Java::NetShibbolethMetadata::TestMarker.new("foo #{count}")
+ count += 1
+ item.itemMetadata.put marker
+end
--
To stop receiving notification emails like this one, please contact
the administrator of this repository.
More information about the commits
mailing list