-/*******************************************************************************\r
- * Copyright (c) 2007, 2010 Association for Decentralized Information Management\r
- * in Industry THTH ry.\r
- * All rights reserved. This program and the accompanying materials\r
- * are made available under the terms of the Eclipse Public License v1.0\r
- * which accompanies this distribution, and is available at\r
- * http://www.eclipse.org/legal/epl-v10.html\r
- *\r
- * Contributors:\r
- * VTT Technical Research Centre of Finland - initial API and implementation\r
- *******************************************************************************/\r
-package org.simantics.utils.datastructures.cache;\r
-\r
-import java.lang.ref.SoftReference;\r
-import java.util.ArrayList;\r
-import java.util.Collection;\r
-import java.util.Collections;\r
-import java.util.HashMap;\r
-import java.util.Map;\r
-import java.util.Timer;\r
-import java.util.concurrent.ScheduledFuture;\r
-import java.util.concurrent.TimeUnit;\r
-\r
-import org.simantics.utils.threads.ThreadUtils;\r
-\r
-/**\r
- * A timed (Key, Value) cache which disposes the cached entry after the\r
- * specified amount of time if it is not removed from the cache. The hold time\r
- * is given to the {@link #put(Object, Object, long)} method, separately for\r
- * each value.\r
- * \r
- * <p>\r
- * The cached values are held as soft references, which makes them collectible\r
- * under memory pressure, even before their hold time has ran out.\r
- * \r
- * @author Tuukka Lehtonen\r
- * \r
- * @param <K> key type, held by strong references\r
- * @param <V> value type, held by soft references to allow collection of the\r
- * cached elements when under memory pressure\r
- */\r
-public class SoftTimedCache<K, V> implements ITimedCache<K, V> {\r
-\r
- protected class Entry {\r
- final K key;\r
- final SoftReference<V> ref;\r
- long holdTime;\r
- TimeUnit unit;\r
-\r
- ScheduledFuture<?> future;\r
-\r
- Entry(K k, V v, long holdTime, TimeUnit unit) {\r
- assert k != null;\r
- assert v != null;\r
-\r
- this.key = k;\r
- this.ref = new SoftReference<V>(v);\r
- this.holdTime = holdTime;\r
- this.unit = unit;\r
- }\r
- }\r
-\r
- private final Map<K, Entry> cache = Collections.synchronizedMap(new HashMap<K, Entry>());\r
-\r
- @SuppressWarnings("unused")\r
- private String name;\r
-\r
- private Timer timer;\r
-\r
- public SoftTimedCache() {\r
- this("Cache Timer");\r
- }\r
-\r
- public SoftTimedCache(String name) {\r
- this.name = name;\r
- }\r
-\r
- public int size() {\r
- return cache.size();\r
- }\r
-\r
- @Override\r
- protected void finalize() throws Throwable {\r
- if (timer != null) {\r
- timer.cancel();\r
- }\r
- clear();\r
- super.finalize();\r
- }\r
-\r
- public synchronized void clear() {\r
- Object[] entries;\r
- synchronized (this) {\r
- entries = cache.values().toArray();\r
- cache.clear();\r
- }\r
- for (Object o : entries) {\r
- @SuppressWarnings("unchecked")\r
- Entry e = (Entry) o;\r
- V v = e.ref.get();\r
- e.ref.clear();\r
- cleanup(e);\r
- disposeValue(v);\r
- }\r
- }\r
-\r
- @Override\r
- public void put(final K k, V v, long holdTime, TimeUnit unit) {\r
- Entry e = new Entry(k, v, holdTime, unit);\r
- synchronized (this) {\r
- // First dispose of a previous entry.\r
- dispose(k);\r
- // Then cache the new one.\r
- cache.put(k, e);\r
-\r
- if (unit != null && holdTime > 0) {\r
- schedule(e);\r
- }\r
- }\r
- }\r
-\r
- @Override\r
- public V release(K k) {\r
- Entry e;\r
- synchronized (this) {\r
- e = cache.remove(k);\r
- }\r
- if (e == null)\r
- return null;\r
- return cleanup(e);\r
- }\r
-\r
- private V cleanup(Entry e) {\r
- if (e.future != null) {\r
- if (!e.future.isCancelled()) {\r
- boolean ret = e.future.cancel(false);\r
- if (ret == false)\r
- // Already disposing of this cached entry, let it be.\r
- return null;\r
- }\r
- }\r
- return e.ref.get();\r
- }\r
-\r
- private void dispose(K k) {\r
- Entry e;\r
- synchronized (this) {\r
- e = cache.remove(k);\r
- }\r
- if (e == null)\r
- // Has been released.\r
- return;\r
-\r
- V v = e.ref.get();\r
- if (v != null) {\r
- if (e.future != null)\r
- e.future.cancel(false);\r
- e.ref.clear();\r
- disposeValue(v);\r
- }\r
- }\r
-\r
- void schedule(final Entry e) {\r
- e.future = ThreadUtils.getNonBlockingWorkExecutor().schedule(new Runnable() {\r
- @Override\r
- public void run() {\r
- // Disposal may block, must transfer to blocking work executor.\r
- ThreadUtils.getBlockingWorkExecutor().execute(new Runnable() {\r
- @Override\r
- public void run() {\r
- dispose(e.key);\r
- }\r
- });\r
- }\r
- }, e.holdTime, e.unit);\r
- }\r
-\r
-\r
- /**\r
- * Override to customize disposal of values when their timer elapses.\r
- * \r
- * @param v the value to dispose of\r
- */\r
- protected void disposeValue(V v) {\r
- // Do nothing by default.\r
- }\r
-\r
- public class CacheEntry {\r
- private final Entry e;\r
- private final V value;\r
- public CacheEntry(Entry e) {\r
- this.e = e;\r
- this.value = e.ref.get();\r
- }\r
- public K getKey() {\r
- return e.key;\r
- }\r
- public V getValue() {\r
- return value;\r
- }\r
- public boolean disposeScheduled() {\r
- return e.future != null;\r
- }\r
- public void schedule(long holdTime, TimeUnit unit) {\r
- if (e.future == null) {\r
- e.holdTime = holdTime;\r
- e.unit = unit;\r
- SoftTimedCache.this.schedule(e);\r
- }\r
- }\r
- }\r
-\r
- public Collection<CacheEntry> getEntries() {\r
- ArrayList<CacheEntry> result = new ArrayList<CacheEntry>();\r
- synchronized (this) {\r
- for (Entry e : cache.values()) {\r
- result.add(new CacheEntry(e));\r
- }\r
- }\r
- return result;\r
- }\r
-\r
-}\r
+/*******************************************************************************
+ * Copyright (c) 2007, 2010 Association for Decentralized Information Management
+ * in Industry THTH ry.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * VTT Technical Research Centre of Finland - initial API and implementation
+ *******************************************************************************/
+package org.simantics.utils.datastructures.cache;
+
+import java.lang.ref.SoftReference;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Timer;
+import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.TimeUnit;
+
+import org.simantics.utils.threads.ThreadUtils;
+
+/**
+ * A timed (Key, Value) cache which disposes the cached entry after the
+ * specified amount of time if it is not removed from the cache. The hold time
+ * is given to the {@link #put(Object, Object, long)} method, separately for
+ * each value.
+ *
+ * <p>
+ * The cached values are held as soft references, which makes them collectible
+ * under memory pressure, even before their hold time has ran out.
+ *
+ * @author Tuukka Lehtonen
+ *
+ * @param <K> key type, held by strong references
+ * @param <V> value type, held by soft references to allow collection of the
+ * cached elements when under memory pressure
+ */
+public class SoftTimedCache<K, V> implements ITimedCache<K, V> {
+
+ protected class Entry {
+ final K key;
+ final SoftReference<V> ref;
+ long holdTime;
+ TimeUnit unit;
+
+ ScheduledFuture<?> future;
+
+ Entry(K k, V v, long holdTime, TimeUnit unit) {
+ assert k != null;
+ assert v != null;
+
+ this.key = k;
+ this.ref = new SoftReference<V>(v);
+ this.holdTime = holdTime;
+ this.unit = unit;
+ }
+ }
+
+ private final Map<K, Entry> cache = Collections.synchronizedMap(new HashMap<K, Entry>());
+
+ @SuppressWarnings("unused")
+ private String name;
+
+ private Timer timer;
+
+ public SoftTimedCache() {
+ this("Cache Timer");
+ }
+
+ public SoftTimedCache(String name) {
+ this.name = name;
+ }
+
+ public int size() {
+ return cache.size();
+ }
+
+ @Override
+ protected void finalize() throws Throwable {
+ if (timer != null) {
+ timer.cancel();
+ }
+ clear();
+ super.finalize();
+ }
+
+ public synchronized void clear() {
+ Object[] entries;
+ synchronized (this) {
+ entries = cache.values().toArray();
+ cache.clear();
+ }
+ for (Object o : entries) {
+ @SuppressWarnings("unchecked")
+ Entry e = (Entry) o;
+ V v = e.ref.get();
+ e.ref.clear();
+ cleanup(e);
+ disposeValue(v);
+ }
+ }
+
+ @Override
+ public void put(final K k, V v, long holdTime, TimeUnit unit) {
+ Entry e = new Entry(k, v, holdTime, unit);
+ synchronized (this) {
+ // First dispose of a previous entry.
+ dispose(k);
+ // Then cache the new one.
+ cache.put(k, e);
+
+ if (unit != null && holdTime > 0) {
+ schedule(e);
+ }
+ }
+ }
+
+ @Override
+ public V release(K k) {
+ Entry e;
+ synchronized (this) {
+ e = cache.remove(k);
+ }
+ if (e == null)
+ return null;
+ return cleanup(e);
+ }
+
+ private V cleanup(Entry e) {
+ if (e.future != null) {
+ if (!e.future.isCancelled()) {
+ boolean ret = e.future.cancel(false);
+ if (ret == false)
+ // Already disposing of this cached entry, let it be.
+ return null;
+ }
+ }
+ return e.ref.get();
+ }
+
+ private void dispose(K k) {
+ Entry e;
+ synchronized (this) {
+ e = cache.remove(k);
+ }
+ if (e == null)
+ // Has been released.
+ return;
+
+ V v = e.ref.get();
+ if (v != null) {
+ if (e.future != null)
+ e.future.cancel(false);
+ e.ref.clear();
+ disposeValue(v);
+ }
+ }
+
+ void schedule(final Entry e) {
+ e.future = ThreadUtils.getNonBlockingWorkExecutor().schedule(new Runnable() {
+ @Override
+ public void run() {
+ // Disposal may block, must transfer to blocking work executor.
+ ThreadUtils.getBlockingWorkExecutor().execute(new Runnable() {
+ @Override
+ public void run() {
+ dispose(e.key);
+ }
+ });
+ }
+ }, e.holdTime, e.unit);
+ }
+
+
+ /**
+ * Override to customize disposal of values when their timer elapses.
+ *
+ * @param v the value to dispose of
+ */
+ protected void disposeValue(V v) {
+ // Do nothing by default.
+ }
+
+ public class CacheEntry {
+ private final Entry e;
+ private final V value;
+ public CacheEntry(Entry e) {
+ this.e = e;
+ this.value = e.ref.get();
+ }
+ public K getKey() {
+ return e.key;
+ }
+ public V getValue() {
+ return value;
+ }
+ public boolean disposeScheduled() {
+ return e.future != null;
+ }
+ public void schedule(long holdTime, TimeUnit unit) {
+ if (e.future == null) {
+ e.holdTime = holdTime;
+ e.unit = unit;
+ SoftTimedCache.this.schedule(e);
+ }
+ }
+ }
+
+ public Collection<CacheEntry> getEntries() {
+ ArrayList<CacheEntry> result = new ArrayList<CacheEntry>();
+ synchronized (this) {
+ for (Entry e : cache.values()) {
+ result.add(new CacheEntry(e));
+ }
+ }
+ return result;
+ }
+
+}