package org.infinispan.cache.impl;
import java.io.ByteArrayInputStream;
import java.io.ObjectInputStream;
import java.util.Collection;
import org.infinispan.AdvancedCache;
import org.infinispan.Cache;
import org.infinispan.commons.CacheException;
import org.infinispan.commons.marshall.Marshaller;
import org.infinispan.commons.util.EnumUtil;
import org.infinispan.commons.util.ServiceFinder;
import org.infinispan.commons.util.Util;
import org.infinispan.compat.DoubleTypeConverter;
import org.infinispan.compat.TypeConverter;
import org.infinispan.context.Flag;
import org.infinispan.context.impl.FlagBitSets;
import org.infinispan.util.logging.Log;
import org.infinispan.util.logging.LogFactory;
/**
* Advanced cache that converts values based on operation type before passing to the underlying cache and subsequently
* converts the values to the original type for responses.
* @author wburns
* @since 9.0
*/
public class CompatibilityAdvancedCache<K, V> extends TypeConverterDelegatingAdvancedCache<K, V> {
private TypeConverter<K, V, K, V> hotRodConverter;
private TypeConverter<K, V, K, V> memcachedConverter;
private TypeConverter<K, V, K, V> embeddedConverter;
public CompatibilityAdvancedCache(AdvancedCache<K, V> cache, Marshaller marshaller, TypeConverter converter) {
super(cache, c -> new CompatibilityAdvancedCache<>(c, marshaller, converter), converter);
Collection<TypeConverter> converters = ServiceFinder.load(TypeConverter.class);
for (TypeConverter foundConverter : converters) {
// We assume these don't produce byte[] values when boxing
if (foundConverter.supportsInvocation(Flag.OPERATION_HOTROD)) {
hotRodConverter = setConverterMarshaller(new DoubleTypeConverter<>(foundConverter, converter), marshaller);
} else if (foundConverter.supportsInvocation(Flag.OPERATION_MEMCACHED)) {
memcachedConverter = setConverterMarshaller(new DoubleTypeConverter<>(foundConverter, converter), marshaller);
}
}
embeddedConverter = setConverterMarshaller(new DoubleTypeConverter<>(new EmbeddedTypeConverter(), converter), marshaller);
}
private static TypeConverter setConverterMarshaller(TypeConverter converter, Marshaller marshaller) {
if (marshaller != null)
converter.setMarshaller(marshaller);
return converter;
}
@Override
protected TypeConverter getConverter() {
Cache<K, V> cache = this.cache;
while (cache instanceof AbstractDelegatingCache && !(cache instanceof DecoratedCache)) {
cache = ((AbstractDelegatingCache<K, V>) cache).getDelegate();
}
if (cache instanceof DecoratedCache) {
long flags = ((DecoratedCache<K, V>) cache).getFlagsBitSet();
if (EnumUtil.containsAny(flags, FlagBitSets.OPERATION_HOTROD)) {
return hotRodConverter;
} else if (EnumUtil.containsAny(flags, FlagBitSets.OPERATION_MEMCACHED)) {
return memcachedConverter;
}
}
return embeddedConverter;
}
private static class EmbeddedTypeConverter implements TypeConverter<Object, Object, Object, Object> {
private static final Log log = LogFactory.getLog(EmbeddedTypeConverter.class);
private Marshaller marshaller;
@Override
public Object boxKey(Object key) {
return key;
}
@Override
public Object boxValue(Object value) {
return value;
}
@Override
public Object unboxKey(Object target) {
return unboxValue(target);
}
@Override
public Object unboxValue(Object target) {
if (marshaller != null && target instanceof byte[]) {
try {
return marshaller.objectFromByteBuffer((byte[]) target);
} catch (Exception e) {
throw new CacheException("Unable to unmarshall return value");
}
}
if (target instanceof byte[]) {
// Try standard deserialization
try {
ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream((byte[]) target));
return ois.readObject();
} catch (Exception ee) {
if (log.isDebugEnabled())
log.debugf("Standard deserialization not in use for %s", Util.printArray((byte[]) target));
}
}
return target;
}
@Override
public boolean supportsInvocation(Flag flag) {
return false;
}
@Override
public void setMarshaller(Marshaller marshaller) {
this.marshaller = marshaller;
}
}
@Override
public AdvancedCache<K, V> withFlags(Flag... flags) {
AdvancedCache<K, V> returned = super.withFlags(flags);
if (returned != this && returned instanceof CompatibilityAdvancedCache) {
CompatibilityAdvancedCache cac = (CompatibilityAdvancedCache) returned;
cac.hotRodConverter = this.hotRodConverter;
cac.memcachedConverter = this.memcachedConverter;
cac.embeddedConverter = this.embeddedConverter;
}
return returned;
}
}