Bläddra i källkod

update: Cache & CacheMap

Ranides Atterwim 9 år sedan
förälder
incheckning
c39ad9b89a

+ 5 - 11
assira.drafts/src/main/java/net/ranides/assira/reflection/impl/WSInpsector.java

@@ -13,23 +13,17 @@ import java.util.Map;
 import java.util.Optional;
 import java.util.Set;
 import java.util.function.Supplier;
+
 import javax.jws.WebMethod;
 import javax.jws.WebParam;
 import javax.jws.WebResult;
 import javax.jws.WebService;
-import net.ranides.assira.collection.maps.Cache;
+
+import net.ranides.assira.collection.maps.CacheMap;
 import net.ranides.assira.collection.maps.MapCollectors;
 import net.ranides.assira.collection.query.CQueryBuilder;
 import net.ranides.assira.generic.LazyReference;
-import net.ranides.assira.reflection.IAnnotations;
-import net.ranides.assira.reflection.IArgument;
-import net.ranides.assira.reflection.IArguments;
-import net.ranides.assira.reflection.IAttribute;
-import net.ranides.assira.reflection.IClass;
-import net.ranides.assira.reflection.IContext;
-import net.ranides.assira.reflection.IMethod;
-import net.ranides.assira.reflection.WSMethod;
-import net.ranides.assira.reflection.WSModel;
+import net.ranides.assira.reflection.*;
 import net.ranides.assira.text.StringTraits;
 
 /**
@@ -38,7 +32,7 @@ import net.ranides.assira.text.StringTraits;
  */
 public class WSInpsector implements WSModel {
     
-    private static final Cache<IClass<?>,WSModel> CACHE = Cache.getInstance(WSInpsector::new);
+    private static final CacheMap<IClass<?>,WSModel> CACHE = CacheMap.getInstance(WSInpsector::new);
 
     private final IClass<?> type;
     private final String name;

+ 10 - 230
assira/src/main/java/net/ranides/assira/collection/maps/Cache.java

@@ -6,250 +6,30 @@
  */
 package net.ranides.assira.collection.maps;
 
-import java.lang.ref.Reference;
-import java.lang.ref.ReferenceQueue;
-import java.lang.ref.SoftReference;
 import java.time.Duration;
-import java.time.Instant;
-import java.util.LinkedHashMap;
-import java.util.Map;
-import java.util.concurrent.ConcurrentHashMap;
 import java.util.function.Function;
 import java.util.function.Supplier;
 
-import net.ranides.assira.system.RuntimeUtils;
-
 /**
  * @author Ranides Atterwim <ranides@gmail.com>
  */
-public abstract class Cache<K,V> {
-
-    protected static final ReferenceQueue QUEUE = new ReferenceQueue<>();
-
-    protected static final boolean USE_THREAD = RuntimeUtils.getProperty("assira.cache.no-threads", "false").equals("false");
-
-    static {
-        if(USE_THREAD) {
-            new Thread(Cache::cleanupThread, "assira cache cleanup").start();
-        }
-    }
-
-    protected final Function<K,V> supplier;
-
-    protected Cache(Function<K, V> supplier) {
-        this.supplier = supplier;
-    }
-
-    public static <K,V> Cache<K,V> getInstance(Function<K,V> supplier) {
-        return new SimpleCache<K, V>(supplier);
-    }
-
-    public static <K,V> Cache<K,V> getInstance(int capacity, Function<K,V> supplier) {
-        return new FixedCache<K, V>(capacity, supplier);
-    }
-
-    public static <K,V> Cache<K,V> getInstance(Duration ttl, Function<K,V> supplier) {
-        return new TTLCache<K, V>(ttl, supplier);
-    }
-
-    public final V get(K key) {
-        return get(key, supplier);
-    }
-
-    public final V get(K key, Function<K,V> function) {
-        return get(key, () -> function.apply(key));
-    }
-
-    public final V get(K key, Supplier<V> function) {
-        Reference<V> ref = iget(key);
-        if(null == ref) {
-            cleanup();
-            return iput(key, function.get());
-        }
-        V ret = ref.get();
-        if(null == ret) {
-            cleanup();
-            return iput(key, function.get());
-        }
-        return ret;
-    }
-
-    public final void clear() {
-        iclear();
-        cleanup();
-    }
-
-    protected abstract Reference<V> iget(K key);
-
-    protected abstract V iput(K key, V value);
-
-    protected abstract void iremove(K key);
-
-    protected abstract void iclear();
-
-    protected static void cleanup() {
-        if(!USE_THREAD) {
-            KRef ref;
-            while ( null != (ref = (KRef)QUEUE.poll()) ) {
-                ref.src.iremove(ref.key);
-            }
-        }
-    }
-
-    protected static void cleanupThread() {
-        try {
-            while (true) {
-                KRef ref = (KRef) QUEUE.remove();
-                ref.src.iremove(ref.key);
-            }
-        } catch (InterruptedException cause) {
-            // shutdown
-        }
-    }
-
-    private static final class SimpleCache<K,V> extends Cache<K,V> {
+public interface Cache<K,V> {
 
-        private final Map<K,KRef<K,V>> map = new ConcurrentHashMap<>();
-
-        public SimpleCache(Function<K, V> supplier) {
-            super(supplier);
-        }
-
-        @Override
-        protected V iput(K key, V value) {
-            map.put(key, new KRef<K, V>(this, key, value));
-            return value;
-        }
-
-        @Override
-        public Reference<V> iget(K key) {
-            return map.get(key);
-        }
-
-        @Override
-        protected void iremove(K key) {
-            map.remove(key);
-        }
-
-        @Override
-        public void iclear() {
-            map.clear();
-        }
+    static <K,V> Cache<K,V> getInstance() {
+        return CacheMap.getInstance(e -> { throw new UnsupportedOperationException(); });
     }
 
-    private static final class FixedCache<K,V> extends Cache<K,V> {
-
-        private final Map<K,KRef<K,V>> map;
-
-        public FixedCache(int capacity, Function<K, V> supplier) {
-            super(supplier);
-            this.map = new FHashMap<>(capacity);
-        }
-
-        @Override
-        protected V iput(K key, V value) {
-            synchronized (map) {
-                map.put(key, new KRef<K, V>(this, key, value));
-            }
-            return value;
-        }
-
-        @Override
-        public Reference<V> iget(K key) {
-            synchronized (map) {
-                return map.get(key);
-            }
-        }
-
-        @Override
-        protected void iremove(K key) {
-            synchronized (map) {
-                map.remove(key);
-            }
-        }
-
-        @Override
-        public void iclear() {
-            synchronized (map) {
-                map.clear();
-            }
-        }
+    static <K,V> Cache<K,V> getInstance(int capacity) {
+        return CacheMap.getInstance(capacity, e -> { throw new UnsupportedOperationException(); });
     }
 
-    private static final class TTLCache<K,V> extends Cache<K,V> {
-
-        private final Duration ttl;
-
-        private final Map<K,KRef<K,V>> map = new ConcurrentHashMap<>();
-
-        public TTLCache(Duration ttl, Function<K, V> supplier) {
-            super(supplier);
-            this.ttl = ttl;
-        }
-
-        @Override
-        protected V iput(K key, V value) {
-            map.put(key, new TRef<K, V>(this, key, value, ttl));
-            return value;
-        }
-
-        @Override
-        public Reference<V> iget(K key) {
-            return map.get(key);
-        }
-
-        @Override
-        protected void iremove(K key) {
-            map.remove(key);
-        }
-
-        @Override
-        public void iclear() {
-            map.clear();
-        }
-    }
-
-    private static class KRef<K,V> extends SoftReference<V> {
-
-        public final Cache<K,V> src;
-        public final K key;
-
-        public KRef(Cache<K,V> src, K key, V value) {
-            super(value, QUEUE);
-            this.src = src;
-            this.key = key;
-        }
+    static <K,V> Cache<K,V> getInstance(Duration ttl) {
+        return CacheMap.getInstance(ttl, e -> { throw new UnsupportedOperationException(); });
     }
 
-    private static class TRef<K,V> extends KRef<K,V> {
-
-        public Instant time;
-
-        public TRef(Cache<K,V> src, K key, V value, Duration ttl) {
-            super(src, key, value);
-            this.time = Instant.now().plus(ttl);
-        }
+    V get(K key, Function<K,V> function);
 
-        @Override
-        public V get() {
-            return time.isBefore(Instant.now()) ? null : super.get();
-        }
-
-    }
-
-    private static class FHashMap<K,V> extends LinkedHashMap<K,V> {
-
-        private final int maxsize;
-
-        public FHashMap(int maxsize) {
-            super(Math.min(Math.round(maxsize / 0.75f)+2, 2048), 0.75f, true);
-            this.maxsize = maxsize;
-        }
-
-        @Override
-        protected boolean removeEldestEntry(Map.Entry<K, V> eldest) {
-            return this.size() > maxsize;
-        }
-    }
+    V get(K key, Supplier<V> function);
 
+    void clear();
 }

+ 255 - 0
assira/src/main/java/net/ranides/assira/collection/maps/CacheMap.java

@@ -0,0 +1,255 @@
+/*
+ * @author Ranides Atterwim <ranides@gmail.com>
+ * @copyright Ranides Atterwim
+ * @license WTFPL
+ * @url http://ranides.net/projects/assira
+ */
+package net.ranides.assira.collection.maps;
+
+import java.lang.ref.Reference;
+import java.lang.ref.ReferenceQueue;
+import java.lang.ref.SoftReference;
+import java.time.Duration;
+import java.time.Instant;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.function.Function;
+import java.util.function.Supplier;
+
+import net.ranides.assira.system.RuntimeUtils;
+
+/**
+ * @author Ranides Atterwim <ranides@gmail.com>
+ */
+public abstract class CacheMap<K,V> implements Cache<K,V> {
+
+    protected static final ReferenceQueue QUEUE = new ReferenceQueue<>();
+
+    protected static final boolean USE_THREAD = RuntimeUtils.getProperty("assira.cache.no-threads", "false").equals("false");
+
+    static {
+        if(USE_THREAD) {
+            new Thread(CacheMap::cleanupThread, "assira cache cleanup").start();
+        }
+    }
+
+    protected final Function<K,V> supplier;
+
+    protected CacheMap(Function<K, V> supplier) {
+        this.supplier = supplier;
+    }
+
+    public static <K,V> CacheMap<K,V> getInstance(Function<K,V> supplier) {
+        return new SimpleCache<K, V>(supplier);
+    }
+
+    public static <K,V> CacheMap<K,V> getInstance(int capacity, Function<K,V> supplier) {
+        return new FixedCache<K, V>(capacity, supplier);
+    }
+
+    public static <K,V> CacheMap<K,V> getInstance(Duration ttl, Function<K,V> supplier) {
+        return new TTLCache<K, V>(ttl, supplier);
+    }
+
+    public final V get(K key) {
+        return get(key, supplier);
+    }
+
+    public final V get(K key, Function<K,V> function) {
+        return get(key, () -> function.apply(key));
+    }
+
+    public final V get(K key, Supplier<V> function) {
+        Reference<V> ref = iget(key);
+        if(null == ref) {
+            cleanup();
+            return iput(key, function.get());
+        }
+        V ret = ref.get();
+        if(null == ret) {
+            cleanup();
+            return iput(key, function.get());
+        }
+        return ret;
+    }
+
+    public final void clear() {
+        iclear();
+        cleanup();
+    }
+
+    protected abstract Reference<V> iget(K key);
+
+    protected abstract V iput(K key, V value);
+
+    protected abstract void iremove(K key);
+
+    protected abstract void iclear();
+
+    protected static void cleanup() {
+        if(!USE_THREAD) {
+            KRef ref;
+            while ( null != (ref = (KRef)QUEUE.poll()) ) {
+                ref.src.iremove(ref.key);
+            }
+        }
+    }
+
+    protected static void cleanupThread() {
+        try {
+            while (true) {
+                KRef ref = (KRef) QUEUE.remove();
+                ref.src.iremove(ref.key);
+            }
+        } catch (InterruptedException cause) {
+            // shutdown
+        }
+    }
+
+    private static final class SimpleCache<K,V> extends CacheMap<K,V> {
+
+        private final Map<K,KRef<K,V>> map = new ConcurrentHashMap<>();
+
+        public SimpleCache(Function<K, V> supplier) {
+            super(supplier);
+        }
+
+        @Override
+        protected V iput(K key, V value) {
+            map.put(key, new KRef<K, V>(this, key, value));
+            return value;
+        }
+
+        @Override
+        public Reference<V> iget(K key) {
+            return map.get(key);
+        }
+
+        @Override
+        protected void iremove(K key) {
+            map.remove(key);
+        }
+
+        @Override
+        public void iclear() {
+            map.clear();
+        }
+    }
+
+    private static final class FixedCache<K,V> extends CacheMap<K,V> {
+
+        private final Map<K,KRef<K,V>> map;
+
+        public FixedCache(int capacity, Function<K, V> supplier) {
+            super(supplier);
+            this.map = new FHashMap<>(capacity);
+        }
+
+        @Override
+        protected V iput(K key, V value) {
+            synchronized (map) {
+                map.put(key, new KRef<K, V>(this, key, value));
+            }
+            return value;
+        }
+
+        @Override
+        public Reference<V> iget(K key) {
+            synchronized (map) {
+                return map.get(key);
+            }
+        }
+
+        @Override
+        protected void iremove(K key) {
+            synchronized (map) {
+                map.remove(key);
+            }
+        }
+
+        @Override
+        public void iclear() {
+            synchronized (map) {
+                map.clear();
+            }
+        }
+    }
+
+    private static final class TTLCache<K,V> extends CacheMap<K,V> {
+
+        private final Duration ttl;
+
+        private final Map<K,KRef<K,V>> map = new ConcurrentHashMap<>();
+
+        public TTLCache(Duration ttl, Function<K, V> supplier) {
+            super(supplier);
+            this.ttl = ttl;
+        }
+
+        @Override
+        protected V iput(K key, V value) {
+            map.put(key, new TRef<K, V>(this, key, value, ttl));
+            return value;
+        }
+
+        @Override
+        public Reference<V> iget(K key) {
+            return map.get(key);
+        }
+
+        @Override
+        protected void iremove(K key) {
+            map.remove(key);
+        }
+
+        @Override
+        public void iclear() {
+            map.clear();
+        }
+    }
+
+    private static class KRef<K,V> extends SoftReference<V> {
+
+        public final CacheMap<K,V> src;
+        public final K key;
+
+        public KRef(CacheMap<K,V> src, K key, V value) {
+            super(value, QUEUE);
+            this.src = src;
+            this.key = key;
+        }
+    }
+
+    private static class TRef<K,V> extends KRef<K,V> {
+
+        public Instant time;
+
+        public TRef(CacheMap<K,V> src, K key, V value, Duration ttl) {
+            super(src, key, value);
+            this.time = Instant.now().plus(ttl);
+        }
+
+        @Override
+        public V get() {
+            return time.isBefore(Instant.now()) ? null : super.get();
+        }
+
+    }
+
+    private static class FHashMap<K,V> extends LinkedHashMap<K,V> {
+
+        private final int maxsize;
+
+        public FHashMap(int maxsize) {
+            super(Math.min(Math.round(maxsize / 0.75f)+2, 2048), 0.75f, true);
+            this.maxsize = maxsize;
+        }
+
+        @Override
+        protected boolean removeEldestEntry(Map.Entry<K, V> eldest) {
+            return this.size() > maxsize;
+        }
+    }
+
+}

+ 2 - 2
assira/src/main/java/net/ranides/assira/reflection/impl/CDParameters.java

@@ -23,13 +23,13 @@ import net.ranides.asm.Label;
 import net.ranides.asm.MethodVisitor;
 import net.ranides.asm.Opcodes;
 import net.ranides.asm.Type;
-import net.ranides.assira.collection.maps.Cache;
+import net.ranides.assira.collection.maps.CacheMap;
 import net.ranides.assira.reflection.util.MemberTraits;
 import net.ranides.assira.trace.ExceptionUtils;
 
 public class CDParameters {
     
-    private static final Cache<Class<?>, CDParameters> CACHE = Cache.getInstance(CDParameters::new);
+    private static final CacheMap<Class<?>, CDParameters> CACHE = CacheMap.getInstance(CDParameters::new);
     
     final Map<Constructor<?>,String[]> cParams = new HashMap<>();
     

+ 2 - 2
assira/src/main/java/net/ranides/assira/reflection/impl/FCache.java

@@ -7,9 +7,9 @@
 
 package net.ranides.assira.reflection.impl;
 
-import java.lang.ref.Reference;
 import java.util.Objects;
 import java.util.function.Supplier;
+
 import net.ranides.assira.collection.maps.Cache;
 import net.ranides.assira.reflection.IContext;
 
@@ -19,7 +19,7 @@ import net.ranides.assira.reflection.IContext;
  */
 public class FCache<T> {
 
-    private final Cache<FCache.CKey, T> cache = Cache.getInstance(a -> null);
+    private final Cache<CKey, T> cache = Cache.getInstance();
 
     public T get(IContext context, Object key, Supplier<T> supplier) {
         return cache.get(new CKey(context, key), supplier);

+ 3 - 2
assira/src/main/java/net/ranides/assira/reflection/impl/FField.java

@@ -8,6 +8,7 @@
 package net.ranides.assira.reflection.impl;
 
 import java.lang.reflect.Field;
+
 import net.ranides.assira.collection.maps.Cache;
 import net.ranides.assira.reflection.IContext;
 import net.ranides.assira.reflection.IField;
@@ -18,8 +19,8 @@ import net.ranides.assira.reflection.IField;
  */
 public final class FField {
     
-    private static final Cache<Object, IField> D_CACHE = Cache.getInstance(v -> null);
-    private static final Cache<Object, IField> R_CACHE = Cache.getInstance(v -> null);
+    private static final Cache<Object, IField> D_CACHE = Cache.getInstance();
+    private static final Cache<Object, IField> R_CACHE = Cache.getInstance();
     
     private FField() {
         /* utility class */

+ 3 - 2
assira/src/main/java/net/ranides/assira/reflection/impl/FMethod.java

@@ -10,8 +10,9 @@ package net.ranides.assira.reflection.impl;
 import java.lang.reflect.Constructor;
 import java.lang.reflect.Method;
 import java.util.List;
-import net.ranides.assira.collection.maps.Cache;
+import net.ranides.assira.collection.maps.CacheMap;
 import static net.ranides.assira.reflection.IAttribute.*;
+
 import net.ranides.assira.reflection.IClass;
 import net.ranides.assira.reflection.IContext;
 import net.ranides.assira.reflection.IMethod;
@@ -24,7 +25,7 @@ public final class FMethod {
     
     private static final FCache<IMethod> D_METHODS = new FCache<>();
     private static final FCache<IMethod> R_METHODS = new FCache<>();
-    private static final Cache<Object, IMethod> FUNCTIONS = Cache.getInstance(FMethod::inewFunction);
+    private static final CacheMap<Object, IMethod> FUNCTIONS = CacheMap.getInstance(FMethod::inewFunction);
     
     private FMethod() {
         /* utility class */

+ 2 - 2
assira/src/main/java/net/ranides/assira/reflection/impl/bean/FBeanModel.java

@@ -8,7 +8,7 @@
 package net.ranides.assira.reflection.impl.bean;
 
 import java.util.Collection;
-import net.ranides.assira.collection.maps.Cache;
+import net.ranides.assira.collection.maps.CacheMap;
 import net.ranides.assira.reflection.BeanMethod;
 import net.ranides.assira.reflection.BeanModel;
 import net.ranides.assira.reflection.BeanProperty;
@@ -22,7 +22,7 @@ import net.ranides.assira.reflection.IMethod;
  */
 public final class FBeanModel {
     
-    private static final Cache<IClass<?>,BeanModel> CACHE = Cache.getInstance(RBeanModel::new);
+    private static final CacheMap<IClass<?>,BeanModel> CACHE = CacheMap.getInstance(RBeanModel::new);
     
     private FBeanModel() {
         /* utility class */

+ 2 - 2
assira/src/main/java/net/ranides/assira/reflection/util/AnnotationUtils.java

@@ -14,7 +14,7 @@ import java.lang.reflect.Proxy;
 import java.util.Collections;
 import java.util.List;
 import java.util.Map;
-import net.ranides.assira.collection.maps.Cache;
+import net.ranides.assira.collection.maps.CacheMap;
 import net.ranides.assira.collection.maps.MapCollectors;
 import net.ranides.assira.generic.CompareUtils;
 import net.ranides.assira.generic.HashUtils;
@@ -35,7 +35,7 @@ public final class AnnotationUtils {
     
     private static final Method TOSTRING  = IClass.OBJECT.method("toString").get().reflective();
     
-    private static final Cache<Class<? extends Annotation>, Model> CACHE = Cache.getInstance(Model::new);
+    private static final CacheMap<Class<? extends Annotation>, Model> CACHE = CacheMap.getInstance(Model::new);
     
     private AnnotationUtils() {
         /* utility class */

+ 3 - 3
assira/src/main/java/net/ranides/assira/text/ResolveDialect.java

@@ -22,7 +22,7 @@ import java.util.function.BiFunction;
 import java.util.function.Function;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
-import net.ranides.assira.collection.maps.Cache;
+import net.ranides.assira.collection.maps.CacheMap;
 import net.ranides.assira.collection.maps.HashMap;
 import net.ranides.assira.generic.CompareUtils;
 import net.ranides.assira.generic.HashUtils;
@@ -45,7 +45,7 @@ public final class ResolveDialect {
     
     private static final class Li { // NOPMD - lazy init idiom
         
-        public static final Cache<String, ResolveDialect> DIALECTS = Cache.getInstance(ResolveDialect::new);
+        public static final CacheMap<String, ResolveDialect> DIALECTS = CacheMap.getInstance(ResolveDialect::new);
         
         public static final ResolveDialect DEFAULT = new ResolveDialect(Locale.getDefault().getCountry());
     
@@ -53,7 +53,7 @@ public final class ResolveDialect {
     
     private final String name;
     
-    private final Cache<String, ResolveFormat> cache = Cache.getInstance(pattern -> new ResolveFormat(this, pattern));
+    private final CacheMap<String, ResolveFormat> cache = CacheMap.getInstance(pattern -> new ResolveFormat(this, pattern));
     
     private final Map<TokenKey,Function<ResolvePattern,ResolveFormat.Token>> mDefined = new HashMap<>();
     

+ 13 - 6
assira/src/test/java/net/ranides/assira/collection/maps/CacheTest.java

@@ -17,7 +17,7 @@ import static net.ranides.assira.junit.NewAssert.*;
  *
  * @author Ranides Atterwim <ranides@gmail.com>
  */
-public class CacheTest {
+public class CacheMapTest {
 
     private static final class Text {
 
@@ -37,7 +37,7 @@ public class CacheTest {
         }
     }
 
-    private Cache<Integer, Text> fill(Cache<Integer, Text> cache) {
+    private CacheMap<Integer, Text> fill(CacheMap<Integer, Text> cache) {
         for(int i=0; i<24; i++) {
             cache.get(i);
         }
@@ -49,12 +49,12 @@ public class CacheTest {
 
     @Test
     public void testBasic() {
-        testBasic(Cache.getInstance(Text::new));
-        testBasic(Cache.getInstance(128, Text::new));
-        testBasic(Cache.getInstance(Duration.ofMillis(1000), Text::new));
+        testBasic(CacheMap.getInstance(Text::new));
+        testBasic(CacheMap.getInstance(128, Text::new));
+        testBasic(CacheMap.getInstance(Duration.ofMillis(1000), Text::new));
     }
 
-    private void testBasic(Cache<Integer, Text> cache) {
+    private void testBasic(CacheMap<Integer, Text> cache) {
         fill(cache);
 
         Text a = cache.get(15, () -> new Text("C15"));
@@ -77,6 +77,13 @@ public class CacheTest {
         assertSame(c, cache.get(15));
         assertEquals("C70", d.value);
         assertSame(d, cache.get(70, () -> new Text("C70!")));
+
+        cache.clear();
+
+        Text e = cache.get(15, () -> new Text("R15"));
+        Text f = cache.get(70, () -> new Text("R70"));
+        assertEquals("R15", e.value);
+        assertEquals("R70", f.value);
     }
 //
 //    @Test