// Path: src/main/java/com/plexobject/rbac/domain/Pair.java
// public class Pair<FIRST, SECOND> implements java.io.Serializable {
//     private static final long serialVersionUID = 1L;
//     public FIRST first;
//     public SECOND second;
// 
//     public Pair(FIRST first, SECOND second) {
//         this.first = first;
//         this.second = second;
//     }
// 
//     public FIRST getFirst() {
//         return first;
//     }
// 
//     public void setFirst(FIRST first) {
//         this.first = first;
//     }
// 
//     public SECOND getSecond() {
//         return second;
//     }
// 
//     public void setSecond(SECOND second) {
//         this.second = second;
//     }
// 
//     /**
//      * @see java.lang.Object#equals(Object)
//      */
//     @SuppressWarnings("unchecked")
//     @Override
//     public boolean equals(Object object) {
//         if (!(object instanceof Pair)) {
//             return false;
//         }
//         Pair<FIRST, SECOND> rhs = (Pair<FIRST, SECOND>) object;
// 
//         EqualsBuilder eqBuilder = new EqualsBuilder();
//         eqBuilder.append(first, rhs.first);
//         eqBuilder.append(second, rhs.second);
// 
//         return eqBuilder.isEquals();
//     }
// 
//     /**
//      * @see java.lang.Object#hashCode()
//      */
//     @Override
//     public int hashCode() {
//         return new HashCodeBuilder(786529047, 1924536713).append(first).append(
//                 second).toHashCode();
//     }
// 
//     /**
//      * @see java.lang.Object#toString()
//      */
//     @Override
//     public String toString() {
//         return new ToStringBuilder(this).append(first).append(second)
//                 .toString();
//     }
// }
// 
// Path: src/main/java/com/plexobject/rbac/utils/TimeUtils.java
// public class TimeUtils {
//     public interface TimeSource {
//         Date getCurrentTime();
//     }
// 
//     private static TimeSource TIME_SOURCE = new TimeSource() {
//         @Override
//         public Date getCurrentTime() {
//             return new Date();
//         }
//     };
// 
//     public static Date getCurrentTime() {
//         return TIME_SOURCE.getCurrentTime();
//     }
// 
//     public static long getCurrentTimeMillis() {
//         return getCurrentTime().getTime();
//     }
// 
// }


import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.locks.ReentrantLock;
import org.apache.commons.lang.builder.EqualsBuilder;
import org.apache.commons.lang.builder.HashCodeBuilder;
import org.apache.log4j.Logger;
import com.plexobject.rbac.domain.Pair;
import com.plexobject.rbac.utils.TimeUtils;
package com.plexobject.rbac.cache;




/**
 * CacheMap - provides lightweight caching based on LRU size and timeout and
 * asynchronous reloads.
 * 
 */
public class CachedMap<K, V> implements Map<K, V>, CacheFlushable {
    private static final Logger LOGGER = Logger.getLogger(CachedMap.class);
    private final static int MAX_THREADS = 2; // for all cache items across VM
    final static int MAX_ITEMS = 1000; // max size
    private final static int EXPIRES_IN_SECS = 0; // indefinite

    private final static ExecutorService executorService = Executors
            .newFixedThreadPool(MAX_THREADS);

    class FixedSizeLruLinkedTreeMap<KK, VV> extends LinkedHashMap<KK, VV> {
        private static final long serialVersionUID = 1L;
        private final int maxSize;

        public FixedSizeLruLinkedTreeMap(int initialCapacity, float loadFactor,
                int maxSize) {
            super(initialCapacity, loadFactor, true);
            this.maxSize = maxSize;
        }

        @Override
        protected boolean removeEldestEntry(java.util.Map.Entry<KK, VV> eldest) {
            return super.size() > maxSize;
        }
    }

    final long expiresInSecs;
    private final CacheLoader<K, V> cacheLoader;
// Path: src/main/java/com/plexobject/rbac/domain/Pair.java
// public class Pair<FIRST, SECOND> implements java.io.Serializable {
//     private static final long serialVersionUID = 1L;
//     public FIRST first;
//     public SECOND second;
// 
//     public Pair(FIRST first, SECOND second) {
//         this.first = first;
//         this.second = second;
//     }
// 
//     public FIRST getFirst() {
//         return first;
//     }
// 
//     public void setFirst(FIRST first) {
//         this.first = first;
//     }
// 
//     public SECOND getSecond() {
//         return second;
//     }
// 
//     public void setSecond(SECOND second) {
//         this.second = second;
//     }
// 
//     /**
//      * @see java.lang.Object#equals(Object)
//      */
//     @SuppressWarnings("unchecked")
//     @Override
//     public boolean equals(Object object) {
//         if (!(object instanceof Pair)) {
//             return false;
//         }
//         Pair<FIRST, SECOND> rhs = (Pair<FIRST, SECOND>) object;
// 
//         EqualsBuilder eqBuilder = new EqualsBuilder();
//         eqBuilder.append(first, rhs.first);
//         eqBuilder.append(second, rhs.second);
// 
//         return eqBuilder.isEquals();
//     }
// 
//     /**
//      * @see java.lang.Object#hashCode()
//      */
//     @Override
//     public int hashCode() {
//         return new HashCodeBuilder(786529047, 1924536713).append(first).append(
//                 second).toHashCode();
//     }
// 
//     /**
//      * @see java.lang.Object#toString()
//      */
//     @Override
//     public String toString() {
//         return new ToStringBuilder(this).append(first).append(second)
//                 .toString();
//     }
// }
// 
// Path: src/main/java/com/plexobject/rbac/utils/TimeUtils.java
// public class TimeUtils {
//     public interface TimeSource {
//         Date getCurrentTime();
//     }
// 
//     private static TimeSource TIME_SOURCE = new TimeSource() {
//         @Override
//         public Date getCurrentTime() {
//             return new Date();
//         }
//     };
// 
//     public static Date getCurrentTime() {
//         return TIME_SOURCE.getCurrentTime();
//     }
// 
//     public static long getCurrentTimeMillis() {
//         return getCurrentTime().getTime();
//     }
// 
// }


// Path: src/main/java/com/plexobject/rbac/cache/CachedMap.java
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.locks.ReentrantLock;
import org.apache.commons.lang.builder.EqualsBuilder;
import org.apache.commons.lang.builder.HashCodeBuilder;
import org.apache.log4j.Logger;
import com.plexobject.rbac.domain.Pair;
import com.plexobject.rbac.utils.TimeUtils;

package com.plexobject.rbac.cache;




/**
 * CacheMap - provides lightweight caching based on LRU size and timeout and
 * asynchronous reloads.
 * 
 */
public class CachedMap<K, V> implements Map<K, V>, CacheFlushable {
    private static final Logger LOGGER = Logger.getLogger(CachedMap.class);
    private final static int MAX_THREADS = 2; // for all cache items across VM
    final static int MAX_ITEMS = 1000; // max size
    private final static int EXPIRES_IN_SECS = 0; // indefinite

    private final static ExecutorService executorService = Executors
            .newFixedThreadPool(MAX_THREADS);

    class FixedSizeLruLinkedTreeMap<KK, VV> extends LinkedHashMap<KK, VV> {
        private static final long serialVersionUID = 1L;
        private final int maxSize;

        public FixedSizeLruLinkedTreeMap(int initialCapacity, float loadFactor,
                int maxSize) {
            super(initialCapacity, loadFactor, true);
            this.maxSize = maxSize;
        }

        @Override
        protected boolean removeEldestEntry(java.util.Map.Entry<KK, VV> eldest) {
            return super.size() > maxSize;
        }
    }

    final long expiresInSecs;
    private final CacheLoader<K, V> cacheLoader;
    private final Map<K, Pair<Long, V>> map;