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.constructs.blocking;
18  
19  import static junit.framework.Assert.assertSame;
20  import net.sf.ehcache.Cache;
21  import net.sf.ehcache.CacheException;
22  import net.sf.ehcache.CacheManager;
23  import net.sf.ehcache.CacheTest;
24  import net.sf.ehcache.Ehcache;
25  import net.sf.ehcache.Element;
26  import net.sf.ehcache.event.CountingCacheEventListener;
27  import org.junit.After;
28  import static org.junit.Assert.assertEquals;
29  import static org.junit.Assert.assertFalse;
30  import static org.junit.Assert.assertNotNull;
31  import static org.junit.Assert.assertNull;
32  import static org.junit.Assert.assertTrue;
33  import static org.junit.Assert.fail;
34  import org.junit.Before;
35  import org.junit.Test;
36  
37  
38  import org.slf4j.Logger;
39  import org.slf4j.LoggerFactory;
40  
41  
42  /***
43   * Test cases for the {@link SelfPopulatingCache}.
44   *
45   *
46   *
47   * @author Adam Murdoch
48   * @author Greg Luck
49   * @version $Id: SelfPopulatingCacheTest.java 2438 2010-05-14 18:15:10Z teck $
50   */
51  public class SelfPopulatingCacheTest extends CacheTest {
52  
53      private static final Logger LOG = LoggerFactory.getLogger(SelfPopulatingCacheTest.class.getName());
54  
55      /***
56       * Shared with subclass
57       */
58      protected CacheManager manager;
59      /***
60       * Shared with subclass
61       */
62      protected SelfPopulatingCache selfPopulatingCache;
63      /***
64       * Shared with subclass
65       */
66      protected Ehcache cache;
67  
68      /***
69       * Number of factory requests
70       */
71      protected volatile int cacheEntryFactoryRequests;
72  
73      /***
74       * Load up the test cache
75       */
76      @Override
77      @Before
78      public void setUp() throws Exception {
79          //Skip update checks. Causing an OutOfMemoryError
80          System.setProperty("net.sf.ehcache.skipUpdateCheck", "true");
81          super.setUp();
82          manager = new CacheManager();
83          cache = manager.getCache("sampleIdlingExpiringCache");
84          selfPopulatingCache = new SelfPopulatingCache(cache, new CountingCacheEntryFactory("value"));
85          cacheEntryFactoryRequests = 0;
86      }
87  
88      /***
89       * teardown
90       */
91      @Override
92      @After
93      public void tearDown() throws Exception {
94          if (selfPopulatingCache != null) {
95              selfPopulatingCache.removeAll();
96          }
97          if (manager != null) {
98              manager.shutdown();
99          }
100         super.tearDown();
101     }
102 
103     /***
104      * Tests fetching an entry.
105      */
106     @Test
107     public void testFetch() throws Exception {
108         LOG.error(".");
109 
110         // Lookup
111         final Element element = selfPopulatingCache.get("key");
112         assertEquals("value", element.getValue());
113     }
114 
115     /***
116      * Tests fetching an unknown entry.
117      */
118     @Test
119     public void testFetchUnknown() throws Exception {
120         final CacheEntryFactory factory = new CountingCacheEntryFactory(null);
121         selfPopulatingCache = new SelfPopulatingCache(cache, factory);
122 
123         // Lookup
124         assertNull(cache.get("key"));
125     }
126 
127     /***
128      * Tests when fetch fails.
129      */
130     @Test
131     public void testFetchFail() throws Exception {
132         final Exception exception = new Exception("Failed.");
133         final CacheEntryFactory factory = new CacheEntryFactory() {
134             public Object createEntry(final Object key) throws Exception {
135                 throw exception;
136             }
137         };
138         selfPopulatingCache = new SelfPopulatingCache(cache, factory);
139 
140         // Lookup
141         try {
142             selfPopulatingCache.get("key");
143             fail();
144         } catch (final Exception e) {
145             Thread.sleep(20);
146             // Check the error
147             assertEquals("Could not fetch object for cache entry with key \"key\".", e.getMessage());
148         }
149     }
150 
151     /***
152      * Tests that an entry is created once only.
153      */
154     @Test
155     public void testCreateOnce() throws Exception {
156         final String value = "value";
157         final CountingCacheEntryFactory factory = new CountingCacheEntryFactory(value);
158         selfPopulatingCache = new SelfPopulatingCache(cache, factory);
159 
160         // Fetch the value several times
161         for (int i = 0; i < 5; i++) {
162             assertSame(value, selfPopulatingCache.get("key").getObjectValue());
163             assertEquals(1, factory.getCount());
164         }
165     }
166 
167     /***
168      * Tests refreshing the entries.
169      */
170     @Test
171     public void testRefresh() throws Exception {
172         final String value = "value";
173         final CountingCacheEntryFactory factory = new CountingCacheEntryFactory(value);
174         selfPopulatingCache = new SelfPopulatingCache(cache, factory);
175 
176         // Check the value
177         assertSame(value, selfPopulatingCache.get("key").getObjectValue());
178         assertEquals(1, factory.getCount());
179 
180         // Refresh
181         selfPopulatingCache.refresh();
182         assertEquals(2, factory.getCount());
183 
184         // Check the value
185         assertSame(value, selfPopulatingCache.get("key").getObjectValue());
186         assertEquals(2, factory.getCount());
187 
188     }
189 
190     /***
191      * Tests refreshing the entries.
192      */
193     @Test
194     public void testRefreshWithException() throws Exception {
195         final String value = "value";
196         final CountingCacheEntryFactory factory = new CountingCacheEntryFactory(value);
197         selfPopulatingCache = new SelfPopulatingCache(cache, factory);
198 
199         // Check the value
200         String explodingKey = "explode";
201         assertSame(value, selfPopulatingCache.get(explodingKey).getObjectValue());
202         assertEquals(1, factory.getCount());
203 
204         // Refresh
205         try {
206             selfPopulatingCache.refresh();
207             fail("This should have exploded!");
208         } catch (CacheException e) {
209             assertNotNull(e.getCause());
210             assertEquals(e.getCause().getMessage() + " on refresh with key " + explodingKey, e.getMessage());
211         }
212     }
213 
214     /***
215      * Tests that the current thread, which gets renamed when it enters a SelfPopulatingCache, comes out with
216      * its old name.
217      */
218     @Test
219     public void testThreadNaming() throws Exception {
220         final String value = "value";
221         final CountingCacheEntryFactory factory = new CountingCacheEntryFactory(value);
222         selfPopulatingCache = new SelfPopulatingCache(cache, factory);
223 
224         String originalThreadName = Thread.currentThread().getName();
225 
226         // Check the value
227         selfPopulatingCache.get("key");
228         assertEquals(originalThreadName, Thread.currentThread().getName());
229 
230         // Refresh
231         selfPopulatingCache.refresh();
232         assertEquals(originalThreadName, Thread.currentThread().getName());
233 
234         // Check the value with null key
235         selfPopulatingCache.get(null);
236         assertEquals(originalThreadName, Thread.currentThread().getName());
237 
238 
239     }
240 
241     /***
242      * Tests discarding little used entries.
243      * <cache name="sampleIdlingExpiringCache"
244      * maxElementsInMemory="1"
245      * eternal="false"
246      * timeToIdleSeconds="2"
247      * timeToLiveSeconds="5"
248      * overflowToDisk="true"
249      * />
250      */
251     @Test
252     public void testDiscardLittleUsed() throws Exception {
253         final CacheEntryFactory factory = new CountingCacheEntryFactory("value");
254         selfPopulatingCache = new SelfPopulatingCache(cache, factory);
255 
256 
257         selfPopulatingCache.get("key1");
258         selfPopulatingCache.get("key2");
259         assertEquals(2, selfPopulatingCache.getSize());
260         selfPopulatingCache.refresh();
261         assertEquals(2, selfPopulatingCache.getSize());
262         Thread.sleep(2020);
263 
264         //Will be two, because counting expired elements
265         assertEquals(2, selfPopulatingCache.getSize());
266 
267         // Check the cache
268         selfPopulatingCache.removeAll();
269         assertEquals(0, selfPopulatingCache.getSize());
270     }
271 
272     /***
273      * Tests discarding little used entries, where refreshing is slow.
274      * <cache name="sampleIdlingExpiringCache"
275      * maxElementsInMemory="1"
276      * eternal="false"
277      * timeToIdleSeconds="2"
278      * timeToLiveSeconds="5"
279      * overflowToDisk="true"
280      * />
281      */
282     @Test
283     public void testDiscardLittleUsedSlow() throws Exception {
284         final CacheEntryFactory factory = new CacheEntryFactory() {
285             public Object createEntry(final Object key) throws Exception {
286                 Thread.sleep(200);
287                 return key;
288             }
289         };
290         selfPopulatingCache = new SelfPopulatingCache(cache, factory);
291     }
292 
293 
294     /***
295      * Expected: If multiple threads try to retrieve the same key from a
296      * SelfPopulatingCache at the same time, and that key is not yet in the cache,
297      * one thread obtains the lock for that key and uses the CacheEntryFactory to
298      * generate the cache entry and all other threads wait on the lock.
299      * Any and all threads which timeout while waiting for this lock should fail
300      * to acquire the lock for that key and throw an exception.
301      * <p/>
302      * This thread tests for this by having several threads to a cache "get" for
303      * the same key, allowing one to acquire the lock and the others to wait.  The
304      * one that acquires the lock and attempts to generate the cache entry for the
305      * key waits for a period of time long enough to allow all other threads to
306      * timeout waiting for the lock.  Any thread that succeeds in acquiring the lock,
307      * including the first to do so, increment a counter when they begin creating
308      * the cache entry using the CacheEntryFactory.  It is expected that this
309      * counter will only be "1" after all threads complete since all but the
310      * first to acquire it should timeout and throw exceptions.
311      * <p/>
312      * We then test that a thread that comes along later increments the counter.
313      */
314     @Test
315     public void testSelfPopulatingBlocksWithTimeoutSetNull() throws InterruptedException {
316         selfPopulatingCache = new SelfPopulatingCache(new Cache("TestCache", 50, false, false, 0, 0), new NullCachePopulator());
317         selfPopulatingCache.setTimeoutMillis(200);
318         manager.addCache(selfPopulatingCache);
319 
320         CacheAccessorThread[] cacheAccessorThreads = new CacheAccessorThread[10];
321 
322         for (int i = 0; i < cacheAccessorThreads.length; i++) {
323             cacheAccessorThreads[i] = new CacheAccessorThread(selfPopulatingCache, "key1");
324             cacheAccessorThreads[i].start();
325             // Do a slight delay here so that all the timeouts
326             // don't happen simultaneously - this is key
327             try {
328                 Thread.sleep(20);
329             } catch (InterruptedException ignored) {
330                 //
331             }
332         }
333 
334         //All of the others should have timed out. The first thread will have returned null.
335         // This thread should be able to have a go, thus setting the count to 2
336         Thread.sleep(1000);
337         Thread lateThread = new CacheAccessorThread(selfPopulatingCache, "key1");
338         lateThread.start();
339         lateThread.join();
340 
341         assertEquals("Too many cacheAccessorThreads tried to create selfPopulatingCache entry for key1",
342                 2, cacheEntryFactoryRequests);
343     }
344 
345 
346     /***
347      * Creating 11 Threads which attempt to get a null entry will result, eventually, in 11
348      * calls to the CacheEntryFactory
349      *
350      * @throws InterruptedException
351      */
352     @Test
353     public void testSelfPopulatingBlocksWithoutTimeoutSetNull() throws InterruptedException {
354         selfPopulatingCache = new SelfPopulatingCache(new Cache("TestCache", 50, false, false, 0, 0), new NullCachePopulator());
355         //selfPopulatingCache.setTimeoutMillis(200);
356         manager.addCache(selfPopulatingCache);
357 
358         CacheAccessorThread[] cacheAccessorThreads = new CacheAccessorThread[10];
359 
360         for (int i = 0; i < cacheAccessorThreads.length; i++) {
361             cacheAccessorThreads[i] = new CacheAccessorThread(selfPopulatingCache, "key1");
362             cacheAccessorThreads[i].start();
363             // Do a slight delay here so that all the timeouts
364             // don't happen simultaneously - this is key
365             try {
366                 Thread.sleep(20);
367             } catch (InterruptedException ignored) {
368                 //
369             }
370         }
371 
372         //All of the others should have timed out. The first thread will have returned null.
373         // This thread should be able to have a go, thus setting the count to 2
374         Thread.sleep(12000);
375         Thread lateThread = new CacheAccessorThread(selfPopulatingCache, "key1");
376         lateThread.start();
377         lateThread.join();
378 
379         assertEquals("The wrong number of cacheAccessorThreads tried to create selfPopulatingCache entry for key1",
380                 11, cacheEntryFactoryRequests);
381     }
382 
383     /***
384      * Creating 11 Threads which attempt to get a non-null entry will result in 1
385      * call to the CacheEntryFactory
386      *
387      * @throws InterruptedException
388      */
389     @Test
390     public void testSelfPopulatingBlocksWithoutTimeoutSetNonNull() throws InterruptedException {
391         selfPopulatingCache = new SelfPopulatingCache(new Cache("TestCache", 50, false, false, 0, 0),
392                 new NonNullCachePopulator());
393         //selfPopulatingCache.setTimeoutMillis(200);
394         manager.addCache(selfPopulatingCache);
395 
396         CacheAccessorThread[] cacheAccessorThreads = new CacheAccessorThread[10];
397 
398         for (int i = 0; i < cacheAccessorThreads.length; i++) {
399             cacheAccessorThreads[i] = new CacheAccessorThread(selfPopulatingCache, "key1");
400             cacheAccessorThreads[i].start();
401             // Do a slight delay here so that all the timeouts
402             // don't happen simultaneously - this is key
403             try {
404                 Thread.sleep(20);
405             } catch (InterruptedException ignored) {
406                 //
407             }
408         }
409 
410         //All of the others should have timed out. The first thread will have returned null.
411         // This thread should be able to have a go, thus setting the count to 2
412         Thread.sleep(2000);
413         Thread lateThread = new CacheAccessorThread(selfPopulatingCache, "key1");
414         lateThread.start();
415         lateThread.join();
416 
417         assertEquals("The wrong number of cacheAccessorThreads tried to create selfPopulatingCache entry for key1",
418                 1, cacheEntryFactoryRequests);
419     }
420 
421 
422     /***
423      * A thread that accesses a selfpopulating cache
424      */
425     private final class CacheAccessorThread extends Thread {
426         private Ehcache cache;
427         private String key;
428 
429         private CacheAccessorThread(Ehcache cache, String key) {
430             this.cache = cache;
431             this.key = key;
432         }
433 
434         /***
435          * Thread run method
436          */
437         @Override
438         public void run() {
439             try {
440                 cache.get(key);
441             } catch (Exception e) {
442                 LOG.info("Exception: " + e.getMessage());
443             }
444         }
445     }
446 
447     /***
448      * A cache entry factory that sleeps beyond the lock timeout
449      */
450     private class NullCachePopulator implements CacheEntryFactory {
451 
452         public Object createEntry(Object key) throws Exception {
453             cacheEntryFactoryRequests++;
454             Thread.sleep(1000);
455             return null;
456         }
457     }
458 
459 
460     /***
461      * A cache entry factory that sleeps beyond the lock timeout
462      */
463     private class NonNullCachePopulator implements CacheEntryFactory {
464 
465         public Object createEntry(Object key) throws Exception {
466             cacheEntryFactoryRequests++;
467             Thread.sleep(1000);
468             return "value";
469         }
470     }
471 
472     /***
473      * Shows the effect of jamming large amounts of puts into a cache that overflows to disk.
474      * The DiskStore should cause puts to back off and avoid an out of memory error.
475      */
476     @Override
477     @Test
478     public void testBehaviourOnDiskStoreBackUp() throws Exception {
479         Cache newCache = new Cache("testGetMemoryStoreSize", 10, true, false, 100, 200, false, 0);
480         manager.addCache(newCache);
481 
482         assertEquals(0, cache.getMemoryStoreSize());
483 
484         Element a = null;
485         int i = 0;
486         try {
487             for (; i < 160000; i++) {
488                 String key = i + "";
489                 String value = key;
490                 a = new Element(key, value + "DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD");
491                 cache.put(a);
492             }
493         } catch (OutOfMemoryError e) {
494             //the disk store backs up on the laptop.
495             LOG.info("OutOfMemoryError: " + e.getMessage() + " " + i);
496             fail();
497         }
498     }
499 
500 
501     /***
502      * Original design behaviour of CacheEntryFactory was that its return was
503      * treated as the value of the Element and an Element was constructed on the fly.
504      * This meant the CacheEntryFactory could not set Element properties, for instance.
505      * As of SVN 950, if the returned Object is an Element then this is directly
506      * used in the Cache.  This test checked this behaviour by setting the
507      * Element version number to an improbable value that is then checked
508      */
509     @Test
510     public void testCacheEntryFactoryReturningElementMake() throws Exception {
511         final long specialVersionNumber = 54321L;
512         final CacheEntryFactory elementReturningFactory = new CacheEntryFactory() {
513             public Object createEntry(final Object key) throws Exception {
514                 Element e = new Element(key, "V_" + key);
515                 e.setVersion(specialVersionNumber);
516                 return e;
517             }
518         };
519         selfPopulatingCache = new SelfPopulatingCache(cache, elementReturningFactory);
520         Element e = null;
521         e = selfPopulatingCache.get("key1");
522         assertEquals("V_key1", e.getValue());
523         assertEquals(specialVersionNumber, e.getVersion());
524         e = selfPopulatingCache.get("key2");
525         assertEquals("V_key2", e.getValue());
526         assertEquals(specialVersionNumber, e.getVersion());
527         assertEquals(2, selfPopulatingCache.getSize());
528     }
529 
530     /***
531      * See {@link #testCacheEntryFactoryReturningElementMake}
532      * this test ensures the Refresh functionality works
533      */
534     @Test
535     public void testCacheEntryFactoryReturningElementRefresh() throws Exception {
536         final long specialVersionNumber = 54321L;
537         final CacheEntryFactory elementReturningFactory = new CacheEntryFactory() {
538             public Object createEntry(final Object key) throws Exception {
539                 Element e = new Element(key, "V_" + key);
540                 e.setVersion(specialVersionNumber);
541                 return e;
542             }
543         };
544         selfPopulatingCache = new SelfPopulatingCache(cache, elementReturningFactory);
545         Element e = null;
546         e = selfPopulatingCache.get("key1");
547         assertEquals("V_key1", e.getValue());
548         assertEquals(specialVersionNumber, e.getVersion());
549         e = selfPopulatingCache.get("key2");
550         assertEquals("V_key2", e.getValue());
551         assertEquals(specialVersionNumber, e.getVersion());
552         assertEquals(2, selfPopulatingCache.getSize());
553         selfPopulatingCache.refresh();
554         e = selfPopulatingCache.get("key1");
555         assertEquals("V_key1", e.getValue());
556         assertEquals(specialVersionNumber, e.getVersion());
557         e = selfPopulatingCache.get("key2");
558         assertEquals("V_key2", e.getValue());
559         assertEquals(specialVersionNumber, e.getVersion());
560         assertEquals(2, selfPopulatingCache.getSize());
561     }
562 
563     /***
564      * See {@link #testCacheEntryFactoryReturningElementMake}
565      * this test ensures the Refresh functionality works
566      */
567     @Test
568     public void testCacheEntryFactoryReturningElementBadKey() throws Exception {
569         final CacheEntryFactory elementReturningFactory = new CacheEntryFactory() {
570             public Object createEntry(final Object key) throws Exception {
571                 Object modifiedKey = key.toString() + "XX";
572                 Element e = new Element(modifiedKey, "V_" + modifiedKey);
573                 return e;
574             }
575         };
576         selfPopulatingCache = new SelfPopulatingCache(cache, elementReturningFactory);
577         try {
578             selfPopulatingCache.get("key");
579             fail("Should fail because key was changed");
580         } catch (final Exception e) {
581             Thread.sleep(20);
582             // Check the error
583             assertEquals("Could not fetch object for cache entry with key \"key\".", e.getMessage());
584         }
585     }
586 
587 
588     @Test
589     public void testRefreshElement() throws Exception {
590         final IncrementingCacheEntryFactory factory = new IncrementingCacheEntryFactory();
591         selfPopulatingCache = new SelfPopulatingCache(cache, factory);
592 
593         Element e1 = selfPopulatingCache.get("key1");
594         Element e2 = selfPopulatingCache.get("key2");
595         assertEquals(2, selfPopulatingCache.getSize());
596         assertEquals(2, factory.getCount());
597         assertEquals(Integer.valueOf(1), e1.getValue());
598         assertEquals(Integer.valueOf(2), e2.getValue());
599 
600         // full refresh
601         selfPopulatingCache.refresh();
602         e1 = selfPopulatingCache.get("key1");
603         e2 = selfPopulatingCache.get("key2");
604         assertEquals(2, selfPopulatingCache.getSize());
605         assertEquals(4, factory.getCount());
606         //we cannot be sure which order key1 or key2 gets refreshed,
607         //as the implementation makes no guarantee over the sequence
608         //of the refresh; all we can be sure of is that between
609         //them key1&2 must have the values 3 & 4
610         int e1i = ((Integer) e1.getValue()).intValue();
611         int e2i = ((Integer) e2.getValue()).intValue();
612         assertTrue(((e1i == 3) && (e2i == 4)) || ((e1i == 4) && (e2i == 3)));
613 
614         // single element refresh
615         selfPopulatingCache.get("key2");
616         Element e2r = selfPopulatingCache.refresh("key2");
617         assertEquals(2, selfPopulatingCache.getSize());
618         assertEquals(5, factory.getCount());
619         assertNotNull(e2r);
620         assertEquals("key2", e2r.getKey());
621         assertEquals(Integer.valueOf(5), e2r.getValue());
622 
623         // additional element
624         Element e3 = selfPopulatingCache.get("key3");
625         assertEquals(3, selfPopulatingCache.getSize());
626         assertEquals(6, factory.getCount());
627         assertNotNull(e3);
628         assertEquals("key3", e3.getKey());
629         assertEquals(Integer.valueOf(6), e3.getValue());
630 
631         // full refresh
632         selfPopulatingCache.refresh();
633         assertEquals(3, selfPopulatingCache.getSize());
634         assertEquals(9, factory.getCount());
635     }
636 
637     @Test
638     public void testRefreshAbsentElement() throws Exception {
639         final IncrementingCacheEntryFactory factory = new IncrementingCacheEntryFactory();
640         selfPopulatingCache = new SelfPopulatingCache(cache, factory);
641 
642         selfPopulatingCache.get("key1");
643         selfPopulatingCache.get("key2");
644         assertEquals(2, selfPopulatingCache.getSize());
645         assertEquals(2, factory.getCount());
646 
647         // full refresh
648         selfPopulatingCache.refresh();
649         assertEquals(2, selfPopulatingCache.getSize());
650         assertEquals(4, factory.getCount());
651 
652         // single element refresh which is not in the cache
653         Element e3 = selfPopulatingCache.refresh("key3");
654         assertEquals(3, selfPopulatingCache.getSize());
655         assertEquals(5, factory.getCount());
656         assertNotNull(e3);
657         assertEquals("key3", e3.getKey());
658         assertEquals(Integer.valueOf(5), e3.getValue());
659     }
660 
661     @Test
662     public void testRefreshQuietly() throws Exception {
663         final CountingCacheEntryFactory factory = new CountingCacheEntryFactory("value");
664         CountingCacheEventListener countingCacheEventListener = new CountingCacheEventListener();
665         CountingCacheEventListener.resetCounters();
666         cache.getCacheEventNotificationService().registerListener(countingCacheEventListener);
667         selfPopulatingCache = new SelfPopulatingCache(cache, factory);
668 
669         //check initial conditions on counters
670         assertEquals(0, CountingCacheEventListener.getCacheElementsPut(cache).size());
671         assertEquals(0, CountingCacheEventListener.getCacheElementsUpdated(cache).size());
672 
673         Element e1 = selfPopulatingCache.get("key1");
674         Element e2 = selfPopulatingCache.get("key2");
675         assertEquals(2, factory.getCount());
676         assertEquals(2, CountingCacheEventListener.getCacheElementsPut(cache).size());
677         assertEquals(0, CountingCacheEventListener.getCacheElementsUpdated(cache).size());
678         long lastUpdateTime1 = e1.getLastUpdateTime();
679         long lastUpdateTime2 = e2.getLastUpdateTime();
680 
681         //wait a little so creation time to allow CPU clock to advance
682         Thread.sleep(100L);
683 
684         // full refresh
685         selfPopulatingCache.refresh();
686         assertEquals(4, factory.getCount());
687         assertEquals(2, CountingCacheEventListener.getCacheElementsPut(cache).size());
688         assertEquals(0, CountingCacheEventListener.getCacheElementsUpdated(cache).size());
689         e1 = selfPopulatingCache.get("key1");
690         e2 = selfPopulatingCache.get("key2");
691         assertTrue("getLastUpdateTime() should be the same", lastUpdateTime1 == e1.getLastUpdateTime());
692         assertTrue("getLastUpdateTime() should be the same", lastUpdateTime2 == e2.getLastUpdateTime());
693         lastUpdateTime2 = e2.getLastUpdateTime();
694 
695         //wait a little to allow CPU clock to advance
696         Thread.sleep(100L);
697 
698         // single element refresh
699         e2 = selfPopulatingCache.refresh("key2");
700         assertEquals(5, factory.getCount());
701         assertEquals(2, CountingCacheEventListener.getCacheElementsPut(cache).size());
702         assertEquals(0, CountingCacheEventListener.getCacheElementsUpdated(cache).size());
703         assertTrue("getLastUpdateTime() should be the same", lastUpdateTime2 == e2.getLastUpdateTime());
704     }
705 
706     @Test
707     public void testRefreshNoisily() throws Exception {
708         final CountingCacheEntryFactory factory = new CountingCacheEntryFactory("value");
709         CountingCacheEventListener countingCacheEventListener = new CountingCacheEventListener();
710         CountingCacheEventListener.resetCounters();
711         cache.getCacheEventNotificationService().registerListener(countingCacheEventListener);
712         selfPopulatingCache = new SelfPopulatingCache(cache, factory);
713 
714         //check initial conditions on counters
715         assertEquals(0, CountingCacheEventListener.getCacheElementsPut(cache).size());
716         assertEquals(0, CountingCacheEventListener.getCacheElementsUpdated(cache).size());
717 
718         Element e1 = selfPopulatingCache.get("key1");
719         Element e2 = selfPopulatingCache.get("key2");
720         assertEquals(2, factory.getCount());
721         assertEquals(2, CountingCacheEventListener.getCacheElementsPut(cache).size());
722         assertEquals(0, CountingCacheEventListener.getCacheElementsUpdated(cache).size());
723         long lastUpdateTime1 = e1.getLastUpdateTime();
724         long lastUpdateTime2 = e2.getLastUpdateTime();
725 
726         //wait a little so creation time to allow CPU clock to advance
727         Thread.sleep(100L);
728 
729         // full refresh
730         selfPopulatingCache.refresh(false);
731         assertEquals(4, factory.getCount());
732         assertEquals(2, CountingCacheEventListener.getCacheElementsPut(cache).size());
733         assertEquals(2, CountingCacheEventListener.getCacheElementsUpdated(cache).size());
734         e1 = selfPopulatingCache.get("key1");
735         e2 = selfPopulatingCache.get("key2");
736         assertEquals(4, factory.getCount());
737         assertEquals(2, CountingCacheEventListener.getCacheElementsPut(cache).size());
738         assertEquals(2, CountingCacheEventListener.getCacheElementsUpdated(cache).size());
739         assertFalse("getLastUpdateTime() should not be the same (" + lastUpdateTime1 + ")", lastUpdateTime1 == e1.getLastUpdateTime());
740         assertFalse("getLastUpdateTime() should not be the same (" + lastUpdateTime2 + ")", lastUpdateTime2 == e2.getLastUpdateTime());
741         lastUpdateTime2 = e2.getLastUpdateTime();
742 
743         //wait a little to allow CPU clock to advance
744         Thread.sleep(100L);
745 
746         // single element refresh
747         e2 = selfPopulatingCache.refresh("key2", false);
748         assertEquals(5, factory.getCount());
749         assertEquals(2, CountingCacheEventListener.getCacheElementsPut(cache).size());
750         assertEquals(3, CountingCacheEventListener.getCacheElementsUpdated(cache).size());
751         assertFalse("getLastUpdateTime() should not be the same (" + lastUpdateTime2 + ")", lastUpdateTime2 == e2.getLastUpdateTime());
752     }
753 
754     /***
755      * Much like CountingCacheEntryFactory, but the value in the Element is
756      * incremented on every update, in line with the 'count'
757      */
758     private class IncrementingCacheEntryFactory implements CacheEntryFactory {
759         private int count;
760 
761         public Object createEntry(Object key) throws Exception {
762             count++;
763             return Integer.valueOf(count);
764         }
765 
766         /***
767          * @return number of entries the factory has created.
768          */
769         public int getCount() {
770             return count;
771         }
772     }
773 }
774 
775