mirror of
https://github.com/OpenHFT/Chronicle-Map.git
synced 2021-09-13 01:26:12 +00:00
Merge branch 'master' of github.com:OpenHFT/Chronicle-Map
Conflicts: pom.xml
This commit is contained in:
@@ -197,8 +197,9 @@ abstract class AbstractChannelReplicator implements Closeable {
|
||||
|
||||
|
||||
/**
|
||||
* checks the number of bytes written in this interval, if this number of bytes exceeds a threshold,
|
||||
* the selected will de-register the socket that is being written to, until the interval is finished.
|
||||
* checks the number of bytes written in this interval, if this number of bytes exceeds a
|
||||
* threshold, the selected will de-register the socket that is being written to, until the
|
||||
* interval is finished.
|
||||
*
|
||||
* @throws ClosedChannelException
|
||||
*/
|
||||
@@ -261,12 +262,22 @@ abstract class AbstractChannelReplicator implements Closeable {
|
||||
|
||||
@Override
|
||||
public boolean onEntry(final Bytes entry, final int chronicleId) {
|
||||
|
||||
long pos0 = in.position();
|
||||
|
||||
// used to denote that this is not a stateless map event
|
||||
in.writeByte(StatelessMapClient.EventId.STATEFUL_UPDATE.ordinal());
|
||||
|
||||
long sizeLocation = in.position();
|
||||
|
||||
// this is where we will store the size of the entry
|
||||
in.skip(SIZE_OF_SHORT);
|
||||
|
||||
final long start = in.position();
|
||||
externalizable.writeExternalEntry(entry, in, chronicleId);
|
||||
|
||||
if (in.position() == start) {
|
||||
in.position(in.position() - SIZE_OF_SHORT);
|
||||
in.position(pos0);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -276,7 +287,10 @@ abstract class AbstractChannelReplicator implements Closeable {
|
||||
if (entrySize > MAX_UNSIGNED_SHORT)
|
||||
throw new IllegalStateException("entry too large, the entry size=" + entrySize + ", " +
|
||||
"entries are limited to a size of " + MAX_UNSIGNED_SHORT);
|
||||
in.writeUnsignedShort(start - SIZE_OF_SHORT, entrySize);
|
||||
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("sending entry of entrySize=" + entrySize);
|
||||
in.writeUnsignedShort(sizeLocation, entrySize);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -407,7 +407,7 @@ public final class ChannelProvider implements Closeable {
|
||||
|
||||
@Override
|
||||
protected Closeable applyTo(ChronicleMapBuilder builder,
|
||||
Replica map, EntryExternalizable entryExternalizable) {
|
||||
Replica map, EntryExternalizable entryExternalizable, final ChronicleMap chronicleMap) {
|
||||
if (builder.entrySize() > maxEntrySize) {
|
||||
throw new IllegalArgumentException("During ReplicatingChannelBuilder setup, " +
|
||||
"maxEntrySize=" + maxEntrySize + " was specified, but map with " +
|
||||
|
||||
@@ -68,7 +68,7 @@ public final class ChannelProviderBuilder {
|
||||
replicatingCluster.asReplica,
|
||||
replicatingCluster.asEntryExternalizable,
|
||||
tcpReplicationConfig,
|
||||
maxEntrySize);
|
||||
maxEntrySize, null);
|
||||
replicatingCluster.add(tcpReplicator);
|
||||
}
|
||||
|
||||
|
||||
@@ -386,8 +386,13 @@ public class ChronicleMapBuilder<K, V> implements Cloneable,
|
||||
if (actualEntriesPerSegment > 0)
|
||||
return actualEntriesPerSegment;
|
||||
int as = actualSegments();
|
||||
long actualEntries =
|
||||
as == 1 ? entries + 1 : // The extra 1 might not be needed.
|
||||
as <= 4 ? entries * 10L / 9 : // 10%, assumes you are trying to be tight.
|
||||
entries * 5L / 4; // 25%
|
||||
|
||||
// round up to the next multiple of 64.
|
||||
return (int) (Math.max(1, entries * 2L / as) + 63) & ~63;
|
||||
return (int) (Math.max(1, actualEntries / as) + 63) & ~63;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -786,7 +791,7 @@ public class ChronicleMapBuilder<K, V> implements Cloneable,
|
||||
return identifier != -1;
|
||||
}
|
||||
|
||||
private void preMapConstruction() {
|
||||
void preMapConstruction() {
|
||||
keyBuilder.objectSerializer(objectSerializer());
|
||||
valueBuilder.objectSerializer(objectSerializer());
|
||||
|
||||
@@ -805,7 +810,7 @@ public class ChronicleMapBuilder<K, V> implements Cloneable,
|
||||
if (map instanceof ReplicatedChronicleMap) {
|
||||
ReplicatedChronicleMap result = (ReplicatedChronicleMap) map;
|
||||
for (Replicator replicator : replicators.values()) {
|
||||
Closeable token = replicator.applyTo(this, result, result);
|
||||
Closeable token = replicator.applyTo(this, result, result, map);
|
||||
if (replicators.size() == 1 && token.getClass() == UdpReplicator.class) {
|
||||
LOG.warn(
|
||||
"MISSING TCP REPLICATION : The UdpReplicator only attempts to read data " +
|
||||
|
||||
@@ -41,6 +41,7 @@ public abstract class Replicator {
|
||||
* map configurations, not accessible via the map instance.
|
||||
* @param map a replicated map instance. Provides basic tools for replication implementation.
|
||||
* @param entryExternalizable the callback for ser/deser implementation in the replicator
|
||||
* @param chronicleMap
|
||||
* @return a {@code Closeable} token to control replication resources.
|
||||
* It should be closed on closing the replicated map.
|
||||
* @throws java.io.IOException if an io error occurred during the replicator setup
|
||||
@@ -49,6 +50,6 @@ public abstract class Replicator {
|
||||
* applied to a map (or the specified number of maps)
|
||||
*/
|
||||
protected abstract Closeable applyTo(ChronicleMapBuilder builder,
|
||||
Replica map, Replica.EntryExternalizable entryExternalizable)
|
||||
Replica map, Replica.EntryExternalizable entryExternalizable, final ChronicleMap chronicleMap)
|
||||
throws IOException;
|
||||
}
|
||||
|
||||
@@ -30,10 +30,18 @@ final class Replicators {
|
||||
|
||||
@Override
|
||||
protected Closeable applyTo(ChronicleMapBuilder builder,
|
||||
Replica map, Replica.EntryExternalizable entryExternalizable)
|
||||
Replica replica, Replica.EntryExternalizable entryExternalizable,
|
||||
final ChronicleMap chronicleMap)
|
||||
throws IOException {
|
||||
return new TcpReplicator(map, entryExternalizable, replicationConfig,
|
||||
builder.entrySize());
|
||||
|
||||
final KeyValueSerializer keyValueSerializer = new KeyValueSerializer(builder
|
||||
.keyBuilder, builder.valueBuilder);
|
||||
|
||||
StatelessServerConnector statelessServerConnector = new StatelessServerConnector
|
||||
(keyValueSerializer, chronicleMap);
|
||||
|
||||
return new TcpReplicator(replica, entryExternalizable, replicationConfig,
|
||||
builder.entrySize(), statelessServerConnector);
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -44,7 +52,7 @@ final class Replicators {
|
||||
|
||||
@Override
|
||||
protected Closeable applyTo(ChronicleMapBuilder builder,
|
||||
Replica map, Replica.EntryExternalizable entryExternalizable)
|
||||
Replica map, Replica.EntryExternalizable entryExternalizable, final ChronicleMap chronicleMap)
|
||||
throws IOException {
|
||||
return new UdpReplicator(map, entryExternalizable, replicationConfig,
|
||||
builder.entrySize());
|
||||
|
||||
@@ -0,0 +1,81 @@
|
||||
package net.openhft.chronicle.map;
|
||||
|
||||
import net.openhft.chronicle.map.serialization.*;
|
||||
import net.openhft.chronicle.map.threadlocal.Provider;
|
||||
import net.openhft.chronicle.map.threadlocal.ThreadLocalCopies;
|
||||
import net.openhft.lang.io.Bytes;
|
||||
import net.openhft.lang.model.Byteable;
|
||||
import net.openhft.lang.model.constraints.NotNull;
|
||||
|
||||
/**
|
||||
* uses a {@code SerializationBuilder} to read and write object(s) as bytes
|
||||
*
|
||||
* @author Rob Austin.
|
||||
*/
|
||||
class Serializer<O> {
|
||||
|
||||
final SizeMarshaller sizeMarshaller;
|
||||
final BytesReader<O> originalReader;
|
||||
transient Provider<BytesReader<O>> readerProvider;
|
||||
final Object originalWriter;
|
||||
transient Provider writerProvider;
|
||||
final MetaBytesInterop originalMetaValueWriter;
|
||||
final MetaProvider metaValueWriterProvider;
|
||||
|
||||
Serializer(final SerializationBuilder serializationBuilder) {
|
||||
|
||||
sizeMarshaller = serializationBuilder.sizeMarshaller();
|
||||
originalMetaValueWriter = serializationBuilder.metaInterop();
|
||||
metaValueWriterProvider = serializationBuilder.metaInteropProvider();
|
||||
|
||||
originalReader = serializationBuilder.reader();
|
||||
originalWriter = serializationBuilder.interop();
|
||||
|
||||
readerProvider = Provider.of((Class) originalReader.getClass());
|
||||
writerProvider = Provider.of((Class) originalWriter.getClass());
|
||||
}
|
||||
|
||||
public O readMarshallable(@NotNull Bytes in) throws IllegalStateException {
|
||||
|
||||
final ThreadLocalCopies copies = readerProvider.getCopies(null);
|
||||
final BytesReader<O> valueReader = readerProvider.get(copies, originalReader);
|
||||
|
||||
try {
|
||||
long valueSize = sizeMarshaller.readSize(in);
|
||||
return valueReader.read(in, valueSize, null);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public void writeMarshallable(O value, @NotNull Bytes out) {
|
||||
|
||||
ThreadLocalCopies copies = readerProvider.getCopies(null);
|
||||
Object valueWriter = writerProvider.get(copies, originalWriter);
|
||||
copies = writerProvider.getCopies(copies);
|
||||
|
||||
final long valueSize;
|
||||
|
||||
MetaBytesWriter metaValueWriter = null;
|
||||
|
||||
if ((value instanceof Byteable)) {
|
||||
valueSize = ((Byteable) value).maxSize();
|
||||
} else {
|
||||
copies = writerProvider.getCopies(copies);
|
||||
valueWriter = writerProvider.get(copies, originalWriter);
|
||||
copies = metaValueWriterProvider.getCopies(copies);
|
||||
metaValueWriter = metaValueWriterProvider.get(
|
||||
copies, originalMetaValueWriter, valueWriter, value);
|
||||
valueSize = metaValueWriter.size(valueWriter, value);
|
||||
}
|
||||
|
||||
sizeMarshaller.writeSize(out, valueSize);
|
||||
|
||||
if (metaValueWriter != null)
|
||||
metaValueWriter.write(valueWriter, out, value);
|
||||
else
|
||||
throw new UnsupportedOperationException("");
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,18 +1,16 @@
|
||||
package net.openhft.chronicle.map;
|
||||
|
||||
import net.openhft.chronicle.map.serialization.*;
|
||||
import net.openhft.chronicle.map.threadlocal.Provider;
|
||||
import net.openhft.chronicle.map.threadlocal.ThreadLocalCopies;
|
||||
import net.openhft.lang.io.ByteBufferBytes;
|
||||
import net.openhft.lang.io.BytesStore;
|
||||
import net.openhft.lang.io.serialization.ObjectFactory;
|
||||
import net.openhft.lang.model.Byteable;
|
||||
import net.openhft.lang.io.Bytes;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.swing.text.Segment;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.Serializable;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.channels.SocketChannel;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
@@ -21,73 +19,27 @@ import java.util.concurrent.atomic.AtomicLong;
|
||||
import static net.openhft.chronicle.map.StatelessMapClient.EventId.*;
|
||||
|
||||
/**
|
||||
*
|
||||
* **** THIS IS VERY MUCH IN DRAFT ***
|
||||
*
|
||||
* @author Rob Austin.
|
||||
*/
|
||||
public class StatelessMapClient<K, KI, MKI extends MetaBytesInterop<K, KI>,
|
||||
V, VW, MVW extends MetaBytesWriter<V, VW>> extends AbstractMap<K, V>
|
||||
implements ChronicleMap<K, V>, Serializable {
|
||||
class StatelessMapClient<K, V> implements ChronicleMap<K, V> {
|
||||
|
||||
final Class<K> kClass;
|
||||
private ThreadLocal<Buffer> sourceBuffer = new ThreadLocal<Buffer>();
|
||||
|
||||
final BytesReader<K> originalKeyReader;
|
||||
transient Provider<BytesReader<K>> keyReaderProvider;
|
||||
final KI originalKeyInterop;
|
||||
transient Provider<KI> keyInteropProvider;
|
||||
final MKI originalMetaKeyInterop;
|
||||
final MetaProvider<K, KI, MKI> metaKeyInteropProvider;
|
||||
|
||||
final Class<V> vClass;
|
||||
final SizeMarshaller valueSizeMarshaller;
|
||||
final BytesReader<V> originalValueReader;
|
||||
transient Provider<BytesReader<V>> valueReaderProvider;
|
||||
final VW originalValueWriter;
|
||||
transient Provider<VW> valueWriterProvider;
|
||||
final MVW originalMetaValueWriter;
|
||||
final MetaProvider<V, VW, MVW> metaValueWriterProvider;
|
||||
final ObjectFactory<V> valueFactory;
|
||||
final DefaultValueProvider<K, V> defaultValueProvider;
|
||||
|
||||
Map<Long, Object> transactionIDs = new HashMap<Long, Object>();
|
||||
private static final Logger LOG = LoggerFactory.getLogger(StatelessMapClient.class);
|
||||
|
||||
|
||||
AtomicLong transactionID = new AtomicLong();
|
||||
private final SocketChannel clientChannel;
|
||||
public static final ByteBuffer STATELESS_CLIENT_IDENTIFER = ByteBuffer.wrap(new byte[]{-127});
|
||||
|
||||
private final long lockTimeOutNS;
|
||||
|
||||
transient Segment[] segments; // non-final for close()
|
||||
// non-final for close() and because it is initialized out of constructor
|
||||
transient BytesStore ms;
|
||||
transient long headerSize;
|
||||
transient Set<Map.Entry<K, V>> entrySet;
|
||||
|
||||
private int bits;
|
||||
private int mask;
|
||||
private long timeoutMs = TimeUnit.SECONDS.toMillis(20);
|
||||
|
||||
|
||||
@Override
|
||||
public File file() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
throw new UnsupportedOperationException("This is not supported in the " + this.getClass()
|
||||
.getSimpleName());
|
||||
}
|
||||
|
||||
enum EventId {
|
||||
public static enum EventId {
|
||||
HEARTBEAT,
|
||||
STATEFUL_UPDATE,
|
||||
LONG_SIZE, SIZE,
|
||||
IS_EMPTY,
|
||||
CONTAINS_KEY,
|
||||
CONTAINS_VALUE,
|
||||
GET, PUT,
|
||||
REMOVE,
|
||||
PUT_ALL,
|
||||
CLEAR,
|
||||
KEY_SET,
|
||||
VALUES,
|
||||
@@ -98,6 +50,55 @@ public class StatelessMapClient<K, KI, MKI extends MetaBytesInterop<K, KI>,
|
||||
REMOVE_WITH_VALUE
|
||||
}
|
||||
|
||||
private final ThreadLocal<Buffer> sourceBuffer = new ThreadLocal<Buffer>();
|
||||
private final Map<Long, Object> transactionIDs = new HashMap<Long, Object>();
|
||||
|
||||
private final AtomicLong transactionID = new AtomicLong();
|
||||
|
||||
private long timeoutMs = TimeUnit.SECONDS.toMillis(20);
|
||||
|
||||
|
||||
public StatelessMapClient(final KeyValueSerializer<K,
|
||||
V> keyValueSerializer, final InetSocketAddress remote) throws IOException {
|
||||
|
||||
this.keyValueSerializer = keyValueSerializer;
|
||||
|
||||
clientChannel = SocketChannel.open();
|
||||
clientChannel.connect(remote);
|
||||
|
||||
doHandShaking();
|
||||
}
|
||||
|
||||
private void doHandShaking() throws IOException {
|
||||
|
||||
|
||||
while (STATELESS_CLIENT_IDENTIFER.remaining() > 0) {
|
||||
clientChannel.write(STATELESS_CLIENT_IDENTIFER);
|
||||
}
|
||||
|
||||
in.buffer().clear();
|
||||
|
||||
while (in.buffer().position() <= 0) {
|
||||
clientChannel.read(in.buffer());
|
||||
}
|
||||
|
||||
in.limit(in.buffer().position());
|
||||
in.position(0);
|
||||
byte remoteIdentifier = in.readByte();
|
||||
|
||||
LOG.info("Attached to a map with a remote identifier=" + remoteIdentifier);
|
||||
|
||||
}
|
||||
|
||||
|
||||
public File file() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public void close() {
|
||||
throw new UnsupportedOperationException("This is not supported in the " + this.getClass()
|
||||
.getSimpleName());
|
||||
}
|
||||
|
||||
long nextUniqueTransaction() {
|
||||
long time = System.currentTimeMillis();
|
||||
@@ -112,400 +113,235 @@ public class StatelessMapClient<K, KI, MKI extends MetaBytesInterop<K, KI>,
|
||||
}
|
||||
|
||||
|
||||
private final ByteBufferBytes target;
|
||||
private final SizeMarshaller keySizeMarshaller;
|
||||
|
||||
public StatelessMapClient(ByteBufferBytes target, ChronicleMapBuilder<K, V> builder) {
|
||||
this.target = target;
|
||||
|
||||
SerializationBuilder<K> keyBuilder = builder.keyBuilder;
|
||||
kClass = keyBuilder.eClass;
|
||||
keySizeMarshaller = keyBuilder.sizeMarshaller();
|
||||
originalKeyReader = keyBuilder.reader();
|
||||
|
||||
originalKeyInterop = (KI) keyBuilder.interop();
|
||||
|
||||
originalMetaKeyInterop = (MKI) keyBuilder.metaInterop();
|
||||
metaKeyInteropProvider = (MetaProvider<K, KI, MKI>) keyBuilder.metaInteropProvider();
|
||||
|
||||
SerializationBuilder<V> valueBuilder = builder.valueBuilder;
|
||||
vClass = valueBuilder.eClass;
|
||||
valueSizeMarshaller = valueBuilder.sizeMarshaller();
|
||||
originalValueReader = valueBuilder.reader();
|
||||
|
||||
originalValueWriter = (VW) valueBuilder.interop();
|
||||
originalMetaValueWriter = (MVW) valueBuilder.metaInterop();
|
||||
metaValueWriterProvider = (MetaProvider) valueBuilder.metaInteropProvider();
|
||||
valueFactory = valueBuilder.factory();
|
||||
defaultValueProvider = builder.defaultValueProvider();
|
||||
|
||||
lockTimeOutNS = builder.lockTimeOut(TimeUnit.NANOSECONDS);
|
||||
private final ByteBufferBytes out = new ByteBufferBytes(ByteBuffer.allocate(1024));
|
||||
private final ByteBufferBytes in = new ByteBufferBytes(ByteBuffer.allocate(1024));
|
||||
private final KeyValueSerializer<K, V> keyValueSerializer;
|
||||
|
||||
|
||||
}
|
||||
public synchronized V putIfAbsent(K key, V value) {
|
||||
|
||||
@Override
|
||||
public V putIfAbsent(K key, V value) {
|
||||
target.writeByte(PUT_IF_ABSENT.ordinal());
|
||||
|
||||
ThreadLocalCopies copies = keyInteropProvider.getCopies(null);
|
||||
|
||||
writeKey(key, copies);
|
||||
writeValue(value, copies);
|
||||
|
||||
return null;
|
||||
long sizeLocation = writeEvent(PUT_IF_ABSENT);
|
||||
writeKey(key);
|
||||
writeValue(value);
|
||||
return readKey(sizeLocation);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean remove(Object key, Object value) {
|
||||
target.writeByte(REMOVE_WITH_VALUE.ordinal());
|
||||
|
||||
ThreadLocalCopies copies = keyInteropProvider.getCopies(null);
|
||||
writeKey((K) key, copies);
|
||||
writeValue((V) value, copies);
|
||||
|
||||
// send the transaction id
|
||||
long transactionId = nextUniqueTransaction();
|
||||
target.writeLong(transactionId);
|
||||
public synchronized boolean remove(Object key, Object value) {
|
||||
long sizeLocation = writeEvent(REMOVE_WITH_VALUE);
|
||||
writeKey((K) key);
|
||||
writeValue((V) value);
|
||||
|
||||
// get the data back from the server
|
||||
return blockingFetch(transactionId).readBoolean();
|
||||
return blockingFetch(sizeLocation).readBoolean();
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean replace(K key, V oldValue, V newValue) {
|
||||
target.writeByte(REPLACE_WITH_OLD_AND_NEW_VALUE.ordinal());
|
||||
ThreadLocalCopies copies = keyInteropProvider.getCopies(null);
|
||||
writeKey(key, copies);
|
||||
writeValue(oldValue, copies);
|
||||
writeValue(newValue, copies);
|
||||
|
||||
// send the transaction id
|
||||
long transactionId = nextUniqueTransaction();
|
||||
target.writeLong(transactionId);
|
||||
public synchronized boolean replace(K key, V oldValue, V newValue) {
|
||||
long sizeLocation = writeEvent(REPLACE_WITH_OLD_AND_NEW_VALUE);
|
||||
writeKey(key);
|
||||
writeValue(oldValue);
|
||||
writeValue(newValue);
|
||||
|
||||
// get the data back from the server
|
||||
return blockingFetch(transactionId).readBoolean();
|
||||
|
||||
return blockingFetch(sizeLocation).readBoolean();
|
||||
}
|
||||
|
||||
@Override
|
||||
public V replace(K key, V value) {
|
||||
target.writeByte(REPLACE.ordinal());
|
||||
ThreadLocalCopies copies = keyInteropProvider.getCopies(null);
|
||||
writeKey(key, copies);
|
||||
writeValue(value, copies);
|
||||
|
||||
// send the transaction id
|
||||
long transactionId = nextUniqueTransaction();
|
||||
target.writeLong(transactionId);
|
||||
public synchronized V replace(K key, V value) {
|
||||
long sizeLocation = writeEvent(REPLACE);
|
||||
writeKey(key);
|
||||
writeValue(value);
|
||||
|
||||
// get the data back from the server
|
||||
return readValue(blockingFetch(transactionId));
|
||||
return readKey(sizeLocation);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
public synchronized int size() {
|
||||
|
||||
target.writeByte(SIZE.ordinal());
|
||||
|
||||
// send the transaction id
|
||||
long transactionId = nextUniqueTransaction();
|
||||
target.writeLong(transactionId);
|
||||
long sizeLocation = writeEvent(SIZE);
|
||||
|
||||
// get the data back from the server
|
||||
return blockingFetch(transactionId).readInt();
|
||||
return blockingFetch(sizeLocation).readInt();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
target.writeByte(IS_EMPTY.ordinal());
|
||||
|
||||
// send the transaction id
|
||||
long transactionId = nextUniqueTransaction();
|
||||
target.writeLong(transactionId);
|
||||
public synchronized boolean isEmpty() {
|
||||
long sizeLocation = writeEvent(IS_EMPTY);
|
||||
|
||||
// get the data back from the server
|
||||
return blockingFetch(transactionId).readBoolean();
|
||||
return blockingFetch(sizeLocation).readBoolean();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsKey(Object key) {
|
||||
target.writeByte(CONTAINS_KEY.ordinal());
|
||||
ThreadLocalCopies copies = keyInteropProvider.getCopies(null);
|
||||
writeKey((K) key, copies);
|
||||
|
||||
// send the transaction id
|
||||
long transactionId = nextUniqueTransaction();
|
||||
target.writeLong(transactionId);
|
||||
public synchronized boolean containsKey(Object key) {
|
||||
long sizeLocation = writeEvent(CONTAINS_KEY);
|
||||
writeKey((K) key);
|
||||
|
||||
// get the data back from the server
|
||||
return blockingFetch(transactionId).readBoolean();
|
||||
return blockingFetch(sizeLocation).readBoolean();
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsValue(Object value) {
|
||||
target.writeByte(CONTAINS_VALUE.ordinal());
|
||||
ThreadLocalCopies copies = keyInteropProvider.getCopies(null);
|
||||
writeValue((V) value, copies);
|
||||
|
||||
// send the transaction id
|
||||
long transactionId = nextUniqueTransaction();
|
||||
target.writeLong(transactionId);
|
||||
public synchronized boolean containsValue(Object value) {
|
||||
long sizeLocation = writeEvent(CONTAINS_VALUE);
|
||||
writeValue((V) value);
|
||||
|
||||
// get the data back from the server
|
||||
return blockingFetch(transactionId).readBoolean();
|
||||
return blockingFetch(sizeLocation).readBoolean();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long longSize() {
|
||||
target.writeByte(LONG_SIZE.ordinal());
|
||||
|
||||
// send the transaction id
|
||||
long transactionId = nextUniqueTransaction();
|
||||
target.writeLong(transactionId);
|
||||
public synchronized long longSize() {
|
||||
long sizeLocation = writeEvent(LONG_SIZE);
|
||||
// get the data back from the server
|
||||
return blockingFetch(sizeLocation).readLong();
|
||||
}
|
||||
|
||||
|
||||
public synchronized V get(Object key) {
|
||||
long sizeLocation = writeEvent(GET);
|
||||
writeKey((K) key);
|
||||
|
||||
// get the data back from the server
|
||||
return blockingFetch(transactionId).readLong();
|
||||
}
|
||||
|
||||
@Override
|
||||
public V get(Object key) {
|
||||
target.writeByte(GET.ordinal());
|
||||
ThreadLocalCopies copies = keyInteropProvider.getCopies(null);
|
||||
writeKey((K) key, copies);
|
||||
|
||||
// send the transaction id
|
||||
long transactionId = nextUniqueTransaction();
|
||||
target.writeLong(transactionId);
|
||||
|
||||
// get the data back from the server
|
||||
return readValue(blockingFetch(transactionId));
|
||||
return readKey(sizeLocation);
|
||||
|
||||
}
|
||||
|
||||
private V readValue(ByteBufferBytes byteBufferBytes) {
|
||||
throw new UnsupportedOperationException("todo");
|
||||
}
|
||||
|
||||
@Override
|
||||
public V getUsing(K key, V value) {
|
||||
public synchronized V getUsing(K key, V value) {
|
||||
throw new UnsupportedOperationException("getUsing is not supported for stateless clients");
|
||||
}
|
||||
|
||||
@Override
|
||||
public V acquireUsing(K key, V value) {
|
||||
|
||||
public synchronized V acquireUsing(K key, V value) {
|
||||
throw new UnsupportedOperationException("getUsing is not supported for stateless clients");
|
||||
}
|
||||
|
||||
@Override
|
||||
public V put(K key, V value) {
|
||||
target.writeByte(PUT.ordinal());
|
||||
|
||||
ThreadLocalCopies copies = keyInteropProvider.getCopies(null);
|
||||
writeKey((K) key, copies);
|
||||
writeValue((V) value, copies);
|
||||
public synchronized V put(K key, V value) {
|
||||
|
||||
// send the transaction id
|
||||
long transactionId = nextUniqueTransaction();
|
||||
target.writeLong(transactionId);
|
||||
long sizeLocation = writeEvent(PUT);
|
||||
writeKey(key);
|
||||
writeValue(value);
|
||||
|
||||
// get the data back from the server
|
||||
return readValue(blockingFetch(transactionId));
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public V remove(Object key) {
|
||||
target.writeByte(REMOVE.ordinal());
|
||||
|
||||
// send the transaction id
|
||||
long transactionId = nextUniqueTransaction();
|
||||
target.writeLong(transactionId);
|
||||
|
||||
// get the data back from the server
|
||||
return readValue(blockingFetch(transactionId));
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void putAll(Map<? extends K, ? extends V> map) {
|
||||
target.writeByte(PUT_ALL.ordinal());
|
||||
|
||||
ThreadLocalCopies copies = keyInteropProvider.getCopies(null);
|
||||
|
||||
HashMap<K, V> safeCopy = new HashMap<K, V>(map);
|
||||
|
||||
writeEntries(copies, safeCopy);
|
||||
|
||||
// send the transaction id
|
||||
long transactionId = nextUniqueTransaction();
|
||||
target.writeLong(transactionId);
|
||||
|
||||
// get the data back from the server
|
||||
blockingFetch(transactionId);
|
||||
|
||||
return;
|
||||
return readKey(sizeLocation);
|
||||
|
||||
|
||||
}
|
||||
|
||||
private void writeEntries(ThreadLocalCopies copies, HashMap<K, V> safeCopy) {
|
||||
target.writeLong(safeCopy.size());
|
||||
|
||||
Set<Entry> entries = (Set) safeCopy.entrySet();
|
||||
public synchronized V remove(Object key) {
|
||||
long sizeLocation = writeEvent(REMOVE);
|
||||
writeKey((K) key);
|
||||
|
||||
// get the data back from the server
|
||||
return readKey(sizeLocation);
|
||||
|
||||
}
|
||||
|
||||
|
||||
public synchronized void putAll(Map<? extends K, ? extends V> map) {
|
||||
|
||||
for (Map.Entry<? extends K, ? extends V> e : map.entrySet()) {
|
||||
put(e.getKey(), e.getValue());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void writeEntries(Map<? extends K, ? extends V> map) {
|
||||
|
||||
final HashMap<K, V> safeCopy = new HashMap<K, V>(map);
|
||||
|
||||
out.writeStopBit(safeCopy.size());
|
||||
|
||||
final Set<Entry> entries = (Set) safeCopy.entrySet();
|
||||
|
||||
for (Entry e : entries) {
|
||||
writeKey(e.getKey(), copies);
|
||||
writeValue(e.getValue(), copies);
|
||||
writeKey(e.getKey());
|
||||
writeValue(e.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear() {
|
||||
target.writeByte(CLEAR.ordinal());
|
||||
|
||||
// send the transaction id
|
||||
long transactionId = nextUniqueTransaction();
|
||||
target.writeLong(transactionId);
|
||||
public synchronized void clear() {
|
||||
long sizeLocation = writeEvent(CLEAR);
|
||||
|
||||
// get the data back from the server
|
||||
blockingFetch(transactionId);
|
||||
|
||||
return;
|
||||
blockingFetch(sizeLocation);
|
||||
|
||||
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Set<K> keySet() {
|
||||
target.writeByte(KEY_SET.ordinal());
|
||||
|
||||
// send the transaction id
|
||||
long transactionId = nextUniqueTransaction();
|
||||
target.writeLong(transactionId);
|
||||
public synchronized Set<K> keySet() {
|
||||
long sizeLocation = writeEvent(KEY_SET);
|
||||
|
||||
// get the data back from the server
|
||||
return readKeySet(blockingFetch(transactionId));
|
||||
return readKeySet(blockingFetch(sizeLocation));
|
||||
}
|
||||
|
||||
private Set<K> readKeySet(ByteBufferBytes bufferBytes) {
|
||||
int size = bufferBytes.readInt();
|
||||
|
||||
HashSet<K> result = new HashSet<>();
|
||||
@NotNull
|
||||
|
||||
public synchronized Collection<V> values() {
|
||||
long sizeLocation = writeEvent(VALUES);
|
||||
|
||||
// get the data back from the server
|
||||
final Bytes in = blockingFetch(sizeLocation);
|
||||
|
||||
final long size = in.readStopBit();
|
||||
|
||||
if (size > Integer.MAX_VALUE)
|
||||
throw new IllegalStateException("size=" + size + " is too large.");
|
||||
|
||||
final ArrayList<V> result = new ArrayList<V>((int) size);
|
||||
|
||||
for (int i = 0; i < size; i++) {
|
||||
result.add(readKey(bufferBytes));
|
||||
result.add(keyValueSerializer.readValue(in));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private K readKey(ByteBufferBytes bufferBytes) {
|
||||
long transactionId = nextUniqueTransaction();
|
||||
target.writeLong(transactionId);
|
||||
|
||||
// get the data back from the server
|
||||
return readKey(blockingFetch(transactionId));
|
||||
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Collection<V> values() {
|
||||
target.writeByte(VALUES.ordinal());
|
||||
long transactionId = nextUniqueTransaction();
|
||||
|
||||
target.writeLong(transactionId);
|
||||
public synchronized Set<Map.Entry<K, V>> entrySet() {
|
||||
long sizeLocation = writeEvent(ENTRY_SET);
|
||||
|
||||
// get the data back from the server
|
||||
ByteBufferBytes bufferBytes = blockingFetch(transactionId);
|
||||
Bytes in = blockingFetch(sizeLocation);
|
||||
|
||||
int size = bufferBytes.readInt();
|
||||
|
||||
ArrayList<V> result = new ArrayList<V>();
|
||||
|
||||
for (int i = 0; i < size; i++) {
|
||||
result.add(readValue(bufferBytes));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Set<Map.Entry<K, V>> entrySet() {
|
||||
target.writeByte(ENTRY_SET.ordinal());
|
||||
|
||||
long transactionId = nextUniqueTransaction();
|
||||
|
||||
// get the data back from the server
|
||||
ByteBufferBytes bufferBytes = blockingFetch(transactionId);
|
||||
|
||||
int size = bufferBytes.readInt();
|
||||
long size = in.readStopBit();
|
||||
|
||||
Set<Map.Entry<K, V>> result = new HashSet<Map.Entry<K, V>>();
|
||||
|
||||
for (int i = 0; i < size; i++) {
|
||||
K k = readKey(bufferBytes);
|
||||
V v = readValue(bufferBytes);
|
||||
K k = keyValueSerializer.readKey(in);
|
||||
V v = keyValueSerializer.readValue(in);
|
||||
result.add(new Entry(k, v));
|
||||
|
||||
}
|
||||
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
private void writeValue(V value, ThreadLocalCopies copies) {
|
||||
|
||||
VW valueWriter = valueWriterProvider.get(copies, originalValueWriter);
|
||||
copies = valueWriterProvider.getCopies(copies);
|
||||
long valueSize;
|
||||
MetaBytesWriter<V, VW> metaValueWriter = null;
|
||||
Byteable valueAsByteable = null;
|
||||
|
||||
if (value instanceof Byteable) {
|
||||
valueAsByteable = (Byteable) value;
|
||||
valueSize = valueAsByteable.maxSize();
|
||||
|
||||
target.writeLong(valueSize);
|
||||
target.write(valueAsByteable.bytes());
|
||||
} else {
|
||||
copies = valueWriterProvider.getCopies(copies);
|
||||
valueWriter = valueWriterProvider.get(copies, originalValueWriter);
|
||||
copies = metaValueWriterProvider.getCopies(copies);
|
||||
metaValueWriter = metaValueWriterProvider.get(
|
||||
copies, originalMetaValueWriter, valueWriter, value);
|
||||
valueSize = metaValueWriter.size(valueWriter, value);
|
||||
target.writeLong(valueSize);
|
||||
metaValueWriter.write(valueWriter, target, value);
|
||||
}
|
||||
private long writeEvent(EventId event) {
|
||||
out.clear();
|
||||
out.buffer().clear();
|
||||
|
||||
out.write((byte) event.ordinal());
|
||||
long sizeLocation = markSizeLocation();
|
||||
return sizeLocation;
|
||||
}
|
||||
|
||||
/**
|
||||
* write the keysize and the key to the the {@code target} buffer
|
||||
*
|
||||
* @param key the key of the map
|
||||
* @param copies
|
||||
*/
|
||||
private void writeKey(K key, final ThreadLocalCopies copies) {
|
||||
KI keyWriter = keyInteropProvider.get(copies, originalKeyInterop);
|
||||
|
||||
MKI metaKeyWriter =
|
||||
metaKeyInteropProvider.get(copies, originalMetaKeyInterop, keyWriter, key);
|
||||
|
||||
long keySize = metaKeyWriter.size(keyWriter, key);
|
||||
|
||||
keySizeMarshaller.writeSize(target, keySize);
|
||||
metaKeyWriter.write(keyWriter, target, key);
|
||||
}
|
||||
|
||||
|
||||
public Buffer buffer() {
|
||||
Buffer result = sourceBuffer.get();
|
||||
|
||||
@@ -518,12 +354,12 @@ public class StatelessMapClient<K, KI, MKI extends MetaBytesInterop<K, KI>,
|
||||
|
||||
volatile ByteBufferBytes buffer = null;
|
||||
|
||||
@Override
|
||||
|
||||
public void set(ByteBufferBytes source) {
|
||||
buffer = source;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
public ByteBufferBytes get() {
|
||||
return buffer;
|
||||
}
|
||||
@@ -540,7 +376,7 @@ public class StatelessMapClient<K, KI, MKI extends MetaBytesInterop<K, KI>,
|
||||
* @param transactionId
|
||||
* @return
|
||||
*/
|
||||
private ByteBufferBytes blockingFetch(long transactionId) {
|
||||
private Bytes blockingFetchWithTransaction(long transactionId) {
|
||||
final Buffer buffer = buffer();
|
||||
buffer.set(null);
|
||||
|
||||
@@ -585,7 +421,9 @@ public class StatelessMapClient<K, KI, MKI extends MetaBytesInterop<K, KI>,
|
||||
|
||||
public final V setValue(V newValue) {
|
||||
V oldValue = value;
|
||||
|
||||
StatelessMapClient.this.put((K) getKey(), (V) newValue);
|
||||
|
||||
return oldValue;
|
||||
}
|
||||
|
||||
@@ -612,9 +450,103 @@ public class StatelessMapClient<K, KI, MKI extends MetaBytesInterop<K, KI>,
|
||||
public final String toString() {
|
||||
return getKey() + "=" + getValue();
|
||||
}
|
||||
}
|
||||
|
||||
private Bytes blockingFetch(long sizeLocation) {
|
||||
try {
|
||||
return blockingFetchThrowable(sizeLocation);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeIOException(e);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
private Bytes blockingFetchThrowable(long sizeLocation) throws IOException {
|
||||
long transactionId = nextUniqueTransaction();
|
||||
|
||||
LOG.info("sending data with transactionId=" + transactionId);
|
||||
out.writeLong(transactionId);
|
||||
writeSizeAt(sizeLocation);
|
||||
|
||||
|
||||
out.buffer().limit((int) out.position());
|
||||
while (out.buffer().remaining() > 0) {
|
||||
int write = clientChannel.write(out.buffer());
|
||||
System.out.println(write);
|
||||
}
|
||||
|
||||
in.clear();
|
||||
in.buffer().clear();
|
||||
|
||||
// read size
|
||||
while (in.buffer().position() < 2) {
|
||||
clientChannel.read(in.buffer());
|
||||
}
|
||||
|
||||
int size = in.readUnsignedShort();
|
||||
|
||||
while (in.buffer().position() < size) {
|
||||
clientChannel.read(in.buffer());
|
||||
}
|
||||
|
||||
boolean isException = in.readBoolean();
|
||||
|
||||
long inTransactionId = in.readLong();
|
||||
|
||||
if (inTransactionId != transactionId) {
|
||||
throw new IllegalStateException("the received transaction-id=" + inTransactionId +
|
||||
", does not match the expected transaction-id=" + transactionId);
|
||||
}
|
||||
|
||||
if (isException) {
|
||||
int len = in.readUnsignedShort();
|
||||
|
||||
in.limit(in.position() + len);
|
||||
|
||||
Object o = in.readObject();
|
||||
RuntimeException runtimeException = (RuntimeException) o;
|
||||
throw runtimeException;
|
||||
}
|
||||
|
||||
|
||||
return in;
|
||||
}
|
||||
|
||||
private void writeSizeAt(long locationOfSize) {
|
||||
long size = out.position() - locationOfSize;
|
||||
out.writeUnsignedShort(locationOfSize, (int) size - 2);
|
||||
}
|
||||
|
||||
private long markSizeLocation() {
|
||||
long position = out.position();
|
||||
out.skip(2);
|
||||
return position;
|
||||
}
|
||||
|
||||
private void writeKey(K key) {
|
||||
keyValueSerializer.writeKey(key, out);
|
||||
}
|
||||
|
||||
private V readKey(final long sizeLocation) {
|
||||
return keyValueSerializer.readValue(blockingFetch(sizeLocation));
|
||||
}
|
||||
|
||||
private void writeValue(V value) {
|
||||
keyValueSerializer.writeValue(value, out);
|
||||
}
|
||||
|
||||
|
||||
private Set<K> readKeySet(Bytes in) {
|
||||
long size = in.readStopBit();
|
||||
final HashSet<K> result = new HashSet<>();
|
||||
|
||||
for (long i = 0; i < size; i++) {
|
||||
result.add(keyValueSerializer.readKey(in));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -624,3 +556,10 @@ interface Buffer {
|
||||
|
||||
ByteBufferBytes get();
|
||||
}
|
||||
|
||||
class RuntimeIOException extends RuntimeException {
|
||||
|
||||
public RuntimeIOException(IOException e) {
|
||||
super(e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,340 @@
|
||||
package net.openhft.chronicle.map;
|
||||
|
||||
import net.openhft.lang.io.Bytes;
|
||||
import net.openhft.lang.model.constraints.NotNull;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* @author Rob Austin.
|
||||
*/
|
||||
class StatelessServerConnector<K, V> {
|
||||
|
||||
|
||||
private final KeyValueSerializer<K, V> keyValueSerializer;
|
||||
private final ChronicleMap<K, V> map;
|
||||
|
||||
|
||||
public StatelessServerConnector(KeyValueSerializer<K, V> keyValueSerializer, ChronicleMap<K, V> map) {
|
||||
this.keyValueSerializer = keyValueSerializer;
|
||||
this.map = map;
|
||||
}
|
||||
|
||||
|
||||
public void processStatelessEvent(byte eventId, @NotNull Bytes in, @NotNull Bytes out) {
|
||||
|
||||
|
||||
StatelessMapClient.EventId event = StatelessMapClient.EventId.values()[eventId];
|
||||
switch (event) {
|
||||
|
||||
case LONG_SIZE:
|
||||
longSize(in, out);
|
||||
break;
|
||||
|
||||
case IS_EMPTY:
|
||||
isEmpty(in, out);
|
||||
break;
|
||||
|
||||
case CONTAINS_KEY:
|
||||
containsKey(in, out);
|
||||
break;
|
||||
case CONTAINS_VALUE:
|
||||
containsValue(in, out);
|
||||
break;
|
||||
case GET:
|
||||
get(in, out);
|
||||
break;
|
||||
case PUT:
|
||||
put(in, out);
|
||||
break;
|
||||
case REMOVE:
|
||||
remove(in, out);
|
||||
break;
|
||||
case CLEAR:
|
||||
clear(in, out);
|
||||
break;
|
||||
case KEY_SET:
|
||||
keySet(in, out);
|
||||
break;
|
||||
case VALUES:
|
||||
values(in, out);
|
||||
break;
|
||||
case ENTRY_SET:
|
||||
entrySet(in, out);
|
||||
break;
|
||||
case REPLACE:
|
||||
replace(in, out);
|
||||
break;
|
||||
case REPLACE_WITH_OLD_AND_NEW_VALUE:
|
||||
replaceWithOldAndNew(in, out);
|
||||
break;
|
||||
case PUT_IF_ABSENT:
|
||||
putIfAbsent(in, out);
|
||||
break;
|
||||
case REMOVE_WITH_VALUE:
|
||||
removeWithValue(in, out);
|
||||
break;
|
||||
|
||||
case SIZE:
|
||||
size(in, out);
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new IllegalStateException("unsupported event=" + event);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void removeWithValue(Bytes in, Bytes out) {
|
||||
boolean result = map.remove(readKey(in), readValue(in));
|
||||
out.writeBoolean(result);
|
||||
}
|
||||
|
||||
private void replaceWithOldAndNew(Bytes in, Bytes out) {
|
||||
|
||||
final K key = readKey(in);
|
||||
V oldValue = readValue(in);
|
||||
V newValue = readValue(in);
|
||||
|
||||
long sizeLocation = reflectTransactionId(in, out);
|
||||
try {
|
||||
map.replace(key, oldValue, newValue);
|
||||
} catch (RuntimeException e) {
|
||||
writeException(e, out);
|
||||
writeSizeAndFlags(sizeLocation, true, out);
|
||||
return;
|
||||
}
|
||||
writeSizeAndFlags(sizeLocation, false, out);
|
||||
}
|
||||
|
||||
|
||||
public void longSize(Bytes in, Bytes out) {
|
||||
long sizeLocation = reflectTransactionId(in, out);
|
||||
out.writeLong(map.longSize());
|
||||
writeSizeAndFlags(sizeLocation, false, out);
|
||||
}
|
||||
|
||||
public void size(Bytes in, Bytes out) {
|
||||
long sizeLocation = reflectTransactionId(in, out);
|
||||
out.writeInt(map.size());
|
||||
writeSizeAndFlags(sizeLocation, false, out);
|
||||
}
|
||||
|
||||
public void isEmpty(Bytes in, Bytes out) {
|
||||
long sizeLocation = reflectTransactionId(in, out);
|
||||
out.writeBoolean(map.isEmpty());
|
||||
writeSizeAndFlags(sizeLocation, false, out);
|
||||
}
|
||||
|
||||
public void containsKey(Bytes in, Bytes out) {
|
||||
K k = readKey(in);
|
||||
long sizeLocation = reflectTransactionId(in, out);
|
||||
out.writeBoolean(map.containsKey(k));
|
||||
writeSizeAndFlags(sizeLocation, false, out);
|
||||
}
|
||||
|
||||
public void containsValue(Bytes in, Bytes out) {
|
||||
V v = readValue(in);
|
||||
long sizeLocation = reflectTransactionId(in, out);
|
||||
out.writeBoolean(map.containsValue(v));
|
||||
writeSizeAndFlags(sizeLocation, false, out);
|
||||
}
|
||||
|
||||
public void get(Bytes in, Bytes out) {
|
||||
K k = readKey(in);
|
||||
long sizeLocation = reflectTransactionId(in, out);
|
||||
try {
|
||||
writeValue(map.get(k), out);
|
||||
} catch (RuntimeException e) {
|
||||
writeException(e, out);
|
||||
writeSizeAndFlags(sizeLocation, true, out);
|
||||
return;
|
||||
}
|
||||
writeSizeAndFlags(sizeLocation, false, out);
|
||||
|
||||
}
|
||||
|
||||
private void writeException(RuntimeException e, Bytes out) {
|
||||
|
||||
long start = out.position();
|
||||
out.skip(2);
|
||||
out.writeObject(e);
|
||||
long len = out.position() - (start + 2L);
|
||||
out.writeUnsignedShort(start,(int) len);
|
||||
|
||||
}
|
||||
|
||||
public void put(Bytes in, Bytes out) {
|
||||
K k = readKey(in);
|
||||
V v = readValue(in);
|
||||
long sizeLocation = reflectTransactionId(in, out);
|
||||
writeValue(map.put(k, v), out);
|
||||
writeSizeAndFlags(sizeLocation, false, out);
|
||||
}
|
||||
|
||||
public void remove(Bytes in, Bytes out) {
|
||||
final V value = map.remove(readKey(in));
|
||||
long sizeLocation = reflectTransactionId(in, out);
|
||||
writeValue(value, out);
|
||||
writeSizeAndFlags(sizeLocation, false, out);
|
||||
}
|
||||
|
||||
public void putAll(Bytes in, Bytes out) {
|
||||
map.putAll(readEntries(in));
|
||||
long sizeLocation = reflectTransactionId(in, out);
|
||||
writeSizeAndFlags(sizeLocation, false, out);
|
||||
}
|
||||
|
||||
private Map<K, V> readEntries(Bytes in) {
|
||||
|
||||
long size = in.readStopBit();
|
||||
final HashMap<K, V> result = new HashMap<K, V>();
|
||||
|
||||
for (long i = 0; i < size; i++) {
|
||||
result.put(readKey(in), readValue(in));
|
||||
}
|
||||
return result;
|
||||
|
||||
}
|
||||
|
||||
public void clear(Bytes in, Bytes out) {
|
||||
map.clear();
|
||||
long sizeLocation = reflectTransactionId(in, out);
|
||||
writeSizeAndFlags(sizeLocation, false, out);
|
||||
}
|
||||
|
||||
|
||||
public void keySet(Bytes in, Bytes out) {
|
||||
long sizeLocation = reflectTransactionId(in, out);
|
||||
|
||||
final Set<K> ks = map.keySet();
|
||||
out.writeStopBit(ks.size());
|
||||
for (K key : ks) {
|
||||
writeKey(key, out);
|
||||
}
|
||||
writeSizeAndFlags(sizeLocation, false, out);
|
||||
}
|
||||
|
||||
public void values(Bytes in, Bytes out) {
|
||||
long sizeLocation = reflectTransactionId(in, out);
|
||||
final Collection<V> values = map.values();
|
||||
out.writeStopBit(values.size());
|
||||
for (final V value : values) {
|
||||
writeValue(value, out);
|
||||
}
|
||||
writeSizeAndFlags(sizeLocation, false, out);
|
||||
}
|
||||
|
||||
public void entrySet(Bytes in, Bytes out) {
|
||||
long sizeLocation = reflectTransactionId(in, out);
|
||||
|
||||
final Set<Map.Entry<K, V>> entries = map.entrySet();
|
||||
out.writeStopBit(entries.size());
|
||||
for (Map.Entry<K, V> e : entries) {
|
||||
writeKey(e.getKey(), out);
|
||||
writeValue(e.getValue(), out);
|
||||
}
|
||||
}
|
||||
|
||||
public void putIfAbsent(Bytes in, Bytes out) {
|
||||
K key = readKey(in);
|
||||
V v = readValue(in);
|
||||
long sizeLocation = reflectTransactionId(in, out);
|
||||
writeValue(map.putIfAbsent(key, v), out);
|
||||
writeSizeAndFlags(sizeLocation, false, out);
|
||||
}
|
||||
|
||||
|
||||
public void replace(Bytes in, Bytes out) {
|
||||
K k = readKey(in);
|
||||
V v = readValue(in);
|
||||
|
||||
long sizeLocation = reflectTransactionId(in, out);
|
||||
map.replace(k, v);
|
||||
writeSizeAndFlags(sizeLocation, false, out);
|
||||
}
|
||||
|
||||
/**
|
||||
* write the keysize and the key to the the {@code target} buffer
|
||||
*
|
||||
* @param key the key of the map
|
||||
*/
|
||||
private void writeKey(K key, Bytes out) {
|
||||
keyValueSerializer.writeKey(key, out);
|
||||
}
|
||||
|
||||
private long reflectTransactionId(Bytes in, Bytes out) {
|
||||
long sizeLocation = out.position();
|
||||
out.skip(3);
|
||||
long transactionId = in.readLong();
|
||||
out.writeLong(transactionId);
|
||||
return sizeLocation;
|
||||
}
|
||||
|
||||
private void writeValue(V value, final Bytes out) {
|
||||
keyValueSerializer.writeValue(value, out);
|
||||
}
|
||||
|
||||
private K readKey(Bytes in) {
|
||||
return keyValueSerializer.readKey(in);
|
||||
}
|
||||
|
||||
private V readValue(Bytes in) {
|
||||
return keyValueSerializer.readValue(in);
|
||||
}
|
||||
|
||||
private void writeSizeAndFlags(long locationOfSize, boolean isException, Bytes out) {
|
||||
long size = out.position() - locationOfSize;
|
||||
out.writeUnsignedShort(0L, (int) size);
|
||||
out.writeBoolean(2L, isException);
|
||||
}
|
||||
}
|
||||
|
||||
class KeyValueSerializer<K, V> {
|
||||
|
||||
private final Serializer<V> valueSerializer;
|
||||
private final Serializer<K> keySerializer;
|
||||
|
||||
KeyValueSerializer(SerializationBuilder<K> keySerializer,
|
||||
SerializationBuilder<V> valueSerializer) {
|
||||
this.keySerializer = new Serializer<K>(keySerializer);
|
||||
this.valueSerializer = new Serializer<V>(valueSerializer);
|
||||
}
|
||||
|
||||
V readValue(Bytes in) {
|
||||
if (in.readBoolean())
|
||||
return null;
|
||||
return valueSerializer.readMarshallable(in);
|
||||
}
|
||||
|
||||
K readKey(Bytes in) {
|
||||
if (in.readBoolean())
|
||||
return null;
|
||||
|
||||
return keySerializer.readMarshallable(in);
|
||||
}
|
||||
|
||||
/**
|
||||
* write the keysize and the key to the the {@code target} buffer
|
||||
*
|
||||
* @param key the key of the map
|
||||
*/
|
||||
void writeKey(K key, Bytes out) {
|
||||
|
||||
out.writeBoolean(key == null);
|
||||
if (key != null)
|
||||
keySerializer.writeMarshallable(key, out);
|
||||
}
|
||||
|
||||
void writeValue(V value, Bytes out) {
|
||||
out.writeBoolean(value == null);
|
||||
if (value != null)
|
||||
valueSerializer.writeMarshallable(value, out);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -17,6 +17,7 @@
|
||||
package net.openhft.chronicle.map;
|
||||
|
||||
import net.openhft.lang.io.ByteBufferBytes;
|
||||
import net.openhft.lang.io.Bytes;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
@@ -32,10 +33,11 @@ import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import static java.nio.channels.SelectionKey.*;
|
||||
import static net.openhft.chronicle.map.StatelessMapClient.EventId.HEARTBEAT;
|
||||
|
||||
/**
|
||||
* Used with a {@see net.openhft.map.ReplicatedSharedHashMap} to send data between the maps using a socket
|
||||
* connection <p/> {@see net.openhft.map.OutSocketReplicator}
|
||||
* Used with a {@see net.openhft.map.ReplicatedSharedHashMap} to send data between the maps using a
|
||||
* socket connection <p/> {@see net.openhft.map.OutSocketReplicator}
|
||||
*
|
||||
* @author Rob Austin.
|
||||
*/
|
||||
@@ -43,6 +45,8 @@ class TcpReplicator extends AbstractChannelReplicator implements Closeable {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(TcpReplicator.class.getName());
|
||||
private static final int BUFFER_SIZE = 0x100000; // 1MB
|
||||
public static final int STATELESS_CLIENT = -127;
|
||||
public static final byte NOT_SET = (byte) HEARTBEAT.ordinal();
|
||||
|
||||
private final SelectionKey[] selectionKeysStore = new SelectionKey[Byte.MAX_VALUE + 1];
|
||||
// used to instruct the selector thread to set OP_WRITE on a key correlated by the bit index in the
|
||||
@@ -57,22 +61,28 @@ class TcpReplicator extends AbstractChannelReplicator implements Closeable {
|
||||
private final Replica.EntryExternalizable externalizable;
|
||||
private final TcpReplicationConfig replicationConfig;
|
||||
|
||||
private final StatelessServerConnector statelessServerConnector;
|
||||
|
||||
private long selectorTimeout;
|
||||
|
||||
/**
|
||||
* @param maxEntrySizeBytes used to check that the last entry will fit into the buffer, it can not be
|
||||
* smaller than the size of and entry, if it is set smaller the buffer will over
|
||||
* flow, it can be larger then the entry, but setting it too large reduces the
|
||||
* workable space in the buffer.
|
||||
* @param maxEntrySizeBytes used to check that the last entry will fit into the buffer,
|
||||
* it can not be smaller than the size of and entry, if it is
|
||||
* set smaller the buffer will over flow, it can be larger then
|
||||
* the entry, but setting it too large reduces the workable
|
||||
* space in the buffer.
|
||||
* @param statelessServerConnector
|
||||
* @throws IOException
|
||||
*/
|
||||
TcpReplicator(@NotNull final Replica replica,
|
||||
@NotNull final Replica.EntryExternalizable externalizable,
|
||||
@NotNull final TcpReplicationConfig replicationConfig,
|
||||
final int maxEntrySizeBytes) throws IOException {
|
||||
final int maxEntrySizeBytes,
|
||||
StatelessServerConnector statelessServerConnector) throws IOException {
|
||||
|
||||
super("TcpSocketReplicator-" + replica.identifier(), replicationConfig.throttlingConfig(),
|
||||
maxEntrySizeBytes);
|
||||
this.statelessServerConnector = statelessServerConnector;
|
||||
|
||||
final ThrottlingConfig throttlingConfig = replicationConfig.throttlingConfig();
|
||||
long throttleBucketInterval = TimeUnit.MILLISECONDS.convert(throttlingConfig
|
||||
@@ -251,7 +261,8 @@ class TcpReplicator extends AbstractChannelReplicator implements Closeable {
|
||||
}
|
||||
|
||||
/**
|
||||
* check to see if we have lost connection with the remote node and if we have attempts a reconnect.
|
||||
* check to see if we have lost connection with the remote node and if we have attempts a
|
||||
* reconnect.
|
||||
*
|
||||
* @param key the key relating to the heartbeat that we are checking
|
||||
* @param approxTimeOutTime the approximate time in milliseconds
|
||||
@@ -374,8 +385,8 @@ class TcpReplicator extends AbstractChannelReplicator implements Closeable {
|
||||
}
|
||||
|
||||
/**
|
||||
* this can be called when a new CHM is added to a cluster, we have to rebootstrap so will clear all the
|
||||
* old bootstrap information
|
||||
* this can be called when a new CHM is added to a cluster, we have to rebootstrap so will clear
|
||||
* all the old bootstrap information
|
||||
*
|
||||
* @param key the nio SelectionKey
|
||||
*/
|
||||
@@ -387,7 +398,8 @@ class TcpReplicator extends AbstractChannelReplicator implements Closeable {
|
||||
}
|
||||
|
||||
/**
|
||||
* used to exchange identifiers and timestamps and heartbeat intervals between the server and client
|
||||
* used to exchange identifiers and timestamps and heartbeat intervals between the server and
|
||||
* client
|
||||
*
|
||||
* @param key the SelectionKey relating to the this cha
|
||||
* @param socketChannel
|
||||
@@ -404,6 +416,11 @@ class TcpReplicator extends AbstractChannelReplicator implements Closeable {
|
||||
|
||||
final byte remoteIdentifier = reader.identifierFromBuffer();
|
||||
|
||||
if (remoteIdentifier == STATELESS_CLIENT) {
|
||||
attached.handShakingComplete = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (remoteIdentifier == Byte.MIN_VALUE)
|
||||
return;
|
||||
|
||||
@@ -425,7 +442,7 @@ class TcpReplicator extends AbstractChannelReplicator implements Closeable {
|
||||
final SocketAddress remoteAddress = socketChannel.getRemoteAddress();
|
||||
|
||||
|
||||
if ( (identifierListener != null && !identifierListener.isIdentifierUnique(remoteIdentifier,
|
||||
if ((identifierListener != null && !identifierListener.isIdentifierUnique(remoteIdentifier,
|
||||
remoteAddress)) || remoteIdentifier == localIdentifier)
|
||||
|
||||
// throwing this exception will cause us to disconnect, both the client and server
|
||||
@@ -477,7 +494,7 @@ class TcpReplicator extends AbstractChannelReplicator implements Closeable {
|
||||
// now we're finished we can get on with reading the entries
|
||||
attached.handShakingComplete = true;
|
||||
attached.remoteModificationIterator.dirtyEntries(attached.remoteBootstrapTimestamp);
|
||||
reader.entriesFromBuffer();
|
||||
reader.entriesFromBuffer(attached);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -531,17 +548,17 @@ class TcpReplicator extends AbstractChannelReplicator implements Closeable {
|
||||
attached.entryReader.lastHeartBeatReceived = approxTime;
|
||||
|
||||
if (attached.isHandShakingComplete()) {
|
||||
attached.entryReader.entriesFromBuffer();
|
||||
attached.entryReader.entriesFromBuffer(attached);
|
||||
} else {
|
||||
doHandShaking(key, socketChannel);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* sets interestOps to "selector keys",The change to interestOps much be on the same thread as the
|
||||
* selector. This class, allows via {@link AbstractChannelReplicator .KeyInterestUpdater#set(int)} to
|
||||
* holds a pending change in interestOps ( via a bitset ), this change is processed later on the same
|
||||
* thread as the selector
|
||||
* sets interestOps to "selector keys",The change to interestOps much be on the same thread as
|
||||
* the selector. This class, allows via {@link AbstractChannelReplicator
|
||||
* .KeyInterestUpdater#set(int)} to holds a pending change in interestOps ( via a bitset ),
|
||||
* this change is processed later on the same thread as the selector
|
||||
*/
|
||||
private static class KeyInterestUpdater {
|
||||
|
||||
@@ -572,8 +589,8 @@ class TcpReplicator extends AbstractChannelReplicator implements Closeable {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param keyIndex the index of the key that has changed, the list of keys is provided by the
|
||||
* constructor {@link KeyInterestUpdater(int, SelectionKey[])}
|
||||
* @param keyIndex the index of the key that has changed, the list of keys is provided by
|
||||
* the constructor {@link KeyInterestUpdater(int, SelectionKey[])}
|
||||
*/
|
||||
public void set(int keyIndex) {
|
||||
changeOfOpWriteRequired.set(keyIndex);
|
||||
@@ -773,12 +790,19 @@ class TcpReplicator extends AbstractChannelReplicator implements Closeable {
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return the buffer messages can be written to
|
||||
*/
|
||||
Bytes buffer() {
|
||||
return in;
|
||||
}
|
||||
|
||||
/**
|
||||
* writes the timestamp into the buffer
|
||||
*
|
||||
* @param localIdentifier the current nodes identifier
|
||||
*/
|
||||
void identifierToBuffer(final int localIdentifier) {
|
||||
void identifierToBuffer(final byte localIdentifier) {
|
||||
in.writeByte(localIdentifier);
|
||||
}
|
||||
|
||||
@@ -792,7 +816,8 @@ class TcpReplicator extends AbstractChannelReplicator implements Closeable {
|
||||
}
|
||||
|
||||
/**
|
||||
* writes all the entries that have changed, to the buffer which will later be written to TCP/IP
|
||||
* writes all the entries that have changed, to the buffer which will later be written to
|
||||
* TCP/IP
|
||||
*
|
||||
* @param modificationIterator a record of which entries have modification
|
||||
* @param selectionKey
|
||||
@@ -804,7 +829,7 @@ class TcpReplicator extends AbstractChannelReplicator implements Closeable {
|
||||
final SocketChannel socketChannel = (SocketChannel) selectionKey.channel();
|
||||
final Attached attached = (Attached) selectionKey.attachment();
|
||||
|
||||
// this can occur when new CHM's are added to a cluster
|
||||
// this can occur when new map are added to a channel
|
||||
final boolean handShakingComplete = attached.isHandShakingComplete();
|
||||
|
||||
for (; ; ) {
|
||||
@@ -867,9 +892,15 @@ class TcpReplicator extends AbstractChannelReplicator implements Closeable {
|
||||
}
|
||||
|
||||
/**
|
||||
* used to send an single zero byte if we have not send any data for up to the localHeartbeatInterval
|
||||
* used to send an single zero byte if we have not send any data for up to the
|
||||
* localHeartbeatInterval
|
||||
*/
|
||||
private void writeHeartbeatToBuffer() {
|
||||
|
||||
// denotes the state - 0 for a heartbeat
|
||||
in.writeByte(HEARTBEAT.ordinal());
|
||||
|
||||
// denotes the size in bytes
|
||||
in.writeUnsignedShort(0);
|
||||
}
|
||||
|
||||
@@ -879,8 +910,8 @@ class TcpReplicator extends AbstractChannelReplicator implements Closeable {
|
||||
|
||||
|
||||
/**
|
||||
* removes back in the OP_WRITE from the selector, otherwise it'll spin loop. The OP_WRITE will get
|
||||
* added back in as soon as we have data to write
|
||||
* removes back in the OP_WRITE from the selector, otherwise it'll spin loop. The OP_WRITE
|
||||
* will get added back in as soon as we have data to write
|
||||
*
|
||||
* @param socketChannel the socketChannel we wish to stop writing to
|
||||
* @param attached data associated with the socketChannels key
|
||||
@@ -914,8 +945,9 @@ class TcpReplicator extends AbstractChannelReplicator implements Closeable {
|
||||
private final ByteBuffer in;
|
||||
private final ByteBufferBytes out;
|
||||
public long lastHeartBeatReceived = System.currentTimeMillis();
|
||||
// we use Integer.MIN_VALUE as N/A
|
||||
private int sizeOfNextEntry = Integer.MIN_VALUE;
|
||||
|
||||
private int sizeInBytes;
|
||||
private byte state;
|
||||
|
||||
private TcpSocketChannelEntryReader() {
|
||||
in = ByteBuffer.allocateDirect(replicationConfig.packetSize() + maxEntrySizeBytes);
|
||||
@@ -943,45 +975,79 @@ class TcpReplicator extends AbstractChannelReplicator implements Closeable {
|
||||
/**
|
||||
* reads entries from the buffer till empty
|
||||
*
|
||||
* @param attached
|
||||
* @throws InterruptedException
|
||||
*/
|
||||
private void entriesFromBuffer() throws InterruptedException, IOException {
|
||||
|
||||
private void entriesFromBuffer(Attached attached) throws InterruptedException, IOException {
|
||||
for (; ; ) {
|
||||
|
||||
out.limit(in.position());
|
||||
|
||||
// its set to MIN_VALUE when it should be read again
|
||||
if (sizeOfNextEntry == Integer.MIN_VALUE) {
|
||||
if (out.remaining() < SIZE_OF_SHORT) {
|
||||
if (state == NOT_SET) {
|
||||
if (out.remaining() < SIZE_OF_SHORT + 1 ) {
|
||||
return;
|
||||
}
|
||||
|
||||
int value = out.readUnsignedShort();
|
||||
// state is used for both heartbeat and stateless
|
||||
state = out.readByte();
|
||||
|
||||
// this is the heartbeat
|
||||
if (value == 0)
|
||||
|
||||
|
||||
sizeInBytes = out.readUnsignedShort();
|
||||
|
||||
if (state == 0 && sizeInBytes != 0) {
|
||||
int i = 1;
|
||||
}
|
||||
|
||||
// this is the :
|
||||
// -- heartbeat if its 0
|
||||
// -- stateful update if its 1
|
||||
// -- the id of the stateful event
|
||||
if (state == NOT_SET)
|
||||
continue;
|
||||
|
||||
sizeOfNextEntry = value;
|
||||
}
|
||||
|
||||
|
||||
if (out.remaining() < sizeOfNextEntry) {
|
||||
|
||||
if (out.remaining() < sizeInBytes) {
|
||||
return;
|
||||
}
|
||||
|
||||
final long nextEntryPos = out.position() + sizeOfNextEntry;
|
||||
if (sizeInBytes == 0) {
|
||||
int j = 1;
|
||||
}
|
||||
|
||||
|
||||
final long nextEntryPos = out.position() + sizeInBytes;
|
||||
final long limit = out.limit();
|
||||
out.limit(nextEntryPos);
|
||||
externalizable.readExternalEntry(out);
|
||||
|
||||
boolean isStateless = (state != 1);
|
||||
|
||||
if (isStateless) {
|
||||
if (statelessServerConnector == null) {
|
||||
|
||||
LOG.error("", new IllegalArgumentException("received an event " +
|
||||
"from a stateless map, stateless maps are not " +
|
||||
"currently supported when using Chronicle Channels"));
|
||||
} else
|
||||
|
||||
statelessServerConnector.processStatelessEvent(state,
|
||||
out, attached.entryWriter.buffer());
|
||||
// to allow the sizeInBytes to be read the next time around
|
||||
|
||||
|
||||
} else
|
||||
externalizable.readExternalEntry(out);
|
||||
|
||||
out.limit(limit);
|
||||
// skip onto the next entry
|
||||
out.position(nextEntryPos);
|
||||
|
||||
// to allow the sizeOfNextEntry to be read the next time around
|
||||
sizeOfNextEntry = Integer.MIN_VALUE;
|
||||
|
||||
state = NOT_SET;
|
||||
sizeInBytes = 0;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1016,7 +1082,12 @@ class TcpReplicator extends AbstractChannelReplicator implements Closeable {
|
||||
* @return the timestamp or -1 if unsuccessful
|
||||
*/
|
||||
long remoteBootstrapTimestamp() {
|
||||
return (out.remaining() >= 8) ? out.readLong() : Long.MIN_VALUE;
|
||||
if (out.remaining() >= 8)
|
||||
return out.readLong();
|
||||
else
|
||||
return Long.MIN_VALUE;
|
||||
|
||||
// return (out.remaining() >= 8) ? out.readLong() : Long.MIN_VALUE;
|
||||
}
|
||||
|
||||
public long remoteHeartbeatIntervalFromBuffer() {
|
||||
|
||||
@@ -16,14 +16,14 @@
|
||||
|
||||
package net.openhft.chronicle.map.serialization;
|
||||
|
||||
import net.openhft.chronicle.map.threadlocal.Provider;
|
||||
import net.openhft.chronicle.map.threadlocal.StatefulCopyable;
|
||||
import net.openhft.chronicle.map.threadlocal.ThreadLocalCopies;
|
||||
import net.openhft.lang.io.Bytes;
|
||||
import net.openhft.lang.io.DirectBytes;
|
||||
import net.openhft.lang.io.DirectStore;
|
||||
import net.openhft.lang.io.serialization.BytesMarshaller;
|
||||
import net.openhft.lang.io.serialization.JDKObjectSerializer;
|
||||
import net.openhft.chronicle.map.threadlocal.Provider;
|
||||
import net.openhft.chronicle.map.threadlocal.StatefulCopyable;
|
||||
import net.openhft.chronicle.map.threadlocal.ThreadLocalCopies;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.ObjectInputStream;
|
||||
@@ -190,7 +190,7 @@ public abstract class CopyingMetaBytesInterop<E, W> implements MetaBytesInterop<
|
||||
forBytesWriter = new ForBytesWriter();
|
||||
}
|
||||
|
||||
private Bytes obtain(long maxSize) {
|
||||
Bytes obtain(long maxSize) {
|
||||
DirectBytes buf;
|
||||
if ((buf = buffer) != null) {
|
||||
if (maxSize <= buf.capacity()) {
|
||||
|
||||
Reference in New Issue
Block a user