package org.apereo.cas.authentication.principal.cache;

import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.CacheLoader;
import com.github.benmanes.caffeine.cache.Caffeine;
import org.apereo.cas.authentication.principal.Principal;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.TreeMap;
import java.util.concurrent.TimeUnit;

/**
 * Wrapper around an attribute repository where attributes cached for a configurable period
 * based on google guava's caching library.
 *
 * @author Misagh Moayyed
 * @since 4.2
 */
public class CachingPrincipalAttributesRepository extends AbstractPrincipalAttributesRepository {
    private static final long serialVersionUID = 6350244643948535906L;
    private static final long DEFAULT_MAXIMUM_CACHE_SIZE = 1000;

    private static final Logger LOGGER = LoggerFactory.getLogger(CachingPrincipalAttributesRepository.class);


    private final transient Cache<String, Map<String, Object>> cache;
    private final transient PrincipalAttributesCacheLoader cacheLoader = new PrincipalAttributesCacheLoader();

    private long maxCacheSize = DEFAULT_MAXIMUM_CACHE_SIZE;

    /**
     * Used for serialization only.
     */
    private CachingPrincipalAttributesRepository() {
        super();
        this.cache = Caffeine.newBuilder().maximumSize(this.maxCacheSize)
                .expireAfterWrite(getExpiration(), TimeUnit.valueOf(getTimeUnit())).build(this.cacheLoader);
    }

    /**
     * Instantiates a new caching attributes principal factory.
     * Sets the default cache size to {@link #DEFAULT_MAXIMUM_CACHE_SIZE}.
     *
     * @param timeUnit       the time unit
     * @param expiryDuration the expiry duration
     */
    public CachingPrincipalAttributesRepository(final String timeUnit, final long expiryDuration) {
        this(DEFAULT_MAXIMUM_CACHE_SIZE, timeUnit, expiryDuration);
    }

    /**
     * Instantiates a new caching attributes principal factory.
     *
     * @param maxCacheSize   the max cache size
     * @param timeUnit       the time unit
     * @param expiryDuration the expiry duration
     */
    public CachingPrincipalAttributesRepository(final long maxCacheSize,
                                                final String timeUnit,
                                                final long expiryDuration) {
        super(expiryDuration, timeUnit);
        this.maxCacheSize = maxCacheSize;

        this.cache = Caffeine.newBuilder().maximumSize(maxCacheSize)
                .expireAfterWrite(getExpiration(), TimeUnit.valueOf(getTimeUnit())).build(this.cacheLoader);
    }

    @Override
    protected void addPrincipalAttributes(final String id, final Map<String, Object> attributes) {
        this.cache.put(id, attributes);
        LOGGER.debug("Cached attributes for [{}]", id);
    }

    @Override
    protected Map<String, Object> getPrincipalAttributes(final Principal p) {
        try {
            return this.cache.get(p.getId(), s -> {
                LOGGER.debug("No cached attributes could be found for [{}]", p.getId());
                return new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
            });
        } catch (final Exception e) {
            LOGGER.error(e.getMessage(), e);
        }
        return new HashMap<>();
    }

    @Override
    public void close() throws IOException {
        this.cache.cleanUp();
    }

    private static class PrincipalAttributesCacheLoader implements CacheLoader<String, Map<String, Object>> {
        @Override
        public Map<String, Object> load(final String key) throws Exception {
            return new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
        }
    }
}

@@ -49,16 +49,68 @@
 cache;%0A%0A
+import com.fasterxml.jackson.annotation.JsonIgnore;%0A
 import c
@@ -371,16 +371,70 @@
 Factory;
+%0Aimport org.springframework.data.annotation.Transient;
 %0A%0Aimport
@@ -1100,17 +1100,51 @@
 class);%0A
-%0A
+    %0A    @JsonIgnore%0A    @Transient
 %0A    pri
@@ -1206,16 +1206,52 @@
  cache;%0A
+    %0A    @JsonIgnore%0A    @Transient%0A
     priv
@@ -2687,17 +2687,16 @@
 heSize;%0A
-%0A
         

Fix test cases