/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 org.apache.ignite.internal.processors.rest.protocols.tcp;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.ByteBuffer;
import java.nio.charset.Charset;
import java.util.Date;
import java.util.UUID;
import org.apache.ignite.IgniteCheckedException;
import org.apache.ignite.internal.client.marshaller.GridClientMarshaller;
import org.apache.ignite.internal.processors.rest.client.message.GridClientHandshakeRequest;
import org.apache.ignite.internal.processors.rest.client.message.GridClientHandshakeResponse;
import org.apache.ignite.internal.processors.rest.client.message.GridClientMessage;
import org.apache.ignite.internal.processors.rest.client.message.GridClientPingPacket;
import org.apache.ignite.internal.processors.rest.client.message.GridRouterRequest;
import org.apache.ignite.internal.processors.rest.client.message.GridRouterResponse;
import org.apache.ignite.internal.processors.rest.protocols.tcp.redis.GridRedisMessage;
import org.apache.ignite.internal.processors.rest.protocols.tcp.redis.GridRedisProtocolParser;
import org.apache.ignite.internal.util.GridByteArrayList;
import org.apache.ignite.internal.util.GridClientByteUtils;
import org.apache.ignite.internal.util.nio.GridNioParser;
import org.apache.ignite.internal.util.nio.GridNioSession;
import org.apache.ignite.internal.util.typedef.internal.S;
import org.apache.ignite.internal.util.typedef.internal.U;
import org.apache.ignite.marshaller.Marshaller;
import org.apache.ignite.marshaller.jdk.JdkMarshaller;
import org.jetbrains.annotations.Nullable;
import static org.apache.ignite.internal.processors.rest.protocols.tcp.GridMemcachedMessage.BOOLEAN_FLAG;
import static org.apache.ignite.internal.processors.rest.protocols.tcp.GridMemcachedMessage.BYTE_ARR_FLAG;
import static org.apache.ignite.internal.processors.rest.protocols.tcp.GridMemcachedMessage.BYTE_FLAG;
import static org.apache.ignite.internal.processors.rest.protocols.tcp.GridMemcachedMessage.DATE_FLAG;
import static org.apache.ignite.internal.processors.rest.protocols.tcp.GridMemcachedMessage.DOUBLE_FLAG;
import static org.apache.ignite.internal.processors.rest.protocols.tcp.GridMemcachedMessage.FLAGS_LENGTH;
import static org.apache.ignite.internal.processors.rest.protocols.tcp.GridMemcachedMessage.FLOAT_FLAG;
import static org.apache.ignite.internal.processors.rest.protocols.tcp.GridMemcachedMessage.HDR_LEN;
import static org.apache.ignite.internal.processors.rest.protocols.tcp.GridMemcachedMessage.IGNITE_HANDSHAKE_FLAG;
import static org.apache.ignite.internal.processors.rest.protocols.tcp.GridMemcachedMessage.IGNITE_HANDSHAKE_RES_FLAG;
import static org.apache.ignite.internal.processors.rest.protocols.tcp.GridMemcachedMessage.IGNITE_REQ_FLAG;
import static org.apache.ignite.internal.processors.rest.protocols.tcp.GridMemcachedMessage.INT_FLAG;
import static org.apache.ignite.internal.processors.rest.protocols.tcp.GridMemcachedMessage.LONG_FLAG;
import static org.apache.ignite.internal.processors.rest.protocols.tcp.GridMemcachedMessage.MEMCACHE_REQ_FLAG;
import static org.apache.ignite.internal.processors.rest.protocols.tcp.GridMemcachedMessage.MEMCACHE_RES_FLAG;
import static org.apache.ignite.internal.processors.rest.protocols.tcp.GridMemcachedMessage.SERIALIZED_FLAG;
import static org.apache.ignite.internal.processors.rest.protocols.tcp.redis.GridRedisMessage.RESP_REQ_FLAG;
import static org.apache.ignite.internal.util.nio.GridNioSessionMetaKey.MARSHALLER;
import static org.apache.ignite.internal.util.nio.GridNioSessionMetaKey.PARSER_STATE;
/**
* Parser for extended memcache protocol. Handles parsing and encoding activity.
*/
public class GridTcpRestParser implements GridNioParser {
/** UTF-8 charset. */
private static final Charset UTF_8 = Charset.forName("UTF-8");
/** JDK marshaller. */
private final Marshaller jdkMarshaller = new JdkMarshaller();
/** Router client flag. */
private final boolean routerClient;
/**
* @param routerClient Router client flag.
*/
public GridTcpRestParser(boolean routerClient) {
this.routerClient = routerClient;
}
/** {@inheritDoc} */
@Nullable @Override public GridClientMessage decode(GridNioSession ses, ByteBuffer buf) throws IOException,
IgniteCheckedException {
ParserState state = ses.removeMeta(PARSER_STATE.ordinal());
if (state == null)
state = new ParserState();
GridClientPacketType type = state.packetType();
if (type == null) {
byte hdr = buf.get(buf.position());
switch (hdr) {
case MEMCACHE_REQ_FLAG:
state.packet(new GridMemcachedMessage());
state.packetType(GridClientPacketType.MEMCACHE);
break;
case RESP_REQ_FLAG:
state.packetType(GridClientPacketType.REDIS);
break;
case IGNITE_REQ_FLAG:
// Skip header.
buf.get();
state.packetType(GridClientPacketType.IGNITE);
break;
case IGNITE_HANDSHAKE_FLAG:
// Skip header.
buf.get();
state.packetType(GridClientPacketType.IGNITE_HANDSHAKE);
break;
case IGNITE_HANDSHAKE_RES_FLAG:
buf.get();
state.packetType(GridClientPacketType.IGNITE_HANDSHAKE_RES);
break;
default:
throw new IOException("Failed to parse incoming packet (invalid packet start) [ses=" + ses +
", b=" + Integer.toHexString(hdr & 0xFF) + ']');
}
}
GridClientMessage res = null;
switch (state.packetType()) {
case MEMCACHE:
res = parseMemcachePacket(ses, buf, state);
break;
case REDIS:
res = GridRedisProtocolParser.readArray(buf);
break;
case IGNITE_HANDSHAKE:
res = parseHandshake(buf, state);
break;
case IGNITE_HANDSHAKE_RES:
if (buf.hasRemaining())
res = new GridClientHandshakeResponse(buf.get());
break;
case IGNITE:
res = parseCustomPacket(ses, buf, state);
break;
}
if (res == null)
// Packet was not fully parsed yet.
ses.addMeta(PARSER_STATE.ordinal(), state);
return res;
}
/** {@inheritDoc} */
@Override public ByteBuffer encode(GridNioSession ses, Object msg0) throws IOException, IgniteCheckedException {
assert msg0 != null;
GridClientMessage msg = (GridClientMessage)msg0;
if (msg instanceof GridMemcachedMessage)
return encodeMemcache((GridMemcachedMessage)msg);
else if (msg instanceof GridRedisMessage)
return ((GridRedisMessage)msg).getResponse();
else if (msg instanceof GridClientPingPacket)
return ByteBuffer.wrap(GridClientPingPacket.PING_PACKET);
else if (msg instanceof GridClientHandshakeRequest) {
byte[] bytes = ((GridClientHandshakeRequest)msg).rawBytes();
ByteBuffer buf = ByteBuffer.allocate(bytes.length + 1);
buf.put(IGNITE_HANDSHAKE_FLAG);
buf.put(bytes);
buf.flip();
return buf;
}
else if (msg instanceof GridClientHandshakeResponse)
return ByteBuffer.wrap(new byte[] {
IGNITE_HANDSHAKE_RES_FLAG,
((GridClientHandshakeResponse)msg).resultCode()
});
else if (msg instanceof GridRouterRequest) {
byte[] body = ((GridRouterRequest)msg).body();
ByteBuffer buf = ByteBuffer.allocate(45 + body.length);
buf.put(IGNITE_REQ_FLAG);
buf.putInt(40 + body.length);
buf.putLong(msg.requestId());
buf.put(U.uuidToBytes(msg.clientId()));
buf.put(U.uuidToBytes(msg.destinationId()));
buf.put(body);
buf.flip();
return buf;
}
else {
GridClientMarshaller marsh = marshaller(ses);
ByteBuffer res = marsh.marshal(msg, 45);
ByteBuffer slice = res.slice();
slice.put(IGNITE_REQ_FLAG);
slice.putInt(res.remaining() - 5);
slice.putLong(msg.requestId());
slice.put(U.uuidToBytes(msg.clientId()));
slice.put(U.uuidToBytes(msg.destinationId()));
return res;
}
}
/**
* Parses memcache protocol message.
*
* @param ses Session.
* @param buf Buffer containing not parsed bytes.
* @param state Current parser state.
* @return Parsed packet.s
* @throws IOException If packet cannot be parsed.
* @throws IgniteCheckedException If deserialization error occurred.
*/
@Nullable private GridClientMessage parseMemcachePacket(GridNioSession ses, ByteBuffer buf, ParserState state)
throws IOException, IgniteCheckedException {
assert state.packetType() == GridClientPacketType.MEMCACHE;
assert state.packet() != null;
assert state.packet() instanceof GridMemcachedMessage;
GridMemcachedMessage req = (GridMemcachedMessage)state.packet();
ByteArrayOutputStream tmp = state.buffer();
int i = state.index();
while (buf.remaining() > 0) {
byte b = buf.get();
if (i == 0)
req.requestFlag(b);
else if (i == 1)
req.operationCode(b);
else if (i == 2 || i == 3) {
tmp.write(b);
if (i == 3) {
req.keyLength(U.bytesToShort(tmp.toByteArray(), 0));
tmp.reset();
}
}
else if (i == 4)
req.extrasLength(b);
else if (i >= 8 && i <= 11) {
tmp.write(b);
if (i == 11) {
req.totalLength(U.bytesToInt(tmp.toByteArray(), 0));
tmp.reset();
}
}
else if (i >= 12 && i <= 15) {
tmp.write(b);
if (i == 15) {
req.opaque(tmp.toByteArray());
tmp.reset();
}
}
else if (i >= HDR_LEN && i < HDR_LEN + req.extrasLength()) {
tmp.write(b);
if (i == HDR_LEN + req.extrasLength() - 1) {
req.extras(tmp.toByteArray());
tmp.reset();
}
}
else if (i >= HDR_LEN + req.extrasLength() &&
i < HDR_LEN + req.extrasLength() + req.keyLength()) {
tmp.write(b);
if (i == HDR_LEN + req.extrasLength() + req.keyLength() - 1) {
req.key(tmp.toByteArray());
tmp.reset();
}
}
else if (i >= HDR_LEN + req.extrasLength() + req.keyLength() &&
i < HDR_LEN + req.totalLength()) {
tmp.write(b);
if (i == HDR_LEN + req.totalLength() - 1) {
req.value(tmp.toByteArray());
tmp.reset();
}
}
if (i == HDR_LEN + req.totalLength() - 1)
// Assembled the packet.
return assemble(ses, req);
i++;
}
state.index(i);
return null;
}
/**
* Parses a client handshake, checking a client version and
* reading the marshaller protocol ID.
*
* @param buf Message bytes.
* @param state Parser state.
* @return True if a hint was parsed, false if still need more bytes to parse.
*/
@Nullable private GridClientMessage parseHandshake(ByteBuffer buf, ParserState state) {
assert state.packetType() == GridClientPacketType.IGNITE_HANDSHAKE;
int idx = state.index();
GridClientHandshakeRequest packet = (GridClientHandshakeRequest)state.packet();
if (packet == null) {
packet = new GridClientHandshakeRequest();
state.packet(packet);
}
int rem = buf.remaining();
if (rem > 0) {
byte[] bbuf = new byte[5]; // Buffer to read data to.
int nRead = Math.min(rem, bbuf.length); // Number of bytes to read.
buf.get(bbuf, 0, nRead); // Batch read from buffer.
int nAvailable = nRead; // Number of available bytes.
if (idx < 4) { // Need to read version bytes.
int len = Math.min(nRead, 4 - idx); // Number of version bytes available in buffer.
packet.putBytes(bbuf, idx, len);
idx += len;
state.index(idx);
nAvailable -= len;
}
assert idx <= 4 : "Wrong idx: " + idx;
assert nAvailable == 0 || nAvailable == 1 : "Wrong nav: " + nAvailable;
if (idx == 4 && nAvailable > 0)
return packet;
}
return null; // Wait for more data.
}
/**
* Parses custom packet serialized by Ignite marshaller.
*
* @param ses Session.
* @param buf Buffer containing not parsed bytes.
* @param state Parser state.
* @return Parsed message.
* @throws IOException If packet parsing or deserialization failed.
* @throws IgniteCheckedException If failed.
*/
@Nullable private GridClientMessage parseCustomPacket(GridNioSession ses, ByteBuffer buf, ParserState state)
throws IOException, IgniteCheckedException {
assert state.packetType() == GridClientPacketType.IGNITE;
assert state.packet() == null;
ByteArrayOutputStream tmp = state.buffer();
int len = state.index();
if (buf.remaining() > 0) {
if (len == 0) { // Don't know the size yet.
byte[] lenBytes = statefulRead(buf, tmp, 4);
if (lenBytes != null) {
len = U.bytesToInt(lenBytes, 0);
if (len == 0)
return GridClientPingPacket.PING_MESSAGE;
else if (len < 0)
throw new IOException("Failed to parse incoming packet (invalid packet length) [ses=" + ses +
", len=" + len + ']');
state.index(len);
}
}
if (len > 0 && state.header() == null) {
byte[] hdrBytes = statefulRead(buf, tmp, 40);
if (hdrBytes != null) {
long reqId = GridClientByteUtils.bytesToLong(hdrBytes, 0);
UUID clientId = GridClientByteUtils.bytesToUuid(hdrBytes, 8);
UUID destId = GridClientByteUtils.bytesToUuid(hdrBytes, 24);
state.header(new HeaderData(reqId, clientId, destId));
}
}
if (len > 0 && state.header() != null) {
final int packetSize = len - 40;
if (tmp.size() + buf.remaining() >= packetSize) {
if (buf.remaining() > 0) {
byte[] bodyBytes = new byte[packetSize - tmp.size()];
buf.get(bodyBytes);
tmp.write(bodyBytes);
}
return parseClientMessage(ses, state);
}
else
copyRemaining(buf, tmp);
}
}
return null;
}
/**
* Tries to read the specified amount of bytes using intermediate buffer. Stores
* the bytes to intermediate buffer, if size requirement is not met.
*
* @param buf Byte buffer to read from.
* @param intBuf Intermediate buffer to read bytes from and to save remaining bytes to.
* @param size Number of bytes to read.
* @return Resulting byte array or {@code null}, if both buffers contain less bytes
* than required. In case of non-null result, the intermediate buffer is empty.
* In case of {@code null} result, the input buffer is empty (read fully).
* @throws IOException If IO error occurs.
*/
@Nullable private byte[] statefulRead(ByteBuffer buf, ByteArrayOutputStream intBuf, int size) throws IOException {
if (intBuf.size() + buf.remaining() >= size) {
int off = 0;
byte[] bytes = new byte[size];
if (intBuf.size() > 0) {
assert intBuf.size() < size;
byte[] tmpBytes = intBuf.toByteArray();
System.arraycopy(tmpBytes, 0, bytes, 0, tmpBytes.length);
off = intBuf.size();
intBuf.reset();
}
buf.get(bytes, off, size - off);
return bytes;
}
else {
copyRemaining(buf, intBuf);
return null;
}
}
/**
* Copies remaining bytes from byte buffer to output stream.
*
* @param src Source buffer.
* @param dest Destination stream.
* @throws IOException If IO error occurs.
*/
private void copyRemaining(ByteBuffer src, OutputStream dest) throws IOException {
byte[] b = new byte[src.remaining()];
src.get(b);
dest.write(b);
}
/**
* Parses {@link GridClientMessage} from raw bytes.
*
* @param ses Session.
* @param state Parser state.
* @return A parsed client message.
* @throws IOException On marshaller error.
* @throws IgniteCheckedException If no marshaller was defined for the session.
*/
protected GridClientMessage parseClientMessage(GridNioSession ses,
ParserState state) throws IOException, IgniteCheckedException {
GridClientMessage msg;
if (routerClient) {
msg = new GridRouterResponse(
state.buffer().toByteArray(),
state.header().reqId(),
state.header().clientId(),
state.header().destinationId());
}
else {
GridClientMarshaller marsh = marshaller(ses);
msg = marsh.unmarshal(state.buffer().toByteArray());
msg.requestId(state.header().reqId());
msg.clientId(state.header().clientId());
msg.destinationId(state.header().destinationId());
}
return msg;
}
/**
* Encodes memcache message to a raw byte array.
*
* @param msg Message being serialized.
* @return Serialized message.
* @throws IgniteCheckedException If serialization failed.
*/
private ByteBuffer encodeMemcache(GridMemcachedMessage msg) throws IgniteCheckedException {
GridByteArrayList res = new GridByteArrayList(HDR_LEN);
int keyLen = 0;
int keyFlags = 0;
if (msg.key() != null) {
ByteArrayOutputStream rawKey = new ByteArrayOutputStream();
keyFlags = encodeObj(msg.key(), rawKey);
msg.key(rawKey.toByteArray());
keyLen = rawKey.size();
}
int dataLen = 0;
int valFlags = 0;
if (msg.value() != null) {
ByteArrayOutputStream rawVal = new ByteArrayOutputStream();
valFlags = encodeObj(msg.value(), rawVal);
msg.value(rawVal.toByteArray());
dataLen = rawVal.size();
}
int flagsLen = 0;
if (msg.addFlags())// || keyFlags > 0 || valFlags > 0)
flagsLen = FLAGS_LENGTH;
res.add(MEMCACHE_RES_FLAG);
res.add(msg.operationCode());
// Cast is required due to packet layout.
res.add((short)keyLen);
// Cast is required due to packet layout.
res.add((byte)flagsLen);
// Data type is always 0x00.
res.add((byte)0x00);
res.add((short)msg.status());
res.add(keyLen + flagsLen + dataLen);
res.add(msg.opaque(), 0, msg.opaque().length);
// CAS, unused.
res.add(0L);
assert res.size() == HDR_LEN;
if (flagsLen > 0) {
res.add((short)keyFlags);
res.add((short)valFlags);
}
assert msg.key() == null || msg.key() instanceof byte[];
assert msg.value() == null || msg.value() instanceof byte[];
if (keyLen > 0)
res.add((byte[])msg.key(), 0, ((byte[])msg.key()).length);
if (dataLen > 0)
res.add((byte[])msg.value(), 0, ((byte[])msg.value()).length);
return ByteBuffer.wrap(res.entireArray());
}
/**
* Validates incoming packet and deserializes all fields that need to be deserialized.
*
* @param ses Session on which packet is being parsed.
* @param req Raw packet.
* @return Same packet with fields deserialized.
* @throws IOException If parsing failed.
* @throws IgniteCheckedException If deserialization failed.
*/
private GridClientMessage assemble(GridNioSession ses,
GridMemcachedMessage req) throws IOException, IgniteCheckedException {
byte[] extras = req.extras();
// First, decode key and value, if any
if (req.key() != null || req.value() != null) {
short keyFlags = 0;
short valFlags = 0;
if (req.hasFlags()) {
if (extras == null || extras.length < FLAGS_LENGTH)
throw new IOException("Failed to parse incoming packet (flags required for command) [ses=" +
ses + ", opCode=" + Integer.toHexString(req.operationCode() & 0xFF) + ']');
keyFlags = U.bytesToShort(extras, 0);
valFlags = U.bytesToShort(extras, 2);
}
if (req.key() != null) {
assert req.key() instanceof byte[];
byte[] rawKey = (byte[])req.key();
// Only values can be hessian-encoded.
req.key(decodeObj(keyFlags, rawKey));
}
if (req.value() != null) {
assert req.value() instanceof byte[];
byte[] rawVal = (byte[])req.value();
req.value(decodeObj(valFlags, rawVal));
}
}
if (req.hasExpiration()) {
if (extras == null || extras.length < 8)
throw new IOException("Failed to parse incoming packet (expiration value required for command) [ses=" +
ses + ", opCode=" + Integer.toHexString(req.operationCode() & 0xFF) + ']');
req.expiration(U.bytesToInt(extras, 4) & 0xFFFFFFFFL);
}
if (req.hasInitial()) {
if (extras == null || extras.length < 16)
throw new IOException("Failed to parse incoming packet (initial value required for command) [ses=" +
ses + ", opCode=" + Integer.toHexString(req.operationCode() & 0xFF) + ']');
req.initial(U.bytesToLong(extras, 8));
}
if (req.hasDelta()) {
if (extras == null || extras.length < 8)
throw new IOException("Failed to parse incoming packet (delta value required for command) [ses=" +
ses + ", opCode=" + Integer.toHexString(req.operationCode() & 0xFF) + ']');
req.delta(U.bytesToLong(extras, 0));
}
if (extras != null) {
// Clients that include cache name must always include flags.
int len = 4;
if (req.hasExpiration())
len += 4;
if (req.hasDelta())
len += 8;
if (req.hasInitial())
len += 8;
if (extras.length - len > 0) {
byte[] cacheName = new byte[extras.length - len];
U.arrayCopy(extras, len, cacheName, 0, extras.length - len);
req.cacheName(new String(cacheName, UTF_8));
}
}
return req;
}
/**
* Decodes value from a given byte array to the object according to the flags given.
*
* @param flags Flags.
* @param bytes Byte array to decode.
* @return Decoded value.
* @throws IgniteCheckedException If deserialization failed.
*/
private Object decodeObj(short flags, byte[] bytes) throws IgniteCheckedException {
assert bytes != null;
if ((flags & SERIALIZED_FLAG) != 0)
return U.unmarshal(jdkMarshaller, bytes, null);
int masked = flags & 0xff00;
switch (masked) {
case BOOLEAN_FLAG:
return bytes[0] == '1';
case INT_FLAG:
return U.bytesToInt(bytes, 0);
case LONG_FLAG:
return U.bytesToLong(bytes, 0);
case DATE_FLAG:
return new Date(U.bytesToLong(bytes, 0));
case BYTE_FLAG:
return bytes[0];
case FLOAT_FLAG:
return Float.intBitsToFloat(U.bytesToInt(bytes, 0));
case DOUBLE_FLAG:
return Double.longBitsToDouble(U.bytesToLong(bytes, 0));
case BYTE_ARR_FLAG:
return bytes;
default:
return new String(bytes, UTF_8);
}
}
/**
* Encodes given object to a byte array and returns flags that describe the type of serialized object.
*
* @param obj Object to serialize.
* @param out Output stream to which object should be written.
* @return Serialization flags.
* @throws IgniteCheckedException If JDK serialization failed.
*/
private int encodeObj(Object obj, ByteArrayOutputStream out) throws IgniteCheckedException {
int flags = 0;
byte[] data = null;
if (obj instanceof String)
data = ((String)obj).getBytes(UTF_8);
else if (obj instanceof Boolean) {
data = new byte[] {(byte)((Boolean)obj ? '1' : '0')};
flags |= BOOLEAN_FLAG;
}
else if (obj instanceof Integer) {
data = U.intToBytes((Integer)obj);
flags |= INT_FLAG;
}
else if (obj instanceof Long) {
data = U.longToBytes((Long)obj);
flags |= LONG_FLAG;
}
else if (obj instanceof Date) {
data = U.longToBytes(((Date)obj).getTime());
flags |= DATE_FLAG;
}
else if (obj instanceof Byte) {
data = new byte[] {(Byte)obj};
flags |= BYTE_FLAG;
}
else if (obj instanceof Float) {
data = U.intToBytes(Float.floatToIntBits((Float)obj));
flags |= FLOAT_FLAG;
}
else if (obj instanceof Double) {
data = U.longToBytes(Double.doubleToLongBits((Double)obj));
flags |= DOUBLE_FLAG;
}
else if (obj instanceof byte[]) {
data = (byte[])obj;
flags |= BYTE_ARR_FLAG;
}
else {
U.marshal(jdkMarshaller, obj, out);
flags |= SERIALIZED_FLAG;
}
if (data != null)
out.write(data, 0, data.length);
return flags;
}
/**
* Returns marshaller.
*
* @return Marshaller.
*/
protected GridClientMarshaller marshaller(GridNioSession ses) {
GridClientMarshaller marsh = ses.meta(MARSHALLER.ordinal());
assert marsh != null;
return marsh;
}
/** {@inheritDoc} */
public String toString() {
return S.toString(GridTcpRestParser.class, this);
}
/**
* Holder for parser state and temporary buffer.
*/
protected static class ParserState {
/** Parser index. */
private int idx;
/** Temporary data buffer. */
private ByteArrayOutputStream buf = new ByteArrayOutputStream();
/** Packet being assembled. */
private GridClientMessage packet;
/** Packet type. */
private GridClientPacketType packetType;
/** Header data. */
private HeaderData hdr;
/**
* @return Stored parser index.
*/
public int index() {
return idx;
}
/**
* @param idx Index to store.
*/
public void index(int idx) {
this.idx = idx;
}
/**
* @return Temporary data buffer.
*/
public ByteArrayOutputStream buffer() {
return buf;
}
/**
* @return Pending packet.
*/
@Nullable public GridClientMessage packet() {
return packet;
}
/**
* @param packet Pending packet.
*/
public void packet(GridClientMessage packet) {
assert this.packet == null;
this.packet = packet;
}
/**
* @return Pending packet type.
*/
public GridClientPacketType packetType() {
return packetType;
}
/**
* @param packetType Pending packet type.
*/
public void packetType(GridClientPacketType packetType) {
this.packetType = packetType;
}
/**
* @return Header.
*/
public HeaderData header() {
return hdr;
}
/**
* @param hdr Header.
*/
public void header(HeaderData hdr) {
this.hdr = hdr;
}
/** {@inheritDoc} */
@Override public String toString() {
return S.toString(ParserState.class, this);
}
}
/**
* Header.
*/
protected static class HeaderData {
/** Request Id. */
private final long reqId;
/** Request Id. */
private final UUID clientId;
/** Request Id. */
private final UUID destId;
/**
* @param reqId Request Id.
* @param clientId Client Id.
* @param destId Destination Id.
*/
private HeaderData(long reqId, UUID clientId, UUID destId) {
this.reqId = reqId;
this.clientId = clientId;
this.destId = destId;
}
/**
* @return Request Id.
*/
public long reqId() {
return reqId;
}
/**
* @return Client Id.
*/
public UUID clientId() {
return clientId;
}
/**
* @return Destination Id.
*/
public UUID destinationId() {
return destId;
}
}
}