package org.infinispan.server.hotrod;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionException;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;
import java.util.stream.Collectors;
import org.infinispan.AdvancedCache;
import org.infinispan.Cache;
import org.infinispan.commons.CacheException;
import org.infinispan.commons.equivalence.AnyEquivalence;
import org.infinispan.commons.equivalence.ByteArrayEquivalence;
import org.infinispan.commons.logging.LogFactory;
import org.infinispan.commons.marshall.AbstractExternalizer;
import org.infinispan.commons.marshall.Marshaller;
import org.infinispan.commons.marshall.WrappedByteArray;
import org.infinispan.commons.marshall.jboss.GenericJBossMarshaller;
import org.infinispan.commons.util.CollectionFactory;
import org.infinispan.configuration.cache.CompatibilityModeConfiguration;
import org.infinispan.container.versioning.EntryVersion;
import org.infinispan.container.versioning.NumericVersion;
import org.infinispan.factories.threads.DefaultThreadFactory;
import org.infinispan.metadata.Metadata;
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.CacheEntryCreatedEvent;
import org.infinispan.notifications.cachelistener.event.CacheEntryEvent;
import org.infinispan.notifications.cachelistener.event.CacheEntryModifiedEvent;
import org.infinispan.notifications.cachelistener.event.CacheEntryRemovedEvent;
import org.infinispan.notifications.cachelistener.filter.AbstractCacheEventFilterConverter;
import org.infinispan.notifications.cachelistener.filter.CacheEventConverter;
import org.infinispan.notifications.cachelistener.filter.CacheEventConverterFactory;
import org.infinispan.notifications.cachelistener.filter.CacheEventFilter;
import org.infinispan.notifications.cachelistener.filter.CacheEventFilterConverter;
import org.infinispan.notifications.cachelistener.filter.CacheEventFilterConverterFactory;
import org.infinispan.notifications.cachelistener.filter.CacheEventFilterFactory;
import org.infinispan.notifications.cachelistener.filter.EventType;
import org.infinispan.server.hotrod.configuration.HotRodServerConfiguration;
import org.infinispan.server.hotrod.logging.Log;
import org.infinispan.util.KeyValuePair;
import io.netty.channel.Channel;
/**
* @author Galder ZamarreƱo
*/
class ClientListenerRegistry {
private final HotRodServerConfiguration configuration;
ClientListenerRegistry(HotRodServerConfiguration configuration) {
this.configuration = configuration;
}
private final static Log log = LogFactory.getLog(ClientListenerRegistry.class, Log.class);
private final static boolean isTrace = log.isTraceEnabled();
private final AtomicLong messageId = new AtomicLong();
private final ConcurrentMap<WrappedByteArray, Object> eventSenders = new ConcurrentHashMap<>();
volatile private Optional<Marshaller> marshaller = Optional.empty();
private final ConcurrentMap<String, CacheEventFilterFactory> cacheEventFilterFactories = CollectionFactory.makeConcurrentMap(4, 0.9f, 16);
private final ConcurrentMap<String, CacheEventConverterFactory> cacheEventConverterFactories = CollectionFactory.makeConcurrentMap(4, 0.9f, 16);
private final ConcurrentMap<String, CacheEventFilterConverterFactory> cacheEventFilterConverterFactories = CollectionFactory.makeConcurrentMap(4, 0.9f, 16);
private final ExecutorService addListenerExecutor = new ThreadPoolExecutor(
0, 10, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<>(),
new DefaultThreadFactory(null, 1, "add-listener-thread-%t", null, null));
void setEventMarshaller(Optional<Marshaller> eventMarshaller) {
// Set a custom marshaller or reset to default if none
marshaller = eventMarshaller;
}
void addCacheEventFilterFactory(String name, CacheEventFilterFactory factory) {
if (factory instanceof CacheEventConverterFactory) {
throw log.illegalFilterConverterEventFactory(name);
}
cacheEventFilterFactories.put(name, factory);
}
void removeCacheEventFilterFactory(String name) {
cacheEventFilterFactories.remove(name);
}
void addCacheEventConverterFactory(String name, CacheEventConverterFactory factory) {
if (factory instanceof CacheEventFilterFactory) {
throw log.illegalFilterConverterEventFactory(name);
}
cacheEventConverterFactories.put(name, factory);
}
void removeCacheEventConverterFactory(String name) {
cacheEventConverterFactories.remove(name);
}
void addCacheEventFilterConverterFactory(String name, CacheEventFilterConverterFactory factory) {
cacheEventFilterConverterFactories.put(name, factory);
}
void removeCacheEventFilterConverterFactory(String name) {
cacheEventFilterConverterFactories.remove(name);
}
void addClientListener(VersionedDecoder decoder, Channel ch, HotRodHeader h, byte[] listenerId,
AdvancedCache<byte[], byte[]> cache, boolean includeState,
KeyValuePair<Optional<KeyValuePair<String, List<byte[]>>>, Optional<KeyValuePair<String, List<byte[]>>>> namedFactories,
boolean useRawData, int listenerInterests) {
ClientEventType eventType = ClientEventType.createType(namedFactories.getValue().isPresent(), useRawData, h.version);
Object clientEventSender = getClientEventSender(includeState, ch, h.version, cache, listenerId, eventType);
List<byte[]> binaryFilterParams = namedFactories.getKey().map(KeyValuePair::getValue).orElse(Collections.emptyList());
List<byte[]> binaryConverterParams = namedFactories.getValue().map(KeyValuePair::getValue).orElse(Collections.emptyList());
boolean compatEnabled = cache.getCacheConfiguration().compatibility().enabled();
KeyValuePair<CacheEventFilter<byte[], byte[]>, CacheEventConverter<byte[], byte[], byte[]>> kvp;
if (namedFactories.getKey().isPresent()) {
KeyValuePair<String, List<byte[]>> filterFactory = namedFactories.getKey().get();
if (namedFactories.getValue().isPresent()) {
KeyValuePair<String, List<byte[]>> converterFactory = namedFactories.getValue().get();
if (filterFactory.getKey().equals(converterFactory.getKey())) {
List<byte[]> binaryParams = binaryFilterParams.isEmpty() ? binaryConverterParams : binaryFilterParams;
CacheEventFilterConverter<byte[], byte[], byte[]> filterConverter = getFilterConverter(
filterFactory.getKey(), compatEnabled, useRawData, binaryParams);
kvp = new KeyValuePair<>(filterConverter, filterConverter);
} else {
kvp = new KeyValuePair<>(getFilter(filterFactory.getKey(), compatEnabled, useRawData, binaryFilterParams),
getConverter(converterFactory.getKey(), compatEnabled, useRawData, binaryConverterParams));
}
} else {
kvp = new KeyValuePair<>(getFilter(namedFactories.getKey().get().getKey(), compatEnabled, useRawData, binaryFilterParams), null);
}
} else {
if (namedFactories.getValue().isPresent()) {
kvp = new KeyValuePair<>(null, getConverter(namedFactories.getValue().get().getKey(), compatEnabled, useRawData, binaryConverterParams));
} else {
kvp = new KeyValuePair<>(null, null);
}
}
eventSenders.put(new WrappedByteArray(listenerId), clientEventSender);
if (includeState) {
// If state included, do it async
CompletableFuture<Void> cf = CompletableFuture.runAsync(() ->
addCacheListener(cache, clientEventSender, kvp, listenerInterests), addListenerExecutor);
cf.whenComplete((t, cause) -> {
Response resp;
if (cause != null) {
if (cause instanceof CompletionException) {
resp = decoder.createErrorResponse(h, cause.getCause());
} else {
resp = decoder.createErrorResponse(h, cause);
}
} else {
resp = decoder.createSuccessResponse(h, null);
}
ch.writeAndFlush(resp);
});
} else {
addCacheListener(cache, clientEventSender, kvp, listenerInterests);
ch.writeAndFlush(decoder.createSuccessResponse(h, null));
}
}
private void addCacheListener(AdvancedCache<byte[], byte[]> cache, Object clientEventSender,
KeyValuePair<CacheEventFilter<byte[], byte[]>, CacheEventConverter<byte[], byte[], byte[]>> kvp,
int listenerInterests) {
Set<Class<? extends Annotation>> filterAnnotations;
if (listenerInterests == 0x00) {
filterAnnotations = new HashSet<>(Arrays.asList(
CacheEntryCreated.class, CacheEntryModified.class,
CacheEntryRemoved.class, CacheEntryExpired.class));
} else {
filterAnnotations = new HashSet<>();
if ((listenerInterests & 0x01) == 0x01)
filterAnnotations.add(CacheEntryCreated.class);
if ((listenerInterests & 0x02) == 0x02)
filterAnnotations.add(CacheEntryModified.class);
if ((listenerInterests & 0x04) == 0x04)
filterAnnotations.add(CacheEntryRemoved.class);
if ((listenerInterests & 0x08) == 0x08)
filterAnnotations.add(CacheEntryExpired.class);
}
cache.addFilteredListener(clientEventSender, kvp.getKey(), kvp.getValue(), filterAnnotations);
}
CacheEventFilter<byte[], byte[]> getFilter(String name, Boolean compatEnabled, Boolean useRawData, List<byte[]> binaryParams) {
KeyValuePair<CacheEventFilterFactory, Marshaller> factory =
findFactory(name, compatEnabled, cacheEventFilterFactories, "key/value filter", useRawData);
List<? extends Object> params = unmarshallParams(binaryParams, factory.getValue(), useRawData);
return factory.getKey().getFilter(params.toArray());
}
CacheEventConverter<byte[], byte[], byte[]> getConverter(String name, boolean compatEnabled, Boolean useRawData, List<byte[]> binaryParams) {
KeyValuePair<CacheEventConverterFactory, Marshaller> factory =
findConverterFactory(name, compatEnabled, cacheEventConverterFactories, "converter", useRawData);
List<? extends Object> params = unmarshallParams(binaryParams, factory.getValue(), useRawData);
return factory.getKey().getConverter(params.toArray());
}
CacheEventFilterConverter<byte[], byte[], byte[]> getFilterConverter(String name, boolean compatEnabled, boolean useRawData, List<byte[]> binaryParams) {
KeyValuePair<CacheEventFilterConverterFactory, Marshaller> factory =
findFactory(name, compatEnabled, cacheEventFilterConverterFactories, "converter", useRawData);
List<? extends Object> params = unmarshallParams(binaryParams, factory.getValue(), useRawData);
return factory.getKey().getFilterConverter(params.toArray());
}
KeyValuePair<CacheEventConverterFactory, Marshaller> findConverterFactory(String name, boolean compatEnabled,
ConcurrentMap<String, CacheEventConverterFactory> factories, String factoryType, boolean useRawData) {
if (name.equals("___eager-key-value-version-converter"))
return new KeyValuePair<>(KeyValueVersionConverterFactory.SINGLETON, new GenericJBossMarshaller());
else
return findFactory(name, compatEnabled, factories, factoryType, useRawData);
}
<T> KeyValuePair<T, Marshaller> findFactory(String name, boolean compatEnabled,
ConcurrentMap<String, T> factories, String factoryType, boolean useRawData) {
T factory = factories.get(name);
if (factory == null) throw log.missingCacheEventFactory(factoryType, name);
Marshaller m = marshaller.orElse(new GenericJBossMarshaller(factory.getClass().getClassLoader()));
if (useRawData || compatEnabled)
return new KeyValuePair<>(factory, m);
else
return new KeyValuePair<>(createFactory(factory, m), m);
}
<T> T createFactory(T factory, Marshaller marshaller) {
if (factory instanceof CacheEventConverterFactory) {
return (T) new UnmarshallConverterFactory((CacheEventConverterFactory) factory, marshaller);
} else if (factory instanceof CacheEventFilterFactory) {
return (T) new UnmarshallFilterFactory((CacheEventFilterFactory) factory, marshaller);
} else if (factory instanceof CacheEventFilterConverterFactory) {
return (T) new UnmarshallFilterConverterFactory((CacheEventFilterConverterFactory) factory, marshaller);
} else {
throw new IllegalArgumentException("Unsupported factory: " + factory);
}
}
private List<? extends Object> unmarshallParams(List<byte[]> binaryParams, Marshaller marshaller, boolean useRawData) {
if (!useRawData) {
return binaryParams.stream().map(bp -> {
try {
return marshaller.objectFromByteBuffer(bp);
} catch (IOException | ClassNotFoundException e) {
throw new CacheException(e);
}
}).collect(Collectors.toList());
} else return binaryParams;
}
boolean removeClientListener(byte[] listenerId, Cache cache) {
Object sender = eventSenders.get(new WrappedByteArray(listenerId));
if (sender != null) {
cache.removeListener(sender);
return true;
} else return false;
}
public void stop() {
eventSenders.clear();
cacheEventFilterFactories.clear();
cacheEventConverterFactories.clear();
addListenerExecutor.shutdown();
}
void findAndWriteEvents(Channel channel) {
// Make sure we write any event in main event loop
channel.eventLoop().execute(() -> eventSenders.values().forEach(s -> {
if (s instanceof BaseClientEventSender) {
BaseClientEventSender bces = (BaseClientEventSender) s;
if (bces.hasChannel(channel)) bces.writeEventsIfPossible();
}
}));
}
// Do not make sync=false, instead move cache operation causing
// listener calls out of the Netty event loop thread
@Listener(clustered = true, includeCurrentState = true)
private class StatefulClientEventSender extends BaseClientEventSender {
protected StatefulClientEventSender(Channel ch, byte[] listenerId, byte version,
org.infinispan.server.hotrod.ClientEventType targetEventType) {
super(ch, listenerId, version, targetEventType);
}
}
@Listener(clustered = true, includeCurrentState = false)
private class StatelessClientEventSender extends BaseClientEventSender {
protected StatelessClientEventSender(Channel ch, byte[] listenerId, byte version, ClientEventType targetEventType) {
super(ch, listenerId, version, targetEventType);
}
}
private abstract class BaseClientEventSender {
protected final Channel ch;
protected final byte[] listenerId;
protected final byte version;
protected final ClientEventType targetEventType;
BlockingQueue<Object> eventQueue = new LinkedBlockingQueue<>(100);
private final Runnable writeEventsIfPossible = this::writeEventsIfPossible;
protected BaseClientEventSender(Channel ch, byte[] listenerId, byte version, ClientEventType targetEventType) {
this.ch = ch;
this.listenerId = listenerId;
this.version = version;
this.targetEventType = targetEventType;
}
boolean hasChannel(Channel channel) {
return ch == channel;
}
void writeEventsIfPossible() {
boolean written = false;
while (!eventQueue.isEmpty() && ch.isWritable()) {
Object event = eventQueue.poll();
if (isTrace) log.tracef("Write event: %s to channel %s", event, ch);
ch.write(event);
written = true;
}
if (written) {
ch.flush();
}
}
@CacheEntryCreated
@CacheEntryModified
@CacheEntryRemoved
@CacheEntryExpired
public void onCacheEvent(CacheEntryEvent<byte[], byte[]> event) {
if (isSendEvent(event)) {
long version;
Metadata metadata;
if ((metadata = event.getMetadata()) != null && metadata.version() != null) {
version = ((NumericVersion) metadata.version()).getVersion();
} else {
version = 0;
}
sendEvent(event.getKey(), event.getValue(), version, event);
}
}
boolean isSendEvent(CacheEntryEvent<?, ?> event) {
if (isChannelDisconnected()) {
log.debug("Channel disconnected, remove event sender listener");
event.getCache().removeListener(this);
return false;
} else {
switch (event.getType()) {
case CACHE_ENTRY_CREATED:
case CACHE_ENTRY_MODIFIED:
return !event.isPre();
case CACHE_ENTRY_REMOVED:
CacheEntryRemovedEvent removedEvent = (CacheEntryRemovedEvent) event;
return !event.isPre() && removedEvent.getOldValue() != null;
case CACHE_ENTRY_EXPIRED:
return true;
default:
throw log.unexpectedEvent(event);
}
}
}
boolean isChannelDisconnected() {
return !ch.isOpen();
}
void sendEvent(byte[] key, byte[] value, long dataVersion, CacheEntryEvent event) {
Object remoteEvent = createRemoteEvent(key, value, dataVersion, event);
if (isTrace)
log.tracef("Queue event %s, before queuing event queue size is %d", remoteEvent, eventQueue.size());
boolean waitingForFlush = !ch.isWritable();
try {
eventQueue.put(remoteEvent);
} catch (InterruptedException e) {
throw new CacheException(e);
}
if (!waitingForFlush) {
// Make sure we write any event in main event loop
ch.eventLoop().submit(writeEventsIfPossible);
}
}
private Object createRemoteEvent(byte[] key, byte[] value, long dataVersion, CacheEntryEvent event) {
long id = messageId.incrementAndGet(); // increment message id
// Embedded listener event implementation implements all interfaces,
// so can't pattern match on the event instance itself. Instead, pattern
// match on the type and the cast down to the expected event instance type
switch (targetEventType) {
case PLAIN:
switch (event.getType()) {
case CACHE_ENTRY_CREATED:
case CACHE_ENTRY_MODIFIED:
KeyValuePair<HotRodOperation, Boolean> responseType = getEventResponseType(event);
return keyWithVersionEvent(key, dataVersion, responseType.getKey(), responseType.getValue());
case CACHE_ENTRY_REMOVED:
case CACHE_ENTRY_EXPIRED:
responseType = getEventResponseType(event);
return new Events.KeyEvent(version, id, responseType.getKey(), listenerId, responseType.getValue(), key);
default:
throw log.unexpectedEvent(event);
}
case CUSTOM_PLAIN:
KeyValuePair<HotRodOperation, Boolean> responseType = getEventResponseType(event);
return new Events.CustomEvent(version, id, responseType.getKey(), listenerId, responseType.getValue(), value);
case CUSTOM_RAW:
responseType = getEventResponseType(event);
return new Events.CustomRawEvent(version, id, responseType.getKey(), listenerId, responseType.getValue(), value);
default:
throw new IllegalArgumentException("Event type not supported: " + targetEventType);
}
}
private KeyValuePair<HotRodOperation, Boolean> getEventResponseType(CacheEntryEvent event) {
switch (event.getType()) {
case CACHE_ENTRY_CREATED:
return new KeyValuePair<>(HotRodOperation.CACHE_ENTRY_CREATED_EVENT,
((CacheEntryCreatedEvent) event).isCommandRetried());
case CACHE_ENTRY_MODIFIED:
return new KeyValuePair<>(HotRodOperation.CACHE_ENTRY_MODIFIED_EVENT,
((CacheEntryModifiedEvent) event).isCommandRetried());
case CACHE_ENTRY_REMOVED:
return new KeyValuePair<>(HotRodOperation.CACHE_ENTRY_REMOVED_EVENT,
((CacheEntryRemovedEvent) event).isCommandRetried());
case CACHE_ENTRY_EXPIRED:
return new KeyValuePair<>(HotRodOperation.CACHE_ENTRY_EXPIRED_EVENT, false);
default:
throw log.unexpectedEvent(event);
}
}
private Events.KeyWithVersionEvent keyWithVersionEvent(byte[] key, long dataVersion, HotRodOperation op, boolean isRetried) {
return new Events.KeyWithVersionEvent(version, messageId.get(), op, listenerId, isRetried, key, dataVersion);
}
}
Object getClientEventSender(boolean includeState, Channel ch, byte version,
Cache cache, byte[] listenerId, ClientEventType eventType) {
CompatibilityModeConfiguration compatibility = cache.getCacheConfiguration().compatibility();
if (compatibility.enabled()) {
if (includeState) {
StatelessClientEventSender delegate = new StatelessClientEventSender(ch, listenerId, version, eventType);
return new StatefulCompatibilityClientEventSender(delegate, new HotRodTypeConverter(compatibility.marshaller()));
} else {
StatelessClientEventSender delegate = new StatelessClientEventSender(ch, listenerId, version, eventType);
return new StatelessCompatibilityClientEventSender(delegate, new HotRodTypeConverter(compatibility.marshaller()));
}
} else {
if (includeState) {
return new StatefulClientEventSender(ch, listenerId, version, eventType);
} else {
return new StatelessClientEventSender(ch, listenerId, version, eventType);
}
}
}
@Listener(clustered = true, includeCurrentState = true)
private class StatefulCompatibilityClientEventSender extends BaseCompatibilityClientEventSender {
protected StatefulCompatibilityClientEventSender(BaseClientEventSender delegate, HotRodTypeConverter converter) {
super(delegate, converter);
}
}
@Listener(clustered = true, includeCurrentState = false)
private class StatelessCompatibilityClientEventSender extends BaseCompatibilityClientEventSender {
protected StatelessCompatibilityClientEventSender(BaseClientEventSender delegate,
org.infinispan.server.hotrod.HotRodTypeConverter converter) {
super(delegate, converter);
}
}
private abstract class BaseCompatibilityClientEventSender {
protected final BaseClientEventSender delegate;
protected final HotRodTypeConverter converter;
protected BaseCompatibilityClientEventSender(BaseClientEventSender delegate, HotRodTypeConverter converter) {
this.delegate = delegate;
this.converter = converter;
}
@CacheEntryCreated
@CacheEntryModified
@CacheEntryRemoved
@CacheEntryExpired
public void onCacheEvent(CacheEntryEvent event) {
Object key = converter.unboxKey(event.getKey());
Object value = converter.unboxValue(event.getValue());
if (delegate.isSendEvent(event)) {
// In compatibility mode, version could be null if stored via embedded
EntryVersion version = event.getMetadata().version();
long dataVersion = version == null ? 0 : ((NumericVersion) version).getVersion();
delegate.sendEvent((byte[]) key, (byte[]) value, dataVersion, event);
}
}
}
private class UnmarshallFilterFactory implements CacheEventFilterFactory {
private final CacheEventFilterFactory filterFactory;
private final Marshaller marshaller;
private UnmarshallFilterFactory(CacheEventFilterFactory filterFactory, Marshaller marshaller) {
this.filterFactory = filterFactory;
this.marshaller = marshaller;
}
@Override
public <K, V> CacheEventFilter<K, V> getFilter(Object[] params) {
return (CacheEventFilter<K, V>) new UnmarshallFilter(filterFactory.getFilter(params), marshaller);
}
}
class UnmarshallConverterFactory implements CacheEventConverterFactory {
private final CacheEventConverterFactory converterFactory;
private final Marshaller marshaller;
UnmarshallConverterFactory(CacheEventConverterFactory converterFactory, Marshaller marshaller) {
this.converterFactory = converterFactory;
this.marshaller = marshaller;
}
@Override
public <K, V, C> CacheEventConverter<K, V, C> getConverter(Object[] params) {
return (CacheEventConverter<K, V, C>) new UnmarshallConverter(converterFactory.getConverter(params), marshaller);
}
}
class UnmarshallFilterConverterFactory implements CacheEventFilterConverterFactory {
private final CacheEventFilterConverterFactory filterConverterFactory;
private final Marshaller marshaller;
UnmarshallFilterConverterFactory(CacheEventFilterConverterFactory filterConverterFactory, Marshaller marshaller) {
this.filterConverterFactory = filterConverterFactory;
this.marshaller = marshaller;
}
@Override
public <K, V, C> CacheEventFilterConverter<K, V, C> getFilterConverter(Object[] params) {
return (CacheEventFilterConverter<K, V, C>) new UnmarshallFilterConverter(filterConverterFactory.getFilterConverter(params), marshaller);
}
}
static class UnmarshallFilter implements CacheEventFilter<byte[], byte[]> {
private final CacheEventFilter<Object, Object> filter;
private final Marshaller marshaller;
UnmarshallFilter(CacheEventFilter<Object, Object> filter, Marshaller marshaller) {
this.filter = filter;
this.marshaller = marshaller;
}
@Override
public boolean accept(byte[] key, byte[] oldValue, Metadata oldMetadata, byte[] newValue, Metadata newMetadata, EventType eventType) {
Object unmarshalledKey;
Object unmarshalledPrevValue;
Object unmarshalledValue;
try {
unmarshalledKey = marshaller.objectFromByteBuffer(key);
unmarshalledPrevValue = oldValue != null ? marshaller.objectFromByteBuffer(oldValue) : null;
unmarshalledValue = newValue != null ? marshaller.objectFromByteBuffer(newValue) : null;
} catch (IOException | ClassNotFoundException e) {
throw new CacheException(e);
}
return filter.accept(unmarshalledKey, unmarshalledPrevValue, oldMetadata, unmarshalledValue, newMetadata, eventType);
}
}
static class UnmarshallFilterExternalizer extends AbstractExternalizer<UnmarshallFilter> {
@Override
public void writeObject(ObjectOutput output, UnmarshallFilter obj) throws IOException {
output.writeObject(obj.filter);
output.writeObject(obj.marshaller.getClass());
}
@Override
public UnmarshallFilter readObject(ObjectInput input) throws IOException, ClassNotFoundException {
CacheEventFilter<Object, Object> filter = (CacheEventFilter<Object, Object>) input.readObject();
Class<? extends Marshaller> marshallerClass = (Class<? extends Marshaller>) input.readObject();
// See if the marshaller can be constructed
Marshaller marshaller = constructMarshaller(filter, marshallerClass);
return new UnmarshallFilter(filter, marshaller);
}
@Override
public Set<Class<? extends UnmarshallFilter>> getTypeClasses() {
return Collections.singleton(UnmarshallFilter.class);
}
}
private static <T> Marshaller constructMarshaller(T t, Class<? extends Marshaller> marshallerClass) {
Constructor<? extends Marshaller> constructor = findClassloaderConstructor(marshallerClass);
try {
if (constructor != null) {
return constructor.newInstance(t.getClass().getClassLoader());
} else {
return marshallerClass.newInstance();
}
} catch (InstantiationException | IllegalAccessException | InvocationTargetException e) {
throw new CacheException(e);
}
}
private static Constructor<? extends Marshaller> findClassloaderConstructor(Class<? extends Marshaller> clazz) {
try {
return clazz.getConstructor(ClassLoader.class);
} catch (NoSuchMethodException e) {
return null;
}
}
static class UnmarshallConverter implements CacheEventConverter<byte[], byte[], byte[]> {
private final CacheEventConverter<Object, Object, Object> converter;
private final Marshaller marshaller;
UnmarshallConverter(CacheEventConverter<Object, Object, Object> converter, Marshaller marshaller) {
this.converter = converter;
this.marshaller = marshaller;
}
@Override
public byte[] convert(byte[] key, byte[] oldValue, Metadata oldMetadata, byte[] newValue, Metadata newMetadata, EventType eventType) {
try {
Object unmarshalledKey = marshaller.objectFromByteBuffer(key);
Object unmarshalledPrevValue = oldValue != null ? marshaller.objectFromByteBuffer(oldValue) : null;
Object unmarshalledValue = newValue != null ? marshaller.objectFromByteBuffer(newValue) : null;
Object converted = converter.convert(unmarshalledKey, unmarshalledPrevValue, oldMetadata, unmarshalledValue, newMetadata, eventType);
return marshaller.objectToByteBuffer(converted);
} catch (IOException | ClassNotFoundException | InterruptedException e) {
throw new CacheException(e);
}
}
}
static class UnmarshallConverterExternalizer extends AbstractExternalizer<UnmarshallConverter> {
@Override
public void writeObject(ObjectOutput output, UnmarshallConverter obj) throws IOException {
output.writeObject(obj.converter);
output.writeObject(obj.marshaller.getClass());
}
@Override
public UnmarshallConverter readObject(ObjectInput input) throws IOException, ClassNotFoundException {
CacheEventConverter<Object, Object, Object> converter = (CacheEventConverter<Object, Object, Object>) input.readObject();
Class<? extends Marshaller> marshallerClass = (Class<? extends Marshaller>) input.readObject();
Marshaller marshaller = constructMarshaller(converter, marshallerClass);
return new UnmarshallConverter(converter, marshaller);
}
@Override
public Set<Class<? extends UnmarshallConverter>> getTypeClasses() {
return Collections.singleton(UnmarshallConverter.class);
}
}
static class UnmarshallFilterConverter extends AbstractCacheEventFilterConverter<byte[], byte[], byte[]> {
private final CacheEventFilterConverter<Object, Object, Object> filterConverter;
private final Marshaller marshaller;
UnmarshallFilterConverter(CacheEventFilterConverter<Object, Object, Object> filterConverter, Marshaller marshaller) {
this.filterConverter = filterConverter;
this.marshaller = marshaller;
}
@Override
public byte[] filterAndConvert(byte[] key, byte[] oldValue, Metadata oldMetadata, byte[] newValue, Metadata newMetadata, EventType eventType) {
try {
Object unmarshalledKey = marshaller.objectFromByteBuffer(key);
Object unmarshalledPrevValue = oldValue != null ? marshaller.objectFromByteBuffer(oldValue) : null;
Object unmarshalledValue = newValue != null ? marshaller.objectFromByteBuffer(newValue) : null;
Object converted = filterConverter.filterAndConvert(unmarshalledKey, unmarshalledPrevValue,
oldMetadata, unmarshalledValue, newMetadata, eventType);
return marshaller.objectToByteBuffer(converted);
} catch (IOException | ClassNotFoundException | InterruptedException e) {
throw new CacheException(e);
}
}
}
static class UnmarshallFilterConverterExternalizer extends AbstractExternalizer<UnmarshallFilterConverter> {
@Override
public void writeObject(ObjectOutput output, UnmarshallFilterConverter obj) throws IOException {
output.writeObject(obj.filterConverter);
output.writeObject(obj.marshaller.getClass());
}
@Override
public UnmarshallFilterConverter readObject(ObjectInput input) throws IOException, ClassNotFoundException {
CacheEventFilterConverter<Object, Object, Object> filterConverter =
(CacheEventFilterConverter<Object, Object, Object>) input.readObject();
Class<? extends Marshaller> marshallerClass = (Class<? extends Marshaller>) input.readObject();
Marshaller marshaller = constructMarshaller(filterConverter, marshallerClass);
return new UnmarshallFilterConverter(filterConverter, marshaller);
}
@Override
public Set<Class<? extends UnmarshallFilterConverter>> getTypeClasses() {
return Collections.singleton(UnmarshallFilterConverter.class);
}
}
}
enum ClientEventType {
PLAIN,
CUSTOM_PLAIN,
CUSTOM_RAW;
static ClientEventType createType(boolean isCustom, boolean useRawData, byte version) {
if (isCustom) {
if (useRawData && Constants.isVersionPost20(version)) {
return CUSTOM_RAW;
}
return CUSTOM_PLAIN;
}
return PLAIN;
}
}