/** * * 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.bookkeeper.proto; import com.google.protobuf.ByteString; import com.google.protobuf.ExtensionRegistry; import com.google.protobuf.InvalidProtocolBufferException; import org.jboss.netty.buffer.ChannelBufferFactory; import org.jboss.netty.buffer.ChannelBufferInputStream; import org.jboss.netty.buffer.ChannelBufferOutputStream; import org.jboss.netty.buffer.ChannelBuffers; import org.jboss.netty.buffer.ChannelBuffer; import org.jboss.netty.channel.Channel; import org.jboss.netty.channel.ChannelHandlerContext; import org.apache.bookkeeper.proto.BookieProtocol.PacketHeader; import org.jboss.netty.handler.codec.oneone.OneToOneEncoder; import org.jboss.netty.handler.codec.oneone.OneToOneDecoder; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class BookieProtoEncoding { private final static Logger LOG = LoggerFactory.getLogger(BookieProtoEncoding.class); static interface EnDecoder { /** * Encode a <i>object</i> into channel buffer. * * @param object * object. * @return encode buffer. * @throws Exception */ public Object encode(Object object, ChannelBufferFactory factory) throws Exception; /** * Decode a <i>packet</i> into an object. * * @param packet * received packet. * @return parsed object. * @throws Exception */ public Object decode(ChannelBuffer packet) throws Exception; } static class RequestEnDeCoderPreV3 implements EnDecoder { final ExtensionRegistry extensionRegistry; RequestEnDeCoderPreV3(ExtensionRegistry extensionRegistry) { this.extensionRegistry = extensionRegistry; } @Override public Object encode(Object msg, ChannelBufferFactory bufferFactory) throws Exception { if (!(msg instanceof BookieProtocol.Request)) { return msg; } BookieProtocol.Request r = (BookieProtocol.Request)msg; if (r instanceof BookieProtocol.AddRequest) { BookieProtocol.AddRequest ar = (BookieProtocol.AddRequest)r; int totalHeaderSize = 4 // for the header + BookieProtocol.MASTER_KEY_LENGTH; // for the master key ChannelBuffer buf = bufferFactory.getBuffer(totalHeaderSize); buf.writeInt(new PacketHeader(r.getProtocolVersion(), r.getOpCode(), r.getFlags()).toInt()); buf.writeBytes(r.getMasterKey(), 0, BookieProtocol.MASTER_KEY_LENGTH); return ChannelBuffers.wrappedBuffer(buf, ar.getData()); } else if (r instanceof BookieProtocol.ReadRequest) { int totalHeaderSize = 4 // for request type + 8 // for ledgerId + 8; // for entryId if (r.hasMasterKey()) { totalHeaderSize += BookieProtocol.MASTER_KEY_LENGTH; } ChannelBuffer buf = bufferFactory.getBuffer(totalHeaderSize); buf.writeInt(new PacketHeader(r.getProtocolVersion(), r.getOpCode(), r.getFlags()).toInt()); buf.writeLong(r.getLedgerId()); buf.writeLong(r.getEntryId()); if (r.hasMasterKey()) { buf.writeBytes(r.getMasterKey(), 0, BookieProtocol.MASTER_KEY_LENGTH); } return buf; } else if (r instanceof BookieProtocol.AuthRequest) { BookkeeperProtocol.AuthMessage am = ((BookieProtocol.AuthRequest)r).getAuthMessage(); int totalHeaderSize = 4; // for request type int totalSize = totalHeaderSize + am.getSerializedSize(); ChannelBuffer buf = bufferFactory.getBuffer(totalSize); buf.writeInt(new PacketHeader(r.getProtocolVersion(), r.getOpCode(), r.getFlags()).toInt()); ChannelBufferOutputStream bufStream = new ChannelBufferOutputStream(buf); am.writeTo(bufStream); return buf; } else { return msg; } } @Override public Object decode(ChannelBuffer packet) throws Exception { PacketHeader h = PacketHeader.fromInt(packet.readInt()); // packet format is different between ADDENTRY and READENTRY long ledgerId = -1; long entryId = BookieProtocol.INVALID_ENTRY_ID; byte[] masterKey = null; short flags = h.getFlags(); ServerStats.getInstance().incrementPacketsReceived(); switch (h.getOpCode()) { case BookieProtocol.ADDENTRY: // first read master key masterKey = new byte[BookieProtocol.MASTER_KEY_LENGTH]; packet.readBytes(masterKey, 0, BookieProtocol.MASTER_KEY_LENGTH); ChannelBuffer bb = packet.duplicate(); ledgerId = bb.readLong(); entryId = bb.readLong(); return new BookieProtocol.AddRequest(h.getVersion(), ledgerId, entryId, flags, masterKey, packet.slice()); case BookieProtocol.READENTRY: ledgerId = packet.readLong(); entryId = packet.readLong(); if ((flags & BookieProtocol.FLAG_DO_FENCING) == BookieProtocol.FLAG_DO_FENCING && h.getVersion() >= 2) { masterKey = new byte[BookieProtocol.MASTER_KEY_LENGTH]; packet.readBytes(masterKey, 0, BookieProtocol.MASTER_KEY_LENGTH); return new BookieProtocol.ReadRequest(h.getVersion(), ledgerId, entryId, flags, masterKey); } else { return new BookieProtocol.ReadRequest(h.getVersion(), ledgerId, entryId, flags); } case BookieProtocol.AUTH: BookkeeperProtocol.AuthMessage.Builder builder = BookkeeperProtocol.AuthMessage.newBuilder(); builder.mergeFrom(new ChannelBufferInputStream(packet), extensionRegistry); return new BookieProtocol.AuthRequest(h.getVersion(), builder.build()); } return packet; } } static class ResponseEnDeCoderPreV3 implements EnDecoder { final ExtensionRegistry extensionRegistry; ResponseEnDeCoderPreV3(ExtensionRegistry extensionRegistry) { this.extensionRegistry = extensionRegistry; } @Override public Object encode(Object msg, ChannelBufferFactory bufferFactory) throws Exception { if (!(msg instanceof BookieProtocol.Response)) { return msg; } BookieProtocol.Response r = (BookieProtocol.Response)msg; ChannelBuffer buf = bufferFactory.getBuffer(24); buf.writeInt(new PacketHeader(r.getProtocolVersion(), r.getOpCode(), (short)0).toInt()); ServerStats.getInstance().incrementPacketsSent(); if (msg instanceof BookieProtocol.ReadResponse) { buf.writeInt(r.getErrorCode()); buf.writeLong(r.getLedgerId()); buf.writeLong(r.getEntryId()); BookieProtocol.ReadResponse rr = (BookieProtocol.ReadResponse)r; if (rr.hasData()) { return ChannelBuffers.wrappedBuffer(buf, ChannelBuffers.wrappedBuffer(rr.getData())); } else { return buf; } } else if (msg instanceof BookieProtocol.AddResponse) { buf.writeInt(r.getErrorCode()); buf.writeLong(r.getLedgerId()); buf.writeLong(r.getEntryId()); return buf; } else if (msg instanceof BookieProtocol.AuthResponse) { BookkeeperProtocol.AuthMessage am = ((BookieProtocol.AuthResponse)r).getAuthMessage(); return ChannelBuffers.wrappedBuffer(buf, ChannelBuffers.wrappedBuffer(am.toByteArray())); } else { LOG.error("Cannot encode unknown response type {}", msg.getClass().getName()); return msg; } } @Override public Object decode(ChannelBuffer buffer) throws Exception { int rc; long ledgerId, entryId; final PacketHeader header; header = PacketHeader.fromInt(buffer.readInt()); switch (header.getOpCode()) { case BookieProtocol.ADDENTRY: rc = buffer.readInt(); ledgerId = buffer.readLong(); entryId = buffer.readLong(); return new BookieProtocol.AddResponse(header.getVersion(), rc, ledgerId, entryId); case BookieProtocol.READENTRY: rc = buffer.readInt(); ledgerId = buffer.readLong(); entryId = buffer.readLong(); if (rc == BookieProtocol.EOK) { return new BookieProtocol.ReadResponse(header.getVersion(), rc, ledgerId, entryId, buffer.slice()); } else { return new BookieProtocol.ReadResponse(header.getVersion(), rc, ledgerId, entryId); } case BookieProtocol.AUTH: ChannelBufferInputStream bufStream = new ChannelBufferInputStream(buffer); BookkeeperProtocol.AuthMessage.Builder builder = BookkeeperProtocol.AuthMessage.newBuilder(); builder.mergeFrom(bufStream, extensionRegistry); BookkeeperProtocol.AuthMessage am = builder.build(); return new BookieProtocol.AuthResponse(header.getVersion(), am); default: return buffer; } } } static class RequestEnDecoderV3 implements EnDecoder { final ExtensionRegistry extensionRegistry; RequestEnDecoderV3(ExtensionRegistry extensionRegistry) { this.extensionRegistry = extensionRegistry; } @Override public Object decode(ChannelBuffer packet) throws Exception { return BookkeeperProtocol.Request.parseFrom(new ChannelBufferInputStream(packet), extensionRegistry); } @Override public Object encode(Object msg, ChannelBufferFactory factory) throws Exception { BookkeeperProtocol.Request request = (BookkeeperProtocol.Request) msg; return ChannelBuffers.wrappedBuffer(request.toByteArray()); } } static class ResponseEnDecoderV3 implements EnDecoder { final ExtensionRegistry extensionRegistry; ResponseEnDecoderV3(ExtensionRegistry extensionRegistry) { this.extensionRegistry = extensionRegistry; } @Override public Object decode(ChannelBuffer packet) throws Exception { return BookkeeperProtocol.Response.parseFrom(new ChannelBufferInputStream(packet), extensionRegistry); } @Override public Object encode(Object msg, ChannelBufferFactory factory) throws Exception { BookkeeperProtocol.Response response = (BookkeeperProtocol.Response) msg; return ChannelBuffers.wrappedBuffer(response.toByteArray()); } } public static class RequestEncoder extends OneToOneEncoder { final EnDecoder REQ_PREV3; final EnDecoder REQ_V3; RequestEncoder(ExtensionRegistry extensionRegistry) { REQ_PREV3 = new RequestEnDeCoderPreV3(extensionRegistry); REQ_V3 = new RequestEnDecoderV3(extensionRegistry); } @Override protected Object encode(ChannelHandlerContext ctx, Channel channel, Object msg) throws Exception { if (LOG.isDebugEnabled()) { LOG.debug("Encode request {} to channel {}.", msg, channel); } if (msg instanceof BookkeeperProtocol.Request) { return REQ_V3.encode(msg, ctx.getChannel().getConfig().getBufferFactory()); } else if (msg instanceof BookieProtocol.Request) { return REQ_PREV3.encode(msg, ctx.getChannel().getConfig().getBufferFactory()); } else { LOG.error("Invalid request to encode to {}: {}", channel, msg.getClass().getName()); return msg; } } } public static class RequestDecoder extends OneToOneDecoder { final EnDecoder REQ_PREV3; final EnDecoder REQ_V3; RequestDecoder(ExtensionRegistry extensionRegistry) { REQ_PREV3 = new RequestEnDeCoderPreV3(extensionRegistry); REQ_V3 = new RequestEnDecoderV3(extensionRegistry); } @Override protected Object decode(ChannelHandlerContext ctx, Channel channel, Object msg) throws Exception { if (LOG.isDebugEnabled()) { LOG.debug("Received request {} from channel {} to decode.", msg, channel); } if (!(msg instanceof ChannelBuffer)) { return msg; } ChannelBuffer buffer = (ChannelBuffer) msg; try { buffer.markReaderIndex(); try { return REQ_V3.decode(buffer); } catch (InvalidProtocolBufferException e) { buffer.resetReaderIndex(); return REQ_PREV3.decode(buffer); } } catch (Exception e) { LOG.error("Failed to decode a request from {} : ", channel, e); throw e; } } } public static class ResponseEncoder extends OneToOneEncoder { final EnDecoder REP_PREV3; final EnDecoder REP_V3; ResponseEncoder(ExtensionRegistry extensionRegistry) { REP_PREV3 = new ResponseEnDeCoderPreV3(extensionRegistry); REP_V3 = new ResponseEnDecoderV3(extensionRegistry); } @Override protected Object encode(ChannelHandlerContext ctx, Channel channel, Object msg) throws Exception { if (LOG.isDebugEnabled()) { LOG.debug("Encode response {} to channel {}.", msg, channel); } if (msg instanceof BookkeeperProtocol.Response) { return REP_V3.encode(msg, ctx.getChannel().getConfig().getBufferFactory()); } else if (msg instanceof BookieProtocol.Response) { return REP_PREV3.encode(msg, ctx.getChannel().getConfig().getBufferFactory()); } else { LOG.error("Invalid response to encode to {}: {}", channel, msg.getClass().getName()); return msg; } } } public static class ResponseDecoder extends OneToOneDecoder { final EnDecoder REP_PREV3; final EnDecoder REP_V3; ResponseDecoder(ExtensionRegistry extensionRegistry) { REP_PREV3 = new ResponseEnDeCoderPreV3(extensionRegistry); REP_V3 = new ResponseEnDecoderV3(extensionRegistry); } @Override protected Object decode(ChannelHandlerContext ctx, Channel channel, Object msg) throws Exception { if (LOG.isDebugEnabled()) { LOG.debug("Received response {} from channel {} to decode.", msg, channel); } if (!(msg instanceof ChannelBuffer)) { return msg; } ChannelBuffer buffer = (ChannelBuffer) msg; try { buffer.markReaderIndex(); try { return REP_V3.decode(buffer); } catch (InvalidProtocolBufferException e) { buffer.resetReaderIndex(); return REP_PREV3.decode(buffer); } } catch (Exception e) { LOG.error("Failed to decode a response from channel {} : ", channel, e); throw e; } } } }