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