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
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
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
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
141 try {
142 selfPopulatingCache.get("key");
143 fail();
144 } catch (final Exception e) {
145 Thread.sleep(20);
146
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
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
177 assertSame(value, selfPopulatingCache.get("key").getObjectValue());
178 assertEquals(1, factory.getCount());
179
180
181 selfPopulatingCache.refresh();
182 assertEquals(2, factory.getCount());
183
184
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
200 String explodingKey = "explode";
201 assertSame(value, selfPopulatingCache.get(explodingKey).getObjectValue());
202 assertEquals(1, factory.getCount());
203
204
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
227 selfPopulatingCache.get("key");
228 assertEquals(originalThreadName, Thread.currentThread().getName());
229
230
231 selfPopulatingCache.refresh();
232 assertEquals(originalThreadName, Thread.currentThread().getName());
233
234
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
265 assertEquals(2, selfPopulatingCache.getSize());
266
267
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
326
327 try {
328 Thread.sleep(20);
329 } catch (InterruptedException ignored) {
330
331 }
332 }
333
334
335
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
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
364
365 try {
366 Thread.sleep(20);
367 } catch (InterruptedException ignored) {
368
369 }
370 }
371
372
373
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
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
402
403 try {
404 Thread.sleep(20);
405 } catch (InterruptedException ignored) {
406
407 }
408 }
409
410
411
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
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
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
601 selfPopulatingCache.refresh();
602 e1 = selfPopulatingCache.get("key1");
603 e2 = selfPopulatingCache.get("key2");
604 assertEquals(2, selfPopulatingCache.getSize());
605 assertEquals(4, factory.getCount());
606
607
608
609
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
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
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
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
648 selfPopulatingCache.refresh();
649 assertEquals(2, selfPopulatingCache.getSize());
650 assertEquals(4, factory.getCount());
651
652
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
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
682 Thread.sleep(100L);
683
684
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
696 Thread.sleep(100L);
697
698
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
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
727 Thread.sleep(100L);
728
729
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
744 Thread.sleep(100L);
745
746
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