package org.infinispan.query.continuous.impl; import java.io.IOException; import java.io.ObjectInput; import java.io.ObjectOutput; import java.util.Collections; import java.util.HashMap; import java.util.Map; import java.util.Set; import org.infinispan.Cache; import org.infinispan.commons.CacheException; import org.infinispan.commons.io.UnsignedNumeric; import org.infinispan.commons.marshall.AbstractExternalizer; import org.infinispan.factories.ComponentRegistry; import org.infinispan.factories.annotations.Inject; import org.infinispan.metadata.Metadata; import org.infinispan.notifications.cachelistener.filter.AbstractCacheEventFilterConverter; import org.infinispan.notifications.cachelistener.filter.EventType; import org.infinispan.notifications.cachelistener.filter.IndexedFilter; import org.infinispan.objectfilter.Matcher; import org.infinispan.objectfilter.ObjectFilter; import org.infinispan.query.dsl.embedded.impl.QueryCache; import org.infinispan.query.impl.externalizers.ExternalizerIds; /** * @author anistor@redhat.com * @since 8.0 */ public class IckleContinuousQueryCacheEventFilterConverter<K, V, C> extends AbstractCacheEventFilterConverter<K, V, C> implements IndexedFilter<K, V, C> { /** * The JPA query to execute. */ protected final String queryString; protected final Map<String, Object> namedParameters; /** * The implementation class of the Matcher component to lookup and use. */ protected Class<? extends Matcher> matcherImplClass; /** * Optional cache for query objects. */ protected QueryCache queryCache; /** * The Matcher, acquired via dependency injection. */ protected Matcher matcher; /** * The ObjectFilter is created lazily. */ protected ObjectFilter objectFilter; public IckleContinuousQueryCacheEventFilterConverter(String queryString, Map<String, Object> namedParameters, Class<? extends Matcher> matcherImplClass) { if (queryString == null || matcherImplClass == null) { throw new IllegalArgumentException("Arguments cannot be null"); } this.queryString = queryString; this.namedParameters = namedParameters; this.matcherImplClass = matcherImplClass; } public Matcher getMatcher() { return matcher; } public String getQueryString() { return queryString; } public Map<String, Object> getNamedParameters() { return namedParameters; } /** * Acquires a Matcher instance from the ComponentRegistry of the given Cache object. */ @Inject protected void injectDependencies(Cache cache) { ComponentRegistry componentRegistry = cache.getAdvancedCache().getComponentRegistry(); queryCache = componentRegistry.getComponent(QueryCache.class); matcher = componentRegistry.getComponent(matcherImplClass); if (matcher == null) { throw new CacheException("Expected component not found in registry: " + matcherImplClass.getName()); } } protected ObjectFilter getObjectFilter() { if (objectFilter == null) { objectFilter = queryCache != null ? queryCache.get(queryString, null, matcherImplClass, (qs, accumulators) -> matcher.getObjectFilter(qs)) : matcher.getObjectFilter(queryString); } return namedParameters != null ? objectFilter.withParameters(namedParameters) : objectFilter; } @Override public C filterAndConvert(K key, V oldValue, Metadata oldMetadata, V newValue, Metadata newMetadata, EventType eventType) { if (eventType.isExpired()) { oldValue = newValue; // expired events have the expired value as newValue newValue = null; } ObjectFilter objectFilter = getObjectFilter(); ObjectFilter.FilterResult f1 = oldValue == null ? null : objectFilter.filter(oldValue); ObjectFilter.FilterResult f2 = newValue == null ? null : objectFilter.filter(newValue); if (f1 == null) { if (f2 != null) { // result joining return (C) new ContinuousQueryResult<>(ContinuousQueryResult.ResultType.JOINING, f2.getProjection() == null ? newValue : null, f2.getProjection()); } } else { if (f2 != null) { // result updated return (C) new ContinuousQueryResult<>(ContinuousQueryResult.ResultType.UPDATED, f2.getProjection() == null ? newValue : null, f2.getProjection()); } else { // result leaving return (C) new ContinuousQueryResult<V>(ContinuousQueryResult.ResultType.LEAVING, null, null); } } return null; } @Override public String toString() { return "IckleContinuousQueryCacheEventFilterConverter{queryString='" + queryString + "'}"; } public static final class Externalizer extends AbstractExternalizer<IckleContinuousQueryCacheEventFilterConverter> { @Override public void writeObject(ObjectOutput output, IckleContinuousQueryCacheEventFilterConverter filterAndConverter) throws IOException { output.writeUTF(filterAndConverter.queryString); Map<String, Object> namedParameters = filterAndConverter.namedParameters; if (namedParameters != null) { UnsignedNumeric.writeUnsignedInt(output, namedParameters.size()); for (Map.Entry<String, Object> e : namedParameters.entrySet()) { output.writeUTF(e.getKey()); output.writeObject(e.getValue()); } } else { UnsignedNumeric.writeUnsignedInt(output, 0); } output.writeObject(filterAndConverter.matcherImplClass); } @Override public IckleContinuousQueryCacheEventFilterConverter readObject(ObjectInput input) throws IOException, ClassNotFoundException { String queryString = input.readUTF(); int paramsSize = UnsignedNumeric.readUnsignedInt(input); Map<String, Object> namedParameters = null; if (paramsSize != 0) { namedParameters = new HashMap<>(paramsSize); for (int i = 0; i < paramsSize; i++) { String paramName = input.readUTF(); Object paramValue = input.readObject(); namedParameters.put(paramName, paramValue); } } Class<? extends Matcher> matcherImplClass = (Class<? extends Matcher>) input.readObject(); return new IckleContinuousQueryCacheEventFilterConverter(queryString, namedParameters, matcherImplClass); } @Override public Integer getId() { return ExternalizerIds.ICKLE_CONTINUOUS_QUERY_CACHE_EVENT_FILTER_CONVERTER; } @Override public Set<Class<? extends IckleContinuousQueryCacheEventFilterConverter>> getTypeClasses() { return Collections.singleton(IckleContinuousQueryCacheEventFilterConverter.class); } } }