package org.infinispan.functional.decorators;
import static org.infinispan.commons.marshall.MarshallableFunctions.identity;
import static org.infinispan.commons.marshall.MarshallableFunctions.removeConsumer;
import static org.infinispan.commons.marshall.MarshallableFunctions.removeIfValueEqualsReturnBoolean;
import static org.infinispan.commons.marshall.MarshallableFunctions.removeReturnBoolean;
import static org.infinispan.commons.marshall.MarshallableFunctions.removeReturnPrevOrNull;
import static org.infinispan.commons.marshall.MarshallableFunctions.returnReadOnlyFindIsPresent;
import static org.infinispan.commons.marshall.MarshallableFunctions.returnReadOnlyFindOrNull;
import static org.infinispan.commons.marshall.MarshallableFunctions.setValueConsumer;
import static org.infinispan.commons.marshall.MarshallableFunctions.setValueIfAbsentReturnBoolean;
import static org.infinispan.commons.marshall.MarshallableFunctions.setValueIfEqualsReturnBoolean;
import static org.infinispan.commons.marshall.MarshallableFunctions.setValueIfPresentReturnBoolean;
import static org.infinispan.commons.marshall.MarshallableFunctions.setValueIfPresentReturnPrevOrNull;
import static org.infinispan.commons.marshall.MarshallableFunctions.setValueReturnPrevOrNull;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.function.Function;
import javax.cache.Cache;
import javax.cache.CacheManager;
import javax.cache.configuration.CacheEntryListenerConfiguration;
import javax.cache.configuration.Configuration;
import javax.cache.integration.CompletionListener;
import javax.cache.processor.EntryProcessor;
import javax.cache.processor.EntryProcessorException;
import javax.cache.processor.EntryProcessorResult;
import javax.cache.processor.MutableEntry;
import org.infinispan.AdvancedCache;
import org.infinispan.commons.api.functional.EntryView.ReadEntryView;
import org.infinispan.commons.api.functional.EntryView.ReadWriteEntryView;
import org.infinispan.commons.api.functional.FunctionalMap.ReadOnlyMap;
import org.infinispan.commons.api.functional.FunctionalMap.ReadWriteMap;
import org.infinispan.commons.api.functional.FunctionalMap.WriteOnlyMap;
import org.infinispan.commons.api.functional.Listeners.ReadWriteListeners;
import org.infinispan.commons.api.functional.Listeners.WriteListeners;
import org.infinispan.commons.api.functional.Traversable;
import org.infinispan.commons.marshall.Externalizer;
import org.infinispan.commons.marshall.SerializeWith;
import org.infinispan.functional.impl.FunctionalMapImpl;
import org.infinispan.functional.impl.ReadOnlyMapImpl;
import org.infinispan.functional.impl.ReadWriteMapImpl;
import org.infinispan.functional.impl.Traversables;
import org.infinispan.functional.impl.WriteOnlyMapImpl;
/**
* A {@link Cache} implementation that uses the operations exposed by
* {@link ReadOnlyMap}, {@link WriteOnlyMap} and {@link ReadWriteMap}, and
* validates their usefulness.
*/
public final class FunctionalJCache<K, V> implements Cache<K, V>, FunctionalListeners<K, V> {
final ReadOnlyMap<K, V> readOnly;
final WriteOnlyMap<K, V> writeOnly;
final ReadWriteMap<K, V> readWrite;
// Rudimentary constructor, we'll provide more idiomatic construction
// via main Infinispan class which is still to be defined
private FunctionalJCache(FunctionalMapImpl<K, V> map) {
this.readOnly = ReadOnlyMapImpl.create(map);
this.writeOnly = WriteOnlyMapImpl.create(map);
this.readWrite = ReadWriteMapImpl.create(map);
}
public static <K, V> Cache<K, V> create(AdvancedCache<K, V> cache) {
return new FunctionalJCache<>(FunctionalMapImpl.create(cache));
}
public static <K, V> Cache<K, V> create(FunctionalMapImpl<K, V> map) {
return new FunctionalJCache<>(map);
}
@Override
public WriteListeners<K, V> writeOnlyListeners() {
return writeOnly.listeners();
}
@Override
public ReadWriteListeners<K, V> readWriteListeners() {
return readWrite.listeners();
}
@Override
public V get(K key) {
return await(readOnly.eval(key, returnReadOnlyFindOrNull()));
}
@Override
public Map<K, V> getAll(Set<? extends K> keys) {
Traversable<ReadEntryView<K, V>> t = readOnly.evalMany(keys, identity());
return t.collect(HashMap::new, (m, ro) -> ro.find().ifPresent(v -> m.put(ro.key(), v)), HashMap::putAll);
}
@Override
public boolean containsKey(K key) {
return await(readOnly.eval(key, returnReadOnlyFindIsPresent()));
}
@Override
public void put(K key, V value) {
await(writeOnly.eval(key, value, setValueConsumer()));
}
@Override
public V getAndPut(K key, V value) {
return await(readWrite.eval(key, value, setValueReturnPrevOrNull()));
}
@Override
public void putAll(Map<? extends K, ? extends V> map) {
await(writeOnly.evalMany(map, setValueConsumer()));
}
@Override
public boolean putIfAbsent(K key, V value) {
return await(readWrite.eval(key, value, setValueIfAbsentReturnBoolean()));
}
@Override
public boolean remove(K key) {
return await(readWrite.eval(key, removeReturnBoolean()));
}
@Override
public boolean remove(K key, V oldValue) {
return await(readWrite.eval(key, oldValue, removeIfValueEqualsReturnBoolean()));
}
@Override
public V getAndRemove(K key) {
return await(readWrite.eval(key, removeReturnPrevOrNull()));
}
@Override
public boolean replace(K key, V oldValue, V newValue) {
return await(readWrite.eval(key, newValue, setValueIfEqualsReturnBoolean(oldValue)));
}
@Override
public boolean replace(K key, V value) {
return await(readWrite.eval(key, value, setValueIfPresentReturnBoolean()));
}
@Override
public V getAndReplace(K key, V value) {
return await(readWrite.eval(key, value, setValueIfPresentReturnPrevOrNull()));
}
@Override
public void removeAll(Set<? extends K> keys) {
await(writeOnly.evalMany(keys, removeConsumer()));
}
@Override
public void removeAll() {
await(writeOnly.evalAll(removeConsumer()));
}
@Override
public void clear() {
await(writeOnly.truncate());
}
@Override
public Iterator<Entry<K, V>> iterator() {
Traversable<Entry<K, V>> t = readOnly.entries().map(rw -> new Entry<K, V>() {
@Override
public K getKey() {
return rw.key();
}
@Override
public V getValue() {
return rw.get();
}
@Override
public <T> T unwrap(Class<T> clazz) {
return null;
}
// In Java 8, default remove() implementation is unsupported, but
// adding support for it would be relatively trivial if following
// similar solution to the one in ConcurrentMapDecorator
});
return Traversables.asIterator(t);
}
@Override
public <T> T invoke(K key, EntryProcessor<K, V, T> entryProcessor, Object... arguments) throws EntryProcessorException {
return await(readWrite.eval(key, new InvokeFunction<>(entryProcessor, arguments)));
}
@SerializeWith(value = InvokeFunction.Externalizer0.class)
private static final class InvokeFunction<K, V, T> implements Function<ReadWriteEntryView<K, V>, T> {
private final EntryProcessor<K, V, T> entryProcessor;
private final Object[] arguments;
private InvokeFunction(EntryProcessor<K, V, T> entryProcessor, Object[] arguments) {
this.entryProcessor = entryProcessor;
this.arguments = arguments;
}
@Override
public T apply(ReadWriteEntryView<K, V> rw) {
return entryProcessor.process(new ReadWriteMutableEntry<>(rw), arguments);
}
public static final class Externalizer0 implements Externalizer<InvokeFunction<?, ?, ?>> {
public void writeObject(ObjectOutput oo, InvokeFunction<?, ?, ?> o) throws IOException {
oo.writeObject(o.entryProcessor);
oo.writeInt(o.arguments.length);
for (Object argument : o.arguments)
oo.writeObject(argument);
}
public InvokeFunction<?, ?, ?> readObject(ObjectInput input) throws IOException, ClassNotFoundException {
EntryProcessor<?, ?, ?> entryProcessor = (EntryProcessor<?, ?, ?>) input.readObject();
int length = input.readInt();
Object[] arguments = new Object[length];
for (int i = 0; i < length; i++) arguments[i] = input.readObject();
return new InvokeFunction<>(entryProcessor, arguments);
}
}
}
private static final class ReadWriteMutableEntry<K, V> implements MutableEntry<K, V> {
final ReadWriteEntryView<K, V> rw;
private ReadWriteMutableEntry(ReadWriteEntryView<K, V> rw) {
this.rw = rw;
}
@Override
public boolean exists() {
return rw.find().isPresent();
}
@Override
public void remove() {
rw.remove();
}
@Override
public void setValue(V value) {
rw.set(value);
}
@Override
public K getKey() {
return rw.key();
}
@Override
public V getValue() {
return rw.find().orElse(null);
}
@Override
public <T> T unwrap(Class<T> clazz) {
return null; // TODO: Customise this generated block
}
}
@Override
public <T> Map<K, EntryProcessorResult<T>> invokeAll(Set<? extends K> keys, EntryProcessor<K, V, T> entryProcessor, Object... arguments) {
Traversable<EntryProcessorResultWithKey<K, T>> t = readWrite.evalMany(
keys, new InvokeAllFunction<>(entryProcessor, arguments));
return t.collect(HashMap::new, (m, res) -> m.put(res.key, res), HashMap::putAll);
}
@SerializeWith(value = InvokeAllFunction.Externalizer0.class)
private static final class InvokeAllFunction<K, V, T> implements Function<ReadWriteEntryView<K, V>, EntryProcessorResultWithKey<K, T>> {
private final EntryProcessor<K, V, T> entryProcessor;
private final Object[] arguments;
private InvokeAllFunction(EntryProcessor<K, V, T> entryProcessor, Object[] arguments) {
this.entryProcessor = entryProcessor;
this.arguments = arguments;
}
@Override
public EntryProcessorResultWithKey<K, T> apply(ReadWriteEntryView<K, V> rw) {
T res = entryProcessor.process(new ReadWriteMutableEntry<>(rw), arguments);
return new EntryProcessorResultWithKey<>(rw.key(), res);
}
public static final class Externalizer0 implements Externalizer<InvokeAllFunction<?, ?, ?>> {
public void writeObject(ObjectOutput oo, InvokeAllFunction<?, ?, ?> o) throws IOException {
oo.writeObject(o.entryProcessor);
oo.writeInt(o.arguments.length);
for (Object argument : o.arguments)
oo.writeObject(argument);
}
public InvokeAllFunction<?, ?, ?> readObject(ObjectInput input) throws IOException, ClassNotFoundException {
EntryProcessor<?, ?, ?> entryProcessor = (EntryProcessor<?, ?, ?>) input.readObject();
int length = input.readInt();
Object[] arguments = new Object[length];
for (int i = 0; i < length; i++) arguments[i] = input.readObject();
return new InvokeAllFunction<>(entryProcessor, arguments);
}
}
}
@SerializeWith(EntryProcessorResultWithKey.Externalizer0.class)
private static final class EntryProcessorResultWithKey<K, T> implements EntryProcessorResult<T> {
final K key;
final T t;
public EntryProcessorResultWithKey(K key, T t) {
this.key = key;
this.t = t;
}
@Override
public T get() throws EntryProcessorException {
return t;
}
public static final class Externalizer0 implements Externalizer<EntryProcessorResultWithKey<?, ?>> {
@Override
public void writeObject(ObjectOutput oo, EntryProcessorResultWithKey<?, ?> o) throws IOException {
oo.writeObject(o.key);
oo.writeObject(o.t);
}
@Override
public EntryProcessorResultWithKey<?, ?> readObject(ObjectInput input) throws IOException, ClassNotFoundException {
Object key = input.readObject();
Object t = input.readObject();
return new EntryProcessorResultWithKey<>(key, t);
}
}
}
@Override
public void close() {
try {
readOnly.close();
} catch (Exception e) {
throw new AssertionError(e);
}
}
@Override
public boolean isClosed() {
return readOnly.getStatus().isTerminated();
}
@Override
public String getName() {
return readOnly.getName();
}
////////////////////////////////////////////////////////////////////////////
@Override
public void loadAll(Set<? extends K> keys, boolean replaceExistingValues, CompletionListener completionListener) {
// TODO: Customise this generated block
}
@Override
public <C extends Configuration<K, V>> C getConfiguration(Class<C> clazz) {
return null; // TODO: Customise this generated block
}
@Override
public <T> T unwrap(Class<T> clazz) {
return null; // TODO: Customise this generated block
}
@Override
public CacheManager getCacheManager() {
return null; // TODO: Customise this generated block
}
@Override
public void registerCacheEntryListener(CacheEntryListenerConfiguration<K, V> cacheEntryListenerConfiguration) {
// TODO: Customise this generated block
}
@Override
public void deregisterCacheEntryListener(CacheEntryListenerConfiguration<K, V> cacheEntryListenerConfiguration) {
// TODO: Customise this generated block
}
private static <T> T await(CompletableFuture<T> cf) {
try {
return cf.get();
} catch (InterruptedException | ExecutionException e) {
throw new Error(e);
}
}
}