1 /* Copyright (2007-2008) Schibsted Søk AS
2 * This file is part of Sesat Commons.
3 *
4 * Sesat Commons is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU Lesser General Public License as published by
6 * the Free Software Foundation, either version 3 of the License, or
7 * (at your option) any later version.
8 *
9 * Sesat Commons is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public License
15 * along with Sesat Commons. If not, see <http://www.gnu.org/licenses/>.
16 *
17 */
18 package no.sesat.commons.ref;
19
20 import java.lang.ref.*;
21 import java.util.Map;
22
23 import org.apache.log4j.Logger;
24
25 /** Provides a Map where values are referenced either weakly or softly.
26 * This can be used to cache immutable objects without preventing them from bing garbaged collected. <br/><br/>
27 *
28 * Concurrency and synchronisation of this class fallbacks to the Map implementation passed in as the cache parameter
29 * in the constructor. For example: use a Hashtable for synchronised access, a ConcurrentHashMap for concurrent access,
30 * and a HashMap for single threaded access (make sure to specify the argument indicating single thread use).
31 *
32 * <br/><br/>
33 *
34 * This implementation improves over org.apache.commons.collections.map.ReferenceMap in that the synchronisation and
35 * concurrency is determined through delegation to the map supplied in the constructor as described above. <br/><br/>
36 *
37 * @param <K> key type
38 * @param <V> value type
39 * @version $Id: ReferenceMap.java 1127 2009-01-21 16:16:08Z ssmiweve $
40 */
41 public final class ReferenceMap<K,V extends Object> {
42
43 public enum Type {
44 WEAK,
45 SOFT;
46
47 <K,V> Reference<V> createReference(
48 final ReferenceMap<K,V> cache,
49 final K key,
50 final V value){
51
52 switch(this){
53 case WEAK:
54 return cache.new WeakReference(key, value);
55 case SOFT:
56 return cache.new SoftReference(key, value);
57 }
58 throw new IllegalStateException("Please implement createReference(..) for " + toString());
59 }
60 }
61
62 // Constants -----------------------------------------------------
63
64 private static final Logger LOG = Logger.getLogger(ReferenceMap.class);
65
66 // Attributes ----------------------------------------------------
67
68 private final Type type;
69 private final Map<K,Reference<V>> cache;
70 private final boolean singleThreaded;
71
72 // Static --------------------------------------------------------
73
74 private static final ReferenceQueue<Object> queue = new ReferenceQueue<Object>();
75 private static final ReferenceCleaner cleaner = new ReferenceCleaner(queue);
76
77 static {
78 cleaner.start();
79 }
80
81 // Constructors --------------------------------------------------
82
83 /**
84 *
85 * @param type
86 * @param cache
87 */
88 public ReferenceMap(final Type type, final Map<K,Reference<V>> cache) {
89 this(type, cache, false);
90 }
91
92 /**
93 *
94 * @param type
95 * @param cache
96 * @param singleThreaded
97 */
98 public ReferenceMap(final Type type, final Map<K,Reference<V>> cache, final boolean singleThreaded) {
99 this.type = type;
100 this.cache = cache;
101 this.singleThreaded = singleThreaded;
102 }
103
104 // Public --------------------------------------------------------
105
106 /**
107 *
108 * @param key
109 * @param value
110 * @return
111 */
112 public V put(final K key, final V value) {
113 // log cache size every 100 increments
114 if(LOG.isDebugEnabled() && cache.size() % 100 == 0){
115 LOG.debug(value.getClass().getSimpleName() + " cache size is " + cache.size());
116 }
117
118 // clean if in single threaded mode
119 if(singleThreaded){
120 Reference<?> reference = (Reference<?>)queue.poll();
121 while(null != reference){
122 reference.clear();
123 reference = (Reference<?>)queue.poll();
124 }
125 }
126
127 final Reference<V> change = cache.put(key, type.createReference(this, key, value));
128 return null != change ? change.get() : null;
129 }
130
131 /**
132 *
133 * @param key
134 * @return
135 */
136 public V get(final K key){
137
138 final Reference<V> reference = cache.get(key);
139 return null != reference ? reference.get() : null;
140 }
141
142 @Override
143 public String toString() {
144 return super.toString() + " Type:" + type + " Size:" + cache.size();
145 }
146
147 // Package protected ---------------------------------------------
148
149 // Protected -----------------------------------------------------
150
151 // Private -------------------------------------------------------
152
153 // Inner classes -------------------------------------------------
154
155 private final class WeakReference extends java.lang.ref.WeakReference<V>{
156
157 private K key;
158
159 WeakReference(final K key, final V value){
160
161 super(value, queue);
162 this.key = key;
163 }
164
165 @Override
166 public void clear() {
167
168 // clear the hashmap entry too.
169 cache.remove(key);
170 key = null;
171
172 // clear the referent
173 super.clear();
174 }
175 }
176
177 private final class SoftReference extends java.lang.ref.SoftReference<V>{
178
179 private K key;
180
181 SoftReference(final K key, final V value){
182
183 super(value, queue);
184 this.key = key;
185 }
186
187 @Override
188 public void clear() {
189
190 // clear the hashmap entry too.
191 cache.remove(key);
192 key = null;
193
194 // clear the referent
195 super.clear();
196 }
197 }
198
199 private final static class ReferenceCleaner extends Thread{
200
201 private final ReferenceQueue<?> queue;
202
203 ReferenceCleaner(ReferenceQueue<?> queue){
204 super("ReferenceMap.ReferenceCleaner");
205 this.queue = queue;
206 setPriority(Thread.MAX_PRIORITY);
207 setDaemon(true);
208 }
209
210 @Override
211 public void run() {
212 while(true){
213 try {
214 queue.remove().clear();
215 }catch (InterruptedException ex) {
216 LOG.error(ex.getMessage(), ex);
217 }
218 }
219 }
220 }
221 }