[spring-extensions] branch maint-5 updated: IDP-1314 Add SpringExpressionFunction (and tests)

Rod Widdowson rdw at steadingsoftware.com
Tue Jul 10 11:47:42 EDT 2018


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

rdw pushed a commit to branch maint-5
in repository spring-extensions.

View the commit online:
http://git.shibboleth.net/view/?p=spring-extensions.git;a=commit;h=104828c10779178c0b2669e40d4a4b633916bd9b

The following commit(s) were added to refs/heads/maint-5 by this push:
       new  104828c   IDP-1314  Add SpringExpressionFunction (and tests)
104828c is described below

commit 104828c10779178c0b2669e40d4a4b633916bd9b
Author: Rod Widdowson <rdw at steadingsoftware.com>
AuthorDate: Tue Jul 10 10:13:05 2018 +0100

    IDP-1314  Add SpringExpressionFunction (and tests)
    
    https://issues.shibboleth.net/jira/browse/IDP-1314
---
 ...java => AbstractSpringExpressionEvaluator.java} | 93 ++++++++++++++++++----
 .../ext/spring/util/SpringExpressionFunction.java  | 53 ++++++++++++
 .../ext/spring/util/SpringExpressionPredicate.java | 82 ++-----------------
 .../ext/spring/util/SpringExpressionTest.java      | 57 +++++++++++++
 4 files changed, 195 insertions(+), 90 deletions(-)

diff --git a/src/main/java/net/shibboleth/ext/spring/util/SpringExpressionPredicate.java b/src/main/java/net/shibboleth/ext/spring/util/AbstractSpringExpressionEvaluator.java
similarity index 59%
copy from src/main/java/net/shibboleth/ext/spring/util/SpringExpressionPredicate.java
copy to src/main/java/net/shibboleth/ext/spring/util/AbstractSpringExpressionEvaluator.java
index 769d97d..dfb9a3c 100644
--- a/src/main/java/net/shibboleth/ext/spring/util/SpringExpressionPredicate.java
+++ b/src/main/java/net/shibboleth/ext/spring/util/AbstractSpringExpressionEvaluator.java
@@ -34,42 +34,85 @@ import org.springframework.expression.ParseException;
 import org.springframework.expression.spel.standard.SpelExpressionParser;
 import org.springframework.expression.spel.support.StandardEvaluationContext;
 
-import com.google.common.base.Predicate;
-
 /**
- * Predicate whose condition is defined by an Spring EL expression.
+ * A component that evaluates a Spring EL expression against a set of inputs
+ * and returns the result.
  * 
  * @param <T> type of input
+ * @param <U> type of output
  * 
  * @since 5.4.0
  */
-public class SpringExpressionPredicate<T> implements Predicate<T> {
+public abstract class AbstractSpringExpressionEvaluator<T, U> {
     
     /** Class logger. */
-    @Nonnull private final Logger log = LoggerFactory.getLogger(SpringExpressionPredicate.class);
+    @Nonnull private final Logger log = LoggerFactory.getLogger(AbstractSpringExpressionEvaluator.class);
 
     /** SpEL expression to evaluate. */
     @Nullable private String springExpression;
     
     /** A custom object to inject into the expression context. */
     @Nullable private Object customObject;
+    
+    /** The output type. */
+    @Nullable private Class<U> outputType;
+
+    /** The input type. */
+    @Nullable private Class<T> inputType;
+
 
     /** Whether to raise runtime exceptions if expression fails. */
     private boolean hideExceptions;
     
     /** Value to return from predicate when an error occurs. */
-    private boolean returnOnError;
+    private U returnOnError;
 
     /**
      * Constructor.
      *
      * @param expression the expression to evaluate
      */
-    public SpringExpressionPredicate(@Nonnull @NotEmpty @ParameterName(name="expression") final String expression) {
+    public AbstractSpringExpressionEvaluator(
+            final @Nonnull @NotEmpty @ParameterName(name="expression") String expression) {
         springExpression = Constraint.isNotNull(StringSupport.trimOrNull(expression),
                 "Expression cannot be null or empty");
     }
+    
+    /**
+     * Set the output type to be enforced.
+     * 
+     * @param type output type
+     */
+    public void setOutputType(@Nullable final Class type) {
+        outputType = type;
+    }
 
+    /**
+     * Get the output type to be enforced.
+     * 
+     * @return output type
+     */
+    @Nullable protected Class<U> getOutputType() {
+        return outputType;
+    }
+
+    /**
+     * Get the input type to be enforced.
+     *
+     * @return input type
+     */
+    @Nullable public  Class<T> getInputType() {
+        return inputType;
+    }
+
+    /**
+     * Set the input type to be enforced.
+     *
+     * @param type input type
+     */
+    public void setInputType(@Nullable final Class<T> type) {
+        inputType = type;
+    }
 
     /**
      * Set a custom (externally provided) object.
@@ -81,6 +124,16 @@ public class SpringExpressionPredicate<T> implements Predicate<T> {
     }
 
     /**
+     * Get the custom (externally provided) object.
+     *
+     * @return the custom object
+     */
+    public Object getCustomObject() {
+        return customObject;
+    }
+
+
+    /**
      * Set whether to hide exceptions in expression execution (default is false).
      * 
      * @param flag flag to set
@@ -92,15 +145,21 @@ public class SpringExpressionPredicate<T> implements Predicate<T> {
     /**
      * Set value to return if an error occurs (default is false).
      * 
-     * @param flag flag to set
+     * @param what value to set
      */
-    public void setReturnOnError(final boolean flag) {
-        returnOnError = flag;
+    public void setReturnOnError(final U what) {
+        returnOnError = what;
     }
 
     /** {@inheritDoc} */
-    @Override
-    public boolean apply(@Nullable final T input) {
+    protected U evaluate(@Nullable final T input) {
+
+        // Try outside the try so as to preserve derived semantics
+        if (null != input && null != getInputType() && !getInputType().isInstance(input)) {
+            log.error("Input was type {} which is not an instance of {}",  input.getClass(), getInputType());
+            throw new ClassCastException("Input was type " + input.getClass() + " which is not an instance of "
+                    + getInputType());
+        }
 
         try {
             final ExpressionParser parser = new SpelExpressionParser();
@@ -108,7 +167,13 @@ public class SpringExpressionPredicate<T> implements Predicate<T> {
             context.setVariable("custom", customObject);
             context.setVariable("input", input);
             prepareContext(context, input);
-            return parser.parseExpression(springExpression).getValue(context, Boolean.class);
+            final Object output = parser.parseExpression(springExpression).getValue(context);
+
+            if (null != getOutputType() && null != output && !getOutputType().isInstance(output)) {
+                log.error("Output of type {} was not of type {}", output.getClass(), getOutputType());
+                return returnOnError;
+            }
+            return (U) output;
         } catch (final ParseException|EvaluationException e) {
             log.error("Error evaluating Spring expression", e);
             if (hideExceptions) {
@@ -124,7 +189,7 @@ public class SpringExpressionPredicate<T> implements Predicate<T> {
      * @param context expression context
      * @param input to predicate
      */
-    protected void prepareContext(@Nonnull final EvaluationContext context, @Nullable final T input) {
+     protected void prepareContext(@Nonnull final EvaluationContext context, @Nullable final T input) {
         
     }
 
diff --git a/src/main/java/net/shibboleth/ext/spring/util/SpringExpressionFunction.java b/src/main/java/net/shibboleth/ext/spring/util/SpringExpressionFunction.java
new file mode 100644
index 0000000..1199888
--- /dev/null
+++ b/src/main/java/net/shibboleth/ext/spring/util/SpringExpressionFunction.java
@@ -0,0 +1,53 @@
+/*
+ * Licensed to the University Corporation for Advanced Internet Development,
+ * Inc. (UCAID) under one or more contributor license agreements.  See the
+ * NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The UCAID 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 net.shibboleth.ext.spring.util;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import net.shibboleth.utilities.java.support.annotation.ParameterName;
+import net.shibboleth.utilities.java.support.annotation.constraint.NotEmpty;
+
+import com.google.common.base.Function;
+
+/**
+ * Function whose output is defined by an Spring EL expression.
+ * 
+ * @param <T> type of input
+ * @param <U> type of output
+ * 
+ * @since 5.4.0
+ */
+public class SpringExpressionFunction<T,U> extends AbstractSpringExpressionEvaluator<T, U> 
+            implements Function<T,U> {
+    
+    /**
+     * Constructor.
+     *
+     * @param expression the expression to evaluate
+     */
+    public SpringExpressionFunction(@Nonnull @NotEmpty @ParameterName(name="expression") final String expression) {
+        super(expression);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public U apply(@Nullable final T input) {
+        return evaluate(input);
+    }
+}
\ No newline at end of file
diff --git a/src/main/java/net/shibboleth/ext/spring/util/SpringExpressionPredicate.java b/src/main/java/net/shibboleth/ext/spring/util/SpringExpressionPredicate.java
index 769d97d..500127c 100644
--- a/src/main/java/net/shibboleth/ext/spring/util/SpringExpressionPredicate.java
+++ b/src/main/java/net/shibboleth/ext/spring/util/SpringExpressionPredicate.java
@@ -22,18 +22,6 @@ import javax.annotation.Nullable;
 
 import net.shibboleth.utilities.java.support.annotation.ParameterName;
 import net.shibboleth.utilities.java.support.annotation.constraint.NotEmpty;
-import net.shibboleth.utilities.java.support.logic.Constraint;
-import net.shibboleth.utilities.java.support.primitive.StringSupport;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.springframework.expression.EvaluationContext;
-import org.springframework.expression.EvaluationException;
-import org.springframework.expression.ExpressionParser;
-import org.springframework.expression.ParseException;
-import org.springframework.expression.spel.standard.SpelExpressionParser;
-import org.springframework.expression.spel.support.StandardEvaluationContext;
-
 import com.google.common.base.Predicate;
 
 /**
@@ -43,50 +31,17 @@ import com.google.common.base.Predicate;
  * 
  * @since 5.4.0
  */
-public class SpringExpressionPredicate<T> implements Predicate<T> {
-    
-    /** Class logger. */
-    @Nonnull private final Logger log = LoggerFactory.getLogger(SpringExpressionPredicate.class);
-
-    /** SpEL expression to evaluate. */
-    @Nullable private String springExpression;
+public class SpringExpressionPredicate<T> extends AbstractSpringExpressionEvaluator<T, Boolean> 
+            implements Predicate<T> {
     
-    /** A custom object to inject into the expression context. */
-    @Nullable private Object customObject;
-
-    /** Whether to raise runtime exceptions if expression fails. */
-    private boolean hideExceptions;
-    
-    /** Value to return from predicate when an error occurs. */
-    private boolean returnOnError;
-
     /**
      * Constructor.
      *
      * @param expression the expression to evaluate
      */
     public SpringExpressionPredicate(@Nonnull @NotEmpty @ParameterName(name="expression") final String expression) {
-        springExpression = Constraint.isNotNull(StringSupport.trimOrNull(expression),
-                "Expression cannot be null or empty");
-    }
-
-
-    /**
-     * Set a custom (externally provided) object.
-     * 
-     * @param object the custom object
-     */
-    public void setCustomObject(@Nullable final Object object) {
-        customObject = object;
-    }
-
-    /**
-     * Set whether to hide exceptions in expression execution (default is false).
-     * 
-     * @param flag flag to set
-     */
-    public void setHideExceptions(final boolean flag) {
-        hideExceptions = flag;
+        super(expression);
+        setOutputType(Boolean.class);
     }
 
     /**
@@ -95,37 +50,12 @@ public class SpringExpressionPredicate<T> implements Predicate<T> {
      * @param flag flag to set
      */
     public void setReturnOnError(final boolean flag) {
-        returnOnError = flag;
+        super.setReturnOnError(flag);
     }
 
     /** {@inheritDoc} */
     @Override
     public boolean apply(@Nullable final T input) {
-
-        try {
-            final ExpressionParser parser = new SpelExpressionParser();
-            final StandardEvaluationContext context = new StandardEvaluationContext();
-            context.setVariable("custom", customObject);
-            context.setVariable("input", input);
-            prepareContext(context, input);
-            return parser.parseExpression(springExpression).getValue(context, Boolean.class);
-        } catch (final ParseException|EvaluationException e) {
-            log.error("Error evaluating Spring expression", e);
-            if (hideExceptions) {
-                return returnOnError;
-            }
-            throw e;
-        }
+        return evaluate(input);
     }
-    
-    /**
-     * Decorate the expression context with any additional content.
-     * 
-     * @param context expression context
-     * @param input to predicate
-     */
-    protected void prepareContext(@Nonnull final EvaluationContext context, @Nullable final T input) {
-        
-    }
-
 }
\ No newline at end of file
diff --git a/src/test/java/net/shibboleth/ext/spring/util/SpringExpressionTest.java b/src/test/java/net/shibboleth/ext/spring/util/SpringExpressionTest.java
new file mode 100644
index 0000000..9a0c608
--- /dev/null
+++ b/src/test/java/net/shibboleth/ext/spring/util/SpringExpressionTest.java
@@ -0,0 +1,57 @@
+/*
+ * Licensed to the University Corporation for Advanced Internet Development,
+ * Inc. (UCAID) under one or more contributor license agreements.  See the
+ * NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The UCAID 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 net.shibboleth.ext.spring.util;
+
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+/**
+ * Test for the {@link SpringExpressionPredicate} and {@link SpringExpressionFunction}.
+ */
+public class SpringExpressionTest {
+
+    /** Helper function to use this as the bean to test.
+     * @return 99
+     */
+    public int getValue99() {
+        return 99;
+    }
+     
+    @Test public void testPredicates() {
+        
+        SpringExpressionPredicate predicate = new SpringExpressionPredicate<>("#input.getValue99() == 99");
+        
+        Assert.assertTrue(predicate.apply(this));
+        
+        predicate = new SpringExpressionPredicate<>("#input.getValue99() == #custom");
+        predicate.setCustomObject(99);
+        Assert.assertTrue(predicate.apply(this));
+
+    }
+
+    @Test public void testFunction() {
+        
+        SpringExpressionFunction<Object, SpringExpressionTest> func = new SpringExpressionFunction<>("#input");
+        Assert.assertNull(func.apply(null));
+
+        Assert.assertEquals(func.apply(this), this);
+
+        Assert.assertEquals((int) new SpringExpressionFunction<SpringExpressionTest, Integer>("#input.getValue99()").apply(this), 99);
+
+    }
+}

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


More information about the commits mailing list