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.event;
18
19 import net.sf.ehcache.AbstractCacheTest;
20 import net.sf.ehcache.CacheException;
21 import net.sf.ehcache.CacheManager;
22 import net.sf.ehcache.Ehcache;
23 import net.sf.ehcache.Element;
24 import org.junit.After;
25
26 import static org.hamcrest.CoreMatchers.equalTo;
27 import static org.junit.Assert.assertEquals;
28 import static org.junit.Assert.assertNotNull;
29 import static org.junit.Assert.assertThat;
30 import static org.junit.Assert.assertTrue;
31 import static org.junit.Assert.fail;
32
33 import org.junit.Before;
34 import org.junit.Test;
35
36 import java.io.IOException;
37 import java.io.Serializable;
38 import java.util.ArrayList;
39 import java.util.Date;
40 import java.util.List;
41 import java.util.concurrent.atomic.AtomicInteger;
42
43 import org.slf4j.Logger;
44 import org.slf4j.LoggerFactory;
45
46
47 /***
48 * Tests the cache listener functionality
49 *
50 * @author Greg Luck
51 * @version $Id: CacheEventListenerTest.html 13146 2011-08-01 17:12:39Z oletizi $
52 */
53 public class CacheEventListenerTest extends AbstractCacheTest {
54
55 private static final Logger LOG = LoggerFactory.getLogger(CacheEventListenerTest.class.getName());
56
57
58 /***
59 * the cache name we wish to test
60 */
61 protected String cacheName = "sampleCache1";
62 /***
63 * the cache we wish to test
64 */
65 protected Ehcache cache;
66
67 /***
68 * {@inheritDoc}
69 *
70 * @throws Exception
71 */
72 @Override
73 @Before
74 public void setUp() throws Exception {
75 super.setUp();
76 CountingCacheEventListener.resetCounters();
77 manager.shutdown();
78 manager = CacheManager.create(AbstractCacheTest.TEST_CONFIG_DIR + "ehcache-countinglisteners.xml");
79 cache = manager.getCache(cacheName);
80 cache.removeAll();
81 }
82
83
84 /***
85 * {@inheritDoc}
86 *
87 * @throws Exception
88 */
89 @Override
90 @After
91 public void tearDown() throws Exception {
92 CountingCacheEventListener.resetCounters();
93 super.tearDown();
94 }
95
96
97 /***
98 * Tests the registration and unregistration of listeners
99 */
100 @Test
101 public void testRegistration() {
102 TestCacheEventListener listener = new TestCacheEventListener();
103
104 int count = cache.getCacheEventNotificationService().getCacheEventListeners().size();
105 cache.getCacheEventNotificationService().registerListener(listener);
106
107 assertEquals(count + 1, cache.getCacheEventNotificationService().getCacheEventListeners().size());
108 cache.getCacheEventNotificationService().unregisterListener(listener);
109
110 assertEquals(count, cache.getCacheEventNotificationService().getCacheEventListeners().size());
111 }
112
113
114 /***
115 * Tests the put listener.
116 */
117 @Test
118 public void testPutNotifications() {
119
120 Serializable key = new Date();
121 Serializable value = new Date();
122 Element element = new Element(key, value);
123
124
125 cache.put(element);
126
127 List notifications = CountingCacheEventListener.getCacheElementsPut(cache);
128
129 assertTrue(notifications.size() == 1);
130 assertEquals(key, ((Element) notifications.get(0)).getObjectKey());
131 assertEquals(element.getObjectValue(), ((Element) notifications.get(0)).getObjectValue());
132
133
134 cache.put(element);
135 notifications = CountingCacheEventListener.getCacheElementsPut(cache);
136 assertTrue(notifications.size() == 1);
137
138 }
139
140 /***
141 * Tests the put and update listeners.
142 */
143 @Test
144 public void testUpdateNotifications() {
145
146
147 for (int i = 0; i < 11; i++) {
148 cache.put(new Element("" + i, "" + i));
149 cache.put(new Element("" + i, "" + i));
150 }
151
152
153 cache.put(new Element("20", "20"));
154
155
156 List notifications = CountingCacheEventListener.getCacheElementsPut(cache);
157 assertTrue(notifications.size() == 12);
158 assertEquals("0", ((Element) notifications.get(0)).getObjectKey());
159 assertEquals("0", ((Element) notifications.get(0)).getObjectValue());
160
161 notifications = CountingCacheEventListener.getCacheElementsUpdated(cache);
162 assertTrue(notifications.size() == 11);
163 assertEquals("0", ((Element) notifications.get(0)).getObjectKey());
164 assertEquals("0", ((Element) notifications.get(0)).getObjectValue());
165
166
167 }
168
169 /***
170 * Tests the remove notifier
171 */
172 @Test
173 public void testRemoveNotifications() {
174
175 Serializable key = "1";
176 Serializable value = new Date();
177 Element element = new Element(key, value);
178
179
180 cache.put(element);
181
182
183 cache.remove(key);
184
185
186 List notifications = CountingCacheEventListener.getCacheElementsRemoved(cache);
187 assertEquals(element, notifications.get(0));
188
189
190 cache.remove(key);
191 notifications = CountingCacheEventListener.getCacheElementsRemoved(cache);
192 assertEquals(2, notifications.size());
193
194
195 cache.remove(null);
196
197 }
198
199
200 /***
201 * Tests the eviction notifier.
202 * sampleCache2 does not overflow, so an evict should trigger a notification
203 */
204 @Test
205 public void testEvictNotificationsWhereNoOverflow() {
206
207 Ehcache cache2 = manager.getCache("sampleCache2");
208
209
210 Element element = null;
211 for (int i = 0; i < 11; i++) {
212 element = new Element("" + i, new Date());
213 cache2.put(element);
214 }
215
216 List notifications = CountingCacheEventListener.getCacheElementsEvicted(cache2);
217 assertEquals(1, notifications.size());
218 }
219
220 /***
221 * Tests the eviction notifier.
222 * sampleCache1 overflows, so the evict should overflow to disk and not trigger a notification
223 */
224 @Test
225 public void testEvictNotificationsWhereOverflow() {
226
227 Ehcache cache1 = manager.getCache("sampleCache1");
228
229
230 Element element = null;
231 for (int i = 0; i < 11; i++) {
232 element = new Element("" + i, new Date());
233 cache1.put(element);
234 }
235
236 List notifications = CountingCacheEventListener.getCacheElementsEvicted(cache1);
237 assertEquals(0, notifications.size());
238 }
239
240 /***
241 * Tests the removeAll notifier.
242 */
243 @Test
244 public void testRemoveAllNotification() {
245
246 Ehcache cache2 = manager.getCache("sampleCache2");
247
248
249 Element element = null;
250 for (int i = 0; i < 11; i++) {
251 element = new Element("" + i, new Date());
252 cache2.put(element);
253 }
254
255 List notifications = CountingCacheEventListener.getCacheRemoveAlls(cache2);
256 assertEquals(0, notifications.size());
257
258
259 cache2.removeAll();
260 notifications = CountingCacheEventListener.getCacheRemoveAlls(cache2);
261 assertEquals(1, notifications.size());
262 }
263
264
265 /***
266 * Tests the remove notifier where the element does not exist in the local cache.
267 * Listener notification is required for correct operation of cluster invalidation.
268 */
269 @Test
270 public void testRemoveNotificationWhereElementDidNotExist() {
271
272 Serializable key = "1";
273
274
275
276
277
278 cache.remove(key);
279
280
281 List notifications = CountingCacheEventListener.getCacheElementsRemoved(cache);
282 assertEquals(key, ((Element) notifications.get(0)).getKey());
283
284
285 cache.remove(key);
286 notifications = CountingCacheEventListener.getCacheElementsRemoved(cache);
287 assertEquals(2, notifications.size());
288
289
290 cache.remove(null);
291
292 }
293
294
295 /***
296 * Tests the expiry notifier. Check a reported scenario
297 */
298 @Test
299 public void testExpiryNotifications() throws InterruptedException {
300 Serializable key = "1";
301 Element element = new Element(key, new Date());
302
303 TestCacheEventListener cacheEventListener = new TestCacheEventListener();
304 cache.getCacheEventNotificationService().registerListener(cacheEventListener);
305
306
307 cache.put(element);
308
309
310 Thread.sleep(15000);
311 assertThat(cacheEventListener.counter.get(), equalTo(1));
312
313
314 assertNotNull(cache.get(key));
315 Element newElement = cache.get(key);
316 assertNotNull(newElement);
317 assertEquals("set on notify", newElement.getValue());
318
319
320 List notifications = CountingCacheEventListener.getCacheElementsExpired(cache);
321 assertEquals(1, notifications.size());
322 assertEquals(element, notifications.get(0));
323
324
325 cache.remove(null);
326 }
327
328 /***
329 * Used to do work on notifyRemoved for the above test.
330 */
331 class TestCacheEventListener implements CacheEventListener {
332
333 final AtomicInteger counter = new AtomicInteger();
334
335 /***
336 * {@inheritDoc}
337 */
338 public void notifyElementRemoved(final Ehcache cache, final Element element) throws CacheException {
339
340 }
341
342 /***
343 * Called immediately after an element has been put into the cache. The {@link net.sf.ehcache.Cache#put(net.sf.ehcache.Element)} method
344 * will block until this method returns.
345 * <p/>
346 * Implementers may wish to have access to the Element's fields, including value, so the element is provided.
347 * Implementers should be careful not to modify the element. The effect of any modifications is undefined.
348 *
349 * @param cache the cache emitting the notification
350 * @param element the element which was just put into the cache.
351 */
352 public void notifyElementPut(final Ehcache cache, final Element element) throws CacheException {
353
354 }
355
356 /***
357 * Called immediately after an element has been put into the cache and the element already
358 * existed in the cache. This is thus an update.
359 * <p/>
360 * The {@link net.sf.ehcache.Cache#put(net.sf.ehcache.Element)} method
361 * will block until this method returns.
362 * <p/>
363 * Implementers may wish to have access to the Element's fields, including value, so the element is provided.
364 * Implementers should be careful not to modify the element. The effect of any modifications is undefined.
365 *
366 * @param cache the cache emitting the notification
367 * @param element the element which was just put into the cache.
368 */
369 public void notifyElementUpdated(final Ehcache cache, final Element element) throws CacheException {
370
371 }
372
373 /***
374 * Called immediately after an element is <i>found</i> to be expired. The
375 * {@link net.sf.ehcache.Cache#remove(Object)} method will block until this method returns.
376 * <p/>
377 * As the {@link net.sf.ehcache.Element} has been expired, only what was the key of the element is known.
378 * <p/>
379 * Elements are checked for expiry in ehcache at the following times:
380 * <ul>
381 * <li>When a get request is made
382 * <li>When an element is spooled to the diskStore in accordance with a MemoryStore eviction policy
383 * <li>In the DiskStore when the expiry thread runs, which by default is
384 * {@link net.sf.ehcache.Cache#DEFAULT_EXPIRY_THREAD_INTERVAL_SECONDS}
385 * </ul>
386 * If an element is found to be expired, it is deleted and this method is notified.
387 *
388 * @param cache the cache emitting the notification
389 * @param element the element that has just expired
390 * <p/>
391 * Deadlock Warning: expiry will often come from the <code>DiskStore</code> expiry thread. It holds a lock to the
392 * DiskStorea the time the notification is sent. If the implementation of this method calls into a
393 * synchronized <code>Cache</code> method and that subsequently calls into DiskStore a deadlock will result.
394 * Accordingly implementers of this method should not call back into Cache.
395 */
396 public void notifyElementExpired(final Ehcache cache, final Element element) {
397 LOG.info("Element expired : " + element);
398 cache.put(new Element(element.getKey(), "set on notify", Boolean.TRUE, 0, 0));
399 counter.incrementAndGet();
400 }
401
402 /***
403 * {@inheritDoc}
404 */
405 public void notifyElementEvicted(final Ehcache cache, final Element element) {
406
407 }
408
409 /***
410 * {@inheritDoc}
411 */
412 public void notifyRemoveAll(final Ehcache cache) {
413
414 }
415
416 /***
417 * Give the replicator a chance to cleanup and free resources when no longer needed
418 */
419 public void dispose() {
420
421 }
422
423 /***
424 * Creates and returns a copy of this object. The precise meaning
425 * of "copy" may depend on the class of the object. The general
426 * intent is that, for any object <tt>x</tt>, the expression:
427 * <blockquote>
428 * <pre>
429 * x.clone() != x</pre></blockquote>
430 * will be true, and that the expression:
431 * <blockquote>
432 * <pre>
433 * x.clone().getClass() == x.getClass()</pre></blockquote>
434 * will be <tt>true</tt>, but these are not absolute requirements.
435 * While it is typically the case that:
436 * <blockquote>
437 * <pre>
438 * x.clone().equals(x)</pre></blockquote>
439 * will be <tt>true</tt>, this is not an absolute requirement.
440 * <p/>
441 * By convention, the returned object should be obtained by calling
442 * <tt>super.clone</tt>. If a class and all of its superclasses (except
443 * <tt>Object</tt>) obey this convention, it will be the case that
444 * <tt>x.clone().getClass() == x.getClass()</tt>.
445 * <p/>
446 * By convention, the object returned by this method should be independent
447 * of this object (which is being cloned). To achieve this independence,
448 * it may be necessary to modify one or more fields of the object returned
449 * by <tt>super.clone</tt> before returning it. Typically, this means
450 * copying any mutable objects that comprise the internal "deep structure"
451 * of the object being cloned and replacing the references to these
452 * objects with references to the copies. If a class contains only
453 * primitive fields or references to immutable objects, then it is usually
454 * the case that no fields in the object returned by <tt>super.clone</tt>
455 * need to be modified.
456 * <p/>
457 * The method <tt>clone</tt> for class <tt>Object</tt> performs a
458 * specific cloning operation. First, if the class of this object does
459 * not implement the interface <tt>Cloneable</tt>, then a
460 * <tt>CloneNotSupportedException</tt> is thrown. Note that all arrays
461 * are considered to implement the interface <tt>Cloneable</tt>.
462 * Otherwise, this method creates a new instance of the class of this
463 * object and initializes all its fields with exactly the contents of
464 * the corresponding fields of this object, as if by assignment; the
465 * contents of the fields are not themselves cloned. Thus, this method
466 * performs a "shallow copy" of this object, not a "deep copy" operation.
467 * <p/>
468 * The class <tt>Object</tt> does not itself implement the interface
469 * <tt>Cloneable</tt>, so calling the <tt>clone</tt> method on an object
470 * whose class is <tt>Object</tt> will result in throwing an
471 * exception at run time.
472 *
473 * @return a clone of this instance.
474 * @throws CloneNotSupportedException if the object's class does not
475 * support the <code>Cloneable</code> interface. Subclasses
476 * that override the <code>clone</code> method can also
477 * throw this exception to indicate that an instance cannot
478 * be cloned.
479 * @see Cloneable
480 */
481 @Override
482 public Object clone() throws CloneNotSupportedException {
483 return super.clone();
484 }
485 }
486
487 /***
488 * When the <code>MemoryStore</code> overflows, and there is no disk
489 * store, then the element gets automatically removed. This should
490 * trigger an eviction notification.
491 */
492 @Test
493 public void testEvictionFromLRUMemoryStoreNoExpiry() throws IOException, CacheException, InterruptedException {
494 String sampleCache2 = "sampleCache2";
495 cache = manager.getCache(sampleCache2);
496 cache.removeAll();
497 for (int i = 0; i < 10; i++) {
498 cache.put(new Element(i + "", new Date()));
499 }
500 cache.put(new Element(11 + "", new Date()));
501 List evictionNotifications = CountingCacheEventListener.getCacheElementsEvicted(cache);
502 assertEquals(1, evictionNotifications.size());
503
504 List expiryNotifications = CountingCacheEventListener.getCacheElementsExpired(cache);
505 assertEquals(0, expiryNotifications.size());
506 }
507
508 /***
509 * When the <code>MemoryStore</code> overflows, and there is no disk
510 * store, then the element gets automatically evicted. This should
511 * trigger an eviction notification.
512 */
513 @Test
514 public void testEvictionFromLRUMemoryStoreNotSerializable() throws IOException, CacheException, InterruptedException {
515 String sampleCache1 = "sampleCache1";
516 cache = manager.getCache(sampleCache1);
517 cache.removeAll();
518
519
520 cache.put(new Element(12 + "", new Object()));
521
522 for (int i = 0; i < 10; i++) {
523
524 cache.put(new Element(i + "", new Object()));
525 cache.get(i + "");
526 }
527
528 List evictionNotifications = CountingCacheEventListener.getCacheElementsEvicted(cache);
529 assertEquals(1, evictionNotifications.size());
530
531 List expiryNotifications = CountingCacheEventListener.getCacheElementsExpired(cache);
532 assertEquals(0, expiryNotifications.size());
533 }
534
535 /***
536 * When the <code>MemoryStore</code> overflows, and there is no disk
537 * store, then the element gets automatically removed. This should
538 * trigger a remove notification.
539 * <p/>
540 * If the element has expired, it should instead trigger an expiry notification.
541 */
542 @Test
543 public void testEvictionFromLRUMemoryStoreExpiry() throws IOException, CacheException, InterruptedException {
544 String sampleCache2 = "sampleCache2";
545 cache = manager.getCache(sampleCache2);
546 cache.removeAll();
547 for (int i = 0; i < 10; i++) {
548 cache.put(new Element(i + "", new Date()));
549 }
550
551 Thread.sleep(1999);
552 cache.put(new Element(11 + "", new Date()));
553
554 List removalNotifications = CountingCacheEventListener.getCacheElementsEvicted(cache);
555 assertEquals(0, removalNotifications.size());
556
557 List expiryNotifications = CountingCacheEventListener.getCacheElementsExpired(cache);
558 assertEquals(1, expiryNotifications.size());
559 }
560
561 /***
562 * When the <code>MemoryStore</code> overflows, and there is no disk
563 * store, then the element gets automatically evicted. This should
564 * trigger a notification.
565 */
566 @Test
567 public void testEvictionFromFIFOMemoryStoreNoExpiry() throws IOException, CacheException {
568 String sampleCache3 = "sampleCache3";
569 cache = manager.getCache(sampleCache3);
570 cache.removeAll();
571 for (int i = 0; i < 10; i++) {
572 cache.put(new Element(i + "", new Date()));
573 }
574
575 cache.put(new Element(11 + "", new Date()));
576
577 List removalNotifications = CountingCacheEventListener.getCacheElementsEvicted(cache);
578 assertEquals(1, removalNotifications.size());
579
580 List expiryNotifications = CountingCacheEventListener.getCacheElementsExpired(cache);
581 assertEquals(0, expiryNotifications.size());
582 }
583
584 /***
585 * When the <code>MemoryStore</code> overflows, and there is no disk
586 * store, then the element gets automatically evicted. This should
587 * trigger a notification.
588 * <p/>
589 * If the element has expired, it should instead trigger an expiry notification.
590 */
591 @Test
592 public void testEvictionFromFIFOMemoryStoreExpiry() throws IOException, CacheException, InterruptedException {
593 String sampleCache3 = "sampleCache3";
594 cache = manager.getCache(sampleCache3);
595 cache.removeAll();
596 for (int i = 0; i < 10; i++) {
597 cache.put(new Element(i + "", new Date()));
598 }
599
600 Thread.sleep(1999);
601 cache.put(new Element(11 + "", new Date()));
602
603 List notifications = CountingCacheEventListener.getCacheElementsEvicted(cache);
604 assertEquals(0, notifications.size());
605
606 List expiryNotifications = CountingCacheEventListener.getCacheElementsExpired(cache);
607 assertEquals(1, expiryNotifications.size());
608 }
609
610 /***
611 * When the <code>MemoryStore</code> overflows, and there is no disk
612 * store, then the element gets automatically evicted. This should
613 * trigger a notification.
614 */
615 @Test
616 public void testEvictionFromLFUMemoryStoreNoExpiry() throws IOException, CacheException {
617 String sampleCache4 = "sampleCache4";
618 cache = manager.getCache(sampleCache4);
619 cache.removeAll();
620 for (int i = 0; i < 10; i++) {
621 cache.put(new Element(i + "", new Date()));
622 }
623
624 cache.put(new Element(11 + "", new Date()));
625
626 List notifications = CountingCacheEventListener.getCacheElementsEvicted(cache);
627 assertEquals(1, notifications.size());
628
629 List expiryNotifications = CountingCacheEventListener.getCacheElementsExpired(cache);
630 assertEquals(0, expiryNotifications.size());
631 }
632
633 /***
634 * When the <code>MemoryStore</code> overflows, and there is no disk
635 * store, then the element gets automatically removed. This should
636 * trigger a notification.
637 * <p/>
638 * If the element has expired, it should instead trigger an expiry notification.
639 */
640 @Test
641 public void testEvictionFromLFUMemoryStoreExpiry() throws IOException, CacheException, InterruptedException {
642 String sampleCache4 = "sampleCache4";
643 cache = manager.getCache(sampleCache4);
644 cache.removeAll();
645 for (int i = 0; i < 10; i++) {
646 cache.put(new Element(i + "", new Date()));
647 }
648
649 Thread.sleep(1999);
650 cache.put(new Element(11 + "", new Date()));
651
652 List notifications = CountingCacheEventListener.getCacheElementsEvicted(cache);
653 assertEquals(0, notifications.size());
654
655 List expiryNotifications = CountingCacheEventListener.getCacheElementsExpired(cache);
656 assertEquals(1, expiryNotifications.size());
657 }
658
659
660 /***
661 * Tests expiry notification is hooked up to searchInMemoryStore
662 *
663 * @throws InterruptedException
664 * @throws CacheException
665 */
666 @Test
667 public void testExpiryViaMemoryStoreCheckingOnGet() throws InterruptedException, CacheException, IOException {
668
669 cache.removeAll();
670 CountingCacheEventListener.resetCounters();
671
672 Serializable key = "1";
673 Serializable value = new Date();
674 Element element = new Element(key, value);
675
676
677 cache.put(element);
678 Thread.sleep(5999);
679
680
681 cache.get(key);
682 List notifications = CountingCacheEventListener.getCacheElementsExpired(cache);
683 assertEquals(1, notifications.size());
684 assertEquals(element, notifications.get(0));
685 }
686
687 /***
688 * Tests expiry notification is hooked up to searchInDiskStore.
689 * This test is not exact, because the expiry thread may also expire some.
690 *
691 * @throws InterruptedException
692 * @throws CacheException
693 */
694 @Test
695 public void testExpiryViaDiskStoreCheckingOnGet() throws InterruptedException, CacheException, IOException {
696
697 for (int i = 0; i < 20; i++) {
698 Element element = new Element("" + i, new Date());
699 cache.put(element);
700 }
701
702
703 Thread.sleep(5999);
704
705
706 for (int i = 0; i < 20; i++) {
707 cache.get("" + i);
708 }
709
710 List notifications = CountingCacheEventListener.getCacheElementsExpired(cache);
711 for (int i = 0; i < notifications.size(); i++) {
712 Element element = (Element) notifications.get(i);
713 element.getObjectKey();
714 }
715 assertTrue(notifications.size() >= 10);
716 }
717
718
719 /***
720 * Tests expiry thread expiry
721 *
722 * @throws InterruptedException
723 */
724 @Test
725 public void testExpiryViaDiskStoreExpiryThread() throws InterruptedException {
726
727 for (int i = 0; i < 20; i++) {
728 Element element = new Element(Integer.toString(i), new Date());
729 cache.put(element);
730 }
731
732
733 Thread.sleep(6999);
734
735 List notifications = CountingCacheEventListener.getCacheElementsExpired(cache);
736 for (int i = 0; i < notifications.size(); i++) {
737 Element element = (Element) notifications.get(i);
738 element.getObjectKey();
739 }
740 assertEquals(10, notifications.size());
741
742 }
743
744
745 /***
746 * Tests that elements evicted from disk are notified to any listeners.
747 * fails from full ant builds?
748 */
749 public void xTestEvictionFromDiskStoreWithExpiry() throws IOException, CacheException, InterruptedException {
750
751 cache.removeAll();
752
753 for (int i = 0; i < 20; i++) {
754 Element element = new Element("" + i, new Date());
755 cache.put(element);
756 }
757 cache.put(new Element(21 + "", new Date()));
758 Thread.sleep(2999);
759
760 List notifications = CountingCacheEventListener.getCacheElementsEvicted(cache);
761 assertEquals(1, notifications.size());
762
763 List expiryNotifications = CountingCacheEventListener.getCacheElementsExpired(cache);
764 assertEquals(10, expiryNotifications.size());
765 }
766
767
768 /***
769 * Test adding and removing of listeners while events are being notified
770 */
771 @Test
772 public void testAddAndRemoveListenerConcurrency() throws Exception {
773
774 final List executables = new ArrayList();
775
776 for (int i = 0; i < 1; i++) {
777 final Executable executable = new Executable() {
778 public void execute() throws Exception {
779 try {
780 CacheEventListener listener = new TestCacheEventListener();
781 cache.getCacheEventNotificationService().registerListener(listener);
782 assertTrue(cache.getCacheEventNotificationService().unregisterListener(listener));
783
784 } catch (Throwable t) {
785 LOG.error("", t);
786 fail();
787 }
788 }
789 };
790 executables.add(executable);
791 }
792
793 runThreads(executables);
794 }
795
796
797 }