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

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

Go to download

Ehcache is an open source, standards-based cache used to boost performance, offload the database and simplify scalability. Ehcache is robust, proven and full-featured and this has made it the most widely-used Java-based cache.

There is a newer version: 2.10.9.2
Show newest version
/**
 *  Copyright 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.CacheOperationOutcomes;
import net.sf.ehcache.CacheOperationOutcomes.ExpiredOutcome;
import net.sf.ehcache.CacheStoreHelper;
import net.sf.ehcache.Ehcache;
import net.sf.ehcache.Element;
import net.sf.ehcache.Status;
import net.sf.ehcache.distribution.CacheReplicator;
import net.sf.ehcache.store.TerracottaStore;
import org.terracotta.statistics.StatisticsManager;
import org.terracotta.statistics.observer.OperationObserver;

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

import static net.sf.ehcache.statistics.StatisticBuilder.operation;

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

* There is one of these per Cache. * * @author Greg Luck * @author Geert Bevin * @version $Id: RegisteredEventListeners.java 10789 2018-04-26 02:08:13Z adahanne $ */ 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 Set orderedListeners = new CopyOnWriteArraySet(); private final Ehcache cache; private final AtomicBoolean hasReplicator = new AtomicBoolean(false); private final CacheStoreHelper helper; private final OperationObserver expiryObserver = operation(ExpiredOutcome.class).named("expiry") .of(this).tag("cache").build(); /** * Constructs a new notification service * * @param cache */ public RegisteredEventListeners(Cache cache) { this(cache, new CacheStoreHelper(cache)); } /** * Construct a registered event listeners service * * @param cache the {@link Cache} * @param helper helper for getting the {@link net.sf.ehcache.store.Store} out of the {@link Cache} */ public RegisteredEventListeners(Ehcache cache, CacheStoreHelper helper) { //XXX this isn't really very nice StatisticsManager.associate(this).withParent(cache); this.cache = cache; this.helper = helper; } /** * Notifies {@link InternalCacheEventListener}s, when an update happens * @param oldElement the old element * @param newElement the new element */ public final void notifyElementUpdatedOrdered(Element oldElement, Element newElement) { if (!orderedListeners.isEmpty()) { for (InternalCacheEventListener listener : orderedListeners) { listener.notifyElementRemoved(cache, oldElement); listener.notifyElementPut(cache, newElement); } } } /** * Notifies {@link InternalCacheEventListener}s, when a remove happens * @param element the element removes */ public final void notifyElementRemovedOrdered(Element element) { if (!orderedListeners.isEmpty()) { for (InternalCacheEventListener listener : orderedListeners) { listener.notifyElementRemoved(cache, element); } } } /** * Notifies {@link InternalCacheEventListener}s, when a put happens * @param element the element put */ public final void notifyElementPutOrdered(Element element) { if (!orderedListeners.isEmpty()) { for (InternalCacheEventListener listener : orderedListeners) { listener.notifyElementPut(cache, element); } } } /** * 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); } void internalNotifyElementRemoved(Element element, ElementCreationCallback callback, boolean remoteEvent) { if (hasCacheEventListeners()) { for (ListenerWrapper listenerWrapper : cacheEventListeners) { if (listenerWrapper.getScope().shouldDeliver(remoteEvent) && !isCircularNotification(remoteEvent, listenerWrapper.getListener())) { CacheEventListener listener = listenerWrapper.getListener(); listener.notifyElementRemoved(cache, resolveElement(listener, element, callback)); } } } } /** * 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); } void internalNotifyElementPut(Element element, ElementCreationCallback callback, boolean remoteEvent) { if (hasCacheEventListeners()) { for (ListenerWrapper listenerWrapper : cacheEventListeners) { if (listenerWrapper.getScope().shouldDeliver(remoteEvent) && !isCircularNotification(remoteEvent, listenerWrapper.getListener())) { CacheEventListener listener = listenerWrapper.getListener(); listener.notifyElementPut(cache, resolveElement(listener, element, callback)); } } } } /** * 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); } void internalNotifyElementUpdated(Element element, ElementCreationCallback callback, boolean remoteEvent) { if (hasCacheEventListeners()) { for (ListenerWrapper listenerWrapper : cacheEventListeners) { if (listenerWrapper.getScope().shouldDeliver(remoteEvent) && !isCircularNotification(remoteEvent, listenerWrapper.getListener())) { CacheEventListener listener = listenerWrapper.getListener(); listener.notifyElementUpdated(cache, resolveElement(listener, element, callback)); } } } } /** * 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); } void internalNotifyElementExpiry(Element element, ElementCreationCallback callback, boolean remoteEvent) { if (!remoteEvent) { expiryObserver.begin(); expiryObserver.end(ExpiredOutcome.SUCCESS); } if (hasCacheEventListeners()) { for (ListenerWrapper listenerWrapper : cacheEventListeners) { if (listenerWrapper.getScope().shouldDeliver(remoteEvent) && !isCircularNotification(remoteEvent, listenerWrapper.getListener())) { CacheEventListener listener = listenerWrapper.getListener(); listener.notifyElementExpired(cache, resolveElement(listener, element, callback)); } } } } /** * 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.isEmpty(); } /** * 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); } void internalNotifyElementEvicted(Element element, ElementCreationCallback callback, boolean remoteEvent) { if (hasCacheEventListeners()) { for (ListenerWrapper listenerWrapper : cacheEventListeners) { if (listenerWrapper.getScope().shouldDeliver(remoteEvent) && !isCircularNotification(remoteEvent, listenerWrapper.getListener())) { CacheEventListener listener = listenerWrapper.getListener(); listener.notifyElementEvicted(cache, resolveElement(listener, element, callback)); } } } } private Element resolveElement(final CacheEventListener listener, final Element element, final ElementCreationCallback callback) { if (callback != null) { return callback.createElement(listener.getClass().getClassLoader()); } else { return element; } } /** * 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) { 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); } if (result) { notifyEventListenersChangedIfNecessary(); } return result; } /** * Adds a listener to the notification service. No guarantee is made that listeners will be * notified in the order they were added. *

* * @param cacheEventListener The listener to add * @return true if the listener is being added and was not already added * @since 2.8 */ final boolean registerOrderedListener(InternalCacheEventListener cacheEventListener) { if (cacheEventListener == null) { return false; } return orderedListeners.add(cacheEventListener); } /** * 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); } if (result) { notifyEventListenersChangedIfNecessary(); } return result; } /** * Removes a listener from the notification service. * * @param cacheEventListener * @return true if the listener was present */ final boolean unregisterOrderedListener(InternalCacheEventListener cacheEventListener) { return orderedListeners.remove(cacheEventListener); } /** * 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(); notifyEventListenersChangedIfNecessary(); for (InternalCacheEventListener orderedListener : orderedListeners) { orderedListener.dispose(); } orderedListeners.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(" "); } sb.append("; orderedCacheEventListeners: "); for (InternalCacheEventListener orderedListener : orderedListeners) { sb.append(orderedListener.getClass().getName()).append(" "); } return sb.toString(); } /** * 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; } private void notifyEventListenersChangedIfNecessary() { if (cache.getStatus() == Status.STATUS_ALIVE && helper.getStore() instanceof TerracottaStore) { ((TerracottaStore)helper.getStore()).notifyCacheEventListenersChanged(); } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy