org.infinispan.client.hotrod.near.NearCacheService Maven / Gradle / Ivy
package org.infinispan.client.hotrod.near;
import org.infinispan.client.hotrod.RemoteCache;
import org.infinispan.client.hotrod.VersionedValue;
import org.infinispan.client.hotrod.annotation.ClientCacheEntryCreated;
import org.infinispan.client.hotrod.annotation.ClientCacheEntryModified;
import org.infinispan.client.hotrod.annotation.ClientCacheEntryRemoved;
import org.infinispan.client.hotrod.annotation.ClientCacheFailover;
import org.infinispan.client.hotrod.annotation.ClientListener;
import org.infinispan.client.hotrod.configuration.NearCacheConfiguration;
import org.infinispan.client.hotrod.event.ClientCacheEntryCreatedEvent;
import org.infinispan.client.hotrod.event.ClientCacheEntryCustomEvent;
import org.infinispan.client.hotrod.event.ClientCacheEntryModifiedEvent;
import org.infinispan.client.hotrod.event.ClientCacheEntryRemovedEvent;
import org.infinispan.client.hotrod.event.ClientCacheFailoverEvent;
import org.infinispan.client.hotrod.event.ClientListenerNotifier;
import org.infinispan.client.hotrod.impl.VersionedValueImpl;
import org.infinispan.client.hotrod.logging.Log;
import org.infinispan.client.hotrod.logging.LogFactory;
import org.infinispan.commons.io.UnsignedNumeric;
import org.infinispan.commons.marshall.Marshaller;
import org.infinispan.commons.util.Util;
import java.nio.ByteBuffer;
public class NearCacheService implements NearCache {
private static final Log log = LogFactory.getLog(NearCacheService.class);
private final NearCacheConfiguration config;
private final ClientListenerNotifier listenerNotifier;
private Object listener;
private byte[] listenerId;
private NearCache cache;
protected NearCacheService(NearCacheConfiguration config, ClientListenerNotifier listenerNotifier) {
this.config = config;
this.listenerNotifier = listenerNotifier;
}
public void start(RemoteCache remote) {
// Create near cache
cache = createNearCache(config);
// Add a listener that updates the near cache
listener = createListener(remote);
remote.addClientListener(listener);
// Get the listener ID for faster listener connected lookups
listenerId = listenerNotifier.findListenerId(listener);
}
private Object createListener(RemoteCache remote) {
return config.mode().eager()
? new EagerNearCacheListener(this, remote.getRemoteCacheManager().getMarshaller())
: new LazyNearCacheListener(this);
}
public void stop(RemoteCache remote) {
if (log.isTraceEnabled())
log.tracef("Stop near cache, remove underlying listener id %s", Util.printArray(listenerId));
// Remove listener
remote.removeClientListener(listener);
// Empty cache
cache.clear();
}
protected NearCache createNearCache(NearCacheConfiguration config) {
return config.maxEntries() > 0
? LinkedMapNearCache.create(config)
: ConcurrentMapNearCache.create();
}
public static NearCacheService create(
NearCacheConfiguration config, ClientListenerNotifier listenerNotifier) {
return new NearCacheService(config, listenerNotifier);
}
@Override
public void put(K key, VersionedValue value) {
cache.put(key, value);
if (log.isTraceEnabled())
log.tracef("Put key=%s and value=%s in near cache (listenerId=%s)",
key, value, Util.printArray(listenerId));
}
@Override
public void putIfAbsent(K key, VersionedValue value) {
cache.putIfAbsent(key, value);
if (log.isTraceEnabled())
log.tracef("Conditionally put key=%s and value=%s if absent in near cache (listenerId=%s)",
key, value, Util.printArray(listenerId));
}
@Override
public void remove(K key) {
cache.remove(key);
if (log.isTraceEnabled())
log.tracef("Removed key=%s from near cache (listenedId=%s)", key, Util.printArray(listenerId));
}
@Override
public VersionedValue get(K key) {
boolean listenerConnected = isConnected();
if (listenerConnected) {
VersionedValue value = cache.get(key);
if (log.isTraceEnabled())
log.tracef("Get key=%s returns value=%s (listenerId=%s)", key, value, Util.printArray(listenerId));
return value;
}
if (log.isTraceEnabled())
log.tracef("Near cache disconnected from server, returning null for key=%s (listenedId=%s)",
key, Util.printArray(listenerId));
return null;
}
@Override
public void clear() {
cache.clear();
if (log.isTraceEnabled()) log.tracef("Cleared near cache (listenerId=%s)", Util.printArray(listenerId));
}
private boolean isConnected() {
return listenerNotifier.isListenerConnected(listenerId);
}
@ClientListener
private static class LazyNearCacheListener {
private static final Log log = LogFactory.getLog(LazyNearCacheListener.class);
private final NearCache cache;
private LazyNearCacheListener(NearCache cache) {
this.cache = cache;
}
@ClientCacheEntryCreated
@SuppressWarnings("unused")
public void handleCreatedEvent(ClientCacheEntryCreatedEvent event) {
invalidate(event.getKey());
}
@ClientCacheEntryModified
@SuppressWarnings("unused")
public void handleModifiedEvent(ClientCacheEntryModifiedEvent event) {
invalidate(event.getKey());
}
@ClientCacheEntryRemoved
@SuppressWarnings("unused")
public void handleRemovedEvent(ClientCacheEntryRemovedEvent event) {
invalidate(event.getKey());
}
@ClientCacheFailover
@SuppressWarnings("unused")
public void handleFailover(ClientCacheFailoverEvent e) {
if (log.isTraceEnabled()) log.trace("Clear near cache after fail-over of server");
cache.clear();
}
private void invalidate(K key) {
cache.remove(key);
}
}
/**
* An near cache listener that eagerly populates the near cache as cache
* entries are created/modified in the server. It uses a converter in order
* to ship value and version information as well as the key. To avoid sharing
* classes between client and server, it uses raw data in the converter.
* This listener does not apply any filtering, so all keys are considered.
*/
@ClientListener(converterFactoryName = "___eager-key-value-version-converter", useRawData = true)
private static class EagerNearCacheListener {
private static final Log log = LogFactory.getLog(EagerNearCacheListener.class);
private final NearCache cache;
private final Marshaller marshaller;
private EagerNearCacheListener(NearCache cache, Marshaller marshaller) {
this.cache = cache;
this.marshaller = marshaller;
}
@ClientCacheEntryCreated
@ClientCacheEntryModified
@SuppressWarnings("unused")
public void handleCreatedModifiedEvent(ClientCacheEntryCustomEvent e) {
ByteBuffer in = ByteBuffer.wrap(e.getEventData());
byte[] keyBytes = extractElement(in);
byte[] valueBytes = extractElement(in);
K key = unmarshallObject(keyBytes, "key");
V value = unmarshallObject(valueBytes, "value");
long version = in.getLong();
if (key != null && value != null) {
VersionedValueImpl entry = new VersionedValueImpl<>(version, value);
cache.put(key, entry);
}
}
@SuppressWarnings("unchecked")
private T unmarshallObject(byte[] bytes, String element) {
try {
return (T) marshaller.objectFromByteBuffer(bytes);
} catch (Exception e) {
log.unableToUnmarshallBytesError(element, Util.toStr(bytes), e);
return null;
}
}
@ClientCacheEntryRemoved
@SuppressWarnings("unused")
public void handleRemovedEvent(ClientCacheEntryCustomEvent e) {
ByteBuffer in = ByteBuffer.wrap(e.getEventData());
byte[] keyBytes = extractElement(in);
K key = unmarshallObject(keyBytes, "key");
if (key != null) {
cache.remove(key);
}
}
@ClientCacheFailover
@SuppressWarnings("unused")
public void handleFailover(ClientCacheFailoverEvent e) {
if (log.isTraceEnabled()) log.trace("Clear near cache after fail-over of server");
cache.clear();
}
private static byte[] extractElement(ByteBuffer in) {
int length = UnsignedNumeric.readUnsignedInt(in);
byte[] element = new byte[length];
in.get(element);
return element;
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy