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
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
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
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
154 try {
155 selfPopulatingCache.get("key");
156 fail();
157 } catch (final Exception e) {
158 Thread.sleep(20);
159
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
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
202 assertSame(value, selfPopulatingCache.get("key").getObjectValue());
203 assertEquals(1, factory.getCount());
204
205
206 selfPopulatingCache.refresh();
207 assertEquals(2, factory.getCount());
208
209
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
225 String explodingKey = "explode";
226 assertSame(value, selfPopulatingCache.get(explodingKey).getObjectValue());
227 assertEquals(1, factory.getCount());
228
229
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
252 selfPopulatingCache.get("key");
253 assertEquals(originalThreadName, Thread.currentThread().getName());
254
255
256 selfPopulatingCache.refresh();
257 assertEquals(originalThreadName, Thread.currentThread().getName());
258
259
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
290 assertEquals(2, selfPopulatingCache.getSize());
291
292
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
351
352 try {
353 Thread.sleep(20);
354 } catch (InterruptedException ignored) {
355
356 }
357 }
358
359
360
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
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
389
390 try {
391 Thread.sleep(20);
392 } catch (InterruptedException ignored) {
393
394 }
395 }
396
397
398
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
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
427
428 try {
429 Thread.sleep(20);
430 } catch (InterruptedException ignored) {
431
432 }
433 }
434
435
436
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
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
597 selfPopulatingCache.refresh();
598 e1 = selfPopulatingCache.get("key1");
599 e2 = selfPopulatingCache.get("key2");
600 assertEquals(2, selfPopulatingCache.getSize());
601 assertEquals(4, factory.getCount());
602
603
604
605
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
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
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
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
644 selfPopulatingCache.refresh();
645 assertEquals(2, selfPopulatingCache.getSize());
646 assertEquals(4, factory.getCount());
647
648
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
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
678 Thread.sleep(100L);
679
680
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
692 Thread.sleep(100L);
693
694
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
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
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
724 Thread.sleep(100L);
725
726
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
741 Thread.sleep(100L);
742
743
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