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