package org.infinispan.client.hotrod.impl.operations;
import java.net.SocketAddress;
import java.nio.ByteBuffer;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.atomic.AtomicInteger;
import org.infinispan.client.hotrod.RemoteCacheManager;
import org.infinispan.client.hotrod.annotation.ClientListener;
import org.infinispan.client.hotrod.configuration.ClientIntelligence;
import org.infinispan.client.hotrod.event.ClientEvent;
import org.infinispan.client.hotrod.event.ClientListenerNotifier;
import org.infinispan.client.hotrod.impl.protocol.Codec;
import org.infinispan.client.hotrod.impl.protocol.HeaderParams;
import org.infinispan.client.hotrod.impl.protocol.HotRodConstants;
import org.infinispan.client.hotrod.impl.transport.Transport;
import org.infinispan.client.hotrod.impl.transport.TransportFactory;
import org.infinispan.client.hotrod.logging.Log;
import org.infinispan.client.hotrod.logging.LogFactory;
import org.infinispan.commons.util.Either;
import org.infinispan.commons.util.ReflectionUtil;
/**
* @author Galder ZamarreƱo
*/
public class AddClientListenerOperation extends RetryOnFailureOperation<Short> {
private static final Log log = LogFactory.getLog(AddClientListenerOperation.class, Log.class);
public final byte[] listenerId;
private final String cacheNameString;
/**
* Decicated transport instance for adding client listener. This transport
* is used to send events back to client and it's only released when the
* client listener is removed.
*/
private Transport dedicatedTransport;
private final ClientListenerNotifier listenerNotifier;
public final Object listener;
public final byte[][] filterFactoryParams;
public final byte[][] converterFactoryParams;
protected AddClientListenerOperation(Codec codec, TransportFactory transportFactory,
String cacheName, AtomicInteger topologyId, int flags, ClientIntelligence clientIntelligence,
ClientListenerNotifier listenerNotifier, Object listener,
byte[][] filterFactoryParams, byte[][] converterFactoryParams) {
super(codec, transportFactory, RemoteCacheManager.cacheNameBytes(cacheName), topologyId, flags, clientIntelligence);
this.listenerId = generateListenerId();
this.listenerNotifier = listenerNotifier;
this.listener = listener;
this.filterFactoryParams = filterFactoryParams;
this.converterFactoryParams = converterFactoryParams;
this.cacheNameString = cacheName;
}
private byte[] generateListenerId() {
UUID uuid = UUID.randomUUID();
byte[] listenerId = new byte[16];
ByteBuffer bb = ByteBuffer.wrap(listenerId);
bb.putLong(uuid.getMostSignificantBits());
bb.putLong(uuid.getLeastSignificantBits());
return listenerId;
}
@Override
protected Transport getTransport(int retryCount, Set<SocketAddress> failedServers) {
this.dedicatedTransport = transportFactory.getTransport(failedServers, cacheName);
return dedicatedTransport;
}
@Override
protected void releaseTransport(Transport transport) {
// Do not release transport instance, it's fully dedicated to events
}
public Transport getDedicatedTransport() {
return dedicatedTransport;
}
@Override
protected Short executeOperation(Transport transport) {
ClientListener clientListener = extractClientListener();
HeaderParams params = writeHeader(transport, ADD_CLIENT_LISTENER_REQUEST);
transport.writeArray(listenerId);
codec.writeClientListenerParams(transport, clientListener, filterFactoryParams, converterFactoryParams);
codec.writeClientListenerInterests(transport, listenerNotifier.findMethods(this.listener).keySet());
transport.flush();
listenerNotifier.addClientListener(this);
Either<Short, ClientEvent> either;
do {
// Process state transfer related events or add listener response
either = codec.readHeaderOrEvent(dedicatedTransport, params, listenerId, listenerNotifier.getMarshaller());
switch(either.type()) {
case LEFT:
if (HotRodConstants.isSuccess(either.left()))
listenerNotifier.startClientListener(listenerId);
else // If error, remove it
listenerNotifier.removeClientListener(listenerId);
break;
case RIGHT:
listenerNotifier.invokeEvent(listenerId, either.right());
break;
}
} while (either.type() == Either.Type.RIGHT);
return either.left();
}
private ClientListener extractClientListener() {
ClientListener l = ReflectionUtil.getAnnotation(listener.getClass(), ClientListener.class);
if (l == null)
throw log.missingClientListenerAnnotation(listener.getClass().getName());
return l;
}
public String getCacheName() {
return cacheNameString;
}
}