package org.infinispan.query.continuous.impl;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.infinispan.Cache;
import org.infinispan.notifications.Listener;
import org.infinispan.notifications.cachelistener.annotation.CacheEntryCreated;
import org.infinispan.notifications.cachelistener.annotation.CacheEntryExpired;
import org.infinispan.notifications.cachelistener.annotation.CacheEntryModified;
import org.infinispan.notifications.cachelistener.annotation.CacheEntryRemoved;
import org.infinispan.notifications.cachelistener.event.CacheEntryEvent;
import org.infinispan.objectfilter.impl.ReflectionMatcher;
import org.infinispan.query.api.continuous.ContinuousQuery;
import org.infinispan.query.api.continuous.ContinuousQueryListener;
import org.infinispan.query.dsl.Query;
import org.infinispan.query.dsl.impl.BaseQuery;
/**
* A container of continuous query listeners for a cache.
* <p>This class is not threadsafe.
*
* @author anistor@redhat.com
* @since 8.2
*/
public final class ContinuousQueryImpl<K, V> implements ContinuousQuery<K, V> {
private final Cache<K, V> cache;
private final List<EntryListener<K, V, ?>> listeners = new ArrayList<>();
public ContinuousQueryImpl(Cache<K, V> cache) {
if (cache == null) {
throw new IllegalArgumentException("cache parameter cannot be null");
}
this.cache = cache;
}
@Override
public <C> void addContinuousQueryListener(String queryString, ContinuousQueryListener<K, C> listener) {
addContinuousQueryListener(queryString, null, listener);
}
@Override
public <C> void addContinuousQueryListener(String queryString, Map<String, Object> namedParameters, ContinuousQueryListener<K, C> listener) {
EntryListener<K, V, C> entryListener = new EntryListener<>(listener);
IckleContinuousQueryCacheEventFilterConverter<K, V, ContinuousQueryResult<V>> filterConverter
= new IckleContinuousQueryCacheEventFilterConverter<>(queryString, namedParameters, ReflectionMatcher.class);
cache.addListener(entryListener, filterConverter, null);
listeners.add(entryListener);
}
@Override
public <C> void addContinuousQueryListener(Query query, ContinuousQueryListener<K, C> listener) {
BaseQuery baseQuery = (BaseQuery) query;
addContinuousQueryListener(baseQuery.getQueryString(), baseQuery.getParameters(), listener);
}
@Override
public void removeContinuousQueryListener(ContinuousQueryListener<K, ?> listener) {
for (Iterator<EntryListener<K, V, ?>> it = listeners.iterator(); it.hasNext(); ) {
EntryListener<K, V, ?> l = it.next();
if (l.listener == listener) {
cache.removeListener(l);
it.remove();
break;
}
}
}
@Override
public List<ContinuousQueryListener<K, ?>> getListeners() {
List<ContinuousQueryListener<K, ?>> queryListeners = new ArrayList<>(listeners.size());
for (EntryListener<K, V, ?> l : listeners) {
queryListeners.add(l.listener);
}
return queryListeners;
}
@Override
public void removeAllListeners() {
for (EntryListener<K, V, ?> l : listeners) {
cache.removeListener(l);
}
listeners.clear();
}
@Listener(clustered = true, includeCurrentState = true, observation = Listener.Observation.POST)
private static final class EntryListener<K, V, C> {
private final ContinuousQueryListener<K, C> listener;
EntryListener(ContinuousQueryListener<K, C> listener) {
this.listener = listener;
}
@CacheEntryRemoved
@CacheEntryCreated
@CacheEntryModified
@CacheEntryExpired
public void handleEvent(CacheEntryEvent<K, ContinuousQueryResult<V>> event) {
ContinuousQueryResult<V> cqr = event.getValue();
switch (cqr.getResultType()) {
case JOINING: {
C value = cqr.getValue() != null ? (C) cqr.getValue() : (C) cqr.getProjection();
listener.resultJoining(event.getKey(), value);
break;
}
case UPDATED: {
C value = cqr.getValue() != null ? (C) cqr.getValue() : (C) cqr.getProjection();
listener.resultUpdated(event.getKey(), value);
break;
}
case LEAVING: {
listener.resultLeaving(event.getKey());
break;
}
default:
throw new IllegalStateException("Unexpected result type : " + cqr.getResultType());
}
}
}
}