src/core/net/sf/basedb/util/listable/TransformCache.java

Code
Comments
Other
Rev Date Author Line
7770 10 Feb 20 nicklas 1 package net.sf.basedb.util.listable;
7770 10 Feb 20 nicklas 2
7770 10 Feb 20 nicklas 3 import java.util.Collections;
7770 10 Feb 20 nicklas 4 import java.util.HashMap;
7770 10 Feb 20 nicklas 5 import java.util.Iterator;
7770 10 Feb 20 nicklas 6 import java.util.Map;
7770 10 Feb 20 nicklas 7 import java.util.Map.Entry;
7770 10 Feb 20 nicklas 8 import java.util.Set;
7770 10 Feb 20 nicklas 9 import java.util.SortedSet;
7770 10 Feb 20 nicklas 10 import java.util.TreeSet;
7770 10 Feb 20 nicklas 11
7770 10 Feb 20 nicklas 12 import net.sf.basedb.core.Item;
7770 10 Feb 20 nicklas 13 import net.sf.basedb.core.SyncFilter;
7770 10 Feb 20 nicklas 14
7770 10 Feb 20 nicklas 15 /**
7770 10 Feb 20 nicklas 16   A simple cache implementation for holding the result of source item to target item transformation.
7770 10 Feb 20 nicklas 17
7770 10 Feb 20 nicklas 18   @author nicklas
7770 10 Feb 20 nicklas 19   @since 3.16
7770 10 Feb 20 nicklas 20 */
7770 10 Feb 20 nicklas 21 public class TransformCache 
7770 10 Feb 20 nicklas 22 {
7770 10 Feb 20 nicklas 23
7770 10 Feb 20 nicklas 24   /**
7770 10 Feb 20 nicklas 25     Get a key that is able to identify a given transformation. We need the source and target
7770 10 Feb 20 nicklas 26     item types as well as the transformation direction and all source items ids. It
7770 10 Feb 20 nicklas 27     is recommended that the sourceIds set is a {@link SortedSet}. If not, a new sorted set
7770 10 Feb 20 nicklas 28     is automatically created.
7770 10 Feb 20 nicklas 29   */
7770 10 Feb 20 nicklas 30   public static CacheKey getKey(Item sourceType, Item targetType, SyncFilter.SourceItemTransform transform, Set<Integer> sourceIds)
7770 10 Feb 20 nicklas 31   {
8094 04 Nov 22 nicklas 32     return getKey(null, sourceType, targetType, transform, sourceIds);
8094 04 Nov 22 nicklas 33   }
8094 04 Nov 22 nicklas 34   
8094 04 Nov 22 nicklas 35   public static CacheKey getKey(String cacheRegion, Item sourceType, Item targetType, SyncFilter.SourceItemTransform transform, Set<Integer> sourceIds)
8094 04 Nov 22 nicklas 36   {
7770 10 Feb 20 nicklas 37     SortedSet<Integer> sortedSrc = sourceIds instanceof SortedSet ? (SortedSet<Integer>)sourceIds : new TreeSet<>(sourceIds);
8094 04 Nov 22 nicklas 38     return new CacheKey(cacheRegion, sourceType, targetType, transform, sortedSrc);
7770 10 Feb 20 nicklas 39   }
7770 10 Feb 20 nicklas 40   
7770 10 Feb 20 nicklas 41   private final Map<CacheKey, CacheEntry> cache;
7770 10 Feb 20 nicklas 42   private final long timeoutInMillis;
7770 10 Feb 20 nicklas 43   private long nextCleanup;
7770 10 Feb 20 nicklas 44   
7770 10 Feb 20 nicklas 45   /**
7770 10 Feb 20 nicklas 46     Creates a new cache with the specified timeout.
7770 10 Feb 20 nicklas 47   */
7770 10 Feb 20 nicklas 48   public TransformCache(int timeoutInMinutes)
7770 10 Feb 20 nicklas 49   {
7770 10 Feb 20 nicklas 50     this.cache = Collections.synchronizedMap(new HashMap<>());
7770 10 Feb 20 nicklas 51     this.timeoutInMillis = timeoutInMinutes * 60000;
7770 10 Feb 20 nicklas 52     this.nextCleanup = System.currentTimeMillis() + timeoutInMillis;
7770 10 Feb 20 nicklas 53   }
7770 10 Feb 20 nicklas 54   
7770 10 Feb 20 nicklas 55   /**
7770 10 Feb 20 nicklas 56     Get a cached entry if. Null is returned if there is no
7770 10 Feb 20 nicklas 57     entry or if the existing entry is too old.
7770 10 Feb 20 nicklas 58   */
7770 10 Feb 20 nicklas 59   public Set<Integer> get(CacheKey key)
7770 10 Feb 20 nicklas 60   {
7770 10 Feb 20 nicklas 61     CacheEntry entry = cache.get(key);
7770 10 Feb 20 nicklas 62     Set<Integer> target = null;
7770 10 Feb 20 nicklas 63     if (entry != null)
7770 10 Feb 20 nicklas 64     {
7770 10 Feb 20 nicklas 65       if (entry.getTimeout() < System.currentTimeMillis())
7770 10 Feb 20 nicklas 66       {
7770 10 Feb 20 nicklas 67         cache.remove(key);
7770 10 Feb 20 nicklas 68       }
7770 10 Feb 20 nicklas 69       else
7770 10 Feb 20 nicklas 70       {
7770 10 Feb 20 nicklas 71         target = entry.getTargetIds();
7770 10 Feb 20 nicklas 72       }
7770 10 Feb 20 nicklas 73     }
7770 10 Feb 20 nicklas 74     return target;
7770 10 Feb 20 nicklas 75   }
7770 10 Feb 20 nicklas 76   
7770 10 Feb 20 nicklas 77   /**
7770 10 Feb 20 nicklas 78     Store a new entry into the cache.
7770 10 Feb 20 nicklas 79   */
7770 10 Feb 20 nicklas 80   public void store(CacheKey key, Set<Integer> targetList)
7770 10 Feb 20 nicklas 81   {
7770 10 Feb 20 nicklas 82     CacheEntry entry = new CacheEntry(System.currentTimeMillis()+timeoutInMillis, targetList);
7770 10 Feb 20 nicklas 83     cache.put(key, entry);
7770 10 Feb 20 nicklas 84     if (System.currentTimeMillis() > nextCleanup) clean();
7770 10 Feb 20 nicklas 85   }
7770 10 Feb 20 nicklas 86   
7770 10 Feb 20 nicklas 87   void clean()
7770 10 Feb 20 nicklas 88   {
7770 10 Feb 20 nicklas 89     synchronized (cache)
7770 10 Feb 20 nicklas 90     {
7770 10 Feb 20 nicklas 91       long time = System.currentTimeMillis();
7770 10 Feb 20 nicklas 92       Iterator<Entry<CacheKey, CacheEntry>> it = cache.entrySet().iterator();
7770 10 Feb 20 nicklas 93       while (it.hasNext())
7770 10 Feb 20 nicklas 94       {
7770 10 Feb 20 nicklas 95         if (it.next().getValue().getTimeout() < time) it.remove();
7770 10 Feb 20 nicklas 96       }
7770 10 Feb 20 nicklas 97       nextCleanup = time + timeoutInMillis;
7770 10 Feb 20 nicklas 98     }
7770 10 Feb 20 nicklas 99   }
7770 10 Feb 20 nicklas 100   
7770 10 Feb 20 nicklas 101   public static class CacheKey
7770 10 Feb 20 nicklas 102   {
8094 04 Nov 22 nicklas 103     private final String cacheRegion;
7770 10 Feb 20 nicklas 104     private final Item sourceType;
7770 10 Feb 20 nicklas 105     private final Item targetType;
7770 10 Feb 20 nicklas 106     private final SyncFilter.SourceItemTransform transform;
7770 10 Feb 20 nicklas 107     private final String sourceIdHash;
7770 10 Feb 20 nicklas 108     
8094 04 Nov 22 nicklas 109     CacheKey(String cacheRegion, Item sourceType, Item targetType, SyncFilter.SourceItemTransform transform, SortedSet<Integer> sourceIds)
7770 10 Feb 20 nicklas 110     {
8094 04 Nov 22 nicklas 111       this.cacheRegion = cacheRegion==null?"":cacheRegion;
7770 10 Feb 20 nicklas 112       this.sourceType = sourceType;
7770 10 Feb 20 nicklas 113       this.targetType = targetType;
7770 10 Feb 20 nicklas 114       this.transform = transform;
7770 10 Feb 20 nicklas 115       this.sourceIdHash = SyncFilter.toMd5(sourceIds);
7770 10 Feb 20 nicklas 116     }
7770 10 Feb 20 nicklas 117
7770 10 Feb 20 nicklas 118     @Override
7770 10 Feb 20 nicklas 119     public int hashCode() 
7770 10 Feb 20 nicklas 120     {
8094 04 Nov 22 nicklas 121       return cacheRegion.hashCode()+sourceType.hashCode() + 3 * targetType.hashCode() + 7 * transform.hashCode() + 11 * sourceIdHash.hashCode();
7770 10 Feb 20 nicklas 122     }
7770 10 Feb 20 nicklas 123
7770 10 Feb 20 nicklas 124     @Override
7770 10 Feb 20 nicklas 125     public boolean equals(Object obj) 
7770 10 Feb 20 nicklas 126     {
7770 10 Feb 20 nicklas 127       if (!(obj instanceof CacheKey)) return false;
7770 10 Feb 20 nicklas 128       CacheKey other = (CacheKey)obj;
8094 04 Nov 22 nicklas 129       return cacheRegion.equals(other.cacheRegion) && 
8094 04 Nov 22 nicklas 130         sourceType == other.sourceType && targetType == other.targetType && 
7770 10 Feb 20 nicklas 131         transform == other.transform && sourceIdHash.equals(other.sourceIdHash);
7770 10 Feb 20 nicklas 132     }
7770 10 Feb 20 nicklas 133
7770 10 Feb 20 nicklas 134     @Override
7770 10 Feb 20 nicklas 135     public String toString() 
7770 10 Feb 20 nicklas 136     {
8094 04 Nov 22 nicklas 137       return "CacheKey["+cacheRegion + ";" + sourceType.name() + "->" + targetType.name() + "; " + transform.name() + "; " + sourceIdHash + "]";
8094 04 Nov 22 nicklas 138     }
7770 10 Feb 20 nicklas 139   }
7770 10 Feb 20 nicklas 140   
7770 10 Feb 20 nicklas 141   
7770 10 Feb 20 nicklas 142   static class CacheEntry
7770 10 Feb 20 nicklas 143   {
7770 10 Feb 20 nicklas 144     private final Set<Integer> targetList;
7770 10 Feb 20 nicklas 145     private final long timeout;
7770 10 Feb 20 nicklas 146     
7770 10 Feb 20 nicklas 147     CacheEntry(long timeout, Set<Integer> targetList)
7770 10 Feb 20 nicklas 148     {
7770 10 Feb 20 nicklas 149       this.targetList = targetList;
7770 10 Feb 20 nicklas 150       this.timeout = timeout;
7770 10 Feb 20 nicklas 151     }
7770 10 Feb 20 nicklas 152     
7770 10 Feb 20 nicklas 153     long getTimeout()
7770 10 Feb 20 nicklas 154     {
7770 10 Feb 20 nicklas 155       return timeout;
7770 10 Feb 20 nicklas 156     }
7770 10 Feb 20 nicklas 157     
7770 10 Feb 20 nicklas 158     Set<Integer> getTargetIds()
7770 10 Feb 20 nicklas 159     {
7770 10 Feb 20 nicklas 160       return targetList;
7770 10 Feb 20 nicklas 161     }
7770 10 Feb 20 nicklas 162   }
7770 10 Feb 20 nicklas 163 }