org.infinispan.jcache.AbstractJCacheNotifier Maven / Gradle / Ivy
package org.infinispan.jcache;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Queue;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.CountDownLatch;
import javax.cache.Cache;
import javax.cache.configuration.CacheEntryListenerConfiguration;
import javax.cache.configuration.Factory;
import javax.cache.event.CacheEntryCreatedListener;
import javax.cache.event.CacheEntryEvent;
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 org.infinispan.commons.logging.LogFactory;
import org.infinispan.commons.util.CollectionFactory;
import org.infinispan.jcache.logging.Log;
/**
* JCache notifications dispatcher.
*
* TODO: Deal with asynchronous listeners...
*
* @author Galder Zamarreño
* @since 5.3
*/
public abstract class AbstractJCacheNotifier {
private static final Log log =
LogFactory.getLog(AbstractJCacheNotifier.class, Log.class);
private static final boolean isTrace = log.isTraceEnabled();
// Traversals are a not more common than mutations when it comes to
// keeping track of registered listeners, so use copy-on-write lists.
private final List> createdListeners =
new CopyOnWriteArrayList>();
private final List> updatedListeners =
new CopyOnWriteArrayList>();
private final List> removedListeners =
new CopyOnWriteArrayList>();
private final List> expiredListeners =
new CopyOnWriteArrayList>();
private final ConcurrentMap, CacheEntryListenerConfiguration> listenerCfgs =
CollectionFactory.makeConcurrentMap();
private AbstractJCacheListenerAdapter listenerAdapter;
private ConcurrentMap, Queue> latchesByEventSource = new ConcurrentHashMap<>();
public void addListener(CacheEntryListenerConfiguration reg,
AbstractJCache jcache, AbstractJCacheNotifier notifier) {
boolean addListenerAdapter = listenerCfgs.isEmpty();
addListener(reg, false);
if (addListenerAdapter) {
listenerAdapter = createListenerAdapter(jcache, notifier);
jcache.addListener(listenerAdapter);
}
}
public void removeListener(CacheEntryListenerConfiguration reg,
AbstractJCache jcache) {
removeListener(reg);
if (listenerCfgs.isEmpty())
jcache.removeListener(listenerAdapter);
}
public void addSyncNotificationLatch(Cache cache, K key, V value, CountDownLatch latch) {
EventSource eventSourceKey = new EventSource(cache, key, value);
latchesByEventSource.computeIfAbsent(eventSourceKey, kvEventSource -> new ConcurrentLinkedQueue<>()).add(latch);
}
public void removeSyncNotificationLatch(Cache cache, K key, V value, CountDownLatch latch) {
EventSource eventSourceKey = new EventSource(cache, key, value);
Queue latches = latchesByEventSource.get(eventSourceKey);
if (latches == null) {
return;
}
latchesByEventSource.compute(eventSourceKey, (kvEventSource, countDownLatches) -> {
countDownLatches.remove(latch);
return countDownLatches.isEmpty() ? null : countDownLatches;
});
}
private void notifySync(Cache cache, K key, V value) {
EventSource eventSourceKey = new EventSource(cache, key, value);
notifySync(latchesByEventSource.get(eventSourceKey));
}
private void notifySync(Queue latches) {
if (latches == null) {
return;
}
CountDownLatch latch = latches.poll();
if (latch != null) {
latch.countDown();
}
}
public void notifyEntryCreated(Cache cache, K key, V value) {
try {
if (!createdListeners.isEmpty()) {
List> events =
createEvent(cache, key, value, EventType.CREATED);
for (CacheEntryCreatedListener listener : createdListeners)
listener.onCreated(getEntryIterable(events, listenerCfgs.get(listener)));
}
} finally {
notifySync(cache, key, value);
}
}
public void notifyEntryUpdated(Cache cache, K key, V value) {
try {
if (!updatedListeners.isEmpty()) {
List> events =
createEvent(cache, key, value, EventType.UPDATED);
for (CacheEntryUpdatedListener listener : updatedListeners)
listener.onUpdated(getEntryIterable(events, listenerCfgs.get(listener)));
}
} finally {
notifySync(cache, key, value);
}
}
public void notifyEntryRemoved(Cache cache, K key, V value) {
try {
if (!removedListeners.isEmpty()) {
List> events =
createEvent(cache, key, value, EventType.REMOVED);
for (CacheEntryRemovedListener listener : removedListeners) {
listener.onRemoved(getEntryIterable(events, listenerCfgs.get(listener)));
}
}
} finally {
notifySync(cache, key, null);
}
}
public void notifyEntryExpired(Cache cache, K key, V value) {
if (!expiredListeners.isEmpty()) {
List> events =
createEvent(cache, key, value, EventType.EXPIRED);
for (CacheEntryExpiredListener listener : expiredListeners) {
listener.onExpired(getEntryIterable(events, listenerCfgs.get(listener)));
}
}
}
public boolean hasSyncCreatedListener() {
return hasSyncListener(CacheEntryCreatedListener.class);
}
public boolean hasSyncRemovedListener() {
return hasSyncListener(CacheEntryRemovedListener.class);
}
public boolean hasSyncUpdatedListener() {
return hasSyncListener(CacheEntryUpdatedListener.class);
}
private boolean hasSyncListener(Class> listenerClass) {
for (Map.Entry, CacheEntryListenerConfiguration> entry : listenerCfgs.entrySet()) {
if (entry.getValue().isSynchronous() && listenerClass.isInstance(entry.getKey())) {
return true;
}
}
return false;
}
private Iterable> getEntryIterable(
List> events,
CacheEntryListenerConfiguration listenerCfg) {
Factory> factory = listenerCfg.getCacheEntryEventFilterFactory();
if (factory != null) {
CacheEntryEventFilter super K, ? super V> filter = factory.create();
return filter == null ? events
: new JCacheEventFilteringIterable(events, filter);
}
return events;
}
@SuppressWarnings("unchecked")
private boolean addListener(CacheEntryListenerConfiguration listenerCfg, boolean addIfAbsent) {
boolean added = false;
CacheEntryListener super K, ? super V> listener =
listenerCfg.getCacheEntryListenerFactory().create();
if (listener instanceof CacheEntryCreatedListener)
added = !containsListener(addIfAbsent, listener, createdListeners)
&& createdListeners.add((CacheEntryCreatedListener) listener);
if (listener instanceof CacheEntryUpdatedListener)
added = !containsListener(addIfAbsent, listener, updatedListeners)
&& updatedListeners.add((CacheEntryUpdatedListener) listener);
if (listener instanceof CacheEntryRemovedListener)
added = !containsListener(addIfAbsent, listener, removedListeners)
&& removedListeners.add((CacheEntryRemovedListener) listener);
if (listener instanceof CacheEntryExpiredListener)
added = !containsListener(addIfAbsent, listener, expiredListeners)
&& expiredListeners.add((CacheEntryExpiredListener) listener);
if (added)
listenerCfgs.put(listener, listenerCfg);
return added;
}
private boolean containsListener(boolean addIfAbsent,
CacheEntryListener super K, ? super V> listenerToAdd,
List extends CacheEntryListener super K, ? super V>> listeners) {
// If add only if no listener present, check the listeners collection
if (addIfAbsent) {
for (CacheEntryListener super K, ? super V> listener : listeners) {
if (listener.equals(listenerToAdd))
return true;
}
}
return false;
}
private void removeListener(CacheEntryListenerConfiguration listenerCfg) {
for (Map.Entry, CacheEntryListenerConfiguration> entry : listenerCfgs.entrySet()) {
CacheEntryListenerConfiguration cfg = entry.getValue();
if (cfg.equals(listenerCfg)) {
CacheEntryListener super K, ? super V> listener = entry.getKey();
if (listener instanceof CacheEntryCreatedListener)
createdListeners.remove(listener);
if (listener instanceof CacheEntryUpdatedListener)
updatedListeners.remove(listener);
if (listener instanceof CacheEntryRemovedListener)
removedListeners.remove(listener);
if (listener instanceof CacheEntryExpiredListener)
expiredListeners.remove(listener);
}
}
}
private List> createEvent(
Cache cache, K key, V value, EventType eventType) {
List> events =
Collections.>singletonList(
new RICacheEntryEvent(cache, key, value, eventType));
if (isTrace) log.tracef("Received event: %s", events);
return events;
}
protected abstract AbstractJCacheListenerAdapter createListenerAdapter(AbstractJCache jcache, AbstractJCacheNotifier notifier);
private static class EventSource {
private final Cache cache;
private final K key;
private final V value;
public EventSource(final Cache cache, final K key, final V value) {
this.cache = cache;
this.key = key;
this.value = value;
}
@Override
public boolean equals(Object obj) {
if (!(obj instanceof EventSource)) {
return false;
}
EventSource, ?> otherEventSource = (EventSource, ?>) obj;
return equalOrNull(cache, otherEventSource.cache)
&& equalOrNull(key, otherEventSource.key)
&& equalOrNull(value, otherEventSource.value);
}
private static boolean equalOrNull(Object obj1, Object obj2) {
return ((obj1 == null) && (obj2 == null)) || ((obj1 != null) && obj1.equals(obj2));
}
@Override
public int hashCode() {
int result = 1;
result = 31 * result + (cache == null ? 0 : cache.hashCode());
result = 31 * result + (key == null ? 0 : key.hashCode());
result = 31 * result + (value == null ? 0 : value.hashCode());
return result;
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy