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    }