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 }