Author: nbelaevski
Date: 2010-10-06 11:19:12 -0400 (Wed, 06 Oct 2010)
New Revision: 19481
Added:
branches/RF-7817/push-redesign/src/main/java/org/richfaces/application/impl/ExpiringMap.java
Log:
Added ExpiringMap.java
Added:
branches/RF-7817/push-redesign/src/main/java/org/richfaces/application/impl/ExpiringMap.java
===================================================================
---
branches/RF-7817/push-redesign/src/main/java/org/richfaces/application/impl/ExpiringMap.java
(rev 0)
+++
branches/RF-7817/push-redesign/src/main/java/org/richfaces/application/impl/ExpiringMap.java 2010-10-06
15:19:12 UTC (rev 19481)
@@ -0,0 +1,275 @@
+package org.richfaces.application.impl;
+
+import java.util.Collection;
+import java.util.Map;
+import java.util.Queue;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.DelayQueue;
+import java.util.concurrent.Delayed;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
+
+/**
+ * Created by IntelliJ IDEA.
+ * User: nick
+ * Date: 16.09.2010
+ * Time: 12:17:11
+ * To change this template use File | Settings | File Templates.
+ */
+public class ExpiringMap<K, V> implements Map<K, V> {
+
+ public interface Entry<V> {
+
+ public Object getKey();
+
+ public V getValue();
+
+ public void acquire();
+
+ public void release();
+ }
+
+ private final class EntryImpl implements Entry<V> {
+
+ private final AtomicInteger usageCounter = new AtomicInteger();
+
+ private final K key;
+
+ private volatile V value;
+
+ public EntryImpl(K key) {
+ super();
+ this.key = key;
+ }
+
+ public V getValue() {
+ return value;
+ }
+
+ public void setValue(V value) {
+ if (usageCounter.get() == 0) {
+ addToExpiryQueue(key, this);
+ }
+ this.value = value;
+ }
+
+ public void acquire() {
+ if (usageCounter.getAndIncrement() == 0) {
+ removeFromExpiryQueue(key);
+ }
+ }
+
+ public void release() {
+ if (usageCounter.decrementAndGet() == 0) {
+ addToExpiryQueue(key, this);
+ }
+ }
+
+ public K getKey() {
+ return key;
+ }
+ }
+
+ private final class DelayedQueueEntry implements Delayed {
+
+ private final long expirationTime;
+
+ private final Object entryKey;
+
+ private final Object entryValue;
+
+ public DelayedQueueEntry(Object entryKey) {
+ this(entryKey, null);
+ }
+
+ public DelayedQueueEntry(Object entryKey, Object entryValue) {
+ this(entryKey, entryValue, -1);
+ }
+
+ public DelayedQueueEntry(Object entryKey, Object entryValue, long expirationTime)
{
+ super();
+ this.entryKey = entryKey;
+ this.entryValue = entryValue;
+ this.expirationTime = expirationTime;
+ }
+
+ public long getDelay(TimeUnit unit) {
+ return unit.convert(System.currentTimeMillis() - expirationTime,
TimeUnit.MILLISECONDS);
+ }
+
+ public int compareTo(Delayed o) {
+ return
Long.valueOf(expirationTime).compareTo(o.getDelay(TimeUnit.MILLISECONDS));
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + ((entryKey == null) ? 0 : entryKey.hashCode());
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null) {
+ return false;
+ }
+ if (getClass() != obj.getClass()) {
+ return false;
+ }
+ @SuppressWarnings("unchecked") DelayedQueueEntry other =
(DelayedQueueEntry) obj;
+ if (entryKey == null) {
+ if (other.entryKey != null) {
+ return false;
+ }
+ } else if (!entryKey.equals(other.entryKey)) {
+ return false;
+ }
+ return true;
+ }
+
+ public Object getEntryKey() {
+ return entryKey;
+ }
+
+ public Object getEntryValue() {
+ return entryValue;
+ }
+ }
+
+ private ConcurrentMap<K, EntryImpl> backingMap;
+
+ private long defaultTTL;
+
+ private Queue<DelayedQueueEntry> expiryQueue = new
DelayQueue<DelayedQueueEntry>();
+
+ private ScheduledExecutorService expiryQueuePurgeExecutorService;
+
+ public ExpiringMap(long defaultTTL, ScheduledExecutorService
expiryQueuePurgeExecutorService) {
+ this.defaultTTL = defaultTTL;
+ this.backingMap = new ConcurrentHashMap<K, EntryImpl>();
+
+ this.expiryQueuePurgeExecutorService = expiryQueuePurgeExecutorService;
+ startPurge();
+ }
+
+ private void startPurge() {
+ expiryQueuePurgeExecutorService.scheduleAtFixedRate(new Runnable() {
+
+ public void run() {
+ DelayedQueueEntry entry;
+
+ while (true) {
+ entry = expiryQueue.poll();
+
+ if (entry == null) {
+ break;
+ }
+
+ backingMap.remove(entry.getEntryKey(), entry.getEntryValue());
+ }
+ }
+ }, 0, 5, TimeUnit.SECONDS);
+ }
+
+ protected void addToExpiryQueue(Object key, Object value) {
+ expiryQueue.add(new DelayedQueueEntry(key, value,
getExpirationTimeForCurrentTime()));
+ }
+
+ protected void removeFromExpiryQueue(K key) {
+ expiryQueue.remove(new DelayedQueueEntry(key));
+ }
+
+ public void clear() {
+ backingMap.clear();
+ expiryQueue.clear();
+ }
+
+ public boolean containsKey(Object key) {
+ return backingMap.containsKey(key);
+ }
+
+ public boolean containsValue(Object value) {
+ return backingMap.containsValue(value);
+ }
+
+ private long getExpirationTimeForCurrentTime() {
+ return System.currentTimeMillis() + defaultTTL;
+ }
+
+ private EntryImpl getOrCreateEntry(K key) {
+ EntryImpl entry = backingMap.get(key);
+
+ if (entry == null) {
+ EntryImpl existingEntry = backingMap.putIfAbsent(key, new EntryImpl(key));
+ if (existingEntry != null) {
+ entry = existingEntry;
+ }
+ }
+
+ return entry;
+ }
+
+ public V get(Object key) {
+ Entry<V> entry = backingMap.get(key);
+
+ if (entry != null) {
+ return entry.getValue();
+ }
+
+ return null;
+ }
+
+ public V put(K key, V value) {
+ EntryImpl entry = getOrCreateEntry(key);
+ V oldValue = entry.getValue();
+ entry.setValue(value);
+ return oldValue;
+ }
+
+ public Entry<V> getEntry(Object key) {
+ return backingMap.get(key);
+ }
+
+ public boolean isEmpty() {
+ return backingMap.isEmpty();
+ }
+
+ public V remove(Object key) {
+ expiryQueue.remove(new DelayedQueueEntry(key));
+ Entry<V> removed = backingMap.remove(key);
+ if (removed != null) {
+ return removed.getValue();
+ }
+ return null;
+ }
+
+ public void putAll(Map<? extends K, ? extends V> m) {
+ for (java.util.Map.Entry<? extends K, ? extends V> entry: m.entrySet()) {
+ put(entry.getKey(), entry.getValue());
+ }
+ }
+
+ public Set<java.util.Map.Entry<K, V>> entrySet() {
+ throw new UnsupportedOperationException();
+ }
+
+ public Set<K> keySet() {
+ throw new UnsupportedOperationException();
+ }
+
+ public Collection<V> values() {
+ throw new UnsupportedOperationException();
+ }
+
+ public int size() {
+ return backingMap.size();
+ }
+
+}
Show replies by date