[java-metadata-aggregator] branch master updated: MDA-188 - refactor DOM traversal with more generic context mechanism

Ian Young ian at iay.org.uk
Tue Aug 8 11:40:37 EDT 2017


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

iay pushed a commit to branch master
in repository java-metadata-aggregator.

View the commit online:
http://git.shibboleth.net/view/?p=java-metadata-aggregator.git;a=commit;h=2660ba0c241a6fac3c443ca19573573906a93b53

The following commit(s) were added to refs/heads/master by this push:
       new  2660ba0   MDA-188 - refactor DOM traversal with more generic context mechanism
2660ba0 is described below

commit 2660ba0c241a6fac3c443ca19573573906a93b53
Author: Ian Young <ian at iay.org.uk>
AuthorDate: Tue Aug 8 16:40:25 2017 +0100

    MDA-188 - refactor DOM traversal with more generic context mechanism
---
 .../metadata/dom/AbstractDOMTraversalStage.java    | 79 ++++++++--------------
 .../metadata/dom/AbstractDOMValidationStage.java   | 18 ++---
 .../metadata/dom/AbstractElementVisitingStage.java |  8 ++-
 .../shibboleth/metadata/dom/CRDetectionStage.java  | 50 ++++++++++++--
 ...TrimmingStage.java => DOMTraversalContext.java} | 30 +++++---
 .../dom/ElementWhitespaceTrimmingStage.java        |  2 +-
 .../metadata/dom/ElementsStrippingStage.java       | 60 ++++++++++++----
 ...ngStage.java => SimpleDOMTraversalContext.java} | 28 ++++++--
 .../metadata/dom/ds/X509ValidationStage.java       | 67 ++++++++++++++++--
 .../dom/AbstractDOMValidationStageTest.java        | 11 ++-
 10 files changed, 247 insertions(+), 106 deletions(-)

diff --git a/aggregator-pipeline/src/main/java/net/shibboleth/metadata/dom/AbstractDOMTraversalStage.java b/aggregator-pipeline/src/main/java/net/shibboleth/metadata/dom/AbstractDOMTraversalStage.java
index e4cd818..b4e24c2 100644
--- a/aggregator-pipeline/src/main/java/net/shibboleth/metadata/dom/AbstractDOMTraversalStage.java
+++ b/aggregator-pipeline/src/main/java/net/shibboleth/metadata/dom/AbstractDOMTraversalStage.java
@@ -36,47 +36,30 @@ import net.shibboleth.utilities.java.support.xml.ElementSupport;
 
 /**
  * An abstract DOM traversal class using the template method pattern.
+ *
+ * A context object, extending {@link DOMTraversalContext}, is created by the
+ * implementing subclass and passed to the {@link #visit} method when
+ * each applicable {@link Element} is visited. In very simple cases, the
+ * {@link SimpleDOMTraversalContext} may suffice, but more complicated
+ * behaviour can be built up by extending or re-implementing that class.
+ *
+ * At the end of the traversal, the context's {@link DOMTraversalContext#end()}
+ * method is called to perform any post-processing required.
+ *
+ * @param <C> the context to carry through the traversal
  */
 @ThreadSafe
-public abstract class AbstractDOMTraversalStage extends AbstractIteratingStage<Element> {
-    
-    /** Context for a particular traversal. */
-    protected class TraversalContext {
-        
-        /** The {@link Item} this traversal is being performed on. */
-        private final Item<Element> item;
-        
-        /** Map of data for this traversal. */
-        private final ClassToInstanceMultiMap<Object> stash = new ClassToInstanceMultiMap<>(true);
-        
-        /**
-         * Constructor.
-         * 
-         * @param contextItem the {@link Item} this traversal is being performed on.
-         */
-        public TraversalContext(@Nonnull final Item<Element> contextItem) {
-            item = contextItem;
-        }
-        
-        /**
-         * Get the {@link Item} this traversal is being performed on.
-         * 
-         * @return the context {@link Item}
-         */
-        public Item<Element> getItem() {
-            return item;
-        }
-        
-        /**
-         * Get the stashed information for this traversal.
-         * 
-         * @return the stashed information
-         */
-        public ClassToInstanceMultiMap<Object> getStash() {
-            return stash;
-        }
-        
-    }
+public abstract class AbstractDOMTraversalStage<C extends DOMTraversalContext>
+    extends AbstractIteratingStage<Element> {
+
+    /**
+     * Build the context for a particular traversal.
+     *
+     * @param item the {@link Item} we are traversing
+     *
+     * @return an appropriate context
+     */
+    protected abstract C buildContext(@Nonnull final Item<Element> item);
 
     /**
      * Indicates whether the visitor should be applied to a particular {@link Element}.
@@ -94,9 +77,9 @@ public abstract class AbstractDOMTraversalStage extends AbstractIteratingStage<E
      * @param context the traversal context
      * @throws StageProcessingException if errors occur during processing
      */
-    protected abstract void visit(@Nonnull final Element element, @Nonnull final TraversalContext context)
+    protected abstract void visit(@Nonnull final Element element, @Nonnull final C context)
         throws StageProcessingException;
-    
+
     /**
      * Depth-first traversal of the DOM tree rooted in an element, applying the
      * visitor when appropriate.  The traversal snapshots the child elements at
@@ -107,7 +90,7 @@ public abstract class AbstractDOMTraversalStage extends AbstractIteratingStage<E
      * @param context context for the traversal
      * @throws StageProcessingException if errors occur during processing
      */
-    private void traverse(@Nonnull final Element element, @Nonnull final TraversalContext context) 
+    private void traverse(@Nonnull final Element element, @Nonnull final C context) 
         throws StageProcessingException {
         final List<Element> children = ElementSupport.getChildElements(element);
         for (final Element child : children) {
@@ -121,9 +104,9 @@ public abstract class AbstractDOMTraversalStage extends AbstractIteratingStage<E
     @Override
     protected void doExecute(final Item<Element> item) throws StageProcessingException {
         final Element docElement = item.unwrap();
-        final TraversalContext context = new TraversalContext(item);
+        final C context = buildContext(item);
         traverse(docElement, context);
-        endTraversal(context);
+        context.end();
     }
 
     /**
@@ -174,12 +157,4 @@ public abstract class AbstractDOMTraversalStage extends AbstractIteratingStage<E
         metadata.put(new ErrorStatus(getId(), prefix + error));
     }
 
-    /**
-     * This method is called once the traversal has finished. It may be overridden by subclasses.
-     *
-     * @param context the context for which the traversal has finished
-     */
-    protected void endTraversal(@Nonnull final TraversalContext context) {
-    }
-
 }
diff --git a/aggregator-pipeline/src/main/java/net/shibboleth/metadata/dom/AbstractDOMValidationStage.java b/aggregator-pipeline/src/main/java/net/shibboleth/metadata/dom/AbstractDOMValidationStage.java
index 985a8ae..2bad57f 100644
--- a/aggregator-pipeline/src/main/java/net/shibboleth/metadata/dom/AbstractDOMValidationStage.java
+++ b/aggregator-pipeline/src/main/java/net/shibboleth/metadata/dom/AbstractDOMValidationStage.java
@@ -22,21 +22,23 @@ import java.util.List;
 
 import javax.annotation.Nonnull;
 
+import com.google.common.base.Predicates;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
+
 import net.shibboleth.metadata.pipeline.StageProcessingException;
 import net.shibboleth.metadata.validate.Validator;
 import net.shibboleth.utilities.java.support.component.ComponentInitializationException;
 import net.shibboleth.utilities.java.support.component.ComponentSupport;
 
-import com.google.common.base.Predicates;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.Iterables;
-
 /**
  * An abstract stage to apply a collection of validators to each object from each item.
- * 
+ *
  * @param <V> type of the object to be validated
- */ 
-public abstract class AbstractDOMValidationStage<V> extends AbstractDOMTraversalStage {
+ * @param <C> the context to carry through the traversal
+ */
+public abstract class AbstractDOMValidationStage<V, C extends DOMTraversalContext>
+    extends AbstractDOMTraversalStage<C> {
 
     /** The list of validators to apply. */
     @Nonnull
@@ -71,7 +73,7 @@ public abstract class AbstractDOMValidationStage<V> extends AbstractDOMTraversal
      * @param context context for the validation
      * @throws StageProcessingException if errors occur during processing
      */
-    protected void applyValidators(@Nonnull final V obj, @Nonnull final TraversalContext context)
+    protected void applyValidators(@Nonnull final V obj, @Nonnull final C context)
             throws StageProcessingException {
         for (final Validator<V> validator: validators) {
             final Validator.Action action = validator.validate(obj, context.getItem(), getId());
diff --git a/aggregator-pipeline/src/main/java/net/shibboleth/metadata/dom/AbstractElementVisitingStage.java b/aggregator-pipeline/src/main/java/net/shibboleth/metadata/dom/AbstractElementVisitingStage.java
index 8532dc8..b390270 100644
--- a/aggregator-pipeline/src/main/java/net/shibboleth/metadata/dom/AbstractElementVisitingStage.java
+++ b/aggregator-pipeline/src/main/java/net/shibboleth/metadata/dom/AbstractElementVisitingStage.java
@@ -27,6 +27,7 @@ import javax.xml.namespace.QName;
 
 import org.w3c.dom.Element;
 
+import net.shibboleth.metadata.Item;
 import net.shibboleth.utilities.java.support.component.ComponentSupport;
 import net.shibboleth.utilities.java.support.logic.Constraint;
 
@@ -34,7 +35,7 @@ import net.shibboleth.utilities.java.support.logic.Constraint;
  * Abstract parent class for stages which visit {@link Element}s named by a
  * collection of {@link QName}s.
  */
-public abstract class AbstractElementVisitingStage extends AbstractDOMTraversalStage {
+public abstract class AbstractElementVisitingStage extends AbstractDOMTraversalStage<DOMTraversalContext> {
 
     /** Collection of element names for those elements we will be visiting. */
     @Nonnull private Set<QName> elementNames = Collections.emptySet();
@@ -88,4 +89,9 @@ public abstract class AbstractElementVisitingStage extends AbstractDOMTraversalS
         return elementNames.contains(q);
     }
 
+    @Override
+    protected DOMTraversalContext buildContext(@Nonnull final Item<Element> item) {
+        return new SimpleDOMTraversalContext(item);
+    }
+
 }
diff --git a/aggregator-pipeline/src/main/java/net/shibboleth/metadata/dom/CRDetectionStage.java b/aggregator-pipeline/src/main/java/net/shibboleth/metadata/dom/CRDetectionStage.java
index 112834b..795bfd0 100644
--- a/aggregator-pipeline/src/main/java/net/shibboleth/metadata/dom/CRDetectionStage.java
+++ b/aggregator-pipeline/src/main/java/net/shibboleth/metadata/dom/CRDetectionStage.java
@@ -17,6 +17,8 @@
 
 package net.shibboleth.metadata.dom;
 
+import javax.annotation.Nonnull;
+
 import org.w3c.dom.Element;
 import org.w3c.dom.NamedNodeMap;
 import org.w3c.dom.Node;
@@ -34,7 +36,40 @@ import net.shibboleth.metadata.pipeline.StageProcessingException;
  * 
  * @see <a href="https://issues.shibboleth.net/jira/browse/SSPCPP-684">SSPCPP-684</a>
  */
-public class CRDetectionStage extends AbstractDOMTraversalStage {
+public class CRDetectionStage extends AbstractDOMTraversalStage<CRDetectionStage.Context> {
+
+    /** Context class for this kind of traversal. */
+    protected static class Context extends SimpleDOMTraversalContext {
+
+        /** <code>true</code> once an error has been detected. */
+        private boolean error;
+
+        /**
+         * Constructor.
+         *
+         * @param contextItem the {@link Item} this traversal is being performed on.
+         */
+        public Context(@Nonnull final Item<Element> contextItem) {
+            super(contextItem);
+        }
+
+        /**
+         * Returns <code>true</code> if an error has been detected.
+         *
+         * @return <code>true</code> if an error has been detected
+         */
+        public boolean hasError() {
+            return error;
+        }
+
+        /**
+         * Record that an error has been detected.
+         */
+        public void setError() {
+            error = true;
+        }
+
+    }
 
     /** Character value we are looking for. */
     private static final char CR = '\r';
@@ -46,9 +81,9 @@ public class CRDetectionStage extends AbstractDOMTraversalStage {
     }
 
     @Override
-    protected void visit(final Element element, final TraversalContext context) throws StageProcessingException {
+    protected void visit(final Element element, final Context context) throws StageProcessingException {
         // Only permit one error; short-circuit any further examinations
-        if (!context.getStash().isEmpty()) {
+        if (context.hasError()) {
             return;
         }
 
@@ -63,7 +98,7 @@ public class CRDetectionStage extends AbstractDOMTraversalStage {
              */
             if (node.getNodeType() == Node.TEXT_NODE && node.getNodeValue().indexOf(CR) >= 0) {
                 addError(item, element, "element text content contains a carriage return character");
-                context.getStash().put(Boolean.TRUE);
+                context.setError();
                 return;
             }
         }
@@ -74,10 +109,15 @@ public class CRDetectionStage extends AbstractDOMTraversalStage {
             final Node attribute = attributes.item(index);
             if (attribute.getNodeValue().indexOf(CR) >= 0) {
                 addError(item, element, "attribute value contains a carriage return character");
-                context.getStash().put(Boolean.TRUE);
+                context.setError();
                 return;
             }
         }
     }
 
+    @Override
+    protected Context buildContext(@Nonnull final Item<Element> item) {
+        return new Context(item);
+    }
+
 }
diff --git a/aggregator-pipeline/src/main/java/net/shibboleth/metadata/dom/ElementWhitespaceTrimmingStage.java b/aggregator-pipeline/src/main/java/net/shibboleth/metadata/dom/DOMTraversalContext.java
similarity index 58%
copy from aggregator-pipeline/src/main/java/net/shibboleth/metadata/dom/ElementWhitespaceTrimmingStage.java
copy to aggregator-pipeline/src/main/java/net/shibboleth/metadata/dom/DOMTraversalContext.java
index 0cbd511..66c4e0c 100644
--- a/aggregator-pipeline/src/main/java/net/shibboleth/metadata/dom/ElementWhitespaceTrimmingStage.java
+++ b/aggregator-pipeline/src/main/java/net/shibboleth/metadata/dom/DOMTraversalContext.java
@@ -21,18 +21,28 @@ import javax.annotation.Nonnull;
 
 import org.w3c.dom.Element;
 
+import net.shibboleth.metadata.Item;
+
 /**
- * Stage to trim leading and trailing whitespace from the text content of named elements
- * within a {@link net.shibboleth.metadata.dom.DOMElementItem}.
+ * The context for a particular DOM traversal.
+ *
+ * Implementations may add additional fields and methods to the definition
+ * of a {@link DOMTraversalContext}, and may define {@link #end()} to perform
+ * operations at the end of the traversal.
  */
-public class ElementWhitespaceTrimmingStage extends AbstractElementVisitingStage {
-
-    /** Visitor to apply to each visited element. */
-    @Nonnull private final ElementVisitor visitor = new WhitespaceTrimmingVisitor();
+public interface DOMTraversalContext {
 
-    @Override
-    protected void visit(@Nonnull final Element e, @Nonnull final TraversalContext context) {
-        visitor.visitElement(e, context.getItem());
-    }
+    /**
+     * Get the {@link Item} this traversal is being performed on.
+     * 
+     * @return the context {@link Item}
+     */
+    @Nonnull Item<Element> getItem();
 
+    /**
+     * Perform any clean-up or final operations for the traversal.
+     *
+     * Called at the end of the traversal, may be overridden by subclasses.
+     */
+    void end();
 }
diff --git a/aggregator-pipeline/src/main/java/net/shibboleth/metadata/dom/ElementWhitespaceTrimmingStage.java b/aggregator-pipeline/src/main/java/net/shibboleth/metadata/dom/ElementWhitespaceTrimmingStage.java
index 0cbd511..f849f17 100644
--- a/aggregator-pipeline/src/main/java/net/shibboleth/metadata/dom/ElementWhitespaceTrimmingStage.java
+++ b/aggregator-pipeline/src/main/java/net/shibboleth/metadata/dom/ElementWhitespaceTrimmingStage.java
@@ -31,7 +31,7 @@ public class ElementWhitespaceTrimmingStage extends AbstractElementVisitingStage
     @Nonnull private final ElementVisitor visitor = new WhitespaceTrimmingVisitor();
 
     @Override
-    protected void visit(@Nonnull final Element e, @Nonnull final TraversalContext context) {
+    protected void visit(@Nonnull final Element e, @Nonnull final DOMTraversalContext context) {
         visitor.visitElement(e, context.getItem());
     }
 
diff --git a/aggregator-pipeline/src/main/java/net/shibboleth/metadata/dom/ElementsStrippingStage.java b/aggregator-pipeline/src/main/java/net/shibboleth/metadata/dom/ElementsStrippingStage.java
index 9706f87..22a8d7a 100644
--- a/aggregator-pipeline/src/main/java/net/shibboleth/metadata/dom/ElementsStrippingStage.java
+++ b/aggregator-pipeline/src/main/java/net/shibboleth/metadata/dom/ElementsStrippingStage.java
@@ -17,8 +17,10 @@
 
 package net.shibboleth.metadata.dom;
 
+import java.util.ArrayList;
 import java.util.Collection;
 import java.util.HashSet;
+import java.util.List;
 import java.util.Set;
 
 import javax.annotation.Nonnull;
@@ -27,6 +29,7 @@ import javax.annotation.concurrent.ThreadSafe;
 
 import org.w3c.dom.Element;
 
+import net.shibboleth.metadata.Item;
 import net.shibboleth.utilities.java.support.annotation.constraint.NotEmpty;
 import net.shibboleth.utilities.java.support.component.ComponentInitializationException;
 import net.shibboleth.utilities.java.support.component.ComponentSupport;
@@ -51,7 +54,42 @@ import net.shibboleth.utilities.java.support.primitive.StringSupport;
  * do <i>not</i> appear in the collection of names.
  */
 @ThreadSafe
-public class ElementsStrippingStage extends AbstractDOMTraversalStage {
+public class ElementsStrippingStage extends AbstractDOMTraversalStage<ElementsStrippingStage.Context> {
+
+    /** Context class for this kind of traversal. */
+    protected static class Context extends SimpleDOMTraversalContext {
+
+        /**
+         * List of {@link Element}s to be removed from the document at the
+         * end of the traversal.
+         */
+        private List<Element> elements = new ArrayList<>();
+
+        /**
+         * Constructor.
+         *
+         * @param contextItem the {@link Item} we are traversing
+         */
+        public Context(@Nonnull final Item<Element> contextItem) {
+            super(contextItem);
+        }
+
+        /**
+         * Add the given {@link Element} to the list for later removal.
+         *
+         * @param element the {@link Element} to be removed
+         */
+        protected void add(@Nonnull final Element element) {
+            elements.add(element);
+        }
+
+        @Override
+        public void end() {
+            for (final Element element : elements) {
+                element.getParentNode().removeChild(element);
+            }
+        }
+    }
 
     /** Namespace of the elements to strip. */
     private String elementNamespace;
@@ -106,7 +144,7 @@ public class ElementsStrippingStage extends AbstractDOMTraversalStage {
     }
 
     /**
-     * Set whether the {@link #names} are to be used as a whitelist.
+     * Set whether the {@link #elementNames} are to be used as a whitelist.
      *
      * The default behaviour is for the names to be used as a blacklist.
      *
@@ -117,7 +155,7 @@ public class ElementsStrippingStage extends AbstractDOMTraversalStage {
     }
 
     /**
-     * Indicates whether the {@link #names} are being used as a whitelist.
+     * Indicates whether the {@link #elementNames} are being used as a whitelist.
      *
      * @return <code>true</code> if the names are being used as a whitelist
      */
@@ -138,15 +176,8 @@ public class ElementsStrippingStage extends AbstractDOMTraversalStage {
 
     @Override
     protected void visit(@Nonnull final Element element,
-            @Nonnull final TraversalContext context) {
-        context.getStash().put(element);
-    }
-
-    @Override
-    protected void endTraversal(@Nonnull final TraversalContext context) {
-        for (final Element element : context.getStash().get(Element.class)) {
-            element.getParentNode().removeChild(element);
-        }
+            @Nonnull final Context context) {
+        context.add(element);
     }
 
     @Override
@@ -165,4 +196,9 @@ public class ElementsStrippingStage extends AbstractDOMTraversalStage {
         }
     }
 
+    @Override
+    protected Context buildContext(@Nonnull final Item<Element> item) {
+        return new Context(item);
+    }
+
 }
diff --git a/aggregator-pipeline/src/main/java/net/shibboleth/metadata/dom/ElementWhitespaceTrimmingStage.java b/aggregator-pipeline/src/main/java/net/shibboleth/metadata/dom/SimpleDOMTraversalContext.java
similarity index 60%
copy from aggregator-pipeline/src/main/java/net/shibboleth/metadata/dom/ElementWhitespaceTrimmingStage.java
copy to aggregator-pipeline/src/main/java/net/shibboleth/metadata/dom/SimpleDOMTraversalContext.java
index 0cbd511..24b1f6a 100644
--- a/aggregator-pipeline/src/main/java/net/shibboleth/metadata/dom/ElementWhitespaceTrimmingStage.java
+++ b/aggregator-pipeline/src/main/java/net/shibboleth/metadata/dom/SimpleDOMTraversalContext.java
@@ -21,18 +21,32 @@ import javax.annotation.Nonnull;
 
 import org.w3c.dom.Element;
 
+import net.shibboleth.metadata.Item;
+
 /**
- * Stage to trim leading and trailing whitespace from the text content of named elements
- * within a {@link net.shibboleth.metadata.dom.DOMElementItem}.
+ * A simple implementation of the {@link DOMTraversalContext} interface.
  */
-public class ElementWhitespaceTrimmingStage extends AbstractElementVisitingStage {
+public class SimpleDOMTraversalContext implements DOMTraversalContext {
+
+    /** The {@link Item} this traversal is being performed on. */
+    private final Item<Element> item;
+
+    /**
+     * Constructor.
+     * 
+     * @param contextItem the {@link Item} this traversal is being performed on.
+     */
+    public SimpleDOMTraversalContext(@Nonnull final Item<Element> contextItem) {
+        item = contextItem;
+    }
 
-    /** Visitor to apply to each visited element. */
-    @Nonnull private final ElementVisitor visitor = new WhitespaceTrimmingVisitor();
+    @Override
+    public Item<Element> getItem() {
+        return item;
+    }
 
     @Override
-    protected void visit(@Nonnull final Element e, @Nonnull final TraversalContext context) {
-        visitor.visitElement(e, context.getItem());
+    public void end() {
     }
 
 }
diff --git a/aggregator-pipeline/src/main/java/net/shibboleth/metadata/dom/ds/X509ValidationStage.java b/aggregator-pipeline/src/main/java/net/shibboleth/metadata/dom/ds/X509ValidationStage.java
index db853a8..117ded2 100644
--- a/aggregator-pipeline/src/main/java/net/shibboleth/metadata/dom/ds/X509ValidationStage.java
+++ b/aggregator-pipeline/src/main/java/net/shibboleth/metadata/dom/ds/X509ValidationStage.java
@@ -21,27 +21,80 @@ import java.io.ByteArrayInputStream;
 import java.security.cert.CertificateException;
 import java.security.cert.CertificateFactory;
 import java.security.cert.X509Certificate;
+import java.util.HashMap;
+import java.util.Map;
 
 import javax.annotation.Nonnull;
 
+import org.w3c.dom.Element;
+
 import net.shibboleth.metadata.ErrorStatus;
+import net.shibboleth.metadata.Item;
 import net.shibboleth.metadata.dom.AbstractDOMValidationStage;
+import net.shibboleth.metadata.dom.SimpleDOMTraversalContext;
 import net.shibboleth.metadata.pipeline.StageProcessingException;
 import net.shibboleth.utilities.java.support.codec.Base64Support;
 import net.shibboleth.utilities.java.support.component.ComponentInitializationException;
 
-import org.w3c.dom.Element;
-
 /**
  * Stage to apply a collection of validators to each X.509 certificate in items.
  * 
  * Each X.509 certificate is processed only once per item, so that duplicate status messages are suppressed.
  */ 
-public class X509ValidationStage extends AbstractDOMValidationStage<X509Certificate> {
+public class X509ValidationStage extends AbstractDOMValidationStage<X509Certificate, X509ValidationStage.Context> {
+
+    /** Context class for this kind of traversal. */
+    protected static class Context extends SimpleDOMTraversalContext {
+
+        /**
+         * Collection of certificates we have already seen.
+         *
+         * This is used to ensure that we only issue one error for each different certificate;
+         * duplicates are ignored.
+         *
+         * This is achieved by using a {@link Map} from certificates to themselves and
+         * detecting presence using {@link Map#containsKey}.
+         */
+        private Map<X509Certificate, X509Certificate> certMap = new HashMap<>();
+
+        /**
+         * Constructor.
+         *
+         * @param contextItem the {@link Item} this traversal is being performed on.
+         */
+        public Context(@Nonnull final Item<Element> contextItem) {
+            super(contextItem);
+        }
+
+        /**
+         * Returns whether we have seen this certificate before.
+         *
+         * @param cert {@link X509Certificate} to check for.
+         * @return <code>true</code> if we have seen this certificate before.
+         */
+        protected boolean haveSeen(@Nonnull final X509Certificate cert) {
+            return certMap.containsKey(cert);
+        }
+
+        /**
+         * Add a certificate to the list of certificates we have seen and processed already.
+         *
+         * @param cert {@link X509Certificate} to add to the list of already seen certificates.
+         */
+        protected void add(@Nonnull final X509Certificate cert) {
+            certMap.put(cert, cert);
+        }
+
+    }
 
     /** Certificate factory to use to convert to X.509 certificates. */
     private CertificateFactory factory;
-    
+
+    @Override
+    protected Context buildContext(@Nonnull final Item<Element> item) {
+        return new Context(item);
+    }
+
     @Override
     protected boolean applicable(@Nonnull final Element e) {
         return XMLDSIGSupport.XML_DSIG_NS.equals(e.getNamespaceURI()) &&
@@ -49,7 +102,7 @@ public class X509ValidationStage extends AbstractDOMValidationStage<X509Certific
     }
 
     @Override
-    protected void visit(@Nonnull final Element element, @Nonnull final TraversalContext context) 
+    protected void visit(@Nonnull final Element element, @Nonnull final Context context) 
         throws StageProcessingException {
         final String text = element.getTextContent();
         final byte[] data = Base64Support.decode(text);
@@ -57,8 +110,8 @@ public class X509ValidationStage extends AbstractDOMValidationStage<X509Certific
             final X509Certificate cert =
                     (X509Certificate) factory.generateCertificate(new ByteArrayInputStream(data));
             // only process each certificate once per item
-            if (!context.getStash().containsValue(cert)) {
-                context.getStash().put(cert);
+            if (!context.haveSeen(cert)) {
+                context.add(cert);
                 applyValidators(cert, context);
             }
         } catch (final CertificateException e) {
diff --git a/aggregator-pipeline/src/test/java/net/shibboleth/metadata/dom/AbstractDOMValidationStageTest.java b/aggregator-pipeline/src/test/java/net/shibboleth/metadata/dom/AbstractDOMValidationStageTest.java
index 3d0cd8e..708b8e4 100644
--- a/aggregator-pipeline/src/test/java/net/shibboleth/metadata/dom/AbstractDOMValidationStageTest.java
+++ b/aggregator-pipeline/src/test/java/net/shibboleth/metadata/dom/AbstractDOMValidationStageTest.java
@@ -37,7 +37,7 @@ public class AbstractDOMValidationStageTest extends BaseDOMTest {
         super(AbstractDOMValidationStage.class);
     }
 
-    private static class StringValidationStage extends AbstractDOMValidationStage<String> {
+    private static class StringValidationStage extends AbstractDOMValidationStage<String, DOMTraversalContext> {
 
         @Override
         protected boolean applicable(Element element) {
@@ -45,10 +45,15 @@ public class AbstractDOMValidationStageTest extends BaseDOMTest {
         }
 
         @Override
-        protected void visit(Element element, TraversalContext context) throws StageProcessingException {
+        protected void visit(Element element, DOMTraversalContext context) throws StageProcessingException {
             applyValidators(element.getTextContent(), context);
         }
-        
+
+        @Override
+        protected DOMTraversalContext buildContext(Item<Element> item) {
+            return new SimpleDOMTraversalContext(item);
+        }
+
     }
 
     private static class FirstStringValidator extends BaseValidator implements Validator<String> {

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


More information about the commits mailing list