1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
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
63
64 private static final Logger LOG = Logger.getLogger(ReferenceMap.class);
65
66
67
68 private final Type type;
69 private final Map<K,Reference<V>> cache;
70 private final boolean singleThreaded;
71
72
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
82
83
84
85
86
87
88 public ReferenceMap(final Type type, final Map<K,Reference<V>> cache) {
89 this(type, cache, false);
90 }
91
92
93
94
95
96
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
105
106
107
108
109
110
111
112 public V put(final K key, final V value) {
113
114 if(LOG.isDebugEnabled() && cache.size() % 100 == 0){
115 LOG.debug(value.getClass().getSimpleName() + " cache size is " + cache.size());
116 }
117
118
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
134
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
148
149
150
151
152
153
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
169 cache.remove(key);
170 key = null;
171
172
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
191 cache.remove(key);
192 key = null;
193
194
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 }