You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@tomcat.apache.org by kk...@apache.org on 2012/04/19 14:04:36 UTC

svn commit: r1327915 - in /tomcat/trunk/java/org/apache/tomcat/util/collections: ./ ManagedConcurrentWeakHashMap.java

Author: kkolinko
Date: Thu Apr 19 12:04:35 2012
New Revision: 1327915

URL: http://svn.apache.org/viewvc?rev=1327915&view=rev
Log:
For https://issues.apache.org/bugzilla/show_bug.cgi?id=53085
Implement ConcurrentHashMap with weak keys.

Added:
    tomcat/trunk/java/org/apache/tomcat/util/collections/   (with props)
    tomcat/trunk/java/org/apache/tomcat/util/collections/ManagedConcurrentWeakHashMap.java   (with props)

Propchange: tomcat/trunk/java/org/apache/tomcat/util/collections/
------------------------------------------------------------------------------
    bugtraq:append = false

Propchange: tomcat/trunk/java/org/apache/tomcat/util/collections/
------------------------------------------------------------------------------
    bugtraq:label = Bugzilla ID (optional)

Propchange: tomcat/trunk/java/org/apache/tomcat/util/collections/
------------------------------------------------------------------------------
--- bugtraq:message (added)
+++ bugtraq:message Thu Apr 19 12:04:35 2012
@@ -0,0 +1 @@
+Fix https://issues.apache.org/bugzilla/show_bug.cgi?id=%BUGID%

Propchange: tomcat/trunk/java/org/apache/tomcat/util/collections/
------------------------------------------------------------------------------
    bugtraq:url = https://issues.apache.org/bugzilla/show_bug.cgi?id=%BUGID%

Added: tomcat/trunk/java/org/apache/tomcat/util/collections/ManagedConcurrentWeakHashMap.java
URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/tomcat/util/collections/ManagedConcurrentWeakHashMap.java?rev=1327915&view=auto
==============================================================================
--- tomcat/trunk/java/org/apache/tomcat/util/collections/ManagedConcurrentWeakHashMap.java (added)
+++ tomcat/trunk/java/org/apache/tomcat/util/collections/ManagedConcurrentWeakHashMap.java Thu Apr 19 12:04:35 2012
@@ -0,0 +1,275 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.apache.tomcat.util.collections;
+
+import java.lang.ref.Reference;
+import java.lang.ref.ReferenceQueue;
+import java.lang.ref.WeakReference;
+import java.util.AbstractMap;
+import java.util.AbstractSet;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+
+/**
+ * Concurrent hash map that holds its keys via weak references. Unlike
+ * <code>WeakHashMap</code> this class does not handle dead keys during common
+ * access operations, but expects you to call its {@link #maintain()} method
+ * periodically. Both keys and values are expected to be not-<code>null</code>.
+ */
+public class ManagedConcurrentWeakHashMap<K, V> extends AbstractMap<K, V> implements
+        ConcurrentMap<K, V> {
+
+    private final ConcurrentMap<Key, V> map = new ConcurrentHashMap<Key, V>();
+    private final ReferenceQueue<Object> queue = new ReferenceQueue<Object>();
+
+    /**
+     * Method, that has to be invoked periodically to clean dead keys from the
+     * map.
+     */
+    public void maintain() {
+        Key key;
+        while ((key = (Key) queue.poll()) != null) {
+            if (key.isDead()) {
+                // No need to lookup if the key is not in the map
+                continue;
+            }
+            key.ackDeath();
+            map.remove(key);
+        }
+    }
+
+    private static class Key extends WeakReference<Object> {
+        private final int hash;
+        private boolean dead;
+
+        public Key(Object key, ReferenceQueue<Object> queue) {
+            super(key, queue);
+            hash = key.hashCode();
+        }
+
+        @Override
+        public int hashCode() {
+            return hash;
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            if (this == obj) {
+                return true;
+            }
+            if (dead) {
+                // Post-mortem cleanup looks for this specific Reference
+                // instance
+                return false;
+            }
+            if (!(obj instanceof Reference<?>)) {
+                return false;
+            }
+            Object oA = get();
+            Object oB = ((Reference<?>) obj).get();
+            if (oA == oB) {
+                return true;
+            }
+            if (oA == null || oB == null) {
+                return false;
+            }
+            return oA.equals(oB);
+        }
+
+        public void ackDeath() {
+            this.dead = true;
+        }
+
+        public boolean isDead() {
+            return dead;
+        }
+    }
+
+    /**
+     * Creates Key instance to be used to store values in the map. It is
+     * registered with the ReferenceQueue.
+     */
+    private Key createStoreKey(Object key) {
+        return new Key(key, queue);
+    }
+
+    /**
+     * Creates Key instance to be used only to lookup values in the map. It is
+     * not registered with the ReferenceQueue.
+     */
+    private Key createLookupKey(Object key) {
+        return new Key(key, null);
+    }
+
+    private static void checkNotNull(Object value) {
+        if (value == null) {
+            throw new NullPointerException();
+        }
+    }
+
+    @Override
+    public int size() {
+        return map.size();
+    }
+
+    @Override
+    public boolean isEmpty() {
+        return map.isEmpty();
+    }
+
+    @Override
+    public boolean containsValue(Object value) {
+        if (value == null) {
+            return false;
+        }
+        return map.containsValue(value);
+    }
+
+    @Override
+    public boolean containsKey(Object key) {
+        if (key == null) {
+            return false;
+        }
+        return map.containsKey(createLookupKey(key));
+    }
+
+    @Override
+    public V get(Object key) {
+        if (key == null) {
+            return null;
+        }
+        return map.get(createLookupKey(key));
+    }
+
+    @Override
+    public V put(K key, V value) {
+        checkNotNull(value);
+        return map.put(createStoreKey(key), value);
+    }
+
+    @Override
+    public V remove(Object key) {
+        return map.remove(createLookupKey(key));
+    }
+
+    @Override
+    public void clear() {
+        map.clear();
+        // maintain() clears the queue, though it is not 100% reliable,
+        // as queue is populated by GC asynchronously
+        maintain();
+    }
+
+    @Override
+    public V putIfAbsent(K key, V value) {
+        checkNotNull(value);
+        Key storeKey = createStoreKey(key);
+        V oldValue = map.putIfAbsent(storeKey, value);
+        if (oldValue != null) { // ack that key has not been stored
+            storeKey.ackDeath();
+        }
+        return oldValue;
+    }
+
+    @Override
+    public boolean remove(Object key, Object value) {
+        if (value == null) {
+            return false;
+        }
+        return map.remove(createLookupKey(key), value);
+    }
+
+    @Override
+    public boolean replace(K key, V oldValue, V newValue) {
+        checkNotNull(newValue);
+        return map.replace(createLookupKey(key), oldValue, newValue);
+    }
+
+    @Override
+    public V replace(K key, V value) {
+        checkNotNull(value);
+        return map.replace(createLookupKey(key), value);
+    }
+
+    @Override
+    public Collection<V> values() {
+        return map.values();
+    }
+
+    @Override
+    public Set<Map.Entry<K, V>> entrySet() {
+        return new AbstractSet<Map.Entry<K, V>>() {
+            @Override
+            public boolean isEmpty() {
+                return map.isEmpty();
+            }
+
+            @Override
+            public int size() {
+                return map.size();
+            }
+
+            @Override
+            public Iterator<Map.Entry<K, V>> iterator() {
+                return new Iterator<Map.Entry<K, V>>() {
+                    private final Iterator<Map.Entry<Key, V>> it = map
+                            .entrySet().iterator();
+
+                    @Override
+                    public boolean hasNext() {
+                        return it.hasNext();
+                    }
+
+                    @Override
+                    public Map.Entry<K, V> next() {
+                        return new Map.Entry<K, V>() {
+                            private final Map.Entry<Key, V> en = it.next();
+
+                            // Warning suppressed, because Key stored in the map
+                            // always wraps a K
+                            @SuppressWarnings("unchecked")
+                            @Override
+                            public K getKey() {
+                                return (K) en.getKey().get();
+                            }
+
+                            @Override
+                            public V getValue() {
+                                return en.getValue();
+                            }
+
+                            @Override
+                            public V setValue(V value) {
+                                checkNotNull(value);
+                                return en.setValue(value);
+                            }
+                        };
+                    }
+
+                    @Override
+                    public void remove() {
+                        it.remove();
+                    }
+                };
+            }
+        };
+    }
+}

Propchange: tomcat/trunk/java/org/apache/tomcat/util/collections/ManagedConcurrentWeakHashMap.java
------------------------------------------------------------------------------
    svn:eol-style = native



---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@tomcat.apache.org
For additional commands, e-mail: dev-help@tomcat.apache.org