if (log.isDebugEnabled()) {
log.debug("Receive command:" + line);
}
final String[] sa = SPLITER.split(line);
if (sa == null || sa.length == 0) {
throw new MetaCodecException("Blank command line.");
}
final byte op = (byte) sa[0].charAt(0);
switch (op) {
case 'p':
return this.decodePut(buff, sa);
case 'g':
return this.decodeGet(sa);
case 't':
return this.decodeTransaction(sa);
case 'r':
return this.decodeBoolean(buff, sa);
case 'v':
if (sa[0].equals("value")) {
return this.decodeData(buff, sa);
}
else {
return this.decodeVersion(sa);
}
case 's':
if (sa[0].equals("stats")) {
return this.decodeStats(sa);
}
else {
return this.decodeSync(buff, sa);
}
case 'o':
return this.decodeOffset(sa);
case 'q':
return this.decodeQuit();
default:
throw new MetaCodecException("Unknow command:" + line);
}
}
else {
return null;
}
}
private Object decodeQuit() {
return new QuitCommand();
}
private Object decodeVersion(final String[] sa) {
if (sa.length >= 2) {
return new VersionCommand(Integer.parseInt(sa[1]));
}
else {
return new VersionCommand(Integer.MAX_VALUE);
}
}
// offset topic group partition offset opaque\r\n
private Object decodeOffset(final String[] sa) {
this.assertCommand(sa[0], "offset");
return new OffsetCommand(sa[1], sa[2], Integer.parseInt(sa[3]), Long.parseLong(sa[4]),
Integer.parseInt(sa[5]));
}
// stats item opaque\r\n
// opaque������
private Object decodeStats(final String[] sa) {
this.assertCommand(sa[0], "stats");
int opaque = Integer.MAX_VALUE;
if (sa.length >= 3) {
opaque = Integer.parseInt(sa[2]);
}
String item = null;
if (sa.length >= 2) {
item = sa[1];
}
return new StatsCommand(opaque, item);
}
// value totalLen opaque\r\n data
private Object decodeData(final IoBuffer buff, final String[] sa) {
this.assertCommand(sa[0], "value");
final int valueLen = Integer.parseInt(sa[1]);
if (buff.remaining() < valueLen) {
buff.reset();
return null;
}
else {
final byte[] data = new byte[valueLen];
buff.get(data);
return new DataCommand(data, Integer.parseInt(sa[2]));
}
}
/**
* result code length opaque\r\n message
*
* @param buff
* @param sa
* @return
*/
private Object decodeBoolean(final IoBuffer buff, final String[] sa) {
this.assertCommand(sa[0], "result");
final int valueLen = Integer.parseInt(sa[2]);
if (valueLen == 0) {
return new BooleanCommand(Integer.parseInt(sa[1]), null, Integer.parseInt(sa[3]));
}
else {
if (buff.remaining() < valueLen) {
buff.reset();
return null;
}
else {
final byte[] data = new byte[valueLen];
buff.get(data);
return new BooleanCommand(Integer.parseInt(sa[1]), ByteUtils.getString(data),
Integer.parseInt(sa[3]));
}
}
}
// get topic group partition offset maxSize opaque\r\n
private Object decodeGet(final String[] sa) {
this.assertCommand(sa[0], "get");
return new GetCommand(sa[1], sa[2], Integer.parseInt(sa[3]), Long.parseLong(sa[4]),
Integer.parseInt(sa[5]), Integer.parseInt(sa[6]));
}
// transaction key sessionId type [timeout] [unique qualifier]
// opaque\r\n
private Object decodeTransaction(final String[] sa) {
this.assertCommand(sa[0], "transaction");
final TransactionId transactionId = this.getTransactionId(sa[1]);
final TransactionType type = TransactionType.valueOf(sa[3]);
switch (sa.length) {
case 7:
// Both include timeout and unique qualifier.
int timeout = Integer.valueOf(sa[4]);
String uniqueQualifier = sa[5];
TransactionInfo info =
new TransactionInfo(transactionId, sa[2], type, uniqueQualifier, timeout);
return new TransactionCommand(info, Integer.parseInt(sa[6]));
case 6:
// Maybe timeout or unique qualifier
if (StringUtils.isNumeric(sa[4])) {
timeout = Integer.valueOf(sa[4]);
info = new TransactionInfo(transactionId, sa[2], type, null, timeout);
return new TransactionCommand(info, Integer.parseInt(sa[5]));
}
else {
uniqueQualifier = sa[4];
info = new TransactionInfo(transactionId, sa[2], type, uniqueQualifier, 0);
return new TransactionCommand(info, Integer.parseInt(sa[5]));
}
case 5:
// Without timeout and unique qualifier.
info = new TransactionInfo(transactionId, sa[2], type, null);
return new TransactionCommand(info, Integer.parseInt(sa[4]));
default:
throw new MetaCodecException("Invalid transaction command:" + StringUtils.join(sa));
}
}
private TransactionId getTransactionId(final String s) {
return TransactionId.valueOf(s);
}
// sync topic partition value-length flag msgId
// opaque\r\n
private Object decodeSync(final IoBuffer buff, final String[] sa) {
this.assertCommand(sa[0], "sync");
final int valueLen = Integer.parseInt(sa[3]);
if (buff.remaining() < valueLen) {
buff.reset();
return null;
}
else {
final byte[] data = new byte[valueLen];
buff.get(data);
switch (sa.length) {
case 7:
// old master before 1.4.4
return new SyncCommand(sa[1], Integer.parseInt(sa[2]), data, Integer.parseInt(sa[4]),
Long.valueOf(sa[5]), -1, Integer.parseInt(sa[6]));
case 8:
// new master since 1.4.4
return new SyncCommand(sa[1], Integer.parseInt(sa[2]), data, Integer.parseInt(sa[4]),
Long.valueOf(sa[5]), Integer.parseInt(sa[6]), Integer.parseInt(sa[7]));
default:
throw new MetaCodecException("Invalid Sync command:" + StringUtils.join(sa));
}
}
}
// put topic partition value-length flag checksum
// [transactionKey]
// opaque\r\n
private Object decodePut(final IoBuffer buff, final String[] sa) {
this.assertCommand(sa[0], "put");
final int valueLen = Integer.parseInt(sa[3]);
if (buff.remaining() < valueLen) {
buff.reset();
return null;
}
else {
final byte[] data = new byte[valueLen];
buff.get(data);
switch (sa.length) {
case 6:
// old clients before 1.4.4
return new PutCommand(sa[1], Integer.parseInt(sa[2]), data, null, Integer.parseInt(sa[4]),
Integer.parseInt(sa[5]));
case 7:
// either transaction command or new clients since
// 1.4.4
String slot = sa[5];
char firstChar = slot.charAt(0);
if (Character.isDigit(firstChar) || '-' == firstChar) {
// slot is checksum.
int checkSum = Integer.parseInt(slot);
return new PutCommand(sa[1], Integer.parseInt(sa[2]), data, Integer.parseInt(sa[4]),
checkSum, null, Integer.parseInt(sa[6]));
}
else {
// slot is transaction id.
return new PutCommand(sa[1], Integer.parseInt(sa[2]), data,
this.getTransactionId(slot), Integer.parseInt(sa[4]), Integer.parseInt(sa[6]));
}
case 8:
// New clients since 1.4.4
// A transaction command
return new PutCommand(sa[1], Integer.parseInt(sa[2]), data, Integer.parseInt(sa[4]),
Integer.parseInt(sa[5]), this.getTransactionId(sa[6]), Integer.parseInt(sa[7]));
default:
throw new MetaCodecException("Invalid put command:" + StringUtils.join(sa));
}
}
}
private void assertCommand(final String cmd, final String expect) {
if (!expect.equals(cmd)) {
throw new MetaCodecException("Expect " + expect + ",but was " + cmd);
}
}
};
}