diff -ruBN infinispan-4.1.0.VAN-01/core/src/main/java/org/infinispan/commands/write/EvictCommand.java infinispan-4.1.0_patch01/core/src/main/java/org/infinispan/commands/write/EvictCommand.java --- infinispan-4.1.0.VAN-01/core/src/main/java/org/infinispan/commands/write/EvictCommand.java 2010-10-05 07:54:55.000000000 -0400 +++ infinispan-4.1.0_patch01/core/src/main/java/org/infinispan/commands/write/EvictCommand.java 2010-10-01 14:26:06.000000000 -0400 @@ -59,7 +59,7 @@ @Override public void notify(InvocationContext ctx, Object value, boolean isPre) { - notifier.notifyCacheEntryEvicted(key, isPre, ctx); + notifier.notifyCacheEntryEvicted(key, value, isPre, ctx); } @Override diff -ruBN infinispan-4.1.0.VAN-01/core/src/main/java/org/infinispan/container/DataContainer.java infinispan-4.1.0_patch01/core/src/main/java/org/infinispan/container/DataContainer.java --- infinispan-4.1.0.VAN-01/core/src/main/java/org/infinispan/container/DataContainer.java 2010-10-05 07:54:55.000000000 -0400 +++ infinispan-4.1.0_patch01/core/src/main/java/org/infinispan/container/DataContainer.java 2010-10-01 14:26:06.000000000 -0400 @@ -28,6 +28,7 @@ import org.infinispan.factories.annotations.Stop; import org.infinispan.factories.scopes.Scope; import org.infinispan.factories.scopes.Scopes; +import org.infinispan.notifications.cachelistener.CacheNotifier; /** * The main internal data structure which stores entries @@ -124,7 +125,7 @@ Set entrySet(); /** - * Purges entries that have passed their expiry time + * Purges entries that have passed their expiry time and notify eviction listeners. */ - void purgeExpired(); + void purgeExpired(CacheNotifier cacheNotifier); } diff -ruBN infinispan-4.1.0.VAN-01/core/src/main/java/org/infinispan/container/DefaultDataContainer.java infinispan-4.1.0_patch01/core/src/main/java/org/infinispan/container/DefaultDataContainer.java --- infinispan-4.1.0.VAN-01/core/src/main/java/org/infinispan/container/DefaultDataContainer.java 2010-10-05 07:54:55.000000000 -0400 +++ infinispan-4.1.0_patch01/core/src/main/java/org/infinispan/container/DefaultDataContainer.java 2010-10-01 14:26:06.000000000 -0400 @@ -18,6 +18,7 @@ import org.infinispan.eviction.EvictionStrategy; import org.infinispan.eviction.EvictionThreadPolicy; import org.infinispan.factories.annotations.Inject; +import org.infinispan.notifications.cachelistener.CacheNotifier; import org.infinispan.util.Immutables; import org.infinispan.util.concurrent.BoundedConcurrentHashMap; import org.infinispan.util.concurrent.BoundedConcurrentHashMap.Eviction; @@ -208,15 +209,16 @@ return new EntrySet(); } - public void purgeExpired() { - for (Iterator entries = mortalEntries.values().iterator(); entries.hasNext();) { - InternalCacheEntry e = entries.next(); - if (e.isExpired()) { - entries.remove(); - numEntries.getAndDecrement(); - } - } - } + public void purgeExpired(CacheNotifier cacheNotifier) { + for (Iterator entries = mortalEntries.values().iterator(); entries.hasNext();) { + InternalCacheEntry e = entries.next(); + if (e.isExpired()) { + cacheNotifier.notifyCacheEntryExpired(e.getKey(), e.getValue()); + entries.remove(); + numEntries.getAndDecrement(); + } + } + } public Iterator iterator() { return new EntryIterator(immortalEntries.values().iterator(), mortalEntries.values().iterator()); @@ -224,8 +226,8 @@ private class DefaultEvictionListener implements EvictionListener { @Override - public void preEvict(Object key) { - evictionManager.preEvict(key); + public void preEvict(Object key, InternalCacheEntry value) { + evictionManager.preEvict(key, value); } @Override diff -ruBN infinispan-4.1.0.VAN-01/core/src/main/java/org/infinispan/eviction/EvictionManager.java infinispan-4.1.0_patch01/core/src/main/java/org/infinispan/eviction/EvictionManager.java --- infinispan-4.1.0.VAN-01/core/src/main/java/org/infinispan/eviction/EvictionManager.java 2010-10-05 07:54:55.000000000 -0400 +++ infinispan-4.1.0_patch01/core/src/main/java/org/infinispan/eviction/EvictionManager.java 2010-10-01 14:26:06.000000000 -0400 @@ -34,7 +34,7 @@ */ boolean isEnabled(); - void preEvict(Object key); + void preEvict(Object key, InternalCacheEntry value); void postEvict(Object key, InternalCacheEntry value); } diff -ruBN infinispan-4.1.0.VAN-01/core/src/main/java/org/infinispan/eviction/EvictionManagerImpl.java infinispan-4.1.0_patch01/core/src/main/java/org/infinispan/eviction/EvictionManagerImpl.java --- infinispan-4.1.0.VAN-01/core/src/main/java/org/infinispan/eviction/EvictionManagerImpl.java 2010-10-05 07:56:47.000000000 -0400 +++ infinispan-4.1.0_patch01/core/src/main/java/org/infinispan/eviction/EvictionManagerImpl.java 2010-10-01 14:26:06.000000000 -0400 @@ -99,7 +99,7 @@ log.trace("Purging data container of expired entries"); start = System.currentTimeMillis(); } - dataContainer.purgeExpired(); + dataContainer.purgeExpired(cacheNotifier); if (trace) log.trace("Purging data container completed in {0}", Util.prettyPrintTime(System.currentTimeMillis() - start)); } catch (Exception e) { @@ -138,13 +138,13 @@ } @Override - public void preEvict(Object key) { + public void preEvict(Object key, InternalCacheEntry value) { try { acquireLock(getInvocationContext(), key); } catch (Exception e) { log.warn("Could not acquire lock for eviction of {0}", key, e); } - cacheNotifier.notifyCacheEntryEvicted(key, true, getInvocationContext()); + cacheNotifier.notifyCacheEntryEvicted(key, value.getValue(), true, getInvocationContext()); } @Override @@ -154,7 +154,7 @@ } catch (CacheLoaderException e) { log.warn("Unable to passivate entry under {0}", key, e); } - cacheNotifier.notifyCacheEntryEvicted(key, false, getInvocationContext()); + cacheNotifier.notifyCacheEntryEvicted(key, value.getValue(), false, getInvocationContext()); releaseLock(key); } diff -ruBN infinispan-4.1.0.VAN-01/core/src/main/java/org/infinispan/notifications/cachelistener/CacheNotifier.java infinispan-4.1.0_patch01/core/src/main/java/org/infinispan/notifications/cachelistener/CacheNotifier.java --- infinispan-4.1.0.VAN-01/core/src/main/java/org/infinispan/notifications/cachelistener/CacheNotifier.java 2010-10-05 07:54:55.000000000 -0400 +++ infinispan-4.1.0_patch01/core/src/main/java/org/infinispan/notifications/cachelistener/CacheNotifier.java 2010-10-01 14:26:06.000000000 -0400 @@ -58,7 +58,14 @@ /** * Notifies all registered listeners of a CacheEntryEvicted event. */ - void notifyCacheEntryEvicted(Object key, boolean pre, InvocationContext ctx); + void notifyCacheEntryEvicted(Object key, Object value, boolean pre, InvocationContext ctx); + + /** + * Notifies all registered listeners of a CacheEntryExpired event. + * @param key Cache key of the expired entry. + * @param value Cache entry + */ + void notifyCacheEntryExpired(Object key, Object value); /** * Notifies all registered listeners of a CacheEntryInvalidated event. diff -ruBN infinispan-4.1.0.VAN-01/core/src/main/java/org/infinispan/notifications/cachelistener/CacheNotifierImpl.java infinispan-4.1.0_patch01/core/src/main/java/org/infinispan/notifications/cachelistener/CacheNotifierImpl.java --- infinispan-4.1.0.VAN-01/core/src/main/java/org/infinispan/notifications/cachelistener/CacheNotifierImpl.java 2010-10-05 07:54:55.000000000 -0400 +++ infinispan-4.1.0_patch01/core/src/main/java/org/infinispan/notifications/cachelistener/CacheNotifierImpl.java 2010-10-01 14:26:06.000000000 -0400 @@ -61,6 +61,7 @@ allowedListeners.put(CacheEntryPassivated.class, CacheEntryPassivatedEvent.class); allowedListeners.put(CacheEntryLoaded.class, CacheEntryLoadedEvent.class); allowedListeners.put(CacheEntryEvicted.class, CacheEntryEvictedEvent.class); + allowedListeners.put(CacheEntryExpired.class, CacheEntryExpiredEvent.class); allowedListeners.put(TransactionRegistered.class, TransactionRegisteredEvent.class); allowedListeners.put(TransactionCompleted.class, TransactionCompletedEvent.class); allowedListeners.put(CacheEntryInvalidated.class, CacheEntryInvalidatedEvent.class); @@ -76,6 +77,7 @@ final List cacheEntryLoadedListeners = new CopyOnWriteArrayList(); final List cacheEntryInvalidatedListeners = new CopyOnWriteArrayList(); final List cacheEntryEvictedListeners = new CopyOnWriteArrayList(); + final List cacheEntryExpiredListeners = new CopyOnWriteArrayList(); final List transactionRegisteredListeners = new CopyOnWriteArrayList(); final List transactionCompletedListeners = new CopyOnWriteArrayList(); @@ -92,6 +94,7 @@ listenersMap.put(CacheEntryPassivated.class, cacheEntryPassivatedListeners); listenersMap.put(CacheEntryLoaded.class, cacheEntryLoadedListeners); listenersMap.put(CacheEntryEvicted.class, cacheEntryEvictedListeners); + listenersMap.put(CacheEntryExpired.class, cacheEntryExpiredListeners); listenersMap.put(TransactionRegistered.class, transactionRegisteredListeners); listenersMap.put(TransactionCompleted.class, transactionCompletedListeners); listenersMap.put(CacheEntryInvalidated.class, cacheEntryInvalidatedListeners); @@ -187,7 +190,7 @@ } } - public void notifyCacheEntryEvicted(final Object key, final boolean pre, InvocationContext ctx) { + public void notifyCacheEntryEvicted(final Object key, final Object value, final boolean pre, InvocationContext ctx) { if (!cacheEntryEvictedListeners.isEmpty()) { final boolean originLocal = ctx.isOriginLocal(); InvocationContext contexts = icc.suspend(); @@ -197,6 +200,7 @@ e.setOriginLocal(originLocal); e.setPre(pre); e.setKey(key); + e.setValue(value); setTx(ctx, e); e.setType(CACHE_ENTRY_EVICTED); for (ListenerInvocation listener : cacheEntryEvictedListeners) listener.invoke(e); @@ -206,6 +210,24 @@ } } + + public void notifyCacheEntryExpired(Object key, Object value) { + if (!cacheEntryExpiredListeners.isEmpty()) { + try { + EventImpl e = new EventImpl(); + e.setCache(cache); + e.setKey(key); + e.setValue(value); + e.setType(CACHE_ENTRY_EXPIRED); + for (ListenerInvocation listener : cacheEntryExpiredListeners) listener.invoke(e); + } finally { + // no-op + } + } + + } + + public void notifyCacheEntryInvalidated(final Object key, final boolean pre, InvocationContext ctx) { if (!cacheEntryInvalidatedListeners.isEmpty()) { final boolean originLocal = ctx.isOriginLocal(); diff -ruBN infinispan-4.1.0.VAN-01/core/src/main/java/org/infinispan/notifications/cachelistener/annotation/CacheEntryExpired.java infinispan-4.1.0_patch01/core/src/main/java/org/infinispan/notifications/cachelistener/annotation/CacheEntryExpired.java --- infinispan-4.1.0.VAN-01/core/src/main/java/org/infinispan/notifications/cachelistener/annotation/CacheEntryExpired.java 1969-12-31 19:00:00.000000000 -0500 +++ infinispan-4.1.0_patch01/core/src/main/java/org/infinispan/notifications/cachelistener/annotation/CacheEntryExpired.java 2010-10-01 14:26:06.000000000 -0400 @@ -0,0 +1,46 @@ +/* + * JBoss, Home of Professional Open Source. + * Copyright 2000 - 2008, Red Hat Middleware LLC, and individual contributors + * as indicated by the @author tags. See the copyright.txt file in the + * distribution for a full listing of individual contributors. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ +package org.infinispan.notifications.cachelistener.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * This annotation should be used on methods that need to be notified when a cache entry is evicted. + *

+ * Methods annotated with this annotation should be public and take in a single parameter, a {@link + * org.infinispan.notifications.cachelistener.event.CacheEntryExpiredEvent} otherwise an {@link + * org.infinispan.notifications.IncorrectListenerException} will be thrown when registering your cache listener. + *

+ * Locking: notification is performed WITH locks on the given key. + * + * @author Manik Surtani + * @see org.infinispan.notifications.Listener + * @see CacheEntryLoaded + * @since 4.0 + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.METHOD) +public @interface CacheEntryExpired { +} diff -ruBN infinispan-4.1.0.VAN-01/core/src/main/java/org/infinispan/notifications/cachelistener/event/CacheEntryEvent.java infinispan-4.1.0_patch01/core/src/main/java/org/infinispan/notifications/cachelistener/event/CacheEntryEvent.java --- infinispan-4.1.0.VAN-01/core/src/main/java/org/infinispan/notifications/cachelistener/event/CacheEntryEvent.java 2010-10-05 07:54:55.000000000 -0400 +++ infinispan-4.1.0_patch01/core/src/main/java/org/infinispan/notifications/cachelistener/event/CacheEntryEvent.java 2010-10-01 14:26:06.000000000 -0400 @@ -32,4 +32,9 @@ * @return the key to the affected cache entry. */ Object getKey(); + + /** + * @return the affected cache entry. + */ + Object getValue(); } diff -ruBN infinispan-4.1.0.VAN-01/core/src/main/java/org/infinispan/notifications/cachelistener/event/CacheEntryExpiredEvent.java infinispan-4.1.0_patch01/core/src/main/java/org/infinispan/notifications/cachelistener/event/CacheEntryExpiredEvent.java --- infinispan-4.1.0.VAN-01/core/src/main/java/org/infinispan/notifications/cachelistener/event/CacheEntryExpiredEvent.java 1969-12-31 19:00:00.000000000 -0500 +++ infinispan-4.1.0_patch01/core/src/main/java/org/infinispan/notifications/cachelistener/event/CacheEntryExpiredEvent.java 2010-10-01 14:26:06.000000000 -0400 @@ -0,0 +1,31 @@ +/* + * JBoss, Home of Professional Open Source. + * Copyright 2000 - 2008, Red Hat Middleware LLC, and individual contributors + * as indicated by the @author tags. See the copyright.txt file in the + * distribution for a full listing of individual contributors. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ +package org.infinispan.notifications.cachelistener.event; + +/** + * This event subtype is passed in to any method annotated with {@link org.infinispan.notifications.cachelistener.annotation.CacheEntryExpired}. + * + * @author Edouard Boily + * @since 4.1 + */ +public interface CacheEntryExpiredEvent extends CacheEntryEvent { +} diff -ruBN infinispan-4.1.0.VAN-01/core/src/main/java/org/infinispan/notifications/cachelistener/event/Event.java infinispan-4.1.0_patch01/core/src/main/java/org/infinispan/notifications/cachelistener/event/Event.java --- infinispan-4.1.0.VAN-01/core/src/main/java/org/infinispan/notifications/cachelistener/event/Event.java 2010-10-05 07:54:55.000000000 -0400 +++ infinispan-4.1.0_patch01/core/src/main/java/org/infinispan/notifications/cachelistener/event/Event.java 2010-10-01 14:26:06.000000000 -0400 @@ -33,7 +33,7 @@ static enum Type { CACHE_ENTRY_ACTIVATED, CACHE_ENTRY_PASSIVATED, CACHE_ENTRY_VISITED, CACHE_ENTRY_LOADED, CACHE_ENTRY_EVICTED, CACHE_ENTRY_CREATED, CACHE_ENTRY_REMOVED, CACHE_ENTRY_MODIFIED, - TRANSACTION_COMPLETED, TRANSACTION_REGISTERED, CACHE_ENTRY_INVALIDATED + TRANSACTION_COMPLETED, TRANSACTION_REGISTERED, CACHE_ENTRY_INVALIDATED, CACHE_ENTRY_EXPIRED } /** diff -ruBN infinispan-4.1.0.VAN-01/core/src/main/java/org/infinispan/notifications/cachelistener/event/EventImpl.java infinispan-4.1.0_patch01/core/src/main/java/org/infinispan/notifications/cachelistener/event/EventImpl.java --- infinispan-4.1.0.VAN-01/core/src/main/java/org/infinispan/notifications/cachelistener/event/EventImpl.java 2010-10-05 07:54:55.000000000 -0400 +++ infinispan-4.1.0_patch01/core/src/main/java/org/infinispan/notifications/cachelistener/event/EventImpl.java 2010-10-01 14:26:06.000000000 -0400 @@ -36,7 +36,7 @@ @NotThreadSafe public class EventImpl implements CacheEntryActivatedEvent, CacheEntryCreatedEvent, CacheEntryEvictedEvent, CacheEntryLoadedEvent, CacheEntryModifiedEvent, CacheEntryPassivatedEvent, CacheEntryRemovedEvent, CacheEntryVisitedEvent, TransactionCompletedEvent, TransactionRegisteredEvent, - CacheEntryInvalidatedEvent { + CacheEntryInvalidatedEvent, CacheEntryExpiredEvent { private boolean pre = false; // by default events are after the fact private Cache cache; private Object key; diff -ruBN infinispan-4.1.0.VAN-01/core/src/main/java/org/infinispan/util/concurrent/BoundedConcurrentHashMap.java infinispan-4.1.0_patch01/core/src/main/java/org/infinispan/util/concurrent/BoundedConcurrentHashMap.java --- infinispan-4.1.0.VAN-01/core/src/main/java/org/infinispan/util/concurrent/BoundedConcurrentHashMap.java 2010-10-05 07:54:55.000000000 -0400 +++ infinispan-4.1.0_patch01/core/src/main/java/org/infinispan/util/concurrent/BoundedConcurrentHashMap.java 2010-10-01 14:26:06.000000000 -0400 @@ -16,6 +16,8 @@ import java.io.Serializable; import java.io.IOException; +import org.infinispan.container.entries.InternalCacheEntry; + /** * A hash table supporting full concurrency of retrievals and @@ -274,8 +276,8 @@ } public interface EvictionListener { - void preEvict(K key); void postEvict(K key, V value); + void preEvict( K key, V value ); } static class NullEvictionListener implements EvictionListener { @@ -284,7 +286,7 @@ } @Override - public void preEvict(K key) { + public void preEvict(K key, V value) { } } @@ -426,7 +428,7 @@ } while (isOverflow()) { HashEntry first = lruQueue.getLast(); - segment.getEvictionListener().preEvict(first.key); + segment.getEvictionListener().preEvict(first.key, first.value); segment.remove(first.key, first.hash, null); evicted.add(first); } @@ -603,7 +605,7 @@ private void removeFromSegment(Set> evicted) { for (HashEntry e : evicted) { - segment.getEvictionListener().preEvict(e.key); + segment.getEvictionListener().preEvict(e.key,e.value); segment.remove(e.key, e.hash, null); } } diff -ruBN infinispan-4.1.0.VAN-01/core/src/test/java/org/infinispan/notifications/cachelistener/CacheNotifierImplTest.java infinispan-4.1.0_patch01/core/src/test/java/org/infinispan/notifications/cachelistener/CacheNotifierImplTest.java --- infinispan-4.1.0.VAN-01/core/src/test/java/org/infinispan/notifications/cachelistener/CacheNotifierImplTest.java 2010-10-05 07:54:56.000000000 -0400 +++ infinispan-4.1.0_patch01/core/src/test/java/org/infinispan/notifications/cachelistener/CacheNotifierImplTest.java 2010-10-01 14:26:06.000000000 -0400 @@ -104,8 +104,8 @@ } public void testNotifyCacheEntryEvicted() { - n.notifyCacheEntryEvicted("k", true, ctx); - n.notifyCacheEntryEvicted("k", false, ctx); + n.notifyCacheEntryEvicted("k", "v", true, ctx); + n.notifyCacheEntryEvicted("k", "v", false, ctx); assert cl.isReceivedPost(); assert cl.isReceivedPre();