/*
* Copyright 2016 higherfrequencytrading.com
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package net.openhft.chronicle.engine.server.internal;
/**
* Created by Rob Austin
*/
import net.openhft.chronicle.core.Jvm;
import net.openhft.chronicle.core.pool.StringBuilderPool;
import net.openhft.chronicle.core.util.SerializableBiFunction;
import net.openhft.chronicle.core.util.SerializableUpdaterWithArg;
import net.openhft.chronicle.engine.api.map.MapView;
import net.openhft.chronicle.engine.api.tree.RequestContext;
import net.openhft.chronicle.engine.map.remote.RemoteKeyValueStore;
import net.openhft.chronicle.map.ChronicleMap;
import net.openhft.chronicle.network.connection.CoreFields;
import net.openhft.chronicle.wire.*;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.function.BiConsumer;
import java.util.function.Function;
import static net.openhft.chronicle.engine.server.internal.MapWireHandler.EventId.*;
import static net.openhft.chronicle.engine.server.internal.MapWireHandler.Params.*;
import static net.openhft.chronicle.network.connection.CoreFields.reply;
/**
* @author Rob Austin.
*/
public class MapWireHandler<K, V> extends AbstractHandler {
private static final StringBuilderPool SBP = new StringBuilderPool();
private static final Logger LOG = LoggerFactory.getLogger(MapWireHandler.class);
private final StringBuilder eventName = new StringBuilder();
private final CspManager cspManager;
private BiConsumer<ValueOut, V> vToWire;
@Nullable
private Function<ValueIn, K> wireToK;
@Nullable
private Function<ValueIn, V> wireToV;
@Nullable
private WireIn inWire = null;
@Nullable
private MapView<K, V> map;
private boolean charSequenceValue;
private long tid;
private final BiConsumer<WireIn, Long> dataConsumer = new BiConsumer<WireIn, Long>() {
@SuppressWarnings("ConstantConditions")
@Override
public void accept(WireIn wireIn, Long inputTid) {
try {
eventName.setLength(0);
@NotNull final ValueIn valueIn = inWire.readEventName(eventName);
assert startEnforceInValueReadCheck(inWire);
if (put.contentEquals(eventName)) {
valueIn.marshallable(wire -> {
@NotNull final Params[] params = put.params();
final K key = wireToK.apply(wire.read(params[0]));
final V value = wireToV.apply(wire.read(params[1]));
nullCheck(key);
nullCheck(value);
if (LOG.isDebugEnabled())
Jvm.debug().on(getClass(), "putting key=" + key);
map.put(key, value);
});
return;
}
if (remove.contentEquals(eventName)) {
final K key = wireToK.apply(valueIn);
nullCheck(key);
map.remove(key);
return;
}
if (update2.contentEquals(eventName)) {
valueIn.marshallable(wire -> {
@NotNull final Params[] params = update2.params();
@NotNull final SerializableUpdaterWithArg updater = (SerializableUpdaterWithArg) wire.read(params[0]).object(Object.class);
@Nullable final Object arg = wire.read(params[1]).object(Object.class);
map.asyncUpdate(updater, arg);
});
return;
}
outWire.writeDocument(true, wire -> outWire.writeEventName(CoreFields.tid).int64(tid));
writeData(inWire, out -> {
if (clear.contentEquals(eventName)) {
skipValue(valueIn);
map.clear();
return;
}
if (putAll.contentEquals(eventName)) {
valueIn.sequence(map, (m, v) -> {
while (v.hasNextSequenceItem()) {
valueIn.marshallable(wire -> m.put(
wireToK.apply(wire.read(put.params()[0])),
wireToV.apply(wire.read(put.params()[1]))));
}
});
return;
}
if (EventId.putIfAbsent.contentEquals(eventName)) {
valueIn.marshallable(wire -> {
@NotNull final Params[] params = putIfAbsent.params();
final K key = wireToK.apply(wire.read(params[0]));
final V newValue = wireToV.apply(wire.read(params[1]));
final V result = map.putIfAbsent(key, newValue);
nullCheck(key);
nullCheck(newValue);
vToWire.accept(outWire.writeEventName(reply), result);
});
return;
}
if (size.contentEquals(eventName)) {
skipValue(valueIn);
outWire.writeEventName(reply).int64(map.longSize());
return;
}
if (keySet.contentEquals(eventName) ||
values.contentEquals(eventName) ||
entrySet.contentEquals(eventName)) {
skipValue(valueIn);
cspManager.createProxy(eventName.toString());
return;
}
if (containsKey.contentEquals(eventName)) {
final K key = wireToK.apply(valueIn);
nullCheck(key);
outWire.writeEventName(reply)
.bool(map.containsKey(key));
return;
}
if (containsValue.contentEquals(eventName)) {
final V value = wireToV.apply(valueIn);
nullCheck(value);
final boolean aBoolean = map.containsValue(value);
outWire.writeEventName(reply).bool(
aBoolean);
return;
}
if (get.contentEquals(eventName)) {
final K key = wireToK.apply(valueIn);
nullCheck(key);
if (charSequenceValue) {
StringBuilder sb = SBP.acquireStringBuilder();
vToWire.accept(outWire.writeEventName(reply), (V) ((ChronicleMap) map).getUsing(key, sb));
} else {
vToWire.accept(outWire.writeEventName(reply), map.get(key));
}
return;
}
if (getAndPut.contentEquals(eventName)) {
valueIn.marshallable(wire -> {
@NotNull final Params[] params = getAndPut.params();
final K key = wireToK.apply(wire.read(params[0]));
final V value = wireToV.apply(wire.read(params[1]));
nullCheck(key);
nullCheck(value);
vToWire.accept(outWire.writeEventName(reply),
map.getAndPut(key, value));
});
return;
}
if (getAndRemove.contentEquals(eventName)) {
final K key = wireToK.apply(valueIn);
nullCheck(key);
vToWire.accept(outWire.writeEventName(reply), map.getAndRemove(key));
return;
}
if (replace.contentEquals(eventName)) {
valueIn.marshallable(wire -> {
@NotNull final Params[] params = replace.params();
final K key = wireToK.apply(wire.read(params[0]));
final V value = wireToV.apply(wire.read(params[1]));
nullCheck(key);
nullCheck(value);
vToWire.accept(outWire.writeEventName(reply),
map.replace(key, value));
});
return;
}
if (replaceForOld.contentEquals(eventName)) {
valueIn.marshallable(wire -> {
@NotNull final Params[] params = replaceForOld.params();
final K key = wireToK.apply(wire.read(params[0]));
V oldValue = wireToV.apply(wire.read(params[1]));
if (charSequenceValue)
oldValue = (V) oldValue.toString();
final V newValue = wireToV.apply(wire.read(params[2]));
nullCheck(key);
nullCheck(oldValue);
nullCheck(newValue);
outWire.writeEventName(reply).bool(map.replace(key, oldValue, newValue));
});
return;
}
if (putIfAbsent.contentEquals(eventName)) {
valueIn.marshallable(wire -> {
@NotNull final Params[] params = putIfAbsent.params();
final K key = wireToK.apply(wire.read(params[0]));
final V value = wireToV.apply(wire.read(params[1]));
nullCheck(key);
nullCheck(value);
vToWire.accept(outWire.writeEventName(reply),
map.putIfAbsent(key, value));
});
return;
}
if (removeWithValue.contentEquals(eventName)) {
valueIn.marshallable(wire -> {
@NotNull final Params[] params = removeWithValue.params();
final K key = wireToK.apply(wire.read(params[0]));
final V value = wireToV.apply(wire.read(params[1]));
nullCheck(key);
nullCheck(value);
outWire.writeEventName(reply).bool(map.remove(key, value));
});
}
if (hashCode.contentEquals(eventName)) {
skipValue(valueIn);
outWire.writeEventName(reply).int32(map.hashCode());
return;
}
if (applyTo2.contentEquals(eventName)) {
valueIn.marshallable(wire -> {
@NotNull final Params[] params = applyTo2.params();
@Nullable final SerializableBiFunction function = (SerializableBiFunction) wire.read(params[0]).object(Object.class);
@Nullable final Object arg = wire.read(params[1]).object(Object.class);
//call typed object
outWire.writeEventName(reply).object(map.applyTo(function, arg));
});
return;
}
if (update4.contentEquals(eventName)) {
valueIn.marshallable(wire -> {
@NotNull final Params[] params = update4.params();
@NotNull final SerializableUpdaterWithArg updater = (SerializableUpdaterWithArg) wire.read(params[0]).object(Object.class);
@Nullable final Object updateArg = wire.read(params[1]).object(Object.class);
@NotNull final SerializableBiFunction returnFunction = (SerializableBiFunction) wire.read(params[2]).object(Object.class);
@Nullable final Object returnArg = wire.read(params[3]).object(Object.class);
outWire.writeEventName(reply).object(map.syncUpdate(updater, updateArg, returnFunction, returnArg));
});
return;
}
throw new IllegalStateException("unsupported event=" + eventName);
});
} catch (Exception e) {
Jvm.warn().on(getClass(), e);
} finally {
assert endEnforceInValueReadCheck(inWire );
}
}
};
public MapWireHandler(CspManager cspManager) {
this.cspManager = cspManager;
}
/**
* @param in the data the has come in from network
* @param out the data that is going out to network
* @param map the map that is being processed
* @param tid the transaction id of the event
* @param wireAdapter adapts keys and values to and from wire
* @param requestContext the uri of the event
*/
public void process(@NotNull final WireIn in,
@NotNull final WireOut out,
@NotNull MapView map,
long tid,
@NotNull final WireAdapter wireAdapter,
@NotNull final RequestContext requestContext) {
this.vToWire = wireAdapter.valueToWire();
this.wireToK = wireAdapter.wireToKey();
this.wireToV = wireAdapter.wireToValue();
this.requestContext = requestContext;
setOutWire(out);
try {
this.inWire = in;
this.outWire = out;
this.map = map;
charSequenceValue = map instanceof ChronicleMap &&
CharSequence.class == ((ChronicleMap) map).valueClass();
assert !(map instanceof RemoteKeyValueStore) : "the server should not be a " +
"remove " +
"map";
this.tid = tid;
dataConsumer.accept(in, tid);
} catch (Exception e) {
Jvm.warn().on(getClass(), "", e);
}
}
public enum Params implements WireKey {
key,
value,
oldValue,
eventType,
newValue,
timestamp,
identifier,
entry,
updateFunction,
updateArg,
function,
arg,
}
public enum EventId implements ParameterizeWireKey {
size,
containsKey(key),
containsValue(value),
get(key),
getAndPut(key, value),
put(key, value),
getAndRemove(key),
remove(key),
clear,
keySet,
values,
entrySet,
replace(key, value),
replaceForOld(key, oldValue, newValue),
putIfAbsent(key, value),
removeWithValue(key, value),
toString,
putAll,
hashCode,
createChannel,
entrySetRestricted,
mapForKey,
putMapped,
keyBuilder,
valueBuilder,
remoteIdentifier,
numberOfSegments,
applyTo2(function, arg),
update2(updateFunction, updateArg),
update4(updateFunction, updateArg, function, arg),
bootstrap;
private final WireKey[] params;
<P extends WireKey> EventId(P... params) {
this.params = params;
}
@NotNull
public <P extends WireKey> P[] params() {
return (P[]) this.params;
}
}
}