package org.cache2k.jcache.provider.event; /* * #%L * cache2k JCache provider * %% * Copyright (C) 2000 - 2017 headissue GmbH, Munich * %% * 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. * #L% */ import org.cache2k.Cache; import org.cache2k.CacheEntry; import org.cache2k.configuration.Cache2kConfiguration; import org.cache2k.configuration.CustomizationSupplier; import org.cache2k.configuration.CustomizationReferenceSupplier; import org.cache2k.event.CacheEntryOperationListener; import org.cache2k.jcache.provider.JCacheManagerAdapter; import javax.cache.configuration.CacheEntryListenerConfiguration; import javax.cache.configuration.Factory; import javax.cache.event.CacheEntryCreatedListener; import javax.cache.event.CacheEntryEventFilter; import javax.cache.event.CacheEntryExpiredListener; import javax.cache.event.CacheEntryListener; import javax.cache.event.CacheEntryRemovedListener; import javax.cache.event.CacheEntryUpdatedListener; import javax.cache.event.EventType; import java.util.ArrayList; import java.util.Collection; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Set; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.Executor; /** * cache2k does not support changing the listener configuration at runtime. Registers one * listener for each event type to cache2k and delivers them to the JCache listeners. * Synchronous events are delivered sequentially. Asynchronous events are delivered by an executor * and get maximum parallelism. * * @see AsyncDispatcher * @param <K> key type * @param <V> value type * * @author Jens Wilke */ public class EventHandling<K,V> { private javax.cache.Cache resolvedJCache; private final List<Listener.Created<K,V>> createdListener = new CopyOnWriteArrayList<Listener.Created<K, V>>(); private final List<Listener.Updated<K,V>> updatedListener = new CopyOnWriteArrayList<Listener.Updated<K, V>>(); private final List<Listener.Removed<K,V>> removedListener = new CopyOnWriteArrayList<Listener.Removed<K, V>>(); private final List<Listener.Expired<K,V>> expiredListener = new CopyOnWriteArrayList<Listener.Expired<K, V>>(); private final AsyncDispatcher<K,V> asyncDispatcher; private final JCacheManagerAdapter manager; public EventHandling(JCacheManagerAdapter m, Executor ex) { asyncDispatcher = new AsyncDispatcher<K, V>(ex); manager = m; } void addAsyncListener(Listener<K,V> l) { asyncDispatcher.addAsyncListener(l); } static <T extends Listener<K,V>, K, V> boolean removeCfgMatch( final CacheEntryListenerConfiguration<K,V> cfg, final List<T> _listenerList) { Iterator<T> it = _listenerList.iterator(); while (it.hasNext()) { Listener<K,V> l = it.next(); if (l.config.equals(cfg)) { _listenerList.remove(l); removeCfgMatch(cfg, _listenerList); return true; } } return false; } public boolean deregisterListener(CacheEntryListenerConfiguration<K,V> cfg) { boolean _found = false; _found |= removeCfgMatch(cfg, createdListener); _found |= removeCfgMatch(cfg, updatedListener); _found |= removeCfgMatch(cfg, removedListener); _found |= removeCfgMatch(cfg, expiredListener); _found |= asyncDispatcher.removeAsyncListener(cfg); return _found; } public Collection<CacheEntryListenerConfiguration<K,V>> getAllListenerConfigurations() { Collection<Listener<K,V>> l = new ArrayList<Listener<K,V>>(); l.addAll(createdListener); l.addAll(updatedListener); l.addAll(removedListener); l.addAll(expiredListener); asyncDispatcher.collectListeners(l); Set<CacheEntryListenerConfiguration<K,V>> _cfgs = new HashSet<CacheEntryListenerConfiguration<K, V>>(); for (Listener<K,V> li : l) { _cfgs.add(li.config); } return _cfgs; } @SuppressWarnings("unchecked") public void registerListener(CacheEntryListenerConfiguration<K,V> cfg) { synchronized (asyncDispatcher) { if (getAllListenerConfigurations().contains(cfg)) { throw new IllegalArgumentException("configuration already registered"); } } Factory<CacheEntryEventFilter<? super K,? super V>> _filterFactory = cfg.getCacheEntryEventFilterFactory(); Factory<CacheEntryListener<? super K,? super V>> _listenerFactory = cfg.getCacheEntryListenerFactory(); if (_listenerFactory == null) { throw new IllegalArgumentException("listener factory missing"); } CacheEntryEventFilter<K, V> _filter = null; if (_filterFactory != null) { _filter = (CacheEntryEventFilter<K, V>) _filterFactory.create(); } Object _listener = _listenerFactory.create(); boolean _synchronous = cfg.isSynchronous(); if (_listener instanceof CacheEntryCreatedListener) { Listener.Created<K,V> l = new Listener.Created<K, V>(cfg, _filter, (CacheEntryCreatedListener<K,V>) _listener); if (_synchronous) { createdListener.add(l); } else { addAsyncListener(l); } } if (_listener instanceof CacheEntryUpdatedListener) { Listener.Updated<K,V> l = new Listener.Updated<K, V>(cfg, _filter, (CacheEntryUpdatedListener<K,V>) _listener); if (_synchronous) { updatedListener.add(l); } else { addAsyncListener(l); } } if (_listener instanceof CacheEntryRemovedListener) { Listener.Removed<K,V> l = new Listener.Removed<K, V>(cfg, _filter, (CacheEntryRemovedListener<K,V>) _listener); if (_synchronous) { removedListener.add(l); } else { addAsyncListener(l); } } if (_listener instanceof CacheEntryExpiredListener) { Listener.Expired<K,V> l = new Listener.Expired<K, V>(cfg, _filter, (CacheEntryExpiredListener<K,V>) _listener); if (_synchronous) { expiredListener.add(l); } else { addAsyncListener(l); } } } public void registerCache2kListeners(Cache2kConfiguration<K,V> cfg) { Collection<CustomizationSupplier<CacheEntryOperationListener<K,V>>> _listeners = cfg.getListeners(); _listeners.add(new CustomizationReferenceSupplier<CacheEntryOperationListener<K, V>>(new CreatedListenerAdapter())); _listeners.add(new CustomizationReferenceSupplier<CacheEntryOperationListener<K, V>>(new UpdatedListenerAdapter())); _listeners.add(new CustomizationReferenceSupplier<CacheEntryOperationListener<K, V>>(new RemovedListenerAdapter())); _listeners.add(new CustomizationReferenceSupplier<CacheEntryOperationListener<K, V>>(new ExpiredListenerAdapter())); } private V extractValue(V _value) { return _value; } @SuppressWarnings("unchecked") class CreatedListenerAdapter implements org.cache2k.event.CacheEntryCreatedListener<K, V> { @Override public void onEntryCreated( final org.cache2k.Cache<K, V> c, final CacheEntry<K, V> e) { if (e.getException() != null) { return; } javax.cache.Cache<K,V> _jCache = getCache(c); fireCreated(_jCache, e); } } private void fireCreated(final javax.cache.Cache<K, V> _jCache, final CacheEntry<K, V> e) { EntryEvent<K, V> cee = new EntryEvent<K, V>(_jCache, EventType.CREATED, e.getKey(), extractValue(e.getValue())); asyncDispatcher.deliverAsyncEvent(cee); for (Listener<K,V> t : createdListener) { t.fire(cee); } } @SuppressWarnings("unchecked") class UpdatedListenerAdapter implements org.cache2k.event.CacheEntryUpdatedListener<K, V> { @Override public void onEntryUpdated(final Cache<K, V> c, final CacheEntry<K, V> _currentEntry, final CacheEntry<K, V> entryWithNewData) { javax.cache.Cache<K,V> _jCache = getCache(c); if (entryWithNewData.getException() != null) { if (_currentEntry.getException() != null) { return; } EntryEvent<K, V> cee = new EntryEvent<K, V>(_jCache, EventType.REMOVED, entryWithNewData.getKey(), extractValue(_currentEntry.getValue())); asyncDispatcher.deliverAsyncEvent(cee); for (Listener<K,V> t : removedListener) { t.fire(cee); } return; } if (_currentEntry.getException() != null) { fireCreated(_jCache, entryWithNewData); return; } V v0 = _currentEntry.getValue(); V v1 = entryWithNewData.getValue(); EntryEvent<K, V> cee = new EntryEventWithOldValue<K, V>(_jCache, EventType.UPDATED, entryWithNewData.getKey(), extractValue(v1), extractValue(v0)); asyncDispatcher.deliverAsyncEvent(cee); for (Listener<K,V> t : updatedListener) { t.fire(cee); } } } private javax.cache.Cache getCache(final Cache<K, V> c) { if (resolvedJCache != null) { return resolvedJCache; } return resolvedJCache = manager.resolveCacheWrapper(c); } @SuppressWarnings("unchecked") class RemovedListenerAdapter implements org.cache2k.event.CacheEntryRemovedListener<K, V> { @Override public void onEntryRemoved( final org.cache2k.Cache<K, V> c, final CacheEntry<K, V> e) { if (e.getException() != null) { return; } javax.cache.Cache<K,V> _jCache = getCache(c); EntryEvent<K, V> cee = new EntryEvent<K, V>(_jCache, EventType.REMOVED, e.getKey(), extractValue(e.getValue())); asyncDispatcher.deliverAsyncEvent(cee); for (Listener<K,V> t : removedListener) { t.fire(cee); } } } @SuppressWarnings("unchecked") class ExpiredListenerAdapter implements org.cache2k.event.CacheEntryExpiredListener<K, V> { @Override public void onEntryExpired( final org.cache2k.Cache<K, V> c, final CacheEntry<K, V> e) { if (e.getException() != null) { return; } javax.cache.Cache<K,V> _jCache = getCache(c); EntryEvent<K, V> cee = new EntryEvent<K, V>(_jCache, EventType.EXPIRED, e.getKey(), extractValue(e.getValue())); asyncDispatcher.deliverAsyncEvent(cee); for (Listener<K,V> t : expiredListener) { t.fire(cee); } } } }