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.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         //Put
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         //A put which updates records as one put, because the second one is an update
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         //Put and update
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         //Put with no update
153         cache.put(new Element("20", "20"));
154 
155         //Should get 12 puts and 11 updates
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         //Put
180         cache.put(element);
181 
182         //Check removal from MemoryStore
183         cache.remove(key);
184 
185 
186         List notifications = CountingCacheEventListener.getCacheElementsRemoved(cache);
187         assertEquals(element, notifications.get(0));
188 
189         //An unsuccessful remove should notify
190         cache.remove(key);
191         notifications = CountingCacheEventListener.getCacheElementsRemoved(cache);
192         assertEquals(2, notifications.size());
193 
194         //check for NPE
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         //Put 11. 1 should be evicted
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         //Put 11. 1 should be evicted
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         //Put 11.
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         //Remove all
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         //Don't Put
275         //cache.put(element);
276 
277         //Check removal from MemoryStore
278         cache.remove(key);
279 
280 
281         List notifications = CountingCacheEventListener.getCacheElementsRemoved(cache);
282         assertEquals(key, ((Element) notifications.get(0)).getKey());
283 
284         //An unsuccessful remove should notify
285         cache.remove(key);
286         notifications = CountingCacheEventListener.getCacheElementsRemoved(cache);
287         assertEquals(2, notifications.size());
288 
289         //check for NPE
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         //Put
307         cache.put(element);
308 
309         //expire
310         Thread.sleep(15000);
311         assertThat(cacheEventListener.counter.get(), equalTo(1));
312 
313         //the TestCacheEventListener does a put of a new Element with the same key on expiry
314         assertNotNull(cache.get(key));
315         Element newElement = cache.get(key);
316         assertNotNull(newElement);
317         assertEquals("set on notify", newElement.getValue());
318 
319         //Check counting listener
320         List notifications = CountingCacheEventListener.getCacheElementsExpired(cache);
321         assertEquals(1, notifications.size());
322         assertEquals(element, notifications.get(0));
323 
324         //check for NPE
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         //should trigger a removal notification because it is not Serializable when it is evicted
520         cache.put(new Element(12 + "", new Object()));
521 
522         for (int i = 0; i < 10; i++) {
523             // use non-serializable object for all values
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         //Check expiry from memory store in 1 second
677         cache.put(element);
678         Thread.sleep(5999);
679 
680         //Trigger expiry
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         //Overflow 10 elements to disk store
697         for (int i = 0; i < 20; i++) {
698             Element element = new Element("" + i, new Date());
699             cache.put(element);
700         }
701 
702         //Wait for expiry
703         Thread.sleep(5999);
704 
705         //Trigger expiry
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         //Overflow 10 elements to disk store
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         // Wait for expiry and expiry thread
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         //Overflow 10 elements to disk store which is maximum
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 }