com.tangosol.internal.util.processor.CacheProcessors Maven / Gradle / Ivy
Show all versions of coherence Show documentation
/*
* Copyright (c) 2000, 2023, Oracle and/or its affiliates.
*
* Licensed under the Universal Permissive License v 1.0 as shown at
* https://oss.oracle.com/licenses/upl.
*/
package com.tangosol.internal.util.processor;
import com.tangosol.io.ExternalizableLite;
import com.tangosol.io.pof.PofReader;
import com.tangosol.io.pof.PofWriter;
import com.tangosol.io.pof.PortableObject;
import com.tangosol.net.GuardSupport;
import com.tangosol.net.Guardian;
import com.tangosol.net.NamedCache;
import com.tangosol.util.BinaryEntry;
import com.tangosol.util.ConverterCollections;
import com.tangosol.util.ExternalizableHelper;
import com.tangosol.util.InvocableMap;
import com.tangosol.util.LiteMap;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.function.BiFunction;
import java.util.function.Function;
import jakarta.json.bind.annotation.JsonbProperty;
/**
* Contains factory methods and entry processor classes that are used to implement
* functionality exposed via different variants of {@link NamedCache} API.
*
* @author as 2015.01.17
* @since 12.2.1
*/
public class CacheProcessors
{
// ---- Factory methods -------------------------------------------------
public static InvocableMap.EntryProcessor nop()
{
return new Null<>();
}
public static InvocableMap.EntryProcessor get()
{
return new Get<>();
}
public static InvocableMap.EntryProcessor binaryGet()
{
return new BinaryGet<>();
}
public static InvocableMap.EntryProcessor> getOrDefault()
{
return new GetOrDefault<>();
}
public static InvocableMap.EntryProcessor put(V value, long cMillis)
{
return new Put<>(value, cMillis);
}
public static InvocableMap.EntryProcessor putAll(Map extends K, ? extends V> map)
{
return new PutAll<>(map);
}
public static InvocableMap.EntryProcessor putIfAbsent(V value)
{
return new PutIfAbsent<>(value);
}
public static InvocableMap.EntryProcessor remove()
{
return new Remove<>();
}
public static InvocableMap.EntryProcessor removeBlind()
{
return new RemoveBlind<>();
}
public static InvocableMap.EntryProcessor remove(Object value)
{
return new RemoveValue<>(value);
}
public static InvocableMap.EntryProcessor replace(V value)
{
return new Replace<>(value);
}
public static InvocableMap.EntryProcessor replace(V oldValue, V newValue)
{
return new ReplaceValue<>(oldValue, newValue);
}
// ---- Lambda-based processors -----------------------------------------
/**
* Return an entry processor which replaces the value of an entry by
* applying the specified function to key and current value.
*
* @param key type
* @param value type
* @param function function that should be used to compute new value
*
* @return a higher order function implementing
* {@link InvocableMap.EntryProcessor} interface
*/
public static InvocableMap.EntryProcessor replace(
BiFunction super K, ? super V, ? extends V> function)
{
Objects.requireNonNull(function);
return (entry) ->
{
entry.setValue(function.apply(entry.getKey(), entry.getValue()));
return null;
};
}
/**
* Return an entry processor that attempts to compute the value
* using the given mapping function, if the specified key is not already
* associated with a value (or is mapped to {@code null}), and enters it
* into this map unless {@code null}.
*
* @param key type
* @param value type
* @param mappingFunction the function to compute a value
*
* @return a higher order function implementing
* {@link InvocableMap.EntryProcessor} interface
*/
public static InvocableMap.EntryProcessor computeIfAbsent(
Function super K, ? extends V> mappingFunction)
{
return (entry) ->
{
V value = entry.getValue();
if (value == null)
{
value = mappingFunction.apply(entry.getKey());
if (value != null)
{
entry.setValue(value);
}
}
return value;
};
}
/**
* Return an entry processor that attempts to compute a new mapping
* using the remapping function, if the value is present and non-null.
*
* If the remapping function returns {@code null}, the mapping is
* removed. If the function itself throws an (unchecked) exception, the
* exception is rethrown, and the current mapping is left unchanged.
*
* @param key type
* @param value type
* @param remappingFunction the function to compute a value
*
* @return a higher order function implementing
* {@link InvocableMap.EntryProcessor} interface
*/
public static InvocableMap.EntryProcessor computeIfPresent(
BiFunction super K, ? super V, ? extends V> remappingFunction)
{
return (entry) ->
{
V oldValue = entry.getValue();
if (oldValue != null)
{
V newValue = remappingFunction.apply(entry.getKey(), oldValue);
if (newValue == null)
{
entry.remove(false);
}
else
{
entry.setValue(newValue);
return newValue;
}
}
return null;
};
}
/**
* Return an entry processor that computes a new value of an entry by
* applying specified remapping function to the key and current value.
*
* If the remapping function returns {@code null}, the mapping is
* removed. If the function itself throws an (unchecked) exception, the
* exception is rethrown, and the current mapping is left unchanged.
*
* @param key type
* @param value type
* @param remappingFunction the function to compute a value
*
* @return a higher order function implementing
* {@link InvocableMap.EntryProcessor} interface
*/
public static InvocableMap.EntryProcessor compute(
BiFunction super K, ? super V, ? extends V> remappingFunction)
{
return (entry) ->
{
Objects.requireNonNull(remappingFunction);
V oldValue = entry.getValue();
V newValue = remappingFunction.apply(entry.getKey(), oldValue);
if (newValue == null)
{
if (entry.isPresent())
{
entry.remove(false);
}
return null;
}
else
{
entry.setValue(newValue);
return newValue;
}
};
}
/**
* Return an entry processor that sets entry value to the given
* non-null value (if the entry is absent or null) or to the result
* of the remapping function applied to the old and new value.
*
* If the remapping function returns {@code null}, the mapping is
* removed. If the function itself throws an (unchecked) exception, the
* exception is rethrown, and the current mapping is left unchanged.
*
* @param key type
* @param value type
* @param value the non-null value to be merged with the
* existing value or to be associated with the
* key if the existing value is absent or null
* @param remappingFunction the function to compute a value
*
* @return a higher order function implementing
* {@link InvocableMap.EntryProcessor} interface
*/
public static InvocableMap.EntryProcessor merge(
V value, BiFunction super V, ? super V, ? extends V> remappingFunction)
{
return (entry) ->
{
Objects.requireNonNull(remappingFunction);
Objects.requireNonNull(value);
V valueOld = entry.getValue();
V valueNew = valueOld == null
? value
: remappingFunction.apply(valueOld, value);
if (valueNew == null)
{
entry.remove(false);
}
else
{
entry.setValue(valueNew);
}
return valueNew;
};
}
// ---- Entry Processors ------------------------------------------------
/**
* Abstract base class for entry processors.
*
* @param the type of the Map entry key
* @param the type of the Map entry value
* @param the type of value returned by the EntryProcessor
*/
static abstract class BaseProcessor
extends ExternalizableHelper
implements InvocableMap.EntryProcessor,
ExternalizableLite, PortableObject
{
// ---- ExternalizableLite methods ----------------------------------
@Override
public void readExternal(DataInput in)
throws IOException
{
}
@Override
public void writeExternal(DataOutput out)
throws IOException
{
}
// ---- PortableObject methods --------------------------------------
@Override
public void readExternal(PofReader in)
throws IOException
{
}
@Override
public void writeExternal(PofWriter out)
throws IOException
{
}
}
/**
* Null entry processor.
*
* @param the type of the Map entry key
* @param the type of the Map entry value
*/
public static class Null
extends BaseProcessor
{
public Void process(InvocableMap.Entry entry)
{
return null;
}
}
/**
* Get entry processor.
*
* @param the type of the Map entry key
* @param the type of the Map entry value
*/
public static class Get
extends BaseProcessor
{
public V process(InvocableMap.Entry entry)
{
return entry.getValue();
}
@Override
public Map processAll(Set extends InvocableMap.Entry> setEntries)
{
Map mapResults = new LiteMap<>();
Guardian.GuardContext ctxGuard = GuardSupport.getThreadContext();
long cMillis = ctxGuard == null ? 0L : ctxGuard.getTimeoutMillis();
for (Iterator extends InvocableMap.Entry> iter = setEntries.iterator(); iter.hasNext(); )
{
InvocableMap.Entry entry = iter.next();
if (entry.isPresent())
{
mapResults.put(entry.getKey(), process(entry));
}
iter.remove();
if (ctxGuard != null)
{
ctxGuard.heartbeat(cMillis);
}
}
return mapResults;
}
}
/**
* Get entry processor that avoids value deserialization by returning
* Binary value directly, assuming it will be converted on the client
* via {@link ConverterCollections.ConverterEntry} or similar.
*
* @param the type of the Map entry key
* @param the type of the Map entry value
*/
public static class BinaryGet
extends Get
{
@SuppressWarnings("unchecked")
@Override
public V process(InvocableMap.Entry entry)
{
// cast below is a hack to make compiler happy,
// and only works because of type erasure, as both
// V and Binary erase into Object
return (V) entry.asBinaryEntry().getBinaryValue();
}
}
/**
* GetOrDefault entry processor.
*
* @param the type of the Map entry key
* @param the type of the Map entry value
*/
public static class GetOrDefault
extends BaseProcessor>
{
public Optional process(InvocableMap.Entry entry)
{
return Optional.ofNullable(entry.getValue());
}
}
/**
* Put entry processor.
*
* @param the type of the Map entry key
* @param the type of the Map entry value
*/
public static class Put
extends BaseProcessor
{
public Put()
{
}
public Put(V value, long cMillis)
{
m_value = value;
m_cMillis = cMillis;
}
// ---- EntryProcessor methods --------------------------------------
@Override
public Void process(InvocableMap.Entry entry)
{
if (entry instanceof BinaryEntry)
{
((BinaryEntry) entry).expire(m_cMillis);
}
entry.setValue(m_value);
return null;
}
// ---- ExternalizableLite methods ----------------------------------
@Override
public void readExternal(DataInput in)
throws IOException
{
m_value = readObject(in);
m_cMillis = readLong(in);
}
@Override
public void writeExternal(DataOutput out)
throws IOException
{
writeObject(out, m_value);
writeLong(out, m_cMillis);
}
// ---- PortableObject methods --------------------------------------
@Override
public void readExternal(PofReader in)
throws IOException
{
m_value = in.readObject(0);
m_cMillis = in.readLong(1);
}
@Override
public void writeExternal(PofWriter out)
throws IOException
{
out.writeObject(0, m_value);
out.writeLong(1, m_cMillis);
}
// ---- accessors ---------------------------------------------------
public V getValue()
{
return m_value;
}
public long getMillis()
{
return m_cMillis;
}
// ---- data members ------------------------------------------------
@JsonbProperty("value")
protected V m_value;
@JsonbProperty("ttl")
protected long m_cMillis;
}
/**
* PutAll entry processor.
*
* @param the type of the Map entry key
* @param the type of the Map entry value
*/
public static class PutAll
extends BaseProcessor
{
public PutAll()
{
}
public PutAll(Map extends K, ? extends V> map)
{
m_map = map;
}
// ---- EntryProcessor methods --------------------------------------
@Override
public Void process(InvocableMap.Entry entry)
{
// avoid returning the old value by using the synthetic variant
// of setValue.
entry.setValue(m_map.get(entry.getKey()), /*fSynthetic*/ false);
return null;
}
// ---- ExternalizableLite methods ----------------------------------
@Override
public void readExternal(DataInput in)
throws IOException
{
readMap(in, m_map, null);
}
@Override
public void writeExternal(DataOutput out)
throws IOException
{
writeMap(out, m_map);
}
// ---- PortableObject methods --------------------------------------
@Override
public void readExternal(PofReader in)
throws IOException
{
in.readMap(0, m_map);
}
@Override
public void writeExternal(PofWriter out)
throws IOException
{
out.writeMap(0, m_map);
}
// ---- accessors ---------------------------------------------------
public Map extends K, ? extends V> getMap()
{
return m_map;
}
// ---- data members ------------------------------------------------
@JsonbProperty("entries")
protected Map extends K, ? extends V> m_map = new HashMap<>();
}
/**
* PutIfAbsent entry processor.
*
* @param the type of the Map entry key
* @param the type of the Map entry value
*/
public static class PutIfAbsent
extends BaseProcessor
{
public PutIfAbsent()
{
}
public PutIfAbsent(V value)
{
m_value = value;
}
// ---- EntryProcessor methods --------------------------------------
@Override
public V process(InvocableMap.Entry entry)
{
if (entry.getValue() != null)
{
return entry.getValue();
}
else
{
entry.setValue(m_value);
return null;
}
}
// ---- ExternalizableLite methods ----------------------------------
@Override
public void readExternal(DataInput in)
throws IOException
{
m_value = readObject(in);
}
@Override
public void writeExternal(DataOutput out)
throws IOException
{
writeObject(out, m_value);
}
// ---- PortableObject methods --------------------------------------
@Override
public void readExternal(PofReader in)
throws IOException
{
m_value = in.readObject(0);
}
@Override
public void writeExternal(PofWriter out)
throws IOException
{
out.writeObject(0, m_value);
}
// ---- accessors ---------------------------------------------------
public V getValue()
{
return m_value;
}
// ---- data members ------------------------------------------------
@JsonbProperty("value")
protected V m_value;
}
/**
* Remove entry processor.
*
* @param the type of the Map entry key
* @param the type of the Map entry value
*/
public static class Remove
extends BaseProcessor
{
public V process(InvocableMap.Entry entry)
{
V value = entry.getValue();
entry.remove(false);
return value;
}
}
/**
* Remove entry processor.
*
* @param the type of the Map entry key
* @param the type of the Map entry value
*/
public static class RemoveBlind
extends BaseProcessor
{
public Boolean process(InvocableMap.Entry entry)
{
V value = entry.getValue();
entry.remove(false);
return value != null;
}
}
/**
* RemoveValue entry processor.
*
* @param the type of the Map entry key
* @param the type of the Map entry value
*/
public static class RemoveValue
extends BaseProcessor
{
public RemoveValue()
{
}
public RemoveValue(Object oValue)
{
m_oValue = oValue;
}
// ---- EntryProcessor methods --------------------------------------
@Override
public Boolean process(InvocableMap.Entry entry)
{
V valueCurrent = entry.getValue();
if (Objects.equals(valueCurrent, m_oValue))
{
entry.remove(false);
return true;
}
return false;
}
// ---- ExternalizableLite methods ----------------------------------
@Override
public void readExternal(DataInput in)
throws IOException
{
m_oValue = readObject(in);
}
@Override
public void writeExternal(DataOutput out)
throws IOException
{
writeObject(out, m_oValue);
}
// ---- PortableObject methods --------------------------------------
@Override
public void readExternal(PofReader in)
throws IOException
{
m_oValue = in.readObject(0);
}
@Override
public void writeExternal(PofWriter out)
throws IOException
{
out.writeObject(0, m_oValue);
}
// ---- accessors ---------------------------------------------------
public Object getValue()
{
return m_oValue;
}
// ---- data members ------------------------------------------------
@JsonbProperty("value")
protected Object m_oValue;
}
/**
* Replace entry processor.
*
* @param the type of the Map entry key
* @param the type of the Map entry value
*/
public static class Replace
extends BaseProcessor
{
public Replace()
{
}
public Replace(V value)
{
m_value = value;
}
// ---- EntryProcessor methods --------------------------------------
@Override
public V process(InvocableMap.Entry entry)
{
return entry.getValue() != null || entry.isPresent()
? entry.setValue(m_value)
: null;
}
// ---- ExternalizableLite methods ----------------------------------
@Override
public void readExternal(DataInput in)
throws IOException
{
m_value = readObject(in);
}
@Override
public void writeExternal(DataOutput out)
throws IOException
{
writeObject(out, m_value);
}
// ---- PortableObject methods --------------------------------------
@Override
public void readExternal(PofReader in)
throws IOException
{
m_value = in.readObject(0);
}
@Override
public void writeExternal(PofWriter out)
throws IOException
{
out.writeObject(0, m_value);
}
// ---- accessors ---------------------------------------------------
public V getValue()
{
return m_value;
}
// ---- data members ------------------------------------------------
@JsonbProperty("value")
protected V m_value;
}
/**
* ReplaceValue entry processor.
*
* @param the type of the Map entry key
* @param the type of the Map entry value
*/
public static class ReplaceValue
extends BaseProcessor
{
public ReplaceValue()
{
}
public ReplaceValue(V oldValue, V newValue)
{
m_oldValue = oldValue;
m_newValue = newValue;
}
// ---- EntryProcessor methods --------------------------------------
@Override
public Boolean process(InvocableMap.Entry entry)
{
V valueCurrent = entry.getValue();
if (entry.isPresent() && Objects.equals(valueCurrent, m_oldValue))
{
entry.setValue(m_newValue);
return true;
}
return false;
}
// ---- ExternalizableLite methods ----------------------------------
@Override
public void readExternal(DataInput in)
throws IOException
{
m_oldValue = readObject(in);
m_newValue = readObject(in);
}
@Override
public void writeExternal(DataOutput out)
throws IOException
{
writeObject(out, m_oldValue);
writeObject(out, m_newValue);
}
// ---- PortableObject methods --------------------------------------
@Override
public void readExternal(PofReader in)
throws IOException
{
m_oldValue = in.readObject(0);
m_newValue = in.readObject(1);
}
@Override
public void writeExternal(PofWriter out)
throws IOException
{
out.writeObject(0, m_oldValue);
out.writeObject(1, m_newValue);
}
// ---- accessors ---------------------------------------------------
public V getOldValue()
{
return m_oldValue;
}
public V getNewValue()
{
return m_newValue;
}
// ---- data members ------------------------------------------------
@JsonbProperty("oldValue")
protected V m_oldValue;
@JsonbProperty("newValue")
protected V m_newValue;
}
}