001 /*
002 * Licensed under the Apache License, Version 2.0 (the "License");
003 * you may not use this file except in compliance with the License.
004 * You may obtain a copy of the License at
005 *
006 * http://www.apache.org/licenses/LICENSE-2.0
007 *
008 * Unless required by applicable law or agreed to in writing, software
009 * distributed under the License is distributed on an "AS IS" BASIS,
010 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
011 * See the License for the specific language governing permissions and
012 * limitations under the License.
013 */
014 package org.apache.commons.classscan.util;
015
016 import java.lang.ref.Reference;
017 import java.lang.ref.ReferenceQueue;
018 import java.lang.ref.WeakReference;
019 import java.util.Collection;
020 import java.util.Map;
021 import java.util.Set;
022 import java.util.concurrent.ConcurrentHashMap;
023 import java.util.concurrent.ConcurrentMap;
024
025 public final class ReapingHashMap<K, V> implements Map<K, V> {
026
027 private final ConcurrentMap<HashWeakReference<K>, V> innerMap = new ConcurrentHashMap<HashWeakReference<K>, V>();
028 private final ReferenceQueue<K> queue = new ReferenceQueue<K>();
029 private final Thread reaper = new ReaperThread();
030
031 public ReapingHashMap() {
032 reaper.setDaemon(true);
033 reaper.start();
034 }
035
036 private class ReaperThread extends Thread {
037 ReaperThread() {
038 super("WeakConcurrentHashMap-" + System.identityHashCode(ReapingHashMap.this) + "-reaper");
039 }
040
041 @Override
042 public void run() {
043 for (;;) {
044 try {
045 Reference<? extends K> ref = queue.remove();
046 HashWeakReference<? extends K> key = (HashWeakReference<? extends K>) ref;
047 innerMap.remove(key);
048 }
049 catch (InterruptedException ignore) {
050 }
051 }
052 }
053 }
054
055 @Override
056 public V put(K key, V value) {
057 HashWeakReference<K> innerKey = new HashWeakReference<K>(key, queue);
058 return innerMap.put(innerKey, value);
059 }
060
061 @Override
062 public V get(Object key) {
063 KeyReference<Object> innerKey = new KeyReference<Object>(key);
064 return innerMap.get(innerKey);
065 }
066
067 @Override
068 public void clear() {
069 throw new UnsupportedOperationException();
070 }
071
072 @Override
073 public boolean containsKey(Object key) {
074 throw new UnsupportedOperationException();
075 }
076
077 @Override
078 public boolean containsValue(Object value) {
079 throw new UnsupportedOperationException();
080 }
081
082 @Override
083 public Set<Map.Entry<K, V>> entrySet() {
084 throw new UnsupportedOperationException();
085 }
086
087 @Override
088 public boolean isEmpty() {
089 throw new UnsupportedOperationException();
090 }
091
092 @Override
093 public Set<K> keySet() {
094 throw new UnsupportedOperationException();
095 }
096
097 @Override
098 public void putAll(Map<? extends K, ? extends V> m) {
099 throw new UnsupportedOperationException();
100 }
101
102 @Override
103 public V remove(Object key) {
104 throw new UnsupportedOperationException();
105 }
106
107 @Override
108 public int size() {
109 throw new UnsupportedOperationException();
110 }
111
112 @Override
113 public Collection<V> values() {
114 throw new UnsupportedOperationException();
115 }
116
117 static class HashWeakReference<T> extends WeakReference<T> {
118 private final int hashCode;
119
120 HashWeakReference(T referent, ReferenceQueue<? super T> queue) {
121 super(referent, queue);
122 hashCode = System.identityHashCode(referent);
123 }
124
125 @Override
126 public int hashCode() {
127 return hashCode;
128 }
129
130 @Override
131 public boolean equals(Object obj) {
132 if(this==obj) {
133 return true;
134 }
135 if(obj instanceof KeyReference) {
136 @SuppressWarnings("unchecked")
137 KeyReference<T> key= (KeyReference<T>)obj;
138 return key.compare(this);
139 }
140 throw new UnsupportedOperationException(obj.getClass().getCanonicalName());
141 }
142 }
143
144 static class KeyReference<T> {
145 private final T key;
146 private final int keyHash;
147
148 public KeyReference(T key) {
149 this.key= key;
150 keyHash= System.identityHashCode(key);
151 }
152
153 @Override
154 public int hashCode() {
155 return keyHash;
156 }
157
158 /**
159 * HashMap should only compare KeyReference with HashWeakReference
160 */
161 @Override
162 public boolean equals(Object obj) {
163 if(obj instanceof HashWeakReference) {
164 @SuppressWarnings("unchecked")
165 HashWeakReference<T> weakKey = (HashWeakReference<T>)obj;
166 return compare(weakKey);
167 }
168 throw new UnsupportedOperationException(obj.getClass().getCanonicalName());
169 }
170
171 boolean compare(HashWeakReference<T> ref) {
172 T t = ref.get();
173 return key==t;
174 }
175 }
176 }