001package com.identityworksllc.iiq.common.cache;
002
003import java.util.Objects;
004import java.util.Optional;
005import java.util.function.BiPredicate;
006
007/**
008 * A cache value wrapper that allows access based on matching an
009 * arbitrary guard value. If the guard value is not matched, the
010 * {@link #getValue(Object)} method returns an empty Optional.
011 *
012 * This can be used, for example, to detect changes to permissions
013 * or logged in users and clear the cache accordingly.
014 *
015 * @param <ValueType> The type of the thing being stored here
016 * @param <GuardType> The type of the guard value
017 */
018public class GuardedCacheValue<ValueType, GuardType> {
019
020    /**
021     * The value that must be matched for the value to return
022     */
023    private final GuardType guardToken;
024
025    /**
026     * The token value matcher
027     */
028    private final BiPredicate<GuardType, GuardType> matcher;
029
030    /**
031     * The actual value wrapped by this object
032     */
033    private final ValueType value;
034
035    /**
036     * Constructs a new cache value with the given token and value. The token
037     * will be matched using Object.equals.
038     *
039     * @param value The value wrapped by this cache token
040     * @param token The token that must be matched to retrieve the value
041     */
042    public GuardedCacheValue(ValueType value, GuardType token) {
043        this(value, token, Object::equals);
044    }
045
046    /**
047     * Constructs a new cache value with the given token, value, and token
048     * matcher. At getValue time, the matcher will be used to decide whether
049     * the input actually matches the token.
050     *
051     * @param value The value wrapped by this cache token
052     * @param token The token that must be matched to retrieve the value
053     * @param matcher The predicate that decides whether the input token matches
054     */
055    public GuardedCacheValue(ValueType value, GuardType token, BiPredicate<GuardType, GuardType> matcher) {
056        this.value = Objects.requireNonNull(value);
057        this.guardToken = Objects.requireNonNull(token);
058
059        if (matcher == null) {
060            this.matcher = Objects::equals;
061        } else {
062            this.matcher = matcher;
063        }
064    }
065
066    /**
067     *
068     * @param guardTest The guard test value
069     * @return An optional containing the stored value, if the guard value input matches, or else an empty optional object
070     */
071    public Optional<ValueType> getValue(GuardType guardTest) {
072        if (this.matcher.test(guardTest, guardToken)) {
073            return Optional.of(value);
074        } else {
075            return Optional.empty();
076        }
077    }
078}