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