package edu.uw.zookeeper.protocol; import io.netty.buffer.ByteBuf; import java.io.IOException; import org.apache.jute.InputArchive; import org.apache.jute.OutputArchive; import org.apache.zookeeper.KeeperException; import com.google.common.base.Function; import edu.uw.zookeeper.common.AbstractPair; import edu.uw.zookeeper.protocol.proto.ByteBufInputArchive; import edu.uw.zookeeper.protocol.proto.ByteBufOutputArchive; import edu.uw.zookeeper.protocol.proto.IErrorResponse; import edu.uw.zookeeper.protocol.proto.IReplyHeader; import edu.uw.zookeeper.protocol.proto.OpCode; import edu.uw.zookeeper.protocol.proto.OpCodeXid; import edu.uw.zookeeper.protocol.proto.Records; public class ProtocolResponseMessage<T extends Records.Response> extends AbstractPair<IReplyHeader, T> implements Message.ServerResponse<T> { public static <T extends Records.Response> ProtocolResponseMessage<T> of( int xid, long zxid, T response) { KeeperException.Code code = (response instanceof Operation.Error) ? ((Operation.Error) response).error() : KeeperException.Code.OK; IReplyHeader header = Records.Responses.Headers.newInstance(xid, zxid, code); return of(header, response); } public static <T extends Records.Response> ProtocolResponseMessage<T> of( IReplyHeader header, T response) { return new ProtocolResponseMessage<T>(header, response); } public static void serialize(Operation.ProtocolResponse<?> value, OutputArchive archive) throws IOException { if (value instanceof ProtocolResponseMessage) { ((ProtocolResponseMessage<?>) value).serialize(archive); } else { Records.Response record = value.record(); KeeperException.Code err; if (record instanceof Operation.Error) { err = ((Operation.Error) record).error(); } else { err = KeeperException.Code.OK; } IReplyHeader header = Records.Responses.Headers.newInstance(value.xid(), value.zxid(), err); Records.Responses.Headers.serialize(header, archive); if (KeeperException.Code.OK.intValue() == header.getErr()) { Records.Responses.serialize(record, archive); } } } public static class ExpectedOpcode implements Function<Integer, OpCode> { public static ExpectedOpcode create(OpCode expected) { return new ExpectedOpcode(expected); } protected final OpCode expected; public ExpectedOpcode(OpCode expected) { this.expected = expected; } @Override public OpCode apply(Integer input) { return expected; } } public static ProtocolResponseMessage<?> decode(OpCode opcode, ByteBuf input) throws IOException { return decode(ExpectedOpcode.create(opcode), input); } public static ProtocolResponseMessage<?> decode( Function<Integer, OpCode> xidToOpCode, ByteBuf input) throws IOException { return deserialize(xidToOpCode, new ByteBufInputArchive(input)); } public static ProtocolResponseMessage<?> deserialize( OpCode opcode, InputArchive archive) throws IOException { return deserialize(ExpectedOpcode.create(opcode), archive); } public static ProtocolResponseMessage<?> deserialize( Function<Integer, OpCode> xidToOpCode, InputArchive archive) throws IOException { IReplyHeader header = Records.Responses.Headers.deserialize(archive); int err = header.getErr(); Records.Response response; if (KeeperException.Code.OK.intValue() == err) { int xid = header.getXid(); OpCode opcode; if (OpCodeXid.has(xid)) { opcode = OpCodeXid.of(xid).opcode(); } else { opcode = xidToOpCode.apply(xid); } switch (opcode) { case CREATE_SESSION: throw new IllegalArgumentException(String.valueOf(opcode)); default: response = Records.Responses.deserialize(opcode, archive); break; } } else { response = new IErrorResponse(err); } return of(header, response); } protected ProtocolResponseMessage(IReplyHeader header, T response) { super(header, response); } public IReplyHeader header() { return first; } @Override public long zxid() { return first.getZxid(); } @Override public int xid() { return first.getXid(); } @Override public T record() { return second; } @Override public void encode(ByteBuf output) throws IOException { ByteBufOutputArchive archive = new ByteBufOutputArchive(output); serialize(archive); } public void serialize(OutputArchive archive) throws IOException { Records.Responses.Headers.serialize(first, archive); // don't serialize errors if (KeeperException.Code.OK.intValue() == first.getErr()) { Records.Responses.serialize(second, archive); } } }