[java-oidc-common] branch main updated: Add metadata validate predicates to the metadata cache

Phil Smart philip.smart at jisc.ac.uk
Thu Dec 2 14:06:38 UTC 2021


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

philsmart pushed a commit to branch main
in repository java-oidc-common.

View the commit online:
http://git.shibboleth.net/view/?p=java-oidc-common.git;a=commit;h=77c5245342040a6bb60cc7e68d2eecfa2ef2663a

The following commit(s) were added to refs/heads/main by this push:
     new 77c5245  Add metadata validate predicates to the metadata cache
77c5245 is described below

commit 77c5245342040a6bb60cc7e68d2eecfa2ef2663a
Author: Phil Smart <philip.smart at jisc.ac.uk>
AuthorDate: Thu Dec 2 14:06:32 2021 +0000

    Add metadata validate predicates to the metadata cache
    
    Create defaults of AlwaysTrue to the cache specification.
---
 .../metadata/cache/impl/AbstractMetadataCache.java |  78 ++++++++++--
 .../cache/impl/BaseMetadataCacheBuilderSpec.java   |  34 +++++-
 .../metadata/cache/impl/BatchMetadataCache.java    | 123 ++++++++++++++++---
 .../cache/impl/BatchMetadataCacheBuilder.java      |   4 +-
 .../cache/impl/BatchMetadataCacheBuilderSpec.java  |  34 +++++-
 .../cache/impl/DefaultFileLoadingStrategy.java     |   6 +-
 .../metadata/cache/impl/DynamicMetadataCache.java  |  22 ++--
 .../cache/impl/DynamicMetadataCacheBuilder.java    |   1 +
 .../impl/DynamicMetadataCacheBuilderSpec.java      |   1 +
 .../cache/impl/BatchMetadataCacheTest.java         |   7 ++
 .../cache/impl/DynamicMetadataCacheTest.java       |   6 +-
 .../metadata/impl/OIDCMapMetadataResolverTest.java |   3 +
 .../impl/OIDCProviderMetadataResolverTest.java     | 132 ++++++++++++++++++---
 13 files changed, 391 insertions(+), 60 deletions(-)

diff --git a/oidc-common-metadata-impl/src/main/java/net/shibboleth/oidc/metadata/cache/impl/AbstractMetadataCache.java b/oidc-common-metadata-impl/src/main/java/net/shibboleth/oidc/metadata/cache/impl/AbstractMetadataCache.java
index 3d84721..e145ae5 100644
--- a/oidc-common-metadata-impl/src/main/java/net/shibboleth/oidc/metadata/cache/impl/AbstractMetadataCache.java
+++ b/oidc-common-metadata-impl/src/main/java/net/shibboleth/oidc/metadata/cache/impl/AbstractMetadataCache.java
@@ -17,10 +17,10 @@
 
 package net.shibboleth.oidc.metadata.cache.impl;
 
-import java.time.Duration;
 import java.time.Instant;
 import java.util.ArrayList;
 import java.util.Collections;
+import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
 import java.util.concurrent.Executors;
@@ -28,6 +28,7 @@ import java.util.concurrent.ScheduledExecutorService;
 import java.util.function.BiConsumer;
 import java.util.function.BiFunction;
 import java.util.function.Function;
+import java.util.function.Predicate;
 
 import javax.annotation.Nonnull;
 import javax.annotation.Nullable;
@@ -40,6 +41,7 @@ import com.google.common.util.concurrent.ThreadFactoryBuilder;
 import net.shibboleth.oidc.metadata.BackingStore;
 import net.shibboleth.oidc.metadata.MetadataManagementData;
 import net.shibboleth.oidc.metadata.cache.MetadataCache;
+import net.shibboleth.oidc.metadata.cache.MetadataCacheException;
 import net.shibboleth.oidc.metadata.filter.MetadataFilterContext;
 import net.shibboleth.oidc.metadata.filter.MetadataSource;
 import net.shibboleth.utilities.java.support.annotation.constraint.NonnullAfterInit;
@@ -66,7 +68,6 @@ import net.shibboleth.utilities.java.support.resolver.CriteriaSet;
  * @param <IdentifierType> the metadata identifier type
  * @param <MetadataType> the metadata type
  */
-//TODO do not set a maximum cache size? make eviction harder
 public abstract class AbstractMetadataCache<IdentifierType, MetadataType> 
                 extends AbstractIdentifiableInitializableComponent implements MetadataCache<MetadataType> {
     
@@ -96,6 +97,9 @@ public abstract class AbstractMetadataCache<IdentifierType, MetadataType>
     /** A strategy to filter metadata. */
     @NonnullAfterInit private BiFunction<MetadataType, MetadataFilterContext , MetadataType> metadataFilterStrategy;
     
+    /** Is the metadata valid? */
+    @NonnullAfterInit private Predicate<MetadataType> metadataValidPredicate;
+    
     /** A single threaded executor service for running background cache tasks.*/
     @NonnullAfterInit private ScheduledExecutorService executorService;  
     
@@ -164,6 +168,9 @@ public abstract class AbstractMetadataCache<IdentifierType, MetadataType>
         if (refreshDelayFactor == null || backingStore == null) {
             throw new ComponentInitializationException("Metadata cache not property initialized");
         }
+        if (metadataValidPredicate == null) {
+            throw new ComponentInitializationException("Metadata validation predicate can not be null");
+        }
         
         // create a schedular for thread tasks.
         if (createOwnSchedular) {
@@ -189,7 +196,26 @@ public abstract class AbstractMetadataCache<IdentifierType, MetadataType>
         return executorService;
     }
     
-
+    /**
+    * Set the predicate which determines if a piece of metadata is valid or not.
+    * 
+    * @param predicate the predicate.
+    */
+   public void setMetadataValidPredicate(@Nonnull final Predicate<MetadataType> predicate) {
+       ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
+       ComponentSupport.ifDestroyedThrowDestroyedComponentException(this);
+       
+      metadataValidPredicate = Constraint.isNotNull(predicate, "Is metadata valid predicate can not be null");
+   }
+   
+   /**
+    * Get the predicate which determines if a piece of metadata is valid or not.
+    * 
+    * @return the predicate.
+    */
+   @Nonnull protected Predicate<MetadataType> getMetadataValidPredicate() {
+       return metadataValidPredicate;
+   }
 
     /**
      * Set the {@link CriteriaSet} to IdentifierType strategy.
@@ -301,16 +327,52 @@ public abstract class AbstractMetadataCache<IdentifierType, MetadataType>
     }
     
     /**
-     * Lookup the specified identifier from the index. The returned list will be a copy of what is stored in the backing
-     * index, and so the list is safe to be manipulated by callers.
+     * Get list of descriptors matching the identifier.
+     * 
+     * @param identifier the identifier to lookup
+     * 
+     * @return a list of metadata
+     * 
+     * @throws MetadataCacheException if an error occurs.
+     */
+    @Nonnull @NonnullElements protected List<MetadataType> lookupIdentifier(
+            @Nonnull @NotEmpty final IdentifierType identifier) throws MetadataCacheException {
+        
+        if (!isInitialized()) {
+            throw new MetadataCacheException("Metadata resolver has not been initialized");
+        }
+        
+        final List<MetadataType> metadata = lookupIndexedIdentifier(identifier);
+       
+        if (metadata.isEmpty()) {
+            log.debug("{} Metadata cache does not contain entry with the identifier: {}", 
+                    getLogPrefix(), identifier);
+            return metadata;
+        }
+        final Iterator<MetadataType> metadataIter = metadata.iterator();
+        
+        while (metadataIter.hasNext()) {
+            final MetadataType individualMetadata = metadataIter.next();
+            if (!metadataValidPredicate.test(individualMetadata)) {
+                log.warn("{} Metadata cache contained an entry with the identifier: {}, " 
+                        + " but it was no longer valid", getLogPrefix(), identifier);
+                metadataIter.remove();
+            }
+        }
+        return metadata;
+    }
+    
+    /**
+     * Lookup the specified entityID from the index. The returned list will be a copy of what is stored in the backing
+     * index, and is safe to be manipulated by callers.
      * 
-     * @param identifier the entityID to lookup
+     * @param identifier the identifier to lookup
      * 
      * @return list copy of indexed metadata, may be empty, will never be null
      */
     @Nonnull @NonnullElements protected List<MetadataType> lookupIndexedIdentifier(
-            @Nonnull @NotEmpty final IdentifierType identifier) {
-        final List<MetadataType> metadata = backingStore.getIndexedValues().get(identifier);
+            @Nonnull final IdentifierType identifier) {
+        final List<MetadataType> metadata = getBackingStore().getIndexedValues().get(identifier);
         if (metadata != null) {
             return new ArrayList<>(metadata);
         }
diff --git a/oidc-common-metadata-impl/src/main/java/net/shibboleth/oidc/metadata/cache/impl/BaseMetadataCacheBuilderSpec.java b/oidc-common-metadata-impl/src/main/java/net/shibboleth/oidc/metadata/cache/impl/BaseMetadataCacheBuilderSpec.java
index bf5583f..d24a10b 100644
--- a/oidc-common-metadata-impl/src/main/java/net/shibboleth/oidc/metadata/cache/impl/BaseMetadataCacheBuilderSpec.java
+++ b/oidc-common-metadata-impl/src/main/java/net/shibboleth/oidc/metadata/cache/impl/BaseMetadataCacheBuilderSpec.java
@@ -22,10 +22,13 @@ import java.util.List;
 import java.util.function.BiConsumer;
 import java.util.function.BiFunction;
 import java.util.function.Function;
+import java.util.function.Predicate;
 
 import javax.annotation.Nonnull;
 import javax.annotation.Nullable;
 
+import com.google.common.base.Predicates;
+
 import net.shibboleth.oidc.metadata.filter.MetadataFilterContext;
 import net.shibboleth.utilities.java.support.annotation.constraint.NotEmpty;
 import net.shibboleth.utilities.java.support.annotation.constraint.Positive;
@@ -61,18 +64,41 @@ public abstract class BaseMetadataCacheBuilderSpec<IdentifierType, MetadataType>
      * A hook that is executed just before a cache entry will been removed/invalidated/evicted.
      * The metadata list could be {@literal null}, the identifier is never {@literal null}.
      */
-    @Nullable private BiConsumer<List<MetadataType>, IdentifierType> metadataBeforeRemovalHook;
+    @Nullable private BiConsumer<List<MetadataType>, IdentifierType> metadataBeforeRemovalHook;    
     
+    /** Is the metadata valid? Defaults to true. */
+    @Nonnull private Predicate<MetadataType> metadataValidPredicate;
     
     /** Constructor.*/
     protected BaseMetadataCacheBuilderSpec() {
         // defaults       
         refreshDelayFactor = 0.75f;         
+        cacheId = "Uknown";
         // create a default direct in/out filter
         metadataFilterStrategy = (metadata, context) -> metadata;  
+        // create default TRUE is metadata valid predicate
+        metadataValidPredicate = Predicates.alwaysTrue();
 
     }
     
+    /**
+     * Set the predicate which determines if a piece of metadata is valid or not.
+     * 
+     * @param predicate the predicate.
+     */
+    public void setMetadataValidPredicate(@Nonnull final Predicate<MetadataType> predicate) {
+       metadataValidPredicate = Constraint.isNotNull(predicate, "Is metadata valid predicate can not be null");
+    }
+    
+    /**
+     * Get the predicate which determines if a piece of metadata is valid or not.
+     * 
+     * @return the predicate.
+     */
+    @Nonnull protected Predicate<MetadataType> getMetadataValidPredicate() {
+        return metadataValidPredicate;
+    }
+    
     /**
      * Set the cache identifier of the cache this specification builds.
      * 
@@ -87,7 +113,7 @@ public abstract class BaseMetadataCacheBuilderSpec<IdentifierType, MetadataType>
      * 
      * @return the cache identifier.
      */
-    @Nonnull public String getCacheId() {
+    @Nonnull protected String getCacheId() {
         return cacheId;
     }
     
@@ -108,7 +134,7 @@ public abstract class BaseMetadataCacheBuilderSpec<IdentifierType, MetadataType>
      * 
      * @return the hook.
      */
-    @Nullable public BiConsumer<List<MetadataType>, IdentifierType> getMetadataBeforeRemovalHook() {
+    @Nullable protected BiConsumer<List<MetadataType>, IdentifierType> getMetadataBeforeRemovalHook() {
         return metadataBeforeRemovalHook;
     }
     
@@ -129,7 +155,7 @@ public abstract class BaseMetadataCacheBuilderSpec<IdentifierType, MetadataType>
      * 
      * @return the metadata filtering strategy
      */
-    @Nonnull public BiFunction<MetadataType, MetadataFilterContext, MetadataType> getMetadataFilterStrategy() {
+    @Nonnull protected BiFunction<MetadataType, MetadataFilterContext, MetadataType> getMetadataFilterStrategy() {
         return metadataFilterStrategy;
     }
 
diff --git a/oidc-common-metadata-impl/src/main/java/net/shibboleth/oidc/metadata/cache/impl/BatchMetadataCache.java b/oidc-common-metadata-impl/src/main/java/net/shibboleth/oidc/metadata/cache/impl/BatchMetadataCache.java
index ead7376..4b1b648 100644
--- a/oidc-common-metadata-impl/src/main/java/net/shibboleth/oidc/metadata/cache/impl/BatchMetadataCache.java
+++ b/oidc-common-metadata-impl/src/main/java/net/shibboleth/oidc/metadata/cache/impl/BatchMetadataCache.java
@@ -25,6 +25,7 @@ import java.util.List;
 import java.util.concurrent.ScheduledExecutorService;
 import java.util.concurrent.TimeUnit;
 import java.util.function.Function;
+import java.util.function.Predicate;
 
 import javax.annotation.Nonnull;
 import javax.annotation.Nullable;
@@ -90,6 +91,9 @@ public class BatchMetadataCache<IdentifierType, MetadataType>
     /** Determine the expiration time of the source batch loaded metadata.*/
     @NonnullAfterInit private Function<byte[], Instant> sourceMetadataExpiryStrategy;
     
+    /** Is the raw metadata bytes from the source valid? */
+    @NonnullAfterInit private Predicate<byte[]> sourceMetadataValidPredicate;
+    
     /** 
      * Is a match based on an identifier required? If not, 
      * all known metadata will be returned. Defaults to true - a match on identifier is required. 
@@ -97,8 +101,7 @@ public class BatchMetadataCache<IdentifierType, MetadataType>
     @Nonnull private boolean matchOnIdentifierRequired;
    
 
-    /**
-     * 
+    /** 
      * Constructor.
      *
      * @param store the backing store.
@@ -136,6 +139,9 @@ public class BatchMetadataCache<IdentifierType, MetadataType>
         if (parsingStrategy == null) {
             throw new ComponentInitializationException("Parsing strategy can not be null");
         }
+        if (sourceMetadataValidPredicate == null) {
+            throw new ComponentInitializationException("Is source metadata valid predicate can not be null");
+        }
    
         try {
             loadCache();
@@ -144,6 +150,28 @@ public class BatchMetadataCache<IdentifierType, MetadataType>
         }      
     }
     
+    /**
+     * Set the predicate which determines if the source metadata is valid or not.
+     * 
+     * @param predicate the predicate.
+     */
+    public void setSourceMetadataValidPredicate(@Nonnull final Predicate<byte[]> predicate) {
+        ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
+        ComponentSupport.ifDestroyedThrowDestroyedComponentException(this);
+        
+       sourceMetadataValidPredicate = 
+               Constraint.isNotNull(predicate, "Is source metadata valid predicate can not be null");
+    }
+    
+    /**
+     * Get the predicate which determines if the source metadata is valid or not.
+     * 
+     * @return the predicate.
+     */
+    @NonnullAfterInit protected Predicate<byte[]> getSourceMetadataValidPredicate() {
+        return sourceMetadataValidPredicate;
+    }
+    
     /**
      * Set the strategy used to batch load metadata from a source.
      * 
@@ -156,6 +184,15 @@ public class BatchMetadataCache<IdentifierType, MetadataType>
         loadingStrategy = Constraint.isNotNull(strategy, "Loading strategy can not be null");
     }
     
+    /**
+     * Get the loading strategy.
+     * 
+     * @return the loading strategy
+     */
+    @NonnullAfterInit protected LoadingStrategy getLoadingStrategy() {
+        return loadingStrategy;
+    }
+    
     /**
      * Set the  strategy used to convert raw metadata in bytes to the given metadata type.
      * 
@@ -168,6 +205,15 @@ public class BatchMetadataCache<IdentifierType, MetadataType>
         parsingStrategy = Constraint.isNotNull(strategy, "Parsing strategy can not be null");
     }
     
+    /**
+     * Get the parsing strategy.
+     * 
+     * @return the parsing strategy
+     */
+    @NonnullAfterInit protected Function<byte[], List<MetadataType>> getParsingStrategy() {
+        return parsingStrategy;
+    }
+    
     /**
      * Set a strategy to find the metadata expiry date from a source metadata document as bytes.
      * 
@@ -181,7 +227,15 @@ public class BatchMetadataCache<IdentifierType, MetadataType>
                 Constraint.isNotNull(strategy, "Source metadata expiry strategy can not be null");
     }
     
-    
+    /**
+     * Get the source metadata expiry strategy.
+     * 
+     * @return the source metadata expiry strategy
+     */
+    @NonnullAfterInit protected Function<byte[], Instant> getSourceMetadataExpiryStrategy() {
+        return sourceMetadataExpiryStrategy;
+    }
+        
     /**
      * Set if a match on identifier is required in order to return results. If false and there are no
      * identifiers in the criteria to match on, all cached entries will be returned. 
@@ -195,6 +249,15 @@ public class BatchMetadataCache<IdentifierType, MetadataType>
         matchOnIdentifierRequired = required;
     }
     
+    /**
+     * Is a match on identifier required?
+     * 
+     * @return true if it is, false otherwise
+     */
+    protected boolean isMatchOnIdentifierRequired() {
+        return matchOnIdentifierRequired;
+    }
+    
         
     /**
      * {@inheritDoc}
@@ -217,6 +280,15 @@ public class BatchMetadataCache<IdentifierType, MetadataType>
         Constraint.isFalse(delay == null || delay.isNegative(), "Minimum refresh delay must be greater than 0");
         minRefreshDelay = delay;
     }
+    
+    /**
+     * Get the minimum amount of time between refreshes.
+     * 
+     * @return the minimum refresh delay
+     */
+    @NonnullAfterInit protected Duration getMinRefreshDelay() {
+        return minRefreshDelay;
+    }
 
     /**
      * Sets the maximum amount of time between refresh intervals.
@@ -230,6 +302,15 @@ public class BatchMetadataCache<IdentifierType, MetadataType>
         Constraint.isFalse(delay == null || delay.isNegative(), "Maximum refresh delay must be greater than 0");
         maxRefreshDelay = delay;
     }
+    
+    /**
+     * Get the maximum amount of time between refresh intervals.
+     * 
+     * @return the delay maximum amount of time, in milliseconds, between refresh intervals
+     */
+    @NonnullAfterInit protected Duration getMaxRefreshDelay() {
+        return maxRefreshDelay;
+    }
 
     //TODO we sure get does not need synchornization with loadingCache?
     @Override @Nonnull @NonnullElements
@@ -244,7 +325,7 @@ public class BatchMetadataCache<IdentifierType, MetadataType>
 
         if (identifier != null) {
 
-            final List<MetadataType> allMetadata = lookupIndexedIdentifier(identifier);
+            final List<MetadataType> allMetadata = lookupIdentifier(identifier);
             if (allMetadata.isEmpty()) {
                 log.debug("{} No metadata candidates for '{}' found, returning empty result", 
                         getLogPrefix(), identifier);
@@ -290,21 +371,29 @@ public class BatchMetadataCache<IdentifierType, MetadataType>
             
             // Any exception here is caught
             final byte[] rawFetchedMetadata = loadingStrategy.apply(createLoadingContext());
-            if (rawFetchedMetadata != null) {
-                final List<MetadataType> parsedMetadata = parsingStrategy.apply(rawFetchedMetadata);
-                if (parsedMetadata != null) {
-                    log.info("{} Parsed {} metadata candidates, loading into cache", 
-                            getLogPrefix(), parsedMetadata.size());
-                    freshLoad(parsedMetadata);
-                    // Store away the original, raw, metadata bytes.
-                    getBackingStore().setOriginalValue(rawFetchedMetadata);
-                } 
+            
+            if (sourceMetadataValidPredicate.test(rawFetchedMetadata)) {            
+                if (rawFetchedMetadata != null) {
+                    final List<MetadataType> parsedMetadata = parsingStrategy.apply(rawFetchedMetadata);
+                    if (parsedMetadata != null) {
+                        log.info("{} Parsed {} metadata candidates, loading into cache", 
+                                getLogPrefix(), parsedMetadata.size());
+                        freshLoad(parsedMetadata);
+                        // Store away the original, raw, metadata bytes.
+                        getBackingStore().setOriginalValue(rawFetchedMetadata);
+                    } 
+                } else {
+                    log.info("{} Metadata has not changed since last refresh", getLogPrefix());
+                }
+                // Compute metadata expiration from whatever is in the cache (updated or not) will
+                // remain null if no cached original value.            
+                metadataExpiration = sourceMetadataExpiryStrategy.apply(getBackingStore().getOriginalValue());
             } else {
-                log.info("{} Metadata has not changed since last refresh", getLogPrefix());
+                // Metadata is not valid
+                log.warn("{} Source metadata is not valid");
+                //TODO MUST FINISH THIS !!
             }
-            // Compute metadata expiration from whatever is in the cache (updated or not) will
-            // remain null if no cached original value.            
-            metadataExpiration = sourceMetadataExpiryStrategy.apply(getBackingStore().getOriginalValue());
+            
         } catch (final Throwable t) {
             log.error("{} Error loading or parsing metadata",getLogPrefix(), t);
             if (t instanceof Exception) {
diff --git a/oidc-common-metadata-impl/src/main/java/net/shibboleth/oidc/metadata/cache/impl/BatchMetadataCacheBuilder.java b/oidc-common-metadata-impl/src/main/java/net/shibboleth/oidc/metadata/cache/impl/BatchMetadataCacheBuilder.java
index 300b17d..fd84e19 100644
--- a/oidc-common-metadata-impl/src/main/java/net/shibboleth/oidc/metadata/cache/impl/BatchMetadataCacheBuilder.java
+++ b/oidc-common-metadata-impl/src/main/java/net/shibboleth/oidc/metadata/cache/impl/BatchMetadataCacheBuilder.java
@@ -74,7 +74,9 @@ public final class BatchMetadataCacheBuilder  {
             cache.setMetadataFilterStrategy(spec.getMetadataFilterStrategy());
             cache.setMetadataBeforeRemovalHook(spec.getMetadataBeforeRemovalHook());
             cache.setMatchOnIdentifierRequired(spec.isMatchOnIdentifierRequired());
-            cache.setId("BatchMetadataCache");
+            cache.setMetadataValidPredicate(spec.getMetadataValidPredicate());
+            cache.setSourceMetadataValidPredicate(spec.getSourceMetadataValidPredicate());
+            cache.setId(spec.getCacheId());
             cache.initialize();
             return cache;
         }
diff --git a/oidc-common-metadata-impl/src/main/java/net/shibboleth/oidc/metadata/cache/impl/BatchMetadataCacheBuilderSpec.java b/oidc-common-metadata-impl/src/main/java/net/shibboleth/oidc/metadata/cache/impl/BatchMetadataCacheBuilderSpec.java
index 7e3f19b..b1213f0 100644
--- a/oidc-common-metadata-impl/src/main/java/net/shibboleth/oidc/metadata/cache/impl/BatchMetadataCacheBuilderSpec.java
+++ b/oidc-common-metadata-impl/src/main/java/net/shibboleth/oidc/metadata/cache/impl/BatchMetadataCacheBuilderSpec.java
@@ -22,13 +22,16 @@ import java.time.Duration;
 import java.time.Instant;
 import java.util.List;
 import java.util.function.Function;
+import java.util.function.Predicate;
 
 import javax.annotation.Nonnull;
 import javax.annotation.Nullable;
 
+import com.google.common.base.Predicates;
+
 import net.shibboleth.oidc.metadata.cache.LoadingStrategy;
-import net.shibboleth.utilities.java.support.annotation.constraint.NonnullAfterInit;
 import net.shibboleth.utilities.java.support.annotation.constraint.Positive;
+import net.shibboleth.utilities.java.support.component.ComponentSupport;
 import net.shibboleth.utilities.java.support.logic.Constraint;
 
 /**
@@ -65,15 +68,40 @@ public class BatchMetadataCacheBuilderSpec<IdentifierType, MetadataType>
     @Nonnull private boolean matchOnIdentifierRequired;
     
     /** Determine the expiration time of the source batch loaded metadata.*/
-    @NonnullAfterInit private Function<byte[], Instant> sourceMetadataExpiryStrategy;
+    @Nullable private Function<byte[], Instant> sourceMetadataExpiryStrategy;
+    
+    /** Is the raw metadata bytes from the source valid? */
+    @Nonnull private Predicate<byte[]> sourceMetadataValidPredicate;
     
     /** Constructor.*/
     public BatchMetadataCacheBuilderSpec() {
+        super();
         maxRefreshDelay = Duration.ofHours(4);
         minRefreshDelay = Duration.ofMinutes(5);    
         matchOnIdentifierRequired = false;
+        sourceMetadataValidPredicate = Predicates.alwaysTrue();
+    }
+    
+    /**
+     * Set the predicate which determines if the source metadata is valid or not.
+     * 
+     * @param predicate the predicate.
+     */
+    public void setSourceMetadataValidPredicate(@Nonnull final Predicate<byte[]> predicate) {        
+       sourceMetadataValidPredicate = 
+               Constraint.isNotNull(predicate, "Is source metadata valid predicate can not be null");
     }
     
+    /**
+     * Get the predicate which determines if the source metadata is valid or not.
+     * 
+     * @return the predicate.
+     */
+    @Nonnull protected Predicate<byte[]> getSourceMetadataValidPredicate() {
+        return sourceMetadataValidPredicate;
+    }
+    
+    
     /**
      * Set a strategy to find the metadata expiry date from a source metadata document as bytes.
      * 
@@ -89,7 +117,7 @@ public class BatchMetadataCacheBuilderSpec<IdentifierType, MetadataType>
      * 
      * @return the strategy.
      */
-    protected Function<byte[], Instant> getSourceMetadataExpiryStrategy() {
+    @Nullable protected Function<byte[], Instant> getSourceMetadataExpiryStrategy() {
         return sourceMetadataExpiryStrategy;
     }
     
diff --git a/oidc-common-metadata-impl/src/main/java/net/shibboleth/oidc/metadata/cache/impl/DefaultFileLoadingStrategy.java b/oidc-common-metadata-impl/src/main/java/net/shibboleth/oidc/metadata/cache/impl/DefaultFileLoadingStrategy.java
index 14b7328..0091926 100644
--- a/oidc-common-metadata-impl/src/main/java/net/shibboleth/oidc/metadata/cache/impl/DefaultFileLoadingStrategy.java
+++ b/oidc-common-metadata-impl/src/main/java/net/shibboleth/oidc/metadata/cache/impl/DefaultFileLoadingStrategy.java
@@ -46,6 +46,9 @@ public class DefaultFileLoadingStrategy implements LoadingStrategy {
     /** The metadata file. */
     @Nonnull private final File metadataFile;
     
+    /** The metadata file name to use in logs. */
+    @Nonnull private final String metadataFileFriendlyName;
+    
     /**
      * 
      * Constructor.
@@ -57,6 +60,7 @@ public class DefaultFileLoadingStrategy implements LoadingStrategy {
     public DefaultFileLoadingStrategy(@Nonnull final Resource metadata) throws IOException {
        Constraint.isNotNull(metadata, "The metadata file can not be null");
        metadataFile = metadata.getFile();
+       metadataFileFriendlyName = metadata.getDescription();
     }
     
     /**
@@ -89,7 +93,7 @@ public class DefaultFileLoadingStrategy implements LoadingStrategy {
 
     @Override
     public String getSourceIdentifier() {
-       return metadataFile.getAbsolutePath();
+       return metadataFileFriendlyName;
     }
 
 }
diff --git a/oidc-common-metadata-impl/src/main/java/net/shibboleth/oidc/metadata/cache/impl/DynamicMetadataCache.java b/oidc-common-metadata-impl/src/main/java/net/shibboleth/oidc/metadata/cache/impl/DynamicMetadataCache.java
index 7e1fb2b..1eb839d 100644
--- a/oidc-common-metadata-impl/src/main/java/net/shibboleth/oidc/metadata/cache/impl/DynamicMetadataCache.java
+++ b/oidc-common-metadata-impl/src/main/java/net/shibboleth/oidc/metadata/cache/impl/DynamicMetadataCache.java
@@ -416,7 +416,8 @@ public class DynamicMetadataCache<IdentifierType, MetadataType>
                     allMetadata = read(mgmtData, identifier);
                 }
                 if (allMetadata.isEmpty()) {
-                    log.debug("Metadata for '{}' does not exist or is stale, attempting to fetch it", identifier);
+                    log.debug("Metadata for '{}' does not exist, is no longer valid, or is stale, "
+                            + "attempting to fetch it", identifier);
                     fetch(mgmtData, identifier, criteria);
                     return read(mgmtData, identifier);
                 } else {
@@ -441,10 +442,11 @@ public class DynamicMetadataCache<IdentifierType, MetadataType>
      * @param mgmtData the metadata management data.
      * @param identifier the identifier of the metadata to fetch.
      * @param criteria the criteria used in determining how to fetch the metadata.
+     * @throws MetadataCacheException on error.
      */
     private void fetch(@Nonnull final MetadataManagementData<IdentifierType> mgmtData,
             @Nonnull final IdentifierType identifier,
-            @Nonnull @NotEmpty final CriteriaSet criteria){
+            @Nonnull @NotEmpty final CriteriaSet criteria) throws MetadataCacheException{
         
         final StampedLock sl = mgmtData.getStampLock();
         final long stamp = sl.writeLock();
@@ -453,7 +455,7 @@ public class DynamicMetadataCache<IdentifierType, MetadataType>
             if (!shouldAttemptRefresh(mgmtData)){
                 // re-check another thread has not acquired this lock before hand - and therefore obtained
                 // the metadata.
-                final List<MetadataType> allMetadata = lookupIndexedIdentifier(identifier);
+                final List<MetadataType> allMetadata = lookupIdentifier(identifier);
                 if (!allMetadata.isEmpty()) {
                     log.debug("{} Metadata for '{}' was acquired while waiting for the write lock", 
                             getLogPrefix(), identifier);
@@ -595,10 +597,12 @@ public class DynamicMetadataCache<IdentifierType, MetadataType>
      * @param identifier the metadata identifier to use as a key to fetch.
      * 
      * @return a list of metadata that matches the given key, an empty list of none are found.
+     * 
+     * @throws MetadataCacheException on error.
      */
     //TODO: Support isValid checks on returned metadata? see AbstractMetadataResolver#lookupEntityID
     @Nonnull private List<MetadataType> read(@Nonnull final MetadataManagementData<IdentifierType> mgmtData,
-            @Nonnull final IdentifierType identifier){
+            @Nonnull final IdentifierType identifier) throws MetadataCacheException {
         
         // record access attempt.
         mgmtData.recordEntityAccess();
@@ -609,7 +613,7 @@ public class DynamicMetadataCache<IdentifierType, MetadataType>
         // A single optimistic read. A write lock will break this, but we check for 
         // that using the validate method e.g. we keep track of locks, but we do not block a write lock
         // here. Most metadata operations will be read-only. Reads are not expensive, but writes (lookups) are.
-        final List<MetadataType> allMetadata = lookupIndexedIdentifier(identifier);
+        final List<MetadataType> allMetadata = lookupIdentifier(identifier);
         
         if (sl.validate(stamp)) {
             return allMetadata;
@@ -617,7 +621,7 @@ public class DynamicMetadataCache<IdentifierType, MetadataType>
             // OK, was tampered with, lets do it with a hard read lock.
             stamp = sl.readLock();            
             try {
-                return lookupIndexedIdentifier(identifier);                  
+                return lookupIdentifier(identifier);                  
             } finally {
                 sl.unlock(stamp);
             }           
@@ -642,7 +646,7 @@ public class DynamicMetadataCache<IdentifierType, MetadataType>
                         isInitialized(), isDestroyed());
                 return;
             }
-            log.info("Running metadata cleanup background timer task {}",this);
+            log.trace("{} Running metadata cleanup background timer task",getLogPrefix());
             removeExpiredAndIdleMetadata();
         }
         
@@ -689,10 +693,10 @@ public class DynamicMetadataCache<IdentifierType, MetadataType>
         private boolean isRemoveData(@Nonnull final MetadataManagementData<IdentifierType> mgmtData, 
                 @Nonnull final Instant now, @Nonnull final Instant earliestValidLastAccessed) {
             if (removeIdleEntityData && mgmtData.getLastAccessedTime().isBefore(earliestValidLastAccessed)) {
-                log.debug("Metadata exceeds maximum idle time, removing: {}", mgmtData.getID());
+                log.debug("{} Metadata exceeds maximum idle time, removing: {}", getLogPrefix(), mgmtData.getID());
                 return true;
             } else if (now.isAfter(mgmtData.getExpirationTime())) {
-                log.debug("Entity metadata is expired, removing: {}", mgmtData.getID());
+                log.debug("{} Metadata has expired, removing: {}", getLogPrefix(), mgmtData.getID());
                 return true;
             } else {
                 return false;
diff --git a/oidc-common-metadata-impl/src/main/java/net/shibboleth/oidc/metadata/cache/impl/DynamicMetadataCacheBuilder.java b/oidc-common-metadata-impl/src/main/java/net/shibboleth/oidc/metadata/cache/impl/DynamicMetadataCacheBuilder.java
index e06b1fb..5fad0b7 100644
--- a/oidc-common-metadata-impl/src/main/java/net/shibboleth/oidc/metadata/cache/impl/DynamicMetadataCacheBuilder.java
+++ b/oidc-common-metadata-impl/src/main/java/net/shibboleth/oidc/metadata/cache/impl/DynamicMetadataCacheBuilder.java
@@ -73,6 +73,7 @@ public final class DynamicMetadataCacheBuilder {
             cache.setInitialCleanupTaskDelay(spec.getInitialCleanupTaskDelay());
             cache.setMetadataFilterStrategy(spec.getMetadataFilterStrategy());
             cache.setMetadataBeforeRemovalHook(spec.getMetadataBeforeRemovalHook());
+            cache.setMetadataValidPredicate(spec.getMetadataValidPredicate());
             cache.setId(spec.getCacheId());
             cache.initialize();
             return cache;
diff --git a/oidc-common-metadata-impl/src/main/java/net/shibboleth/oidc/metadata/cache/impl/DynamicMetadataCacheBuilderSpec.java b/oidc-common-metadata-impl/src/main/java/net/shibboleth/oidc/metadata/cache/impl/DynamicMetadataCacheBuilderSpec.java
index bb460c8..f9f238c 100644
--- a/oidc-common-metadata-impl/src/main/java/net/shibboleth/oidc/metadata/cache/impl/DynamicMetadataCacheBuilderSpec.java
+++ b/oidc-common-metadata-impl/src/main/java/net/shibboleth/oidc/metadata/cache/impl/DynamicMetadataCacheBuilderSpec.java
@@ -66,6 +66,7 @@ public class DynamicMetadataCacheBuilderSpec <IdentifierType, MetadataType>
     
     /** Constructor. */
     protected DynamicMetadataCacheBuilderSpec() {
+        super();
         maxCacheDuration = Duration.ofHours(8);
         minCacheDuration = Duration.ofMinutes(10); 
         maxIdleEntityData = Duration.ofHours(8);
diff --git a/oidc-common-metadata-impl/src/test/java/net/shibboleth/oidc/metadata/cache/impl/BatchMetadataCacheTest.java b/oidc-common-metadata-impl/src/test/java/net/shibboleth/oidc/metadata/cache/impl/BatchMetadataCacheTest.java
index 10d5a2d..de06c83 100644
--- a/oidc-common-metadata-impl/src/test/java/net/shibboleth/oidc/metadata/cache/impl/BatchMetadataCacheTest.java
+++ b/oidc-common-metadata-impl/src/test/java/net/shibboleth/oidc/metadata/cache/impl/BatchMetadataCacheTest.java
@@ -42,6 +42,7 @@ import org.testng.annotations.AfterMethod;
 import org.testng.annotations.BeforeMethod;
 import org.testng.annotations.Test;
 
+import com.google.common.base.Predicates;
 import com.nimbusds.oauth2.sdk.ParseException;
 import com.nimbusds.oauth2.sdk.id.Issuer;
 import com.nimbusds.openid.connect.sdk.SubjectType;
@@ -118,6 +119,8 @@ public class BatchMetadataCacheTest {
 
         cache.setRefreshDelayFactor(0.75f);
         cache.setMetadataFilterStrategy((metadata, context) -> metadata);
+        cache.setMetadataValidPredicate(Predicates.alwaysTrue());
+        cache.setSourceMetadataValidPredicate(Predicates.alwaysTrue());
         cache.setId("MockRefreshableCache");
         // Initialise when you need to use it, if creating a local version, do not init this one.
         // cache.initialize();
@@ -145,6 +148,7 @@ public class BatchMetadataCacheTest {
         localCache.setIdentifierExtractionStrategy(m -> m.getIssuer());
         localCache.setMinRefreshDelay(Duration.ofMillis(100));
         localCache.setMaxRefreshDelay(Duration.ofMillis(200));
+        localCache.setSourceMetadataValidPredicate(Predicates.alwaysTrue());
         localCache.setCriteriaToIdentifierStrategy(crit -> {
             final IssuerIDCriterion issuerId = crit.get(IssuerIDCriterion.class);
             if (issuerId != null) {
@@ -155,6 +159,7 @@ public class BatchMetadataCacheTest {
 
         localCache.setRefreshDelayFactor(0.75f);   
         localCache.setMetadataFilterStrategy((metadata, context) -> metadata);
+        localCache.setMetadataValidPredicate(Predicates.alwaysTrue());
         localCache.setId("MockLocalRefreshableCache");
         localCache.initialize();
         
@@ -214,6 +219,8 @@ public class BatchMetadataCacheTest {
 
         localCache.setRefreshDelayFactor(0.75f);       
         localCache.setMetadataFilterStrategy((metadata, context) -> metadata);
+        localCache.setMetadataValidPredicate(Predicates.alwaysTrue());
+        localCache.setSourceMetadataValidPredicate(Predicates.alwaysTrue());
         localCache.setId("MockLocalRefreshableCache");
         //If no match, return all
         localCache.setMatchOnIdentifierRequired(false);
diff --git a/oidc-common-metadata-impl/src/test/java/net/shibboleth/oidc/metadata/cache/impl/DynamicMetadataCacheTest.java b/oidc-common-metadata-impl/src/test/java/net/shibboleth/oidc/metadata/cache/impl/DynamicMetadataCacheTest.java
index c8d6087..3c13ca4 100644
--- a/oidc-common-metadata-impl/src/test/java/net/shibboleth/oidc/metadata/cache/impl/DynamicMetadataCacheTest.java
+++ b/oidc-common-metadata-impl/src/test/java/net/shibboleth/oidc/metadata/cache/impl/DynamicMetadataCacheTest.java
@@ -18,7 +18,6 @@
 package net.shibboleth.oidc.metadata.cache.impl;
 
 import static org.testng.Assert.assertFalse;
-import static org.testng.Assert.assertNotNull;
 import static org.testng.Assert.assertSame;
 import static org.testng.Assert.assertTrue;
 
@@ -42,6 +41,7 @@ import org.testng.annotations.AfterMethod;
 import org.testng.annotations.BeforeMethod;
 import org.testng.annotations.Test;
 
+import com.google.common.base.Predicates;
 import com.nimbusds.oauth2.sdk.id.Issuer;
 import com.nimbusds.openid.connect.sdk.SubjectType;
 import com.nimbusds.openid.connect.sdk.op.OIDCProviderMetadata;
@@ -98,6 +98,7 @@ public class DynamicMetadataCacheTest {
         cache.setRemoveIdleEntityData(true);
         cache.setRefreshDelayFactor(0.75f);
         cache.setMinCacheDuration(Duration.ofMinutes(10));
+        cache.setMetadataValidPredicate(Predicates.alwaysTrue());
         cache.setMaxCacheDuration(Duration.ofMinutes(20));
         cache.setMetadataFilterStrategy((metadata, context) -> metadata);
         cache.setId("MockCache");
@@ -139,6 +140,7 @@ public class DynamicMetadataCacheTest {
         cacheLocal.setMinCacheDuration(Duration.ofMinutes(10));
         cacheLocal.setMaxCacheDuration(Duration.ofMinutes(20));
         cacheLocal.setMetadataFilterStrategy((metadata, context) -> metadata);
+        cacheLocal.setMetadataValidPredicate(Predicates.alwaysTrue());
         cacheLocal.setId("MockCache");
         cacheLocal.initialize();
         
@@ -209,6 +211,7 @@ public class DynamicMetadataCacheTest {
         cacheLocal.setRefreshDelayFactor(0.75f);
         cacheLocal.setMinCacheDuration(Duration.ofMinutes(10));
         cacheLocal.setMaxCacheDuration(Duration.ofMinutes(20));
+        cacheLocal.setMetadataValidPredicate(Predicates.alwaysTrue());
         cacheLocal.setMetadataFilterStrategy((metadata, context) -> metadata);
         cacheLocal.setId("MockCache");
         cacheLocal.initialize();
@@ -318,6 +321,7 @@ public class DynamicMetadataCacheTest {
         localCache.setRefreshDelayFactor(0.75f);
         localCache.setMinCacheDuration(Duration.ofMinutes(10));
         localCache.setMaxCacheDuration(Duration.ofMinutes(20));
+        localCache.setMetadataValidPredicate(Predicates.alwaysTrue());
         localCache.setMetadataFilterStrategy((metadata, context) -> metadata);
         localCache.setId("MockCache");
         localCache.initialize();
diff --git a/oidc-common-metadata-impl/src/test/java/net/shibboleth/oidc/metadata/impl/OIDCMapMetadataResolverTest.java b/oidc-common-metadata-impl/src/test/java/net/shibboleth/oidc/metadata/impl/OIDCMapMetadataResolverTest.java
index 0959c97..1302699 100644
--- a/oidc-common-metadata-impl/src/test/java/net/shibboleth/oidc/metadata/impl/OIDCMapMetadataResolverTest.java
+++ b/oidc-common-metadata-impl/src/test/java/net/shibboleth/oidc/metadata/impl/OIDCMapMetadataResolverTest.java
@@ -39,6 +39,7 @@ import org.testng.annotations.Test;
 import com.fasterxml.jackson.core.JsonProcessingException;
 import com.fasterxml.jackson.core.type.TypeReference;
 import com.fasterxml.jackson.databind.ObjectMapper;
+import com.google.common.base.Predicates;
 import com.nimbusds.oauth2.sdk.id.Issuer;
 
 import net.shibboleth.oidc.metadata.BatchBackingStore;
@@ -168,6 +169,8 @@ public class OIDCMapMetadataResolverTest {
         batchCache.setRefreshDelayFactor(0.75f);
         batchCache.setMinRefreshDelay(Duration.ofMillis(1000));
         batchCache.setMaxRefreshDelay(Duration.ofMillis(1000));
+        batchCache.setMetadataValidPredicate(Predicates.alwaysTrue());
+        batchCache.setSourceMetadataValidPredicate(Predicates.alwaysTrue());
         batchCache.setRefreshDelayFactor(0.75f);
         //This needs thinking about
         batchCache.setSourceMetadataExpiryStrategy(b -> Instant.now().plus(Duration.ofMinutes(5)));
diff --git a/oidc-common-metadata-impl/src/test/java/net/shibboleth/oidc/metadata/impl/OIDCProviderMetadataResolverTest.java b/oidc-common-metadata-impl/src/test/java/net/shibboleth/oidc/metadata/impl/OIDCProviderMetadataResolverTest.java
index 91b957c..d75be90 100644
--- a/oidc-common-metadata-impl/src/test/java/net/shibboleth/oidc/metadata/impl/OIDCProviderMetadataResolverTest.java
+++ b/oidc-common-metadata-impl/src/test/java/net/shibboleth/oidc/metadata/impl/OIDCProviderMetadataResolverTest.java
@@ -32,6 +32,7 @@ import java.time.Duration;
 import java.time.Instant;
 import java.util.List;
 import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.atomic.AtomicInteger;
 import java.util.function.Function;
 
 import javax.annotation.Nonnull;
@@ -52,6 +53,7 @@ import org.testng.annotations.AfterMethod;
 import org.testng.annotations.BeforeMethod;
 import org.testng.annotations.Test;
 
+import com.google.common.base.Predicates;
 import com.nimbusds.oauth2.sdk.ParseException;
 import com.nimbusds.oauth2.sdk.id.Issuer;
 import com.nimbusds.openid.connect.sdk.op.OIDCProviderMetadata;
@@ -214,10 +216,12 @@ public class OIDCProviderMetadataResolverTest {
         batchCache.setRefreshDelayFactor(0.75f);
         batchCache.setMinRefreshDelay(Duration.ofMillis(1000));
         batchCache.setMaxRefreshDelay(Duration.ofMillis(1000));
+        batchCache.setSourceMetadataValidPredicate(Predicates.alwaysTrue());
         batchCache.setRefreshDelayFactor(0.75f);
+        batchCache.setMetadataValidPredicate(Predicates.alwaysTrue());
         //This needs thinking about
         batchCache.setSourceMetadataExpiryStrategy(n -> Instant.now().plus(Duration.ofMinutes(5)));
-        batchCache.initialize();
+        
         
     }
     
@@ -260,8 +264,8 @@ public class OIDCProviderMetadataResolverTest {
         dynCache.setMinCacheDuration(Duration.ofMinutes(10));
         dynCache.setMaxCacheDuration(Duration.ofMinutes(20));
         dynCache.setMetadataFilterStrategy((metadata, context) -> metadata);
+        dynCache.setMetadataValidPredicate(Predicates.alwaysTrue());
         dynCache.setId("MockDynCache");
-        dynCache.initialize();
         
     }
     
@@ -276,7 +280,9 @@ public class OIDCProviderMetadataResolverTest {
     }
     
     @Test
-    void testBatchResolve() throws ResolverException, IOException {
+    void testBatchResolve() throws Exception {
+        batchCache.initialize();
+        
         Iterable<OIDCProviderMetadata> found = 
                 batchResolver.resolve(new CriteriaSet(new IssuerIDCriterion(new Issuer("https://example.oidc.op.org"))));
         assertNotNull(found);
@@ -284,9 +290,86 @@ public class OIDCProviderMetadataResolverTest {
         assertTrue(found.iterator().next().getIssuer().equals(new Issuer("https://example.oidc.op.org")));
     }
     
+    /* 
+     * Test fails to return metadata because it is always invalid.
+     * After the first read attempt it should then fetch it, but again fail to return it 
+     * because IsMetadataValidPredicate is always false.
+     */
+    @Test
+    void testLookupFails_InvalidMetadata() throws Exception {
+        dynCache.setMetadataValidPredicate(Predicates.alwaysFalse());
+        dynCache.initialize();
+        
+        final Issuer iss = new Issuer("https://example.oidc.op.org");
+        final MetadataManagementData<Issuer> mgmtData = dynCache.getBackingStore()
+                .computeManagementDataIfAbsent(iss, MetadataManagementData::new);
+        final Instant now = Instant.now();
+        // set last update in the past, so we can check a new one is fetched.
+        mgmtData.setLastUpdateTime(now.minus(Duration.ofMinutes(10)));
+        //metadata not expired
+        mgmtData.setExpirationTime(now.plus(Duration.ofMinutes(10)));
+        //refresh is fine
+        mgmtData.setRefreshTriggerTime(now.plus(Duration.ofMinutes(10)));
+        
+        
+        final OIDCProviderMetadata metadata = OIDCProviderMetadata.parse(GOOD_PROVIDER_CONFIGURATION_INFO);
+        dynCache.getBackingStore().getOrderedValues().add(metadata);
+        dynCache.getBackingStore().getIndexedValues().put(iss, List.of(metadata));
+        
+        final Iterable<OIDCProviderMetadata> found = 
+                dynResolver.resolve(new CriteriaSet(new IssuerIDCriterion(new Issuer("https://example.oidc.op.org"))));
+        assertNotNull(found);
+        assertFalse(found.iterator().hasNext());
+    } 
+    
+    /* 
+     * Initial lookup should fail even though the metadata is cached because it is 'invalid'. 
+     * A fresh metadata instance should be sought, loaded, and returned.
+     */
+    @Test
+    void testLookupEventualSuccess_InvalidMetadata() throws Exception { 
+        
+        /*
+         * There is nothing in the OIDC metadata that will expire, so we
+         * fake it by saying, first two attempts to check validity return invalid,
+         * but the third attempt return valid. 
+         */
+        final AtomicInteger count = new AtomicInteger();
+        dynCache.setMetadataValidPredicate(m -> {
+            final int counter = count.getAndIncrement();
+            return !(counter == 0 || counter == 1);            
+        });
+        
+        dynCache.initialize();
+        
+        final Issuer iss = new Issuer("https://example.oidc.op.org");
+        final MetadataManagementData<Issuer> mgmtData = dynCache.getBackingStore()
+                .computeManagementDataIfAbsent(iss, MetadataManagementData::new);
+        final Instant now = Instant.now();
+        // set last update in the past, so we can check a new one is fetched.
+        final Instant firstUpdateTime = now.minus(Duration.ofMinutes(10));
+        mgmtData.setLastUpdateTime(firstUpdateTime);
+        //metadata not expired
+        mgmtData.setExpirationTime(now.plus(Duration.ofMinutes(10)));
+        //refresh is fine
+        mgmtData.setRefreshTriggerTime(now.plus(Duration.ofMinutes(10)));
+        
+        
+        final OIDCProviderMetadata metadata = OIDCProviderMetadata.parse(GOOD_PROVIDER_CONFIGURATION_INFO);
+        dynCache.getBackingStore().getOrderedValues().add(metadata);
+        dynCache.getBackingStore().getIndexedValues().put(iss, List.of(metadata));
+        
+        final Iterable<OIDCProviderMetadata> found = 
+                dynResolver.resolve(new CriteriaSet(new IssuerIDCriterion(new Issuer("https://example.oidc.op.org"))));
+        assertNotNull(found);
+        assertTrue(found.iterator().hasNext());
+        assertTrue(mgmtData.getLastUpdateTime().isAfter(firstUpdateTime));
+    }
     
     @Test
-    void testDynResolve() throws ResolverException, IOException {
+    void testDynResolve() throws ResolverException, IOException, ComponentInitializationException {
+        dynCache.initialize();
+        
         Iterable<OIDCProviderMetadata> found = 
                 dynResolver.resolve(new CriteriaSet(new IssuerIDCriterion(new Issuer("https://example.oidc.op.org"))));
         assertNotNull(found);
@@ -294,7 +377,9 @@ public class OIDCProviderMetadataResolverTest {
     }
     
     @Test
-    void testDynResolve_Filter() throws ResolverException, IOException {
+    void testDynResolve_Filter() throws ResolverException, IOException, ComponentInitializationException {
+        dynCache.initialize();
+        
         Iterable<OIDCProviderMetadata> found = 
                 dynResolver.resolve(new CriteriaSet(
                         new IssuerIDCriterion(new Issuer("https://example.oidc.op.org")),
@@ -320,7 +405,9 @@ public class OIDCProviderMetadataResolverTest {
     }
     
     @Test
-    void testDynResolve_MetadataNeedsRefresh() throws ResolverException, IOException, ParseException {
+    void testDynResolve_MetadataNeedsRefresh() 
+            throws ResolverException, IOException, ParseException, ComponentInitializationException {
+        dynCache.initialize();
         
         final Issuer iss = new Issuer("https://example.oidc.op.org");
         final MetadataManagementData<Issuer> mgmtData = dynCache.getBackingStore()
@@ -345,7 +432,10 @@ public class OIDCProviderMetadataResolverTest {
     }
     
     @Test
-    void testDynResolve_Filter_WrongMetadataType() throws ResolverException, IOException {
+    void testDynResolve_Filter_WrongMetadataType() 
+            throws ResolverException, IOException, ComponentInitializationException {
+        dynCache.initialize();
+        
         Iterable<OIDCProviderMetadata> found = 
                 dynResolver.resolve(new CriteriaSet(
                         new IssuerIDCriterion(new Issuer("https://example.oidc.op.org")),
@@ -372,7 +462,10 @@ public class OIDCProviderMetadataResolverTest {
 
     
     @Test
-    void testDynResolve_Filter_WrongClassType() throws ResolverException, IOException {
+    void testDynResolve_Filter_WrongClassType() 
+            throws ResolverException, IOException, ComponentInitializationException {
+        dynCache.initialize();
+        
         Iterable<OIDCProviderMetadata> found = 
                 dynResolver.resolve(new CriteriaSet(
                         new IssuerIDCriterion(new Issuer("https://example.oidc.op.org")),
@@ -391,7 +484,8 @@ public class OIDCProviderMetadataResolverTest {
     @Test
     void testResponseHandler() throws IOException {
         final OIDCProviderMetadataResponseHandler handler = new OIDCProviderMetadataResponseHandler();
-        final BasicHttpResponse httpResponse = new BasicHttpResponse(new ProtocolVersion("HTTP", 1, 1), HttpStatus.SC_OK, "OK");
+        final BasicHttpResponse httpResponse = 
+                new BasicHttpResponse(new ProtocolVersion("HTTP", 1, 1), HttpStatus.SC_OK, "OK");
         final ByteArrayEntity entity = new ByteArrayEntity(GOOD_PROVIDER_CONFIGURATION_INFO.getBytes());
         entity.setContentType(new BasicHeader(HttpHeaders.CONTENT_TYPE, "application/json"));
         httpResponse.setEntity(entity);
@@ -404,7 +498,8 @@ public class OIDCProviderMetadataResolverTest {
     @Test
     void testResponseHandler_WrongMIMEType() throws IOException {
         final OIDCProviderMetadataResponseHandler handler = new OIDCProviderMetadataResponseHandler();
-        final BasicHttpResponse httpResponse = new BasicHttpResponse(new ProtocolVersion("HTTP", 1, 1), HttpStatus.SC_OK, "OK");
+        final BasicHttpResponse httpResponse = 
+                new BasicHttpResponse(new ProtocolVersion("HTTP", 1, 1), HttpStatus.SC_OK, "OK");
         final ByteArrayEntity entity = new ByteArrayEntity(GOOD_PROVIDER_CONFIGURATION_INFO.getBytes());
         entity.setContentType(new BasicHeader(HttpHeaders.CONTENT_TYPE, "application/not_json"));
         httpResponse.setEntity(entity);
@@ -416,10 +511,12 @@ public class OIDCProviderMetadataResolverTest {
     /* Run it twice, so the second is resolved from cache.*/
     @SuppressWarnings("unchecked")
     @Test
-    void testResolve_FromCache() throws ResolverException, IOException {
+    void testResolve_FromCache() throws ResolverException, IOException, ComponentInitializationException {
+        dynCache.initialize();
         
         // test not in cache
-        assertFalse(dynCache.getBackingStore().getIndexedValues().containsKey(new Issuer("https://example.oidc.op.org")));
+        assertFalse(dynCache.getBackingStore().getIndexedValues()
+                .containsKey(new Issuer("https://example.oidc.op.org")));
         
         // find and cache
         Iterable<OIDCProviderMetadata> found = 
@@ -428,7 +525,8 @@ public class OIDCProviderMetadataResolverTest {
         assertTrue(found.iterator().hasNext());        
         
         // test is in cache
-        assertTrue(dynCache.getBackingStore().getIndexedValues().containsKey(new Issuer("https://example.oidc.op.org")));
+        assertTrue(dynCache.getBackingStore().getIndexedValues()
+                .containsKey(new Issuer("https://example.oidc.op.org")));
         
         
         // Take down the source and see if it still resolves from the cache.
@@ -443,7 +541,9 @@ public class OIDCProviderMetadataResolverTest {
     }
     
     @Test
-    void testResolve_NullResponse() throws ResolverException, IOException {
+    void testResolve_NullResponse() throws ResolverException, IOException, ComponentInitializationException {
+        dynCache.initialize();
+        
         when(httpClient.
                 execute(any(HttpUriRequest.class),any(ResponseHandler.class),any(HttpContext.class)))
                 .thenReturn(null);
@@ -457,8 +557,8 @@ public class OIDCProviderMetadataResolverTest {
     class TestableDynamicMetadataCache<IdentifierType, MetadataType> 
                             extends DynamicMetadataCache<IdentifierType, MetadataType> {
 
-        TestableDynamicMetadataCache(DynamicBackingStore<IdentifierType, MetadataType> store,
-                ScheduledExecutorService executor) {
+        TestableDynamicMetadataCache(final DynamicBackingStore<IdentifierType, MetadataType> store,
+                final ScheduledExecutorService executor) {
             super(store, executor);
         }
         

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


More information about the commits mailing list