1 /***
2 * Copyright 2003-2010 Terracotta, Inc.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 package net.sf.ehcache.concurrent;
18
19 import java.util.Collections;
20 import java.util.HashSet;
21 import java.util.List;
22 import java.util.Set;
23 import java.util.concurrent.Callable;
24 import java.util.concurrent.ExecutionException;
25 import java.util.concurrent.ExecutorService;
26 import java.util.concurrent.Executors;
27 import java.util.concurrent.Future;
28 import java.util.concurrent.TimeUnit;
29 import java.util.concurrent.TimeoutException;
30 import java.util.concurrent.atomic.AtomicInteger;
31
32 import net.sf.ehcache.Cache;
33 import net.sf.ehcache.CacheManager;
34 import net.sf.ehcache.Ehcache;
35 import net.sf.ehcache.Element;
36 import net.sf.ehcache.config.CacheConfiguration;
37
38 import org.hamcrest.CoreMatchers;
39 import org.junit.After;
40 import org.junit.Assert;
41 import org.junit.Before;
42 import org.junit.Test;
43
44 import static org.hamcrest.CoreMatchers.is;
45 import static org.hamcrest.CoreMatchers.nullValue;
46 import static org.hamcrest.CoreMatchers.sameInstance;
47 import static org.junit.Assert.assertThat;
48
49 public class ConcurrentCacheMethodsTest {
50
51 private volatile CacheManager manager;
52 private volatile Ehcache cache;
53
54 @Before
55 public void setUp() {
56 manager = CacheManager.create();
57 cache = new Cache(new CacheConfiguration("testCache", 0));
58 manager.addCache(cache);
59 }
60
61 @After
62 public void clearup() {
63 manager.removalAll();
64 manager.shutdown();
65 }
66
67 @Test
68 public void testPutIfAbsent() {
69 Element e = new Element("key", "value");
70 Assert.assertNull(cache.putIfAbsent(e));
71 Assert.assertEquals(e, cache.putIfAbsent(new Element("key", "value2")));
72
73 try {
74 cache.putIfAbsent(null);
75 Assert.fail("putIfAbsent with null Element should throw NPE");
76 } catch (NullPointerException npe) {
77
78 }
79
80 try {
81 cache.putIfAbsent(new Element(null, "value"));
82 Assert.fail("putIfAbsent with null key should throw NPE");
83 } catch (NullPointerException npe) {
84
85 }
86 }
87
88 @Test
89 public void testPutIfAbsentAffectsStats() {
90 cache.removeAll();
91 cache.setStatisticsEnabled(true);
92 cache.getStatistics().clearStatistics();
93 assertThat(cache.getStatistics().getCacheMisses(), is(0L));
94 assertThat(cache.getStatistics().getCacheHits(), is(0L));
95
96 assertThat(cache.get("someKey"), CoreMatchers.nullValue());
97 assertThat(cache.getStatistics().getCacheMisses(), is(1L));
98 assertThat(cache.getStatistics().getCacheHits(), is(0L));
99
100 final Element element = new Element("someKey", "someValue");
101 assertThat(cache.putIfAbsent(element), nullValue());
102 assertThat(cache.getStatistics().getCacheMisses(), is(1L));
103 assertThat(cache.getStatistics().getCacheHits(), is(0L));
104
105 assertThat(cache.get("someKey"), sameInstance(element));
106 assertThat(cache.getStatistics().getCacheMisses(), is(1L));
107 assertThat(cache.getStatistics().getCacheHits(), is(1L));
108
109 assertThat(cache.putIfAbsent(new Element("someKey", "someValue")), sameInstance(element));
110 assertThat(cache.getStatistics().getCacheMisses(), is(1L));
111 assertThat(cache.getStatistics().getCacheHits(), is(1L));
112 }
113
114 @Test
115 public void testRemoveElement() {
116 Element e = new Element("key", "value");
117 cache.put(e);
118
119 Assert.assertFalse(cache.removeElement(new Element("key", "value2")));
120 Assert.assertFalse(cache.removeElement(new Element("key2", "value")));
121 Assert.assertTrue(cache.removeElement(new Element("key", "value")));
122
123 try {
124 cache.removeElement(null);
125 Assert.fail("removeElement with null Element should throw NPE");
126 } catch (NullPointerException npe) {
127
128 }
129
130 try {
131 cache.removeElement(new Element(null, "value"));
132 Assert.fail("removeElement with null key should throw NPE");
133 } catch (NullPointerException npe) {
134
135 }
136 }
137
138 @Test
139 public void testTwoArgReplace() {
140 Assert.assertFalse(cache.replace(new Element("key", "value1"), new Element("key", "value2")));
141 cache.put(new Element("key", "value1"));
142 Assert.assertTrue(cache.replace(new Element("key", "value1"), new Element("key", "value2")));
143 Assert.assertFalse(cache.replace(new Element("key", "value1"), new Element("key", "value2")));
144
145 try {
146 cache.replace(null, new Element("key", "value2"));
147 Assert.fail("replace with null key should throw NPE");
148 } catch (NullPointerException npe) {
149
150 }
151
152 try {
153 cache.replace(new Element("key", "value1"), null);
154 Assert.fail("replace with null key should throw NPE");
155 } catch (NullPointerException npe) {
156
157 }
158
159 try {
160 cache.replace(null, null);
161 Assert.fail("replace with null key should throw NPE");
162 } catch (NullPointerException npe) {
163
164 }
165
166 try {
167 cache.replace(new Element(null, "value1"), new Element("key", "value2"));
168 Assert.fail("replace with null key should throw NPE");
169 } catch (NullPointerException npe) {
170
171 }
172
173 try {
174 cache.replace(new Element("key", "value1"), new Element(null, "value2"));
175 Assert.fail("replace with null key should throw NPE");
176 } catch (NullPointerException npe) {
177
178 }
179
180 try {
181 cache.replace(new Element(null, "value1"), new Element(null, "value2"));
182 Assert.fail("replace with null keys should throw NPE");
183 } catch (NullPointerException npe) {
184
185 }
186
187 try {
188 cache.replace(new Element("key", "value1"), new Element("different", "value2"));
189 Assert.fail("replace with non-matching keys should throw IllegalArgumentException");
190 } catch (IllegalArgumentException iae) {
191
192 }
193 }
194
195 @Test
196 public void testOneArgReplace() {
197
198 Assert.assertNull(cache.replace(new Element("key", "value")));
199 Assert.assertNull(cache.replace(new Element("key", "value2")));
200
201 Element e = new Element("key", "value");
202 cache.put(e);
203
204 Element e2 = new Element("key", "value2");
205 Assert.assertEquals(e, cache.replace(e2));
206
207 Assert.assertEquals(cache.get("key").getObjectValue(), e2.getObjectValue());
208
209 try {
210 cache.replace(null);
211 Assert.fail("replace with null Element should throw NPE");
212 } catch (NullPointerException npe) {
213
214 }
215
216 try {
217 cache.replace(new Element(null, "value1"));
218 Assert.fail("replace with null keys should throw NPE");
219 } catch (NullPointerException npe) {
220
221 }
222 }
223
224 @Test
225 public void testMultiThreadedPutIfAbsent() throws InterruptedException, ExecutionException {
226
227 Callable<Element> putIfAbsent = new Callable<Element>() {
228 public Element call() throws Exception {
229 return cache.putIfAbsent(new Element("key", Long.valueOf(Thread.currentThread().getId())));
230 }
231 };
232
233 ExecutorService executor = Executors.newFixedThreadPool(4 * Runtime.getRuntime().availableProcessors());
234 try {
235 List<Future<Element>> futures = executor.invokeAll(Collections.nCopies(100, putIfAbsent));
236
237 boolean seenNull = false;
238 Long threadId = null;
239 for (Future<Element> f : futures) {
240 Element e = f.get();
241 if (e == null) {
242 Assert.assertFalse(seenNull);
243 seenNull = true;
244 } else if (threadId == null) {
245 threadId = (Long) e.getValue();
246 } else {
247 Assert.assertEquals(threadId, e.getValue());
248 }
249 }
250 Assert.assertTrue(seenNull);
251 } finally {
252 executor.shutdownNow();
253 executor.awaitTermination(60, TimeUnit.SECONDS);
254 }
255 }
256
257 @Test
258 public void testMultiThreadedRemoveElement() throws InterruptedException, ExecutionException, TimeoutException {
259
260 Callable<Void> removeElement = new Callable<Void>() {
261 public Void call() throws Exception {
262 while (!cache.removeElement(new Element("key", "value"))) {
263 Thread.yield();
264 }
265 return null;
266 }
267 };
268
269 ExecutorService executor = Executors.newFixedThreadPool(4 * Runtime.getRuntime().availableProcessors());
270 try {
271 executor.submit(new Callable<Void>() {
272 public Void call() throws Exception {
273 for (int i = 0; i < 100; i++) {
274 cache.put(new Element("key", "value"));
275 while (cache.get("key") != null) {
276 Thread.yield();
277 }
278 }
279 return null;
280 }
281 });
282
283 List<Future<Void>> futures = executor.invokeAll(Collections.nCopies(100, removeElement));
284
285 for (Future<Void> f : futures) {
286 f.get();
287 }
288 Assert.assertNull(cache.get("key"));
289 } finally {
290 executor.shutdownNow();
291 executor.awaitTermination(60, TimeUnit.SECONDS);
292 }
293 }
294
295 @Test
296 public void testMultiThreadedTwoArgReplace() throws InterruptedException, ExecutionException {
297
298 cache.put(new Element("key", Integer.valueOf(0)));
299
300 Callable<Integer> twoArgReplace = new Callable<Integer>() {
301 private final AtomicInteger index = new AtomicInteger();
302
303 public Integer call() throws Exception {
304 while (true) {
305 Element old = cache.get("key");
306 Element replace = new Element("key", Integer.valueOf(((Integer) old.getObjectValue()).intValue() + 1));
307 if (cache.replace(old, replace)) {
308 return (Integer) replace.getObjectValue();
309 }
310 }
311 }
312 };
313
314 ExecutorService executor = Executors.newFixedThreadPool(4 * Runtime.getRuntime().availableProcessors());
315 try {
316 List<Future<Integer>> futures = executor.invokeAll(Collections.nCopies(100, twoArgReplace));
317
318 Set<Integer> values = new HashSet<Integer>();
319 for (Future<Integer> f : futures) {
320 values.add(f.get());
321 }
322 Assert.assertEquals(futures.size(), values.size());
323 Assert.assertTrue(Integer.valueOf(futures.size()).equals(cache.get("key").getObjectValue()));
324 } finally {
325 executor.shutdownNow();
326 executor.awaitTermination(60, TimeUnit.SECONDS);
327 }
328 }
329
330 @Test
331 public void testMultiThreadedOneArgReplace() throws InterruptedException, ExecutionException {
332
333 cache.put(new Element("key", null));
334
335 Callable<Element> oneArgReplace = new Callable<Element>() {
336 private final AtomicInteger index = new AtomicInteger();
337
338 public Element call() throws Exception {
339 return cache.replace(new Element("key", Integer.valueOf(index.getAndIncrement())));
340 }
341 };
342
343 ExecutorService executor = Executors.newFixedThreadPool(4 * Runtime.getRuntime().availableProcessors());
344 try {
345 List<Future<Element>> futures = executor.invokeAll(Collections.nCopies(100, oneArgReplace));
346
347 boolean seenNull = false;
348 Long threadId = null;
349 Set<Integer> indices = new HashSet<Integer>();
350 for (Future<Element> f : futures) {
351 Element e = f.get();
352 if (e.getValue() == null) {
353 Assert.assertFalse(seenNull);
354 seenNull = true;
355 } else {
356 indices.add((Integer) e.getObjectValue());
357 }
358 }
359 Assert.assertTrue(seenNull);
360 Assert.assertEquals(futures.size() - 1, indices.size());
361 Assert.assertFalse(indices.contains(cache.get("key").getObjectValue()));
362 } finally {
363 executor.shutdownNow();
364 executor.awaitTermination(60, TimeUnit.SECONDS);
365 }
366 }
367
368
369 }