1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.commons.collections4.map;
18
19 import java.io.ByteArrayInputStream;
20 import java.io.ByteArrayOutputStream;
21 import java.io.IOException;
22 import java.io.ObjectInputStream;
23 import java.io.ObjectOutputStream;
24 import java.io.Serializable;
25 import java.lang.ref.WeakReference;
26 import java.util.ArrayList;
27 import java.util.List;
28 import java.util.Map;
29 import java.util.function.Consumer;
30
31 import org.apache.commons.collections4.BulkTest;
32 import org.apache.commons.collections4.map.AbstractHashedMap.HashEntry;
33 import org.apache.commons.collections4.map.AbstractReferenceMap.ReferenceEntry;
34 import org.apache.commons.collections4.map.AbstractReferenceMap.ReferenceStrength;
35
36 import junit.framework.Test;
37
38
39
40
41
42 public class ReferenceMapTest<K, V> extends AbstractIterableMapTest<K, V> {
43
44 public ReferenceMapTest(final String testName) {
45 super(testName);
46 }
47
48 public static Test suite() {
49 return BulkTest.makeSuite(ReferenceMapTest.class);
50 }
51
52 @Override
53 public ReferenceMap<K, V> makeObject() {
54 return new ReferenceMap<>(ReferenceStrength.WEAK, ReferenceStrength.WEAK);
55 }
56
57 @Override
58 public boolean isAllowNullKey() {
59 return false;
60 }
61
62 @Override
63 public boolean isAllowNullValue() {
64 return false;
65 }
66
67 @Override
68 public String getCompatibilityVersion() {
69 return "4";
70 }
71
72
73
74
75
76
77
78
79
80
81
82
83
84 @SuppressWarnings("unchecked")
85 public void testNullHandling() {
86 resetFull();
87 assertEquals(null, map.get(null));
88 assertEquals(false, map.containsKey(null));
89 assertEquals(false, map.containsValue(null));
90 assertEquals(null, map.remove(null));
91 assertEquals(false, map.entrySet().contains(null));
92 assertEquals(false, map.keySet().contains(null));
93 assertEquals(false, map.values().contains(null));
94 try {
95 map.put(null, null);
96 fail();
97 } catch (final NullPointerException ex) {}
98 try {
99 map.put((K) new Object(), null);
100 fail();
101 } catch (final NullPointerException ex) {}
102 try {
103 map.put(null, (V) new Object());
104 fail();
105 } catch (final NullPointerException ex) {}
106 }
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219 WeakReference<K> keyReference;
220 WeakReference<V> valueReference;
221
222 @SuppressWarnings("unchecked")
223 public Map<K, V> buildRefMap() {
224 final K key = (K) new Object();
225 final V value = (V) new Object();
226
227 keyReference = new WeakReference<>(key);
228 valueReference = new WeakReference<>(value);
229
230 final Map<K, V> testMap = new ReferenceMap<>(ReferenceStrength.WEAK, ReferenceStrength.HARD, true);
231 testMap.put(key, value);
232
233 assertEquals("In map", value, testMap.get(key));
234 assertNotNull("Weak reference released early (1)", keyReference.get());
235 assertNotNull("Weak reference released early (2)", valueReference.get());
236 return testMap;
237 }
238
239
240 public void testPurgeValues() throws Exception {
241
242 final Map<K, V> testMap = buildRefMap();
243
244 int iterations = 0;
245 int bytz = 2;
246 while (true) {
247 System.gc();
248 if (iterations++ > 50) {
249 fail("Max iterations reached before resource released.");
250 }
251 testMap.isEmpty();
252 if (keyReference.get() == null && valueReference.get() == null) {
253 break;
254
255 }
256
257 @SuppressWarnings("unused")
258 final byte[] b = new byte[bytz];
259 bytz = bytz * 2;
260 }
261 }
262
263 public void testCustomPurge() {
264 List<Integer> expiredValues = new ArrayList<>();
265 @SuppressWarnings("unchecked")
266 final Consumer<Integer> consumer = (Consumer<Integer> & Serializable) v -> expiredValues.add(v);
267 final Map<Integer, Integer> map = new ReferenceMap<Integer, Integer>(ReferenceStrength.WEAK, ReferenceStrength.HARD, false) {
268 private static final long serialVersionUID = 1L;
269
270 @Override
271 protected ReferenceEntry<Integer, Integer> createEntry(HashEntry<Integer, Integer> next, int hashCode, Integer key, Integer value) {
272 return new AccessibleEntry<>(this, next, hashCode, key, value, consumer);
273 }
274 };
275 for (int i = 100000; i < 100010; i++) {
276 map.put(Integer.valueOf(i), Integer.valueOf(i));
277 }
278 int iterations = 0;
279 int bytz = 2;
280 while (true) {
281 System.gc();
282 if (iterations++ > 50 || bytz < 0) {
283 fail("Max iterations reached before resource released.");
284 }
285 map.isEmpty();
286 if (!expiredValues.isEmpty()) {
287 break;
288 }
289
290 @SuppressWarnings("unused")
291 final byte[] b = new byte[bytz];
292 bytz = bytz * 2;
293 }
294 assertFalse("Value should be stored", expiredValues.isEmpty());
295 }
296
297
298
299
300
301
302 public void testDataSizeAfterSerialization() throws IOException, ClassNotFoundException {
303
304 final ReferenceMap<String,String> serialiseMap = new ReferenceMap<>(ReferenceStrength.WEAK, ReferenceStrength.WEAK, true);
305 serialiseMap.put("KEY", "VALUE");
306
307 final ByteArrayOutputStream baos = new ByteArrayOutputStream();
308 try (ObjectOutputStream out = new ObjectOutputStream(baos)) {
309 out.writeObject(serialiseMap);
310 }
311
312 final ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
313 try (ObjectInputStream in = new ObjectInputStream(bais)) {
314 @SuppressWarnings("unchecked")
315 final
316 ReferenceMap<String,String> deserialisedMap = (ReferenceMap<String,String>) in.readObject();
317 assertEquals(1, deserialisedMap.size());
318 assertEquals(serialiseMap.data.length, deserialisedMap.data.length);
319 }
320
321 }
322
323 @SuppressWarnings("unused")
324 private static void gc() {
325 try {
326
327 final byte[][] tooLarge = new byte[1000000000][1000000000];
328 fail("you have too much RAM");
329 } catch (final OutOfMemoryError ex) {
330 System.gc();
331 }
332 }
333
334 private static class AccessibleEntry<K, V> extends ReferenceEntry<K, V> {
335 final AbstractReferenceMap<K, V> parent;
336 final Consumer<V> consumer;
337
338 public AccessibleEntry(final AbstractReferenceMap<K, V> parent, final HashEntry<K, V> next, final int hashCode, final K key, final V value, final Consumer<V> consumer) {
339 super(parent, next, hashCode, key, value);
340 this.parent = parent;
341 this.consumer = consumer;
342 }
343
344 @Override
345 protected void onPurge() {
346 if (parent.isValueType(ReferenceStrength.HARD)) {
347 consumer.accept(getValue());
348 }
349 }
350 }
351 }