All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.sf.ehcache.event.RegisteredEventListeners Maven / Gradle / Ivy

/**
 *  Copyright 2003-2010 Terracotta, Inc.
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */

package net.sf.ehcache.event;

import net.sf.ehcache.Cache;
import net.sf.ehcache.CacheException;
import net.sf.ehcache.CacheStoreHelper;
import net.sf.ehcache.Ehcache;
import net.sf.ehcache.Element;
import net.sf.ehcache.distribution.CacheReplicator;
import net.sf.ehcache.statistics.LiveCacheStatisticsData;
import net.sf.ehcache.store.FrontEndCacheTier;
import net.sf.ehcache.store.Store;

import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArraySet;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicLong;

/**
 * Registered listeners for registering and unregistering CacheEventListeners and multicasting notifications to registrants.
 * 

* There is one of these per Cache. *

* This class also has counters to accumulate the numbers of each type of event for statistics purposes. * * @author Greg Luck * @author Geert Bevin * @version $Id: RegisteredEventListeners.java 4555 2011-08-18 12:28:13Z alexsnaps $ */ public class RegisteredEventListeners { /** * A Set of CacheEventListeners keyed by listener instance. * CacheEventListener implementations that will be notified of this cache's events. * * @see CacheEventListener */ private final Set cacheEventListeners = new CopyOnWriteArraySet(); private final Ehcache cache; private final AtomicBoolean hasReplicator = new AtomicBoolean(false); private final AtomicLong elementsRemovedCounter = new AtomicLong(0); private final AtomicLong elementsPutCounter = new AtomicLong(0); private final AtomicLong elementsUpdatedCounter = new AtomicLong(0); private final AtomicLong elementsExpiredCounter = new AtomicLong(0); private final AtomicLong elementsEvictedCounter = new AtomicLong(0); private final AtomicLong elementsRemoveAllCounter = new AtomicLong(0); private final CacheStoreHelper helper; /** * Constructs a new notification service * * @param cache */ public RegisteredEventListeners(Ehcache cache) { this.cache = cache; helper = new CacheStoreHelper((Cache)cache); } /** * Notifies all registered listeners, in no guaranteed order, that an element was removed * * @param element * @param remoteEvent whether the event came from a remote cache peer * @see CacheEventListener#notifyElementRemoved */ public final void notifyElementRemoved(Element element, boolean remoteEvent) throws CacheException { internalNotifyElementRemoved(element, null, remoteEvent); } /** * Notifies all registered listeners, in no guaranteed order, that an element was removed * * @param callback * @param remoteEvent whether the event came from a remote cache peer * @see CacheEventListener#notifyElementRemoved */ public final void notifyElementRemoved(ElementCreationCallback callback, boolean remoteEvent) throws CacheException { internalNotifyElementRemoved(null, callback, remoteEvent); } private void internalNotifyElementRemoved(Element element, ElementCreationCallback callback, boolean remoteEvent) { elementsRemovedCounter.incrementAndGet(); if (hasCacheEventListeners()) { for (ListenerWrapper listenerWrapper : cacheEventListeners) { if (listenerWrapper.getScope().shouldDeliver(remoteEvent) && !isCircularNotification(remoteEvent, listenerWrapper.getListener())) { CacheEventListener listener = listenerWrapper.getListener(); invokeListener(listener, element, callback, Event.REMOVED); } } } } /** * Notifies all registered listeners, in no guaranteed order, that an element was put into the cache * * @param element * @param remoteEvent whether the event came from a remote cache peer * @see CacheEventListener#notifyElementPut(net.sf.ehcache.Ehcache,net.sf.ehcache.Element) */ public final void notifyElementPut(Element element, boolean remoteEvent) throws CacheException { internalNotifyElementPut(element, null, remoteEvent); } /** * Notifies all registered listeners, in no guaranteed order, that an element was put into the cache * * @param callback * @param remoteEvent whether the event came from a remote cache peer * @see CacheEventListener#notifyElementPut(net.sf.ehcache.Ehcache,net.sf.ehcache.Element) */ public final void notifyElementPut(ElementCreationCallback callback, boolean remoteEvent) throws CacheException { internalNotifyElementPut(null, callback, remoteEvent); } private void internalNotifyElementPut(Element element, ElementCreationCallback callback, boolean remoteEvent) { elementsPutCounter.incrementAndGet(); if (hasCacheEventListeners()) { for (ListenerWrapper listenerWrapper : cacheEventListeners) { if (listenerWrapper.getScope().shouldDeliver(remoteEvent) && !isCircularNotification(remoteEvent, listenerWrapper.getListener())) { CacheEventListener listener = listenerWrapper.getListener(); invokeListener(listener, element, callback, Event.PUT); } } } } /** * Notifies all registered listeners, in no guaranteed order, that an element in the cache was updated * * @param element * @param remoteEvent whether the event came from a remote cache peer * @see CacheEventListener#notifyElementPut(net.sf.ehcache.Ehcache,net.sf.ehcache.Element) */ public final void notifyElementUpdated(Element element, boolean remoteEvent) { internalNotifyElementUpdated(element, null, remoteEvent); } /** * Notifies all registered listeners, in no guaranteed order, that an element in the cache was updated * * @param callback * @param remoteEvent whether the event came from a remote cache peer * @see CacheEventListener#notifyElementPut(net.sf.ehcache.Ehcache,net.sf.ehcache.Element) */ public final void notifyElementUpdated(ElementCreationCallback callback, boolean remoteEvent) { internalNotifyElementUpdated(null, callback, remoteEvent); } private void internalNotifyElementUpdated(Element element, ElementCreationCallback callback, boolean remoteEvent) { elementsUpdatedCounter.incrementAndGet(); if (hasCacheEventListeners()) { for (ListenerWrapper listenerWrapper : cacheEventListeners) { if (listenerWrapper.getScope().shouldDeliver(remoteEvent) && !isCircularNotification(remoteEvent, listenerWrapper.getListener())) { CacheEventListener listener = listenerWrapper.getListener(); invokeListener(listener, element, callback, Event.UPDATED); } } } } /** * Notifies all registered listeners, in no guaranteed order, that an element has expired * * @param element the Element to perform the notification on * @param remoteEvent whether the event came from a remote cache peer * @see CacheEventListener#notifyElementExpired */ public final void notifyElementExpiry(Element element, boolean remoteEvent) { internalNotifyElementExpiry(element, null, remoteEvent); } /** * Notifies all registered listeners, in no guaranteed order, that an element has expired * * @param callback * @param remoteEvent whether the event came from a remote cache peer * @see CacheEventListener#notifyElementExpired */ public final void notifyElementExpiry(ElementCreationCallback callback, boolean remoteEvent) { internalNotifyElementExpiry(null, callback, remoteEvent); } private void internalNotifyElementExpiry(Element element, ElementCreationCallback callback, boolean remoteEvent) { elementsExpiredCounter.incrementAndGet(); if (hasCacheEventListeners()) { for (ListenerWrapper listenerWrapper : cacheEventListeners) { if (listenerWrapper.getScope().shouldDeliver(remoteEvent) && !isCircularNotification(remoteEvent, listenerWrapper.getListener())) { CacheEventListener listener = listenerWrapper.getListener(); invokeListener(listener, element, callback, Event.EXPIRY); } } } } /** * Returns whether or not at least one cache event listeners has been registered. * * @return true if a one or more listeners have registered, otherwise false */ public final boolean hasCacheEventListeners() { return cacheEventListeners.size() > 0; } /** * Notifies all registered listeners, in no guaranteed order, that an element has been * evicted from the cache * * @param element the Element to perform the notification on * @param remoteEvent whether the event came from a remote cache peer * @see CacheEventListener#notifyElementEvicted */ public final void notifyElementEvicted(Element element, boolean remoteEvent) { internalNotifyElementEvicted(element, null, remoteEvent); } /** * Notifies all registered listeners, in no guaranteed order, that an element has been * evicted from the cache * * @param callback * @param remoteEvent whether the event came from a remote cache peer * @see CacheEventListener#notifyElementEvicted */ public final void notifyElementEvicted(ElementCreationCallback callback, boolean remoteEvent) { internalNotifyElementEvicted(null, callback, remoteEvent); } private void internalNotifyElementEvicted(Element element, ElementCreationCallback callback, boolean remoteEvent) { if (cache.getCacheConfiguration().getPinningConfiguration() == null) { elementsEvictedCounter.incrementAndGet(); if (hasCacheEventListeners()) { for (ListenerWrapper listenerWrapper : cacheEventListeners) { if (listenerWrapper.getScope().shouldDeliver(remoteEvent) && !isCircularNotification(remoteEvent, listenerWrapper.getListener())) { CacheEventListener listener = listenerWrapper.getListener(); invokeListener(listener, element, callback, Event.EVICTED); } } } } } private void invokeListener(CacheEventListener listener, Element element, ElementCreationCallback callback, Event eventType) { final Element e; if (listener instanceof LiveCacheStatisticsData) { // These listeners don't touch the element and since this is an internal listener there might not be // an appropriate loader for resolving it e = null; } else if (callback != null) { e = callback.createElement(listener.getClass().getClassLoader()); } else { e = element; } notifyListener(listener, e, eventType); } private void notifyListener(CacheEventListener listener, Element element, Event eventType) { switch (eventType) { case EVICTED: { listener.notifyElementEvicted(cache, element); break; } case PUT: { listener.notifyElementPut(cache, element); break; } case EXPIRY: { listener.notifyElementExpired(cache, element); break; } case REMOVED: { listener.notifyElementRemoved(cache, element); break; } case UPDATED: { listener.notifyElementUpdated(cache, element); break; } default: { throw new AssertionError(eventType.toString()); } } } /** * Notifies all registered listeners, in no guaranteed order, that removeAll * has been called and all elements cleared * * @param remoteEvent whether the event came from a remote cache peer * @see CacheEventListener#notifyElementEvicted */ public final void notifyRemoveAll(boolean remoteEvent) { elementsRemoveAllCounter.incrementAndGet(); if (hasCacheEventListeners()) { for (ListenerWrapper listenerWrapper : cacheEventListeners) { if (listenerWrapper.getScope().shouldDeliver(remoteEvent) && !isCircularNotification(remoteEvent, listenerWrapper.getListener())) { listenerWrapper.getListener().notifyRemoveAll(cache); } } } } /** * CacheReplicators should not be notified of events received remotely, as this would cause * a circular notification * * @param remoteEvent * @param cacheEventListener * @return true is notifiying the listener would cause a circular notification */ private static boolean isCircularNotification(boolean remoteEvent, CacheEventListener cacheEventListener) { return remoteEvent && (cacheEventListener instanceof CacheReplicator || cacheEventListener instanceof TerracottaCacheEventReplication); } /** * Adds a listener to the notification service. No guarantee is made that listeners will be * notified in the order they were added. * * @param cacheEventListener * @return true if the listener is being added and was not already added */ public final boolean registerListener(CacheEventListener cacheEventListener) { return registerListener(cacheEventListener, NotificationScope.ALL); } /** * Adds a listener to the notification service. No guarantee is made that listeners will be * notified in the order they were added. *

* If a cache is configured in a cluster, listeners in each node will get triggered by an event * depending on the value of the

listenFor
parameter. * * @param cacheEventListener The listener to add * @param scope The notification scope * @return true if the listener is being added and was not already added * @since 2.0 */ public final boolean registerListener(CacheEventListener cacheEventListener, NotificationScope scope) { if (cacheEventListener == null) { return false; } boolean result = cacheEventListeners.add(new ListenerWrapper(cacheEventListener, scope)); if (result && cacheEventListener instanceof CacheReplicator) { this.hasReplicator.set(true); } return result; } /** * Removes a listener from the notification service. * * @param cacheEventListener * @return true if the listener was present */ public final boolean unregisterListener(CacheEventListener cacheEventListener) { boolean result = false; int cacheReplicators = 0; Iterator it = cacheEventListeners.iterator(); while (it.hasNext()) { ListenerWrapper listenerWrapper = it.next(); if (listenerWrapper.getListener().equals(cacheEventListener)) { cacheEventListeners.remove(listenerWrapper); result = true; } else { if (listenerWrapper.getListener() instanceof CacheReplicator) { cacheReplicators++; } } } if (cacheReplicators > 0) { hasReplicator.set(true); } else { hasReplicator.set(false); } return result; } /** * Determines whether any registered listeners are CacheReplicators. * * @return whether any registered listeners are CacheReplicators. */ public final boolean hasCacheReplicators() { return hasReplicator.get(); } /** * Gets a copy of the set of the listeners registered to this class * * @return a set of type CacheEventListener */ public final Set getCacheEventListeners() { Set listenerSet = new HashSet(); for (ListenerWrapper listenerWrapper : cacheEventListeners) { listenerSet.add(listenerWrapper.getListener()); } return listenerSet; } /** * Tell listeners to dispose themselves. * Because this method is only ever called from a synchronized cache method, it does not itself need to be * synchronized. */ public final void dispose() { for (ListenerWrapper listenerWrapper : cacheEventListeners) { listenerWrapper.getListener().dispose(); } cacheEventListeners.clear(); } /** * Returns a string representation of the object. In general, the * toString method returns a string that * "textually represents" this object. The result should * be a concise but informative representation that is easy for a * person to read. * * @return a string representation of the object. */ @Override public final String toString() { StringBuilder sb = new StringBuilder(" cacheEventListeners: "); for (ListenerWrapper listenerWrapper : cacheEventListeners) { sb.append(listenerWrapper.getListener().getClass().getName()).append(" "); } return sb.toString(); } /** * Clears all event counters */ public void clearCounters() { elementsRemovedCounter.set(0); elementsPutCounter.set(0); elementsUpdatedCounter.set(0); elementsExpiredCounter.set(0); elementsEvictedCounter.set(0); elementsRemoveAllCounter.set(0); } /** * Gets the number of events, irrespective of whether there are any registered listeners. * * @return the number of events since cache creation or last clearing of counters */ public long getElementsRemovedCounter() { return elementsRemovedCounter.get(); } /** * Gets the number of events, irrespective of whether there are any registered listeners. * * @return the number of events since cache creation or last clearing of counters */ public long getElementsPutCounter() { return elementsPutCounter.get(); } /** * Gets the number of events, irrespective of whether there are any registered listeners. * * @return the number of events since cache creation or last clearing of counters */ public long getElementsUpdatedCounter() { return elementsUpdatedCounter.get(); } /** * Gets the number of events, irrespective of whether there are any registered listeners. * * @return the number of events since cache creation or last clearing of counters */ public long getElementsExpiredCounter() { return elementsExpiredCounter.get(); } /** * Gets the number of events, irrespective of whether there are any registered listeners. * * @return the number of events since cache creation or last clearing of counters */ public long getElementsEvictedCounter() { return elementsEvictedCounter.get(); } /** * Gets the number of events, irrespective of whether there are any registered listeners. * * @return the number of events since cache creation or last clearing of counters */ public long getElementsRemoveAllCounter() { return elementsRemoveAllCounter.get(); } /** * Returns the {@link FrontEndCacheTier} this RegisteredEventListeners is backing, or null if the cache isn't backed by one * @return the Cache's FrontEndCacheTier */ public FrontEndCacheTier getFrontEndCacheTier() { Store store; try { store = helper.getStore(); } catch (IllegalStateException e) { return null; } return store instanceof FrontEndCacheTier ? (FrontEndCacheTier) store : null; } /** * Combine a Listener and its NotificationScope. Equality and hashcode are based purely on the listener. * This implies that the same listener cannot be added to the set of registered listeners more than * once with different notification scopes. * * @author Alex Miller */ private static final class ListenerWrapper { private final CacheEventListener listener; private final NotificationScope scope; private ListenerWrapper(CacheEventListener listener, NotificationScope scope) { this.listener = listener; this.scope = scope; } private CacheEventListener getListener() { return this.listener; } private NotificationScope getScope() { return this.scope; } /** * Hash code based on listener * * @see java.lang.Object#hashCode() */ @Override public int hashCode() { return listener.hashCode(); } /** * Equals based on listener (NOT based on scope) - can't have same listener with two different scopes * * @see java.lang.Object#equals(java.lang.Object) */ @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (obj == null) { return false; } if (getClass() != obj.getClass()) { return false; } ListenerWrapper other = (ListenerWrapper) obj; if (listener == null) { if (other.listener != null) { return false; } } else if (!listener.equals(other.listener)) { return false; } return true; } @Override public String toString() { return listener.toString(); } } /** * Callback interface for creating elements to pass to registered listeners. * For replicated/clustered caches the event notification thread that receives * events from the network might not have the correct context for resolving * cache values * * @author teck * */ public interface ElementCreationCallback { /** * Materialize the relevant element in the given classloader * * @param loader * @return element */ Element createElement(ClassLoader loader); } /** * Event callback types */ private static enum Event { EVICTED, PUT, EXPIRY, UPDATED, REMOVED; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy