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

org.ehcache.impl.events.CacheEventDispatcherImpl Maven / Gradle / Ivy

There is a newer version: 3.10.8
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 org.ehcache.impl.events;

import org.ehcache.Cache;
import org.ehcache.core.CacheConfigurationChangeListener;
import org.ehcache.core.CacheConfigurationProperty;
import org.ehcache.core.events.CacheEventDispatcher;
import org.ehcache.core.events.CacheEvents;
import org.ehcache.core.internal.events.EventListenerWrapper;
import org.ehcache.event.CacheEvent;
import org.ehcache.event.CacheEventListener;
import org.ehcache.event.EventFiring;
import org.ehcache.event.EventOrdering;
import org.ehcache.event.EventType;
import org.ehcache.core.spi.store.events.StoreEvent;
import org.ehcache.core.spi.store.events.StoreEventListener;
import org.ehcache.core.spi.store.events.StoreEventSource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.ArrayList;
import java.util.EnumSet;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;

/**
 * Per-cache component that manages cache event listener registrations, and provides event delivery based on desired
 * firing mode for specified event types.
 * 

* Use of this class is linked to having cache events on a {@link org.ehcache.UserManagedCache user managed cache}. *

* Note on event ordering guarantees: Events are received and transmitted to register listeners through the * registration of a {@link StoreEventListener} on the linked {@link StoreEventSource} which is responsible for event * ordering. */ public class CacheEventDispatcherImpl implements CacheEventDispatcher { private static final Logger LOGGER = LoggerFactory.getLogger(CacheEventDispatcherImpl.class); private final ExecutorService unOrderedExectuor; private final ExecutorService orderedExecutor; private int listenersCount = 0; private int orderedListenerCount = 0; private final List> syncListenersList = new CopyOnWriteArrayList<>(); private final List> aSyncListenersList = new CopyOnWriteArrayList<>(); private final StoreEventListener eventListener = new StoreListener(); private volatile Cache listenerSource; private volatile StoreEventSource storeEventSource; /** * Creates a new {@link CacheEventDispatcher} instance that will use the provided {@link ExecutorService} to handle * events firing. * * @param unOrderedExecutor the executor service used when ordering is not required * @param orderedExecutor the executor service used when ordering is required */ public CacheEventDispatcherImpl(ExecutorService unOrderedExecutor, ExecutorService orderedExecutor) { this.unOrderedExectuor = unOrderedExecutor; this.orderedExecutor = orderedExecutor; } /** * {@inheritDoc} */ @Override public void registerCacheEventListener(CacheEventListener listener, EventOrdering ordering, EventFiring firing, EnumSet forEventTypes) { EventListenerWrapper wrapper = new EventListenerWrapper<>(listener, firing, ordering, forEventTypes); registerCacheEventListener(wrapper); } /** * Synchronized to make sure listener addition is atomic in order to prevent having the same listener registered * under multiple configurations * * @param wrapper the listener wrapper to register */ private synchronized void registerCacheEventListener(EventListenerWrapper wrapper) { if(aSyncListenersList.contains(wrapper) || syncListenersList.contains(wrapper)) { throw new IllegalStateException("Cache Event Listener already registered: " + wrapper.getListener()); } if (wrapper.isOrdered() && orderedListenerCount++ == 0) { storeEventSource.setEventOrdering(true); } switch (wrapper.getFiringMode()) { case ASYNCHRONOUS: aSyncListenersList.add(wrapper); break; case SYNCHRONOUS: syncListenersList.add(wrapper); break; default: throw new AssertionError("Unhandled EventFiring value: " + wrapper.getFiringMode()); } if (listenersCount++ == 0) { storeEventSource.addEventListener(eventListener); } } /** * {@inheritDoc} */ @Override public void deregisterCacheEventListener(CacheEventListener listener) { EventListenerWrapper wrapper = new EventListenerWrapper<>(listener); if (!removeWrapperFromList(wrapper, aSyncListenersList)) { if (!removeWrapperFromList(wrapper, syncListenersList)) { throw new IllegalStateException("Unknown cache event listener: " + listener); } } } /** * Synchronized to make sure listener removal is atomic * * @param wrapper the listener wrapper to unregister * @param listenersList the listener list to remove from */ private synchronized boolean removeWrapperFromList(EventListenerWrapper wrapper, List> listenersList) { int index = listenersList.indexOf(wrapper); if (index != -1) { EventListenerWrapper containedWrapper = listenersList.remove(index); if(containedWrapper.isOrdered() && --orderedListenerCount == 0) { storeEventSource.setEventOrdering(false); } if (--listenersCount == 0) { storeEventSource.removeEventListener(eventListener); } return true; } return false; } /** * {@inheritDoc} */ @Override public synchronized void shutdown() { storeEventSource.removeEventListener(eventListener); storeEventSource.setEventOrdering(false); syncListenersList.clear(); aSyncListenersList.clear(); unOrderedExectuor.shutdown(); orderedExecutor.shutdown(); } /** * {@inheritDoc} */ @Override public synchronized void setListenerSource(Cache source) { this.listenerSource = source; } void onEvent(CacheEvent event) { ExecutorService executor; if (storeEventSource.isEventOrdering()) { executor = orderedExecutor; } else { executor = unOrderedExectuor; } if (!aSyncListenersList.isEmpty()) { executor.submit(new EventDispatchTask<>(event, aSyncListenersList)); } if (!syncListenersList.isEmpty()) { Future future = executor.submit(new EventDispatchTask<>(event, syncListenersList)); try { future.get(); } catch (Exception e) { LOGGER.error("Exception received as result from synchronous listeners", e); } } } /** * {@inheritDoc} */ @Override @SuppressWarnings("unchecked") public List getConfigurationChangeListeners() { List configurationChangeListenerList = new ArrayList<>(); configurationChangeListenerList.add(event -> { if (event.getProperty().equals(CacheConfigurationProperty.ADD_LISTENER)) { registerCacheEventListener((EventListenerWrapper)event.getNewValue()); } else if (event.getProperty().equals(CacheConfigurationProperty.REMOVE_LISTENER)) { CacheEventListener oldListener = (CacheEventListener)event.getOldValue(); deregisterCacheEventListener(oldListener); } }); return configurationChangeListenerList; } private final class StoreListener implements StoreEventListener { @Override public void onEvent(StoreEvent event) { switch (event.getType()) { case CREATED: CacheEventDispatcherImpl.this.onEvent(CacheEvents.creation(event.getKey(), event.getNewValue(), listenerSource)); break; case UPDATED: CacheEventDispatcherImpl.this.onEvent(CacheEvents.update(event.getKey(), event.getOldValue(), event.getNewValue(), listenerSource)); break; case REMOVED: CacheEventDispatcherImpl.this.onEvent(CacheEvents.removal(event.getKey(), event.getOldValue(), listenerSource)); break; case EXPIRED: CacheEventDispatcherImpl.this.onEvent(CacheEvents.expiry(event.getKey(), event.getOldValue(), listenerSource)); break; case EVICTED: CacheEventDispatcherImpl.this.onEvent(CacheEvents.eviction(event.getKey(), event.getOldValue(), listenerSource)); break; default: throw new AssertionError("Unexpected StoreEvent value: " + event.getType()); } } } /** * {@inheritDoc} */ @Override public synchronized void setStoreEventSource(StoreEventSource eventSource) { this.storeEventSource = eventSource; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy