[java-shib-attribute] 04/05: JSATTR-41 Look to make the ScriptedDataConnector Cached

Rod Widdowson rdw at steadingsoftware.com
Mon Jan 20 11:45:47 UTC 2025


This is an automated email from the git hooks/post-receive script.

rdw pushed a commit to branch main
in repository java-shib-attribute.

View the commit online:
http://git.shibboleth.net/view/?p=java-shib-attribute.git;a=commit;h=d9db4fc06d77045a6445166615a570cc31b2b4f5

commit d9db4fc06d77045a6445166615a570cc31b2b4f5
Author: Rod Widdowson <rdw at steadingsoftware.com>
AuthorDate: Sat Jan 18 16:21:53 2025 +0000

    JSATTR-41 Look to make the ScriptedDataConnector Cached
    
    https://shibboleth.atlassian.net/browse/JSATTR-41
    
    Add tests for the caching bit.
---
 .../dc/scripted/impl/ScriptedDataConnector.java    |   1 -
 .../dc/impl/ScriptedDataConnectorTest.java         |  94 ----------
 .../scripted/impl/ScriptedDataConnectorTest.java   | 191 +++++++++++++++++++++
 .../idp/attribute/resolver/impl/dc/cache.js        |   1 +
 .../dc/impl/ScriptedDataConnectorParser.java       |   4 +
 5 files changed, 196 insertions(+), 95 deletions(-)

diff --git a/shib-attribute-resolver-impl/src/main/java/net/shibboleth/idp/attribute/resolver/dc/scripted/impl/ScriptedDataConnector.java b/shib-attribute-resolver-impl/src/main/java/net/shibboleth/idp/attribute/resolver/dc/scripted/impl/ScriptedDataConnector.java
index 88f8b2386..ee03c4fa6 100644
--- a/shib-attribute-resolver-impl/src/main/java/net/shibboleth/idp/attribute/resolver/dc/scripted/impl/ScriptedDataConnector.java
+++ b/shib-attribute-resolver-impl/src/main/java/net/shibboleth/idp/attribute/resolver/dc/scripted/impl/ScriptedDataConnector.java
@@ -80,7 +80,6 @@ public class ScriptedDataConnector extends AbstractSearchDataConnector<ScriptedS
         contextExtenders = CollectionSupport.emptyList();
         setValidator(new NullValidator());
         setMappingStrategy(new ScriptedMappingStrategy());
-        setExecutableSearchBuilder(new ScriptedSearchBuilder());
     }
 
     /**
diff --git a/shib-attribute-resolver-impl/src/test/java/net/shibboleth/idp/attribute/resolver/dc/impl/ScriptedDataConnectorTest.java b/shib-attribute-resolver-impl/src/test/java/net/shibboleth/idp/attribute/resolver/dc/impl/ScriptedDataConnectorTest.java
deleted file mode 100644
index 47110a925..000000000
--- a/shib-attribute-resolver-impl/src/test/java/net/shibboleth/idp/attribute/resolver/dc/impl/ScriptedDataConnectorTest.java
+++ /dev/null
@@ -1,94 +0,0 @@
-/*
- * Licensed 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 net.shibboleth.idp.attribute.resolver.dc.impl;
-
-import static org.testng.Assert.assertEquals;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.util.Map;
-
-import javax.annotation.Nonnull;
-import javax.script.ScriptException;
-
-import org.opensaml.profile.context.ProfileRequestContext;
-import org.testng.annotations.Test;
-
-import net.shibboleth.idp.attribute.IdPAttribute;
-import net.shibboleth.idp.attribute.IdPAttributeValue;
-import net.shibboleth.idp.attribute.StringAttributeValue;
-import net.shibboleth.idp.attribute.resolver.ResolutionException;
-import net.shibboleth.idp.attribute.resolver.context.AttributeResolutionContext;
-import net.shibboleth.idp.attribute.resolver.context.AttributeResolverWorkContext;
-import net.shibboleth.idp.attribute.resolver.dc.scripted.impl.ScriptedDataConnector;
-import net.shibboleth.shared.collection.CollectionSupport;
-import net.shibboleth.shared.component.ComponentInitializationException;
-import net.shibboleth.shared.scripting.EvaluableScript;
-
-/**
- * Tests for {@link ScriptedDataConnector}
- * 
- */
- at SuppressWarnings("javadoc")
-public class ScriptedDataConnectorTest {
-
-    @Nonnull private EvaluableScript getScript(String fileName) throws IOException, ComponentInitializationException {
-        final String name = "/net/shibboleth/idp/attribute/resolver/impl/dc/" + fileName;
-        final EvaluableScript es = new EvaluableScript();
-        es.setEngineName("javascript");
-        final InputStream is = getClass().getResourceAsStream(name);
-        assert is != null;
-        es.setScript(is);
-        es.initialize();
-        return es;
-    }
-
-    @Test(expectedExceptions=ResolutionException.class)
-    public void error() throws ComponentInitializationException, ScriptException, IOException, ResolutionException {
-        final ScriptedDataConnector connector = new ScriptedDataConnector();
-        connector.setId("Scripted");
-        connector.setScript(getScript("error.js"));
-
-        connector.initialize();
-
-        final AttributeResolutionContext context = new ProfileRequestContext().ensureSubcontext(AttributeResolutionContext.class);
-        context.ensureSubcontext(AttributeResolverWorkContext.class);
-        
-        connector.resolve(context);
-    }
-    
-    @Test public void custom() throws ComponentInitializationException, ResolutionException, ScriptException, IOException {
-
-        final ScriptedDataConnector connector = new ScriptedDataConnector();
-        connector.setId("Scripted");
-        
-        final IdPAttribute attribute = new IdPAttribute("attr");
-        attribute.setValues(CollectionSupport.singletonList((IdPAttributeValue)new StringAttributeValue("bar")));
-        connector.setCustomObject(attribute);
-        
-        connector.setScript(getScript("custom.js"));
-
-        connector.initialize();
-
-        final AttributeResolutionContext context = new ProfileRequestContext().ensureSubcontext(AttributeResolutionContext.class);
-        context.ensureSubcontext(AttributeResolverWorkContext.class);
-        final Map<String, IdPAttribute> result = connector.resolve(context);
-        assert result != null;
-        
-        assertEquals(result.size(), 1);
-        assertEquals(result.get(attribute.getId()),attribute);
-    }
-
-}
\ No newline at end of file
diff --git a/shib-attribute-resolver-impl/src/test/java/net/shibboleth/idp/attribute/resolver/dc/scripted/impl/ScriptedDataConnectorTest.java b/shib-attribute-resolver-impl/src/test/java/net/shibboleth/idp/attribute/resolver/dc/scripted/impl/ScriptedDataConnectorTest.java
new file mode 100644
index 000000000..b031f00c0
--- /dev/null
+++ b/shib-attribute-resolver-impl/src/test/java/net/shibboleth/idp/attribute/resolver/dc/scripted/impl/ScriptedDataConnectorTest.java
@@ -0,0 +1,191 @@
+/*
+ * Licensed 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 net.shibboleth.idp.attribute.resolver.dc.scripted.impl;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertTrue;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Map;
+import java.util.function.Function;
+
+import javax.annotation.Nonnull;
+import javax.script.ScriptException;
+
+import org.opensaml.messaging.context.BaseContext;
+import org.opensaml.profile.context.ProfileRequestContext;
+import org.testng.annotations.Test;
+
+import net.shibboleth.idp.attribute.IdPAttribute;
+import net.shibboleth.idp.attribute.IdPAttributeValue;
+import net.shibboleth.idp.attribute.StringAttributeValue;
+import net.shibboleth.idp.attribute.resolver.ResolutionException;
+import net.shibboleth.idp.attribute.resolver.context.AttributeResolutionContext;
+import net.shibboleth.idp.attribute.resolver.context.AttributeResolverWorkContext;
+import net.shibboleth.idp.attribute.resolver.dc.impl.TestCache;
+import net.shibboleth.shared.collection.CollectionSupport;
+import net.shibboleth.shared.component.ComponentInitializationException;
+import net.shibboleth.shared.scripting.EvaluableScript;
+import net.shibboleth.shared.testing.VelocityEngine;
+
+/**
+ * Tests for {@link ScriptedDataConnector}.
+ */
+ at SuppressWarnings("javadoc")
+public class ScriptedDataConnectorTest {
+
+    @Nonnull private EvaluableScript getScript(String fileName) throws IOException, ComponentInitializationException {
+        final String name = "/net/shibboleth/idp/attribute/resolver/impl/dc/" + fileName;
+        final EvaluableScript es = new EvaluableScript();
+        es.setEngineName("javascript");
+        final InputStream is = getClass().getResourceAsStream(name);
+        assert is != null;
+        es.setScript(is);
+        es.initialize();
+        return es;
+    }
+
+    @Test(expectedExceptions=ResolutionException.class)
+    public void error() throws ComponentInitializationException, ScriptException, IOException, ResolutionException {
+        final ScriptedDataConnector connector = new ScriptedDataConnector();
+        connector.setId("Scripted");
+        connector.setScript(getScript("error.js"));
+        connector.setExecutableSearchBuilder(new ScriptedSearchBuilder());
+        connector.initialize();
+
+        final AttributeResolutionContext context = new ProfileRequestContext().ensureSubcontext(AttributeResolutionContext.class);
+        context.ensureSubcontext(AttributeResolverWorkContext.class);
+
+        connector.resolve(context);
+    }
+
+    @Test public void custom() throws ComponentInitializationException, ResolutionException, ScriptException, IOException {
+
+        final ScriptedDataConnector connector = new ScriptedDataConnector();
+        connector.setId("Scripted");
+
+        final IdPAttribute attribute = new IdPAttribute("attr");
+        attribute.setValues(CollectionSupport.singletonList((IdPAttributeValue)new StringAttributeValue("bar")));
+        connector.setCustomObject(attribute);
+
+        connector.setScript(getScript("custom.js"));
+        connector.setExecutableSearchBuilder(new ScriptedSearchBuilder());
+        connector.initialize();
+
+        final AttributeResolutionContext context = new ProfileRequestContext().ensureSubcontext(AttributeResolutionContext.class);
+        context.ensureSubcontext(AttributeResolverWorkContext.class);
+        final Map<String, IdPAttribute> result = connector.resolve(context);
+        assert result != null;
+
+        assertEquals(result.size(), 1);
+        assertEquals(result.get(attribute.getId()),attribute);
+    }
+
+    private int callCount;  // used for cache hit/miss
+
+    @Test public void cache() throws ComponentInitializationException, ResolutionException, ScriptException, IOException {
+
+        final ScriptedDataConnector connector = new ScriptedDataConnector();
+        connector.setId("Scripted");
+
+        final IdPAttribute attribute = new IdPAttribute("attr");
+        attribute.setValues(CollectionSupport.singletonList((IdPAttributeValue)new StringAttributeValue("bar")));
+
+        Function<Object, IdPAttribute> fn = new Function<Object, IdPAttribute>() {
+                @Override
+                public IdPAttribute apply(Object t) {
+                    ScriptedDataConnectorTest.this.callCount++;
+                    return attribute;
+                }
+            };
+
+        connector.setCustomObject(fn);
+
+        final TestCache cache = new TestCache();
+        connector.setResultsCache(cache);
+        connector.setScript(getScript("cache.js"));
+        final ScriptedSearchBuilder builder = new ScriptedSearchBuilder();
+        builder.setVelocityEngine(VelocityEngine.newVelocityEngine());
+
+        builder.setCacheKeyTemplateText("${resolutionContext.getSubcontext(\"" + MyCacheKeyContextContext.class.getName() + "\")}");
+        builder.initialize();
+
+        connector.setExecutableSearchBuilder(builder);
+        connector.initialize();
+
+        AttributeResolutionContext context = new ProfileRequestContext().ensureSubcontext(AttributeResolutionContext.class);
+        context.setResolvedIdPAttributes(CollectionSupport.singleton(attribute));
+        context.ensureSubcontext(AttributeResolverWorkContext.class);
+        context.addSubcontext(new MyCacheKeyContextContext("1"));
+        callCount = 0;
+        assertTrue(cache.size() == 0);
+        Map<String, IdPAttribute> result = connector.resolve(context);
+        assert result != null;
+        assertEquals(result.size(), 1);
+        assertEquals(result.get(attribute.getId()),attribute);
+
+        assertTrue(cache.size() == 1);
+        assertEquals(cache.iterator().next(), result);
+
+        assertEquals(callCount, 1);
+
+        //
+        // And go again checking for a cache hit
+        //
+        context = new ProfileRequestContext().ensureSubcontext(AttributeResolutionContext.class);
+        context.ensureSubcontext(AttributeResolverWorkContext.class);
+        context.addSubcontext(new MyCacheKeyContextContext("1"));
+        callCount = 0;
+
+        assertTrue(cache.size() == 1);
+        result = connector.resolve(context);
+        assert result != null;
+        assertEquals(result.size(), 1);
+        assertEquals(result.get(attribute.getId()),attribute);
+
+        assertTrue(cache.size() == 1);
+        assertEquals(cache.iterator().next(), result);
+
+        assertEquals(callCount, 0);
+
+        //
+        // And again - cache miss
+        //
+        context = new ProfileRequestContext().ensureSubcontext(AttributeResolutionContext.class);
+        context.ensureSubcontext(AttributeResolverWorkContext.class);
+        context.addSubcontext(new MyCacheKeyContextContext("2"));
+        callCount = 0;
+        assertTrue(cache.size() == 1);
+        result = connector.resolve(context);
+        assert result != null;
+        assertEquals(result.size(), 1);
+        assertEquals(result.get(attribute.getId()),attribute);
+
+        assertTrue(cache.size() == 2);
+        assertEquals(callCount, 1);
+    }
+
+    /** Silly little class whose only reason is to inject a cacheKey.*/
+    private class MyCacheKeyContextContext extends BaseContext {
+        private final String key;
+        public MyCacheKeyContextContext(String parm) {
+            key = parm;
+        }
+        public String toString() {
+            return key;
+        }
+    }
+}
diff --git a/shib-attribute-resolver-impl/src/test/resources/net/shibboleth/idp/attribute/resolver/impl/dc/cache.js b/shib-attribute-resolver-impl/src/test/resources/net/shibboleth/idp/attribute/resolver/impl/dc/cache.js
new file mode 100644
index 000000000..a2affa0b5
--- /dev/null
+++ b/shib-attribute-resolver-impl/src/test/resources/net/shibboleth/idp/attribute/resolver/impl/dc/cache.js
@@ -0,0 +1 @@
+connectorResults.add(custom.apply(1));
diff --git a/shib-attribute-resolver-spring/src/main/java/net/shibboleth/idp/attribute/resolver/spring/dc/impl/ScriptedDataConnectorParser.java b/shib-attribute-resolver-spring/src/main/java/net/shibboleth/idp/attribute/resolver/spring/dc/impl/ScriptedDataConnectorParser.java
index a62c225a1..52c4c9524 100644
--- a/shib-attribute-resolver-spring/src/main/java/net/shibboleth/idp/attribute/resolver/spring/dc/impl/ScriptedDataConnectorParser.java
+++ b/shib-attribute-resolver-spring/src/main/java/net/shibboleth/idp/attribute/resolver/spring/dc/impl/ScriptedDataConnectorParser.java
@@ -27,6 +27,7 @@ import org.springframework.beans.factory.xml.ParserContext;
 import org.w3c.dom.Element;
 
 import net.shibboleth.idp.attribute.resolver.dc.scripted.impl.ScriptedDataConnector;
+import net.shibboleth.idp.attribute.resolver.dc.scripted.impl.ScriptedSearchBuilder;
 import net.shibboleth.idp.attribute.resolver.spring.dc.AbstractDataConnectorParser;
 import net.shibboleth.idp.attribute.resolver.spring.impl.AttributeResolverNamespaceHandler;
 import net.shibboleth.shared.primitive.LoggerFactory;
@@ -109,6 +110,9 @@ public class ScriptedDataConnectorParser extends AbstractDataConnectorParser {
         }
 
         builder.addPropertyValue("script", scriptBuilder.getBeanDefinition());
+        final BeanDefinitionBuilder searchBuilder =
+                BeanDefinitionBuilder.genericBeanDefinition(ScriptedSearchBuilder.class);
+        builder.addPropertyValue("executableSearchBuilder", searchBuilder.getBeanDefinition());
     }
     // Checkstyle: CyclomaticComplexity ON
 }
\ No newline at end of file

-- 
To stop receiving notification emails like this one, please contact
the administrator of this repository.


More information about the commits mailing list