View Javadoc

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              // expected
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              // expected
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             //expected
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             //expected
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             //expected
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             //expected
157         }
158 
159         try {
160             cache.replace(null, null);
161             Assert.fail("replace with null key should throw NPE");
162         } catch (NullPointerException npe) {
163             //expected
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             //expected
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             //expected
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             //expected
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             //expected
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             //expected
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             //expected
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 }