diff --git a/src/zutil/Timer.java b/src/zutil/Timer.java old mode 100644 new mode 100755 index 98988aa..2254d30 --- a/src/zutil/Timer.java +++ b/src/zutil/Timer.java @@ -44,9 +44,12 @@ public class Timer { /** * Will start or restart the timer if it is already running + * + * @return a reference of itself */ - public void start(){ + public Timer start(){ timestamp = System.currentTimeMillis(); + return this; } /** diff --git a/src/zutil/db/bean/DBBeanSQLResultHandler.java b/src/zutil/db/bean/DBBeanSQLResultHandler.java index f72f43f..fae6249 100755 --- a/src/zutil/db/bean/DBBeanSQLResultHandler.java +++ b/src/zutil/db/bean/DBBeanSQLResultHandler.java @@ -156,18 +156,17 @@ public class DBBeanSQLResultHandler implements SQLResultHandler{ } int removed = 0; - long time = System.currentTimeMillis(); for(Object classKey : cache.keySet()){ if( classKey == null ) continue; Map class_cache = cache.get(classKey); - for(Object objKey : class_cache.keySet()){ - if( objKey == null ) continue; - - DBBeanCache beanCache = class_cache.get(objKey); + for(Iterator> it = class_cache.entrySet().iterator(); it.hasNext(); ) { + Map.Entry entry = it.next(); + if( entry.getKey() == null ) continue; + // Check if session is still valid - if( beanCache.bean.get() == null ){ - class_cache.remove(objKey); + if( entry.getValue().bean.get() == null ){ + it.remove(); removed++; } } diff --git a/src/zutil/struct/ObjectCache.java b/src/zutil/struct/ObjectCache.java new file mode 100755 index 0000000..00fcf41 --- /dev/null +++ b/src/zutil/struct/ObjectCache.java @@ -0,0 +1,91 @@ +package zutil.struct; + +import zutil.Timer; + +import java.lang.ref.WeakReference; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; + +/** + * This is a Object cache where objects can be stored with {@link java.lang.ref.WeakReference} and a time limit. + * + * Created by Ziver on 2016-07-29. + */ +public class ObjectCache { + + private static class CacheEntry{ + public Timer timer; + public WeakReference value; + } + + private HashMap> cache = new HashMap<>(); + private long ttl; + + + public ObjectCache(long ttl){ + this.ttl = ttl; + } + + + /** + * Stores a key and value pair in the cache. + */ + public void put(K key, V value){ + CacheEntry entry = new CacheEntry<>(); + entry.timer = new Timer(ttl).start(); + entry.value = new WeakReference<>(value); + cache.put(key, entry); + } + + + /** + * Checks if the specific key is available in + * the cache and that it is valid. + */ + public boolean containsKey(Object key){ + if(cache.containsKey(key)){ + CacheEntry entry = cache.get(key); + if (entry.timer.hasTimedOut() || entry.value.get() == null) // entry to old or not valid + cache.remove(key); + else + return true; + } + return false; + } + + + public V get(Object key){ + if (containsKey(key)) + return cache.get(key).value.get(); + return null; + } + + + + /** + * This method will return the number of stored entries(valid and timed out entries) in the cache. + */ + public int size() { + return cache.size(); + } + + + + /** + * Iterates through the Set and removes all entries that has passed its TTL. + * + * @return the number of objects removed from the Set + */ + public int garbageCollect(){ + int count = 0; + for(Iterator>> it = cache.entrySet().iterator(); it.hasNext(); ) { + Map.Entry> mapEntry = it.next(); + if (mapEntry.getValue().timer.hasTimedOut() || mapEntry.getValue().value.get() == null) { // entry to old or not valid + it.remove(); + ++count; + } + } + return count; + } +} diff --git a/src/zutil/struct/TimedHashSet.java b/src/zutil/struct/TimedHashSet.java index bb249c1..f28b1a3 100755 --- a/src/zutil/struct/TimedHashSet.java +++ b/src/zutil/struct/TimedHashSet.java @@ -24,14 +24,18 @@ package zutil.struct; +import zutil.Timer; + import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; /** * This is a timed HashSet. Each entry has a limited time to live. */ public class TimedHashSet { - private HashMap map; + private HashMap map; private long ttl; /** @@ -47,12 +51,12 @@ public class TimedHashSet { * @return true if the object already existed in the set which will reset the TTL. */ public boolean add(T o){ - return map.put(o, System.currentTimeMillis()) != null; + return map.put(o, new Timer(ttl).start()) != null; } public boolean contains(Object o){ if(map.containsKey(o)){ - if(map.get(o) + ttl < System.currentTimeMillis()) // entry to old + if(map.get(o).hasTimedOut()) // entry to old map.remove(o); else return true; @@ -62,12 +66,28 @@ public class TimedHashSet { /** - * Iterates through the Set and removes all entries that has passed the TTL + * This method will return the number of stored entries(valid and timed out entries) in the Set. */ - public void garbageCollect(){ - for(T o : map.keySet()){ - if(map.get(o) + ttl < System.currentTimeMillis()) // entry to old - map.remove(o); - } + public int size() { + return map.size(); } + + + /** + * Iterates through the Set and removes all entries that has passed its TTL. + * + * @return the number of objects removed from the Set + */ + public int garbageCollect(){ + int count = 0; + for(Iterator> it = map.entrySet().iterator(); it.hasNext(); ) { + Map.Entry entry = it.next(); + if (entry.getValue().hasTimedOut()) { // entry to old + it.remove(); + ++count; + } + } + return count; + } + } diff --git a/test/zutil/struct/ObjectCacheTest.java b/test/zutil/struct/ObjectCacheTest.java new file mode 100755 index 0000000..3a5c79c --- /dev/null +++ b/test/zutil/struct/ObjectCacheTest.java @@ -0,0 +1,105 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2015 Ziver Koc + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +package zutil.struct; + +import org.junit.Test; + +import static junit.framework.TestCase.assertTrue; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; + + +/** + * Created by Ziver on 2015-11-20. + */ +public class ObjectCacheTest { + public static final String KEY = "key"; + public static final String OBJECT = "object"; + public static final String KEY2 = "key2"; + public static final String OBJECT2 = "object2"; + + + @Test + public void emptyCache() throws InterruptedException { + ObjectCache cache = new ObjectCache(10); + assertFalse(cache.containsKey(KEY)); + assertEquals(0, cache.size()); + } + + @Test + public void zeroTTL() throws InterruptedException { + ObjectCache cache = new ObjectCache(0); + cache.put(KEY, OBJECT); + assertEquals(1, cache.size()); + Thread.sleep(1); + assertFalse(cache.containsKey(KEY)); + } + + @Test + public void tenMsTTL() throws InterruptedException { + ObjectCache cache = new ObjectCache(10); + cache.put(KEY, OBJECT); + assertEquals(OBJECT, cache.get(KEY)); + Thread.sleep(1); + assertTrue(cache.containsKey(KEY)); + Thread.sleep(10); + assertFalse(cache.containsKey(KEY)); + assertEquals(0, cache.size()); + } + + @Test + public void oneSecTTL() throws InterruptedException { + ObjectCache cache = new ObjectCache(1000); + cache.put(KEY, OBJECT); + Thread.sleep(1); + assertTrue(cache.containsKey(KEY)); + } + + //@Test + // This TC does not work + public void javaGRC() throws InterruptedException { + ObjectCache cache = new ObjectCache(10000); + { + String tmp = new String("temporary obj"); + cache.put(KEY, tmp); + assertEquals(1, cache.size()); + } + System.gc(); // will probably not do anything + Thread.sleep(10); + assertEquals(1, cache.garbageCollect()); + } + + @Test + public void grc() throws InterruptedException { + ObjectCache cache = new ObjectCache(10); + cache.put(KEY, OBJECT); + cache.put(KEY2, OBJECT2); + assertEquals(2, cache.size()); + Thread.sleep(12); + assertEquals(2, cache.size()); + assertEquals(2, cache.garbageCollect()); + assertEquals(0, cache.size()); + } +} \ No newline at end of file diff --git a/test/zutil/struct/TimedHashSetTest.java b/test/zutil/struct/TimedHashSetTest.java index d92daf9..dda7ed3 100755 --- a/test/zutil/struct/TimedHashSetTest.java +++ b/test/zutil/struct/TimedHashSetTest.java @@ -26,6 +26,8 @@ package zutil.struct; import org.junit.Test; +import static junit.framework.TestCase.assertTrue; +import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; @@ -34,6 +36,7 @@ import static org.junit.Assert.assertFalse; */ public class TimedHashSetTest { public static final String ENTRY = "key"; + public static final String ENTRY2 = "key2"; @Test public void zeroTTL() throws InterruptedException { @@ -48,7 +51,7 @@ public class TimedHashSetTest { TimedHashSet set = new TimedHashSet(10); set.add(ENTRY); Thread.sleep(1); - assert(set.contains(ENTRY)); + assertTrue(set.contains(ENTRY)); Thread.sleep(10); assertFalse(set.contains(ENTRY)); } @@ -58,6 +61,18 @@ public class TimedHashSetTest { TimedHashSet set = new TimedHashSet(1000); set.add(ENTRY); Thread.sleep(1); - assert(set.contains(ENTRY)); + assertTrue(set.contains(ENTRY)); + } + + @Test + public void grc() throws InterruptedException { + TimedHashSet set = new TimedHashSet(10); + set.add(ENTRY); + set.add(ENTRY2); + assertEquals(2, set.size()); + Thread.sleep(12); + assertEquals(2, set.size()); + assertEquals(2, set.garbageCollect()); + assertEquals(0, set.size()); } } \ No newline at end of file