[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