package cassandra.protocol;
import cassandra.CassandraDriver;
import cassandra.CassandraException;
import cassandra.cql.BatchStatement;
import cassandra.cql.Consistency;
import cassandra.cql.PreparedStatement;
import cassandra.cql.RowMetadata.Column;
import cassandra.cql.WriteType;
import cassandra.cql.type.CQL3Type;
import cassandra.protocol.internal.Message;
import cassandra.protocol.internal.MessageInputStream;
import cassandra.protocol.internal.MessageOutputStream;
import cassandra.protocol.internal.MessageParser;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.util.*;
public abstract class CassandraMessage extends Message {
public static class Header extends Message {
public static enum Flag {
COMPRESSED, TRACING
}
public static final MessageParser<Header> PARSER = new MessageParser<Header>() {
@Override
public Header parseFrom(MessageInputStream input) {
return new Header(input);
}
};
public static Header parseFrom(MessageInputStream input) {
return PARSER.parseFrom(input);
}
public static Header valueOf(CassandraMessage message) {
Header header = new Header(message.getType());
header.setVersion(CassandraDriver.NATIVE_PROTOCOL_VERSION_NUMBER);
header.setCompressedFlag(message.getType() != Type.STARTUP && message.hasCompression());
header.setTracingFlag(message.isTracing());
header.setStreamId(message.streamId);
header.setMessageLength(message.getApproximateSize());
return header;
}
private int version;
private EnumSet<Flag> flags;
private int streamId;
private CassandraMessage.Type messageType;
private int messageLength;
public Header(CassandraMessage.Type messageType) {
flags = EnumSet.noneOf(Flag.class);
setMessageType(messageType);
}
private Header(MessageInputStream input) {
version = input.readInt8();
version &= 0x7F;
flags = input.readEnumSet8(Flag.class);
streamId = input.readInt8();
int opcode = input.readInt8();
messageType = CassandraMessage.Type.valueOf(opcode);
messageLength = input.readInt32();
}
public int getVersion() {
return version;
}
public Header setVersion(int version) {
this.version = version;
return this;
}
public boolean getCompressedFlag() {
return flags.contains(Flag.COMPRESSED);
}
public Header setCompressedFlag(boolean compressedFlag) {
if (compressedFlag) {
flags.add(Flag.COMPRESSED);
} else {
flags.remove(Flag.COMPRESSED);
}
return this;
}
public boolean getTracingFlag() {
return flags.contains(Flag.TRACING);
}
public Header setTracingFlag(boolean tracingFlag) {
if (tracingFlag) {
flags.add(Flag.TRACING);
} else {
flags.remove(Flag.TRACING);
}
return this;
}
public CassandraMessage.Type getMessageType() {
return messageType;
}
protected Header setMessageType(CassandraMessage.Type messageType) {
this.messageType = messageType;
return this;
}
public int getStreamId() {
return streamId;
}
public Header setStreamId(int streamId) {
this.streamId = streamId;
return this;
}
public int getMessageLength() {
return messageLength;
}
public Header setMessageLength(int messageLength) {
this.messageLength = messageLength;
return this;
}
@Override
public MessageParser<Header> getParserForType() {
return PARSER;
}
@Override
public int getApproximateSize() {
return 8;
}
@Override
public void writeTo(MessageOutputStream output) {
if (messageType.direction == CassandraMessage.Direction.REQUEST) {
output.writeInt8(version & 0x7F);
} else {
output.writeInt8(version | 0x80);
}
output.writeEnumSet8(flags);
output.writeInt8(streamId);
output.writeInt8(messageType.opcode);
output.writeInt32(messageLength);
}
}
public static enum Direction {
REQUEST, RESPONSE
}
public static enum Type {
ERROR(0, Direction.RESPONSE, Error.PARSER),
STARTUP(1, Direction.REQUEST, Startup.PARSER),
READY(2, Direction.RESPONSE, Ready.PARSER),
AUTHENTICATE(3, Direction.RESPONSE, Authenticate.PARSER),
//CREDENTIALS(4, Direction.REQUEST, null),
OPTIONS(5, Direction.REQUEST, Options.PARSER),
SUPPORTED(6, Direction.RESPONSE, Supported.PARSER),
QUERY(7, Direction.REQUEST, Query.PARSER),
RESULT(8, Direction.RESPONSE, Result.PARSER),
PREPARE(9, Direction.REQUEST, Prepare.PARSER),
EXECUTE(10, Direction.REQUEST, Execute.PARSER),
REGISTER(11, Direction.REQUEST, Register.PARSER),
EVENT(12, Direction.RESPONSE, Event.PARSER),
BATCH(13, Direction.REQUEST, Batch.PARSER),
AUTH_CHALLENGE(14, Direction.RESPONSE, AuthChallenge.PARSER),
AUTH_RESPONSE(15, Direction.REQUEST, AuthResponse.PARSER),
AUTH_SUCCESS(16, Direction.RESPONSE, AuthSuccess.PARSER);
public final int opcode;
public final Direction direction;
public final MessageParser<? extends CassandraMessage> parser;
private Type(int opcode, Direction direction, MessageParser<? extends CassandraMessage> parser) {
this.opcode = opcode;
this.direction = direction;
this.parser = parser;
}
public static Type valueOf(int opcode) {
for (Type type : Type.values()) {
if (type.opcode == opcode) {
return type;
}
}
throw new IllegalStateException(String.format("unknown opcode %d", opcode));
}
}
public static CassandraMessage parseFrom(Header header, MessageInputStream input) {
CassandraMessage message;
if (header.getMessageType().direction == Direction.RESPONSE && header.getTracingFlag()) {
UUID tracingId = input.readUUID();
Response response = ((Response)header.getMessageType().parser.parseFrom(input));
response.setTracingId(tracingId);
message = response;
} else {
message = header.getMessageType().parser.parseFrom(input);
}
message.setCompression(header.getCompressedFlag());
message.setTracing(header.getTracingFlag());
message.setStreamId(header.getStreamId());
return message;
}
private Type type;
private boolean compression, tracing;
private int streamId;
protected CassandraMessage(Type type) {
if (type == null) {
throw new NullPointerException("type");
}
this.type = type;
}
public Type getType() {
return type;
}
public boolean hasCompression() {
return compression;
}
public void setCompression(boolean compression) {
this.compression = compression;
}
public boolean isTracing() {
return tracing;
}
public void setTracing(boolean tracing) {
this.tracing = tracing;
}
public int getStreamId() {
return streamId;
}
public void setStreamId(int streamId) {
this.streamId = streamId;
}
@Override
@SuppressWarnings("unchecked")
public MessageParser<CassandraMessage> getParserForType() {
return (MessageParser<CassandraMessage>)getType().parser;
}
@Override
public void writeTo(MessageOutputStream output) {
writePartialTo(output);
}
protected abstract void writePartialTo(MessageOutputStream output);
@Override
public String toString() {
return getType().toString();
}
public static abstract class Request extends CassandraMessage {
protected Request(Type type) {
super(type);
if (type.direction != Direction.REQUEST) {
throw new IllegalArgumentException();
}
}
}
public static abstract class Response extends CassandraMessage {
private UUID tracingId;
protected Response(Type type) {
super(type);
if (type.direction != Direction.RESPONSE) {
throw new IllegalArgumentException();
}
}
public boolean hasTracingId() {
return tracingId != null;
}
public UUID getTracingId() {
return tracingId;
}
public void setTracingId(UUID tracingId) {
if (tracingId != null) {
setTracing(true);
this.tracingId = tracingId;
}
}
}
public static class Options extends Request {
public static final MessageParser<Options> PARSER = new MessageParser<Options>() {
@Override
public Options parseFrom(MessageInputStream input) {
return new Options(input);
}
};
public static Options parseFrom(MessageInputStream input) {
return PARSER.parseFrom(input);
}
public Options() {
super(Type.OPTIONS);
}
private Options(MessageInputStream input) {
super(Type.OPTIONS);
}
@Override
public int getApproximateSize() {
return 0;
}
@Override
protected void writePartialTo(MessageOutputStream output) {
// header-only
}
}
public static class Supported extends Response {
public static final MessageParser<Supported> PARSER = new MessageParser<Supported>() {
@Override
public Supported parseFrom(MessageInputStream input) {
return new Supported(input);
}
};
public static Supported parseFrom(MessageInputStream input) {
return PARSER.parseFrom(input);
}
public final Map<String, List<String>> options;
public Supported(Map<String, List<String>> options) {
super(Type.SUPPORTED);
this.options = options;
}
private Supported(MessageInputStream input) {
this(input.readStringToStringListMap());
}
@Override
public int getApproximateSize() {
return MessageOutputStream.computeStringToStringListMapSize(options);
}
@Override
protected void writePartialTo(MessageOutputStream output) {
output.writeStringToStringListMap(options);
}
}
public static class Startup extends Request {
public static final MessageParser<Startup> PARSER = new MessageParser<Startup>() {
@Override
public Startup parseFrom(MessageInputStream input) {
return new Startup(input);
}
};
public static Startup parseFrom(MessageInputStream input) {
return PARSER.parseFrom(input);
}
public final Map<String, String> options;
public Startup(Map<String, String> options) {
super(Type.STARTUP);
this.options = options;
}
private Startup(MessageInputStream input) {
this(input.readStringMap());
}
@Override
public int getApproximateSize() {
return MessageOutputStream.computeStringMapSize(options);
}
@Override
protected void writePartialTo(MessageOutputStream output) {
output.writeStringMap(options);
}
}
public static class Authenticate extends Response {
public static final MessageParser<Authenticate> PARSER = new MessageParser<Authenticate>() {
@Override
public Authenticate parseFrom(MessageInputStream input) {
return new Authenticate(input);
}
};
public static Authenticate parseFrom(MessageInputStream input) {
return PARSER.parseFrom(input);
}
public final String authenticator;
public Authenticate(String authenticator) {
super(Type.AUTHENTICATE);
this.authenticator = authenticator;
}
private Authenticate(MessageInputStream input) {
this(input.readString());
}
@Override
public int getApproximateSize() {
return MessageOutputStream.computeStringSize(authenticator);
}
@Override
protected void writePartialTo(MessageOutputStream output) {
output.writeString(authenticator);
}
}
public static class AuthResponse extends Request {
public static final MessageParser<AuthResponse> PARSER = new MessageParser<AuthResponse>() {
@Override
public AuthResponse parseFrom(MessageInputStream input) {
return new AuthResponse(input);
}
};
public static AuthResponse parseFrom(MessageInputStream input) {
return PARSER.parseFrom(input);
}
public final byte[] token;
public AuthResponse(byte[] token) {
super(Type.AUTH_RESPONSE);
this.token = token;
}
private AuthResponse(MessageInputStream input) {
this(input.readValue().array());
}
@Override
public int getApproximateSize() {
return MessageOutputStream.computeValueSize(token);
}
@Override
protected void writePartialTo(MessageOutputStream output) {
output.writeValue(token);
}
}
public static class AuthChallenge extends Response {
public static final MessageParser<AuthChallenge> PARSER = new MessageParser<AuthChallenge>() {
@Override
public AuthChallenge parseFrom(MessageInputStream input) {
return new AuthChallenge(input);
}
};
public static AuthChallenge parseFrom(MessageInputStream input) {
return PARSER.parseFrom(input);
}
public final byte[] token;
public AuthChallenge(byte[] token) {
super(Type.AUTH_CHALLENGE);
this.token = token;
}
private AuthChallenge(MessageInputStream input) {
this(input.readValue().array());
}
@Override
public int getApproximateSize() {
return MessageOutputStream.computeValueSize(token);
}
@Override
protected void writePartialTo(MessageOutputStream output) {
output.writeValue(token);
}
}
public static class AuthSuccess extends Response {
public static final MessageParser<AuthSuccess> PARSER = new MessageParser<AuthSuccess>() {
@Override
public AuthSuccess parseFrom(MessageInputStream input) {
return new AuthSuccess(input);
}
};
public static AuthSuccess parseFrom(MessageInputStream input) {
return PARSER.parseFrom(input);
}
public final byte[] token;
public AuthSuccess(byte[] token) {
super(Type.AUTH_SUCCESS);
this.token = token;
}
private AuthSuccess(MessageInputStream input) {
this(input.readValue().array());
}
@Override
public int getApproximateSize() {
return MessageOutputStream.computeValueSize(token);
}
@Override
protected void writePartialTo(MessageOutputStream output) {
output.writeValue(token);
}
}
public static class Register extends Request {
public static final MessageParser<Register> PARSER = new MessageParser<Register>() {
@Override
public Register parseFrom(MessageInputStream input) {
return new Register(input);
}
};
public static Register parseFrom(MessageInputStream input) {
return PARSER.parseFrom(input);
}
public final List<Event.Type> events;
public Register(List<Event.Type> events) {
super(Type.REGISTER);
this.events = events;
}
private Register(MessageInputStream input) {
super(Type.REGISTER);
int size = input.readInt16();
events = new ArrayList<Event.Type>(size);
for (int i = 0; i < size; i++) {
Event.Type event = input.readEnum(Event.Type.class);
events.add(event);
}
}
@Override
public int getApproximateSize() {
int size = 2;
for (Event.Type event : events) {
size += MessageOutputStream.computeEnumSize(event);
}
return size;
}
@Override
protected void writePartialTo(MessageOutputStream output) {
output.writeInt16(events.size());
for (Event.Type event : events) {
output.writeEnum(event);
}
}
}
public static class Ready extends Response {
public static final MessageParser<Ready> PARSER = new MessageParser<Ready>() {
@Override
public Ready parseFrom(MessageInputStream input) {
return new Ready(input);
}
};
public static Ready parseFrom(MessageInputStream input) {
return PARSER.parseFrom(input);
}
public Ready() {
super(Type.READY);
}
private Ready(MessageInputStream input) {
this();
}
@Override
public int getApproximateSize() {
return 0;
}
@Override
protected void writePartialTo(MessageOutputStream output) {
// header-only
}
}
public static abstract class Event extends Response {
public static final MessageParser<Event> PARSER = new MessageParser<Event>() {
@Override
public Event parseFrom(MessageInputStream input) {
Event.Type type = input.readEnum(Event.Type.class);
switch (type) {
case TOPOLOGY_CHANGE:
return new TopologyChange(input);
case STATUS_CHANGE:
return new StatusChange(input);
case SCHEMA_CHANGE:
return new SchemaChange(input);
default:
throw new IllegalStateException(String.format("unknown event %s", type));
}
}
};
public static Event parseFrom(MessageInputStream input) {
return PARSER.parseFrom(input);
}
public static enum Type {
TOPOLOGY_CHANGE, STATUS_CHANGE, SCHEMA_CHANGE
}
public final Type type;
protected Event(Type type) {
super(CassandraMessage.Type.EVENT);
this.type = type;
}
public static class TopologyChange extends Event {
public static TopologyChange newNode(InetSocketAddress node) {
return new TopologyChange(Change.NEW_NODE, node);
}
public static TopologyChange removedNode(InetSocketAddress node) {
return new TopologyChange(Change.REMOVED_NODE, node);
}
public static TopologyChange movedNode(InetSocketAddress node) {
return new TopologyChange(Change.MOVED_NODE, node);
}
public enum Change {
NEW_NODE, REMOVED_NODE, MOVED_NODE
}
public final Change change;
public final InetSocketAddress node;
public TopologyChange(Change change, InetSocketAddress node) {
super(Type.TOPOLOGY_CHANGE);
this.change = change;
this.node = node;
}
private TopologyChange(MessageInputStream input) {
super(Type.TOPOLOGY_CHANGE);
change = input.readEnum(TopologyChange.Change.class);
node = input.readInet();
}
@Override
public int getApproximateSize() {
int size = MessageOutputStream.computeEnumSize(type);
size += MessageOutputStream.computeEnumSize(change);
size += MessageOutputStream.computeInetSize(node);
return size;
}
@Override
protected void writePartialTo(MessageOutputStream output) {
output.writeEnum(type);
output.writeEnum(change);
output.writeInet(node);
}
@Override
public String toString() {
return String.format("%s.%s %s", type, change, node);
}
}
public static class StatusChange extends Event {
public static StatusChange up(InetSocketAddress node) {
return new StatusChange(Status.UP, node);
}
public static StatusChange down(InetSocketAddress node) {
return new StatusChange(Status.DOWN, node);
}
public enum Status {
UP, DOWN
}
public final Status status;
public final InetSocketAddress node;
public StatusChange(Status status, InetSocketAddress node) {
super(Type.STATUS_CHANGE);
this.status = status;
this.node = node;
}
private StatusChange(MessageInputStream input) {
super(Type.STATUS_CHANGE);
status = input.readEnum(StatusChange.Status.class);
node = input.readInet();
}
@Override
public int getApproximateSize() {
int size = MessageOutputStream.computeEnumSize(type);
size += MessageOutputStream.computeEnumSize(status);
size += MessageOutputStream.computeInetSize(node);
return size;
}
@Override
protected void writePartialTo(MessageOutputStream output) {
output.writeEnum(type);
output.writeEnum(status);
output.writeInet(node);
}
@Override
public String toString() {
return String.format("%s.%s %s", type, status, node);
}
}
public static class SchemaChange extends Event {
public enum Change {
CREATED, UPDATED, DROPPED
}
public final Change change;
public final String keyspace;
public final String table;
public SchemaChange(Change change, String keyspace, String table) {
super(Type.SCHEMA_CHANGE);
this.change = change;
this.keyspace = keyspace;
this.table = table;
}
private SchemaChange(MessageInputStream input) {
super(Type.SCHEMA_CHANGE);
change = input.readEnum(SchemaChange.Change.class);
keyspace = input.readString();
table = input.readString();
}
@Override
public int getApproximateSize() {
int size = MessageOutputStream.computeEnumSize(type);
size += MessageOutputStream.computeEnumSize(change);
size += MessageOutputStream.computeStringSize(keyspace);
size += MessageOutputStream.computeStringSize(table);
return size;
}
@Override
protected void writePartialTo(MessageOutputStream output) {
output.writeEnum(type);
output.writeEnum(change);
output.writeString(keyspace);
output.writeString(table);
}
@Override
public String toString() {
if (table.isEmpty()) {
return String.format("%s.%s %s", type, change, keyspace);
} else {
return String.format("%s.%s %s.%s", type, change, keyspace, table);
}
}
}
}
public static class Batch extends Request {
public static final MessageParser<Batch> PARSER = new MessageParser<Batch>() {
@Override
public Batch parseFrom(MessageInputStream input) {
return new Batch(input);
}
};
public static Batch parseFrom(MessageInputStream input) {
return PARSER.parseFrom(input);
}
public static class QueryValue {
public final Object stringOrId;
public final List<ByteBuffer> values;
public QueryValue(Object stringOrId, List<ByteBuffer> values) {
this.stringOrId = stringOrId;
this.values = values;
}
}
public final BatchStatement.Type type;
public final List<QueryValue> queries;
public final Consistency consistency;
public Batch(BatchStatement.Type type, List<QueryValue> queries, Consistency consistency) {
super(Type.BATCH);
this.type = type;
this.queries = queries;
this.consistency = consistency;
}
private Batch(MessageInputStream input) {
super(Type.BATCH);
type = BatchStatement.Type.valueOf(input.readInt8());
int size = input.readInt16();
queries = new ArrayList<QueryValue>(size);
for (int i = 0; i < size; i++) {
int kind = input.readInt8();
Object stringOrId;
if (kind == 0) {
stringOrId = input.readLongString();
} else {
stringOrId = new PreparedStatement.StatementId(input.readBytes());
}
List<ByteBuffer> values = input.readValueList();
queries.add(new QueryValue(stringOrId, values));
}
consistency = Consistency.valueOf(input.readInt16());
}
@Override
public int getApproximateSize() {
int size = 1 + 2;
for (Batch.QueryValue query : queries) {
size += 1;
if (query.stringOrId instanceof String) {
size += MessageOutputStream.computeLongStringSize((String)query.stringOrId);
} else {
size += MessageOutputStream.computeBytesSize(((PreparedStatement.StatementId)query.stringOrId).array());
}
size += MessageOutputStream.computeValueListSize(query.values);
}
size += 2;
return size;
}
@Override
protected void writePartialTo(MessageOutputStream output) {
output.writeInt8(type.value);
output.writeInt16(queries.size());
for (Batch.QueryValue query : queries) {
if (query.stringOrId instanceof String) {
output.writeInt8(0);
output.writeLongString((String)query.stringOrId);
} else {
output.writeInt8(1);
output.writeBytes(((PreparedStatement.StatementId)query.stringOrId).array());
}
output.writeValueList(query.values);
}
output.writeInt16(consistency.code);
}
}
public static class Execute extends Request {
public static final MessageParser<Execute> PARSER = new MessageParser<Execute>() {
@Override
public Execute parseFrom(MessageInputStream input) {
return new Execute(input);
}
};
public static Execute parseFrom(MessageInputStream input) {
return PARSER.parseFrom(input);
}
public final PreparedStatement.StatementId statementId;
public final QueryParameters queryParameters;
public Execute(PreparedStatement.StatementId statementId, QueryParameters queryParameters) {
super(Type.EXECUTE);
this.statementId = statementId;
this.queryParameters = queryParameters;
}
private Execute(MessageInputStream input) {
this(new PreparedStatement.StatementId(input.readBytes()), QueryParameters.parseFrom(input));
}
@Override
public int getApproximateSize() {
int size = MessageOutputStream.computeBytesSize(statementId.array());
size += queryParameters.getApproximateSize();
return size;
}
@Override
protected void writePartialTo(MessageOutputStream output) {
output.writeBytes(statementId.array());
queryParameters.writeTo(output);
}
}
public static class Prepare extends Request {
public static final MessageParser<Prepare> PARSER = new MessageParser<Prepare>() {
@Override
public Prepare parseFrom(MessageInputStream input) {
return new Prepare(input);
}
};
public static Prepare parseFrom(MessageInputStream input) {
return PARSER.parseFrom(input);
}
public final String query;
public Prepare(String query) {
super(Type.PREPARE);
this.query = query;
}
private Prepare(MessageInputStream input) {
this(input.readLongString());
}
@Override
public int getApproximateSize() {
return MessageOutputStream.computeLongStringSize(query);
}
@Override
protected void writePartialTo(MessageOutputStream output) {
output.writeLongString(query);
}
}
public static class Query extends Request {
public static final MessageParser<Query> PARSER = new MessageParser<Query>() {
@Override
public Query parseFrom(MessageInputStream input) {
return new Query(input);
}
};
public static Query parseFrom(MessageInputStream input) {
return PARSER.parseFrom(input);
}
public final String query;
public final QueryParameters queryParameters;
public Query(String query, QueryParameters queryParameters) {
super(Type.QUERY);
this.query = query;
this.queryParameters = queryParameters;
}
private Query(MessageInputStream input) {
this(input.readLongString(), QueryParameters.parseFrom(input));
}
@Override
public int getApproximateSize() {
int size = MessageOutputStream.computeLongStringSize(query);
size += queryParameters.getApproximateSize();
return size;
}
@Override
protected void writePartialTo(MessageOutputStream output) {
output.writeLongString(query);
queryParameters.writeTo(output);
}
}
public static abstract class Result extends Response {
public static final MessageParser<Result> PARSER = new MessageParser<Result>() {
@Override
public Result parseFrom(MessageInputStream input) {
int id = input.readInt32();
Kind kind = Kind.valueOf(id);
switch (kind) {
case VOID:
return new Void(input);
case ROWS:
return new Rows(input);
case SET_KEYSPACE:
return new SetKeyspace(input);
case PREPARED:
return new Prepared(input);
case SCHEMA_CHANGE:
return new SchemaChange(input);
default:
throw new IllegalStateException(String.format("unknown kind id %d in RESULT message ", id));
}
}
};
public static Result parseFrom(MessageInputStream input) {
return PARSER.parseFrom(input);
}
@Override
public void writeTo(MessageOutputStream output) {
if (hasTracingId()) {
output.writeUUID(getTracingId());
}
super.writeTo(output);
}
public enum Kind {
VOID(1), ROWS(2), SET_KEYSPACE(3), PREPARED(4), SCHEMA_CHANGE(5);
public final int id;
private Kind(int id) {
this.id = id;
}
public static Kind valueOf(int id) {
for (Kind kind : Kind.values()) {
if (kind.id == id) {
return kind;
}
}
throw new IllegalStateException(String.format("unknown kind id %d in RESULT message ", id));
}
}
public final Kind kind;
protected Result(Kind kind) {
super(Type.RESULT);
this.kind = kind;
}
public static class Void extends Result {
public Void() {
super(Kind.VOID);
}
private Void(MessageInputStream input) {
this();
}
@Override
public int getApproximateSize() {
return 4;
}
@Override
protected void writePartialTo(MessageOutputStream output) {
output.writeInt32(kind.id);
}
}
public static class Rows extends Result {
public final Metadata metadata;
public final Queue<List<ByteBuffer>> rows;
public Rows(Metadata metadata, Queue<List<ByteBuffer>> rows) {
super(Kind.ROWS);
this.metadata = metadata;
this.rows = rows;
}
private Rows(MessageInputStream input) {
super(Kind.ROWS);
metadata = Metadata.parseFrom(input);
int rowCount = input.readInt32();
int columnCount = metadata.columnCount;
rows = new ArrayDeque<List<ByteBuffer>>(rowCount);
for (int i = 0; i < rowCount; i++) {
List<ByteBuffer> row = new ArrayList<ByteBuffer>(columnCount);
for (int j = 0; j < columnCount; j++) {
row.add(input.readValue());
}
rows.add(row);
}
}
@Override
public int getApproximateSize() {
int size = 4;
size += metadata.getApproximateSize();
size += 4;
for (List<ByteBuffer> row : rows) {
for (ByteBuffer column : row) {
size += MessageOutputStream.computeValueSize(column);
}
}
if (hasTracingId()) {
size += 16;
}
return size;
}
@Override
protected void writePartialTo(MessageOutputStream output) {
output.writeInt32(kind.id);
metadata.writeTo(output);
output.writeInt32(rows.size());
for (List<ByteBuffer> row : rows) {
for (ByteBuffer column : row) {
output.writeValue(column);
}
}
}
}
public static class SetKeyspace extends Result {
public final String keyspace;
public SetKeyspace(String keyspace) {
super(Kind.SET_KEYSPACE);
this.keyspace = keyspace;
}
private SetKeyspace(MessageInputStream input) {
this(input.readString());
}
@Override
public int getApproximateSize() {
return 4 + MessageOutputStream.computeStringSize(keyspace);
}
@Override
protected void writePartialTo(MessageOutputStream output) {
output.writeInt32(kind.id);
output.writeString(keyspace);
}
}
public static class Prepared extends Result {
public final PreparedStatement.StatementId statementId;
public final Metadata metadata, resultMetadata;
public Prepared(PreparedStatement.StatementId statementId, Metadata metadata, Metadata resultMetadata) {
super(Kind.PREPARED);
this.statementId = statementId;
this.metadata = metadata;
this.resultMetadata = resultMetadata;
}
private Prepared(MessageInputStream input) {
this(new PreparedStatement.StatementId(input.readBytes()), Metadata.parseFrom(input), Metadata.parseFrom(input));
}
@Override
public int getApproximateSize() {
int size = 4;
size += MessageOutputStream.computeBytesSize(statementId.array());
size += metadata.getApproximateSize();
size += resultMetadata.getApproximateSize();
if (hasTracingId()) {
size += 16;
}
return size;
}
@Override
protected void writePartialTo(MessageOutputStream output) {
output.writeInt32(kind.id);
output.writeBytes(statementId.array());
metadata.writeTo(output);
resultMetadata.writeTo(output);
}
}
public static class SchemaChange extends Result {
public enum Change {
CREATED, UPDATED, DROPPED
}
public final Change change;
public final String keyspace;
public final String table;
public SchemaChange(Change change, String keyspace, String table) {
super(Kind.SCHEMA_CHANGE);
this.change = change;
this.keyspace = keyspace;
this.table = table;
}
private SchemaChange(MessageInputStream input) {
this(input.readEnum(SchemaChange.Change.class), input.readString(), input.readString());
}
@Override
public int getApproximateSize() {
int size = 4;
size += MessageOutputStream.computeEnumSize(change);
size += MessageOutputStream.computeStringSize(keyspace);
size += MessageOutputStream.computeStringSize(table);
return size;
}
@Override
protected void writePartialTo(MessageOutputStream output) {
output.writeInt32(kind.id);
output.writeEnum(change);
output.writeString(keyspace);
output.writeString(table);
}
}
}
public static class Error extends Response {
public static final MessageParser<Error> PARSER = new MessageParser<Error>() {
@Override
public Error parseFrom(MessageInputStream input) {
return new Error(input);
}
};
public static Error parseFrom(MessageInputStream input) {
return PARSER.parseFrom(input);
}
public final CassandraException.Code code;
public final String message;
public final CassandraException exception;
public Error(CassandraException.Code code, String message, CassandraException exception) {
super(Type.ERROR);
this.code = code;
this.message = message;
this.exception = exception;
}
private Error(MessageInputStream input) {
super(Type.ERROR);
code = CassandraException.Code.valueOf(input.readInt32());
message = input.readString();
switch (code) {
case SERVER_ERROR:
exception = new CassandraException.ServerError(message);
break;
case PROTOCOL_ERROR:
exception = new CassandraException.ProtocolError(message);
break;
case BAD_CREDENTIALS:
exception = new CassandraException.BadCredentials(message);
break;
case UNAVAILABLE:
exception = new CassandraException.Unavailable(Consistency.valueOf(input.readUInt16()), input.readInt32(), input.readInt32());
break;
case OVERLOADED:
exception = new CassandraException.Overloaded();
break;
case IS_BOOTSTRAPPING:
exception = new CassandraException.IsBootstrapping();
break;
case TRUNCATE_ERROR:
exception = new CassandraException.Truncate(message);
break;
case WRITE_TIMEOUT:
case READ_TIMEOUT:
Consistency consistency = Consistency.valueOf(input.readUInt16());
int received = input.readInt32();
int blockFor = input.readInt32();
if (CassandraException.Code.WRITE_TIMEOUT == code) {
WriteType writeType = input.readEnum(WriteType.class);
exception = new CassandraException.WriteTimeout(writeType, consistency, received, blockFor);
} else {
exception = new CassandraException.ReadTimeout(consistency, received, blockFor, input.readBool());
}
break;
case SYNTAX_ERROR:
exception = new CassandraException.SyntaxError(message);
break;
case UNAUTHORIZED:
exception = new CassandraException.Unauthorized(message);
break;
case INVALID:
exception = new CassandraException.Invalid(message);
break;
case CONFIG_ERROR:
exception = new CassandraException.ConfigError(message);
break;
case ALREADY_EXISTS:
exception = new CassandraException.AlreadyExists(input.readString(), input.readString());
break;
case UNPREPARED:
exception = new CassandraException.Unprepared(input.readBytes());
break;
default:
throw new IllegalStateException(String.format("unknown error code %s", code));
}
}
@Override
public int getApproximateSize() {
int size = 4;
size += MessageOutputStream.computeStringSize(message);
switch (code) {
case UNAVAILABLE:
size += 2 + 4 + 4;
break;
case WRITE_TIMEOUT:
CassandraException.WriteTimeout writeTimeout = (CassandraException.WriteTimeout)exception;
size += MessageOutputStream.computeEnumSize(writeTimeout.writeType) + 2 + 4 + 4;
case READ_TIMEOUT:
size += 2 + 4 + 4 + 1;
break;
case ALREADY_EXISTS:
CassandraException.AlreadyExists alreadyExists = (CassandraException.AlreadyExists)exception;
size += MessageOutputStream.computeStringSize(alreadyExists.keyspace);
size += MessageOutputStream.computeStringSize(alreadyExists.table);
break;
case UNPREPARED:
CassandraException.Unprepared unprepared = (CassandraException.Unprepared)exception;
size += MessageOutputStream.computeBytesSize(unprepared.id);
break;
default:
break;
}
return size;
}
@Override
protected void writePartialTo(MessageOutputStream output) {
output.writeInt32(code.value);
output.writeString(message);
switch (code) {
case UNAVAILABLE:
CassandraException.Unavailable unavailable = (CassandraException.Unavailable)exception;
output.writeInt16(unavailable.consistency.code);
output.writeInt32(unavailable.required);
output.writeInt32(unavailable.alive);
break;
case WRITE_TIMEOUT:
CassandraException.WriteTimeout writeTimeout = (CassandraException.WriteTimeout)exception;
output.writeEnum(writeTimeout.writeType);
output.writeInt16(writeTimeout.consistency.code);
output.writeInt32(writeTimeout.received);
output.writeInt32(writeTimeout.blockFor);
break;
case READ_TIMEOUT:
CassandraException.ReadTimeout readTimeout = (CassandraException.ReadTimeout)exception;
output.writeInt16(readTimeout.consistency.code);
output.writeInt32(readTimeout.received);
output.writeInt32(readTimeout.blockFor);
output.writeBool(readTimeout.dataPresent);
break;
case ALREADY_EXISTS:
CassandraException.AlreadyExists alreadyExists = (CassandraException.AlreadyExists)exception;
output.writeString(alreadyExists.keyspace);
output.writeString(alreadyExists.table);
break;
case UNPREPARED:
CassandraException.Unprepared unprepared = (CassandraException.Unprepared)exception;
output.writeBytes(unprepared.id);
break;
default:
break;
}
}
}
public static class QueryParameters extends Message {
public static final MessageParser<QueryParameters> PARSER = new MessageParser<QueryParameters>() {
@Override
public QueryParameters parseFrom(MessageInputStream input) {
return new QueryParameters(input);
}
};
public static QueryParameters parseFrom(MessageInputStream input) {
return PARSER.parseFrom(input);
}
public static enum Flag {
VALUES, SKIP_METADATA, PAGE_SIZE, WITH_PAGING_STATE, WITH_SERIAL_CONSISTENCY
}
public static final QueryParameters DEFAULT = new QueryParameters(Consistency.ONE, MessageInputStream.EMPTY_VALUE_ARRAY, false, -1, null, Consistency.SERIAL);
public final Consistency consistency, serialConsistency;
public final ByteBuffer[] values;
public final boolean skipMetadata;
public final int pageSize;
public final ByteBuffer pagingState;
public QueryParameters(Consistency consistency, ByteBuffer[] values, boolean skipMetadata, int pageSize, ByteBuffer pagingState, Consistency serialConsistency) {
this.consistency = consistency;
this.values = values;
this.skipMetadata = skipMetadata;
this.pageSize = pageSize;
this.pagingState = pagingState;
this.serialConsistency = serialConsistency;
}
private QueryParameters(MessageInputStream input) {
consistency = Consistency.valueOf(input.readInt16());
EnumSet<Flag> flags = input.readEnumSet8(Flag.class);
if (flags.contains(Flag.VALUES)) {
values = input.readValueArray();
} else {
values = MessageInputStream.EMPTY_VALUE_ARRAY;
}
skipMetadata = flags.contains(Flag.SKIP_METADATA);
if (flags.contains(Flag.PAGE_SIZE)) {
pageSize = input.readInt32();
} else {
pageSize = -1;
}
if (flags.contains(Flag.WITH_PAGING_STATE)) {
pagingState = input.readValue();
} else {
pagingState = null;
}
if (flags.contains(Flag.WITH_SERIAL_CONSISTENCY)) {
serialConsistency = Consistency.valueOf(input.readInt16());
} else {
serialConsistency = Consistency.SERIAL;
}
}
@Override
public int getApproximateSize() {
int size = 2 + 1;
if (values != null && values.length > 0) {
size += MessageOutputStream.computeValueArraySize(values);
}
if (pageSize >= 0) {
size += 4;
}
if (pagingState != null) {
size += MessageOutputStream.computeValueSize(pagingState);
}
if (serialConsistency != Consistency.SERIAL) {
size += 2;
}
return size;
}
@Override
public void writeTo(MessageOutputStream output) {
EnumSet<Flag> flags = EnumSet.noneOf(Flag.class);
if (values != null && values.length > 0) {
flags.add(Flag.VALUES);
}
if (skipMetadata) {
flags.add(Flag.SKIP_METADATA);
}
if (pageSize >= 0) {
flags.add(Flag.PAGE_SIZE);
}
if (pagingState != null) {
flags.add(Flag.WITH_PAGING_STATE);
}
if (serialConsistency != Consistency.SERIAL) {
flags.add(Flag.WITH_SERIAL_CONSISTENCY);
}
output.writeInt16(consistency.code);
output.writeEnumSet8(flags);
if (flags.contains(Flag.VALUES)) {
output.writeValueArray(values);
}
if (flags.contains(Flag.PAGE_SIZE)) {
output.writeInt32(pageSize);
}
if (flags.contains(Flag.WITH_PAGING_STATE)) {
output.writeValue(pagingState);
}
if (flags.contains(Flag.WITH_SERIAL_CONSISTENCY)) {
output.writeInt16(serialConsistency.code);
}
}
}
public static class Metadata extends Message {
public static final MessageParser<Metadata> PARSER = new MessageParser<Metadata>() {
@Override
public Metadata parseFrom(MessageInputStream input) {
return new Metadata(input);
}
};
public static Metadata parseFrom(MessageInputStream input) {
return PARSER.parseFrom(input);
}
public static enum Flag {
GLOBAL_TABLES_SPEC, HAS_MORE_PAGES, NO_METADATA
}
public final int columnCount;
public final Column[] columns;
public final ByteBuffer pagingState;
public Metadata(int columnCount, Column[] columns, ByteBuffer pagingState) {
this.columnCount = columnCount;
this.columns = columns;
this.pagingState = pagingState;
}
private Metadata(MessageInputStream input) {
EnumSet<Flag> flags = input.readEnumSet32(Flag.class);
columnCount = input.readInt32();
if (flags.contains(Flag.HAS_MORE_PAGES)) {
pagingState = input.readValue();
} else {
pagingState = null;
}
if (flags.contains(Flag.NO_METADATA)) {
columns = null;
} else {
boolean globalTablesSpec = flags.contains(Flag.GLOBAL_TABLES_SPEC);
String globalKeyspace = null;
String globalTable = null;
if (globalTablesSpec) {
globalKeyspace = input.readString();
globalTable = input.readString();
}
columns = new Column[columnCount];
for (int i = 0; i < columnCount; i++) {
String keyspace = globalKeyspace;
if (!globalTablesSpec) {
keyspace = input.readString();
}
String table = globalTable;
if (!globalTablesSpec) {
table = input.readString();
}
String name = input.readString();
CQL3Type type = input.readCQLType();
columns[i] = new Column(keyspace, table, name, type);
}
}
}
@Override
public int getApproximateSize() {
int size = 4 + 4;
if (pagingState != null) {
size += MessageOutputStream.computeValueSize(pagingState);
}
boolean noMetadata = columns == null || columns.length == 0;
if (!noMetadata) {
boolean globalTablesSpec = true;
for (int i = 1; i < columns.length; i++) {
if (!columns[i].keyspace().equals(columns[0].keyspace()) || !columns[i].table().equals(columns[0].table())) {
globalTablesSpec = false;
break;
}
}
if (globalTablesSpec) {
size += MessageOutputStream.computeStringSize(columns[0].keyspace());
size += MessageOutputStream.computeStringSize(columns[0].table());
}
for (Column column : columns) {
if (!globalTablesSpec) {
size += MessageOutputStream.computeStringSize(column.keyspace());
size += MessageOutputStream.computeStringSize(column.table());
}
size += MessageOutputStream.computeStringSize(column.name());
size += MessageOutputStream.computeCQLTypeSize(column.type());
}
}
return size;
}
@Override
public void writeTo(MessageOutputStream output) {
EnumSet<Flag> flags = EnumSet.noneOf(Flag.class);
if (columns == null || columns.length == 0) {
flags.add(Flag.NO_METADATA);
} else {
boolean globalTablesSpec = true;
for (int i = 1; i < columns.length; i++) {
if (!columns[i].keyspace().equals(columns[0].keyspace()) || !columns[i].table().equals(columns[0].table())) {
globalTablesSpec = false;
break;
}
}
if (globalTablesSpec) {
flags.add(Flag.GLOBAL_TABLES_SPEC);
}
}
if (pagingState != null) {
flags.add(Flag.HAS_MORE_PAGES);
}
output.writeEnumSet32(flags);
output.writeInt32(columnCount);
if (flags.contains(Flag.HAS_MORE_PAGES)) {
output.writeValue(pagingState);
}
if (!flags.contains(Flag.NO_METADATA)) {
if (flags.contains(Flag.GLOBAL_TABLES_SPEC)) {
output.writeString(columns[0].keyspace());
output.writeString(columns[0].table());
}
for (Column column : columns) {
if (!flags.contains(Flag.GLOBAL_TABLES_SPEC)) {
output.writeString(column.keyspace());
output.writeString(column.table());
}
output.writeString(column.name());
output.writeCQLType(column.type());
}
}
}
}
}