/** * Copyright 2016 Yahoo Inc. * * Licensed 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 com.yahoo.pulsar.common.api; import static com.google.common.base.Preconditions.checkArgument; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.yahoo.pulsar.common.api.proto.PulsarApi.BaseCommand; import com.yahoo.pulsar.common.api.proto.PulsarApi.CommandAck; import com.yahoo.pulsar.common.api.proto.PulsarApi.CommandCloseConsumer; import com.yahoo.pulsar.common.api.proto.PulsarApi.CommandCloseProducer; import com.yahoo.pulsar.common.api.proto.PulsarApi.CommandConnect; import com.yahoo.pulsar.common.api.proto.PulsarApi.CommandConnected; import com.yahoo.pulsar.common.api.proto.PulsarApi.CommandConsumerStats; import com.yahoo.pulsar.common.api.proto.PulsarApi.CommandConsumerStatsResponse; import com.yahoo.pulsar.common.api.proto.PulsarApi.CommandError; import com.yahoo.pulsar.common.api.proto.PulsarApi.CommandFlow; import com.yahoo.pulsar.common.api.proto.PulsarApi.CommandLookupTopic; import com.yahoo.pulsar.common.api.proto.PulsarApi.CommandLookupTopicResponse; import com.yahoo.pulsar.common.api.proto.PulsarApi.CommandMessage; import com.yahoo.pulsar.common.api.proto.PulsarApi.CommandPartitionedTopicMetadata; import com.yahoo.pulsar.common.api.proto.PulsarApi.CommandPartitionedTopicMetadataResponse; import com.yahoo.pulsar.common.api.proto.PulsarApi.CommandPing; import com.yahoo.pulsar.common.api.proto.PulsarApi.CommandPong; import com.yahoo.pulsar.common.api.proto.PulsarApi.CommandProducer; import com.yahoo.pulsar.common.api.proto.PulsarApi.CommandProducerSuccess; import com.yahoo.pulsar.common.api.proto.PulsarApi.CommandRedeliverUnacknowledgedMessages; import com.yahoo.pulsar.common.api.proto.PulsarApi.CommandSend; import com.yahoo.pulsar.common.api.proto.PulsarApi.CommandSendError; import com.yahoo.pulsar.common.api.proto.PulsarApi.CommandSendReceipt; import com.yahoo.pulsar.common.api.proto.PulsarApi.CommandSubscribe; import com.yahoo.pulsar.common.api.proto.PulsarApi.CommandSuccess; import com.yahoo.pulsar.common.api.proto.PulsarApi.CommandUnsubscribe; import com.yahoo.pulsar.common.util.protobuf.ByteBufCodedInputStream; import io.netty.buffer.ByteBuf; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelInboundHandlerAdapter; public abstract class PulsarDecoder extends ChannelInboundHandlerAdapter { // Max message size is limited by max BookKeeper entry size which is 5MB, and we need to account // for headers as well. public final static int MaxMessageSize = (5 * 1024 * 1024 - (10 * 1024)); public final static int MaxFrameSize = 5 * 1024 * 1024; @Override final public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { // Get a buffer that contains the full frame ByteBuf buffer = (ByteBuf) msg; BaseCommand cmd = null; BaseCommand.Builder cmdBuilder = null; try { // De-serialize the command int cmdSize = (int) buffer.readUnsignedInt(); int writerIndex = buffer.writerIndex(); buffer.writerIndex(buffer.readerIndex() + cmdSize); ByteBufCodedInputStream cmdInputStream = ByteBufCodedInputStream.get(buffer); cmdBuilder = BaseCommand.newBuilder(); cmd = cmdBuilder.mergeFrom(cmdInputStream, null).build(); buffer.writerIndex(writerIndex); cmdInputStream.recycle(); if (log.isDebugEnabled()) { log.debug("[{}] Received cmd {}", ctx.channel().remoteAddress(), cmd.getType()); } messageReceived(); switch (cmd.getType()) { case PARTITIONED_METADATA: checkArgument(cmd.hasPartitionMetadata()); handlePartitionMetadataRequest(cmd.getPartitionMetadata()); cmd.getPartitionMetadata().recycle(); break; case PARTITIONED_METADATA_RESPONSE: checkArgument(cmd.hasPartitionMetadataResponse()); handlePartitionResponse(cmd.getPartitionMetadataResponse()); cmd.getPartitionMetadataResponse().recycle(); break; case LOOKUP: checkArgument(cmd.hasLookupTopic()); handleLookup(cmd.getLookupTopic()); cmd.getLookupTopic().recycle(); break; case LOOKUP_RESPONSE: checkArgument(cmd.hasLookupTopicResponse()); handleLookupResponse(cmd.getLookupTopicResponse()); cmd.getLookupTopicResponse().recycle(); break; case ACK: checkArgument(cmd.hasAck()); handleAck(cmd.getAck()); cmd.getAck().getMessageId().recycle(); cmd.getAck().recycle(); break; case CLOSE_CONSUMER: checkArgument(cmd.hasCloseConsumer()); handleCloseConsumer(cmd.getCloseConsumer()); cmd.getCloseConsumer().recycle(); break; case CLOSE_PRODUCER: checkArgument(cmd.hasCloseProducer()); handleCloseProducer(cmd.getCloseProducer()); cmd.getCloseProducer().recycle(); break; case CONNECT: checkArgument(cmd.hasConnect()); handleConnect(cmd.getConnect()); cmd.getConnect().recycle(); break; case CONNECTED: checkArgument(cmd.hasConnected()); handleConnected(cmd.getConnected()); cmd.getConnected().recycle(); break; case ERROR: checkArgument(cmd.hasError()); handleError(cmd.getError()); cmd.getError().recycle(); break; case FLOW: checkArgument(cmd.hasFlow()); handleFlow(cmd.getFlow()); cmd.getFlow().recycle(); break; case MESSAGE: { checkArgument(cmd.hasMessage()); handleMessage(cmd.getMessage(), buffer); cmd.getMessage().recycle(); break; } case PRODUCER: checkArgument(cmd.hasProducer()); handleProducer(cmd.getProducer()); cmd.getProducer().recycle(); break; case SEND: { checkArgument(cmd.hasSend()); // Store a buffer marking the content + headers ByteBuf headersAndPayload = buffer.markReaderIndex(); handleSend(cmd.getSend(), headersAndPayload); cmd.getSend().recycle(); break; } case SEND_ERROR: checkArgument(cmd.hasSendError()); handleSendError(cmd.getSendError()); cmd.getSendError().recycle(); break; case SEND_RECEIPT: checkArgument(cmd.hasSendReceipt()); handleSendReceipt(cmd.getSendReceipt()); cmd.getSendReceipt().recycle(); break; case SUBSCRIBE: checkArgument(cmd.hasSubscribe()); handleSubscribe(cmd.getSubscribe()); cmd.getSubscribe().recycle(); break; case SUCCESS: checkArgument(cmd.hasSuccess()); handleSuccess(cmd.getSuccess()); cmd.getSuccess().recycle(); break; case PRODUCER_SUCCESS: checkArgument(cmd.hasProducerSuccess()); handleProducerSuccess(cmd.getProducerSuccess()); cmd.getProducerSuccess().recycle(); break; case UNSUBSCRIBE: checkArgument(cmd.hasUnsubscribe()); handleUnsubscribe(cmd.getUnsubscribe()); cmd.getUnsubscribe().recycle(); break; case PING: checkArgument(cmd.hasPing()); handlePing(cmd.getPing()); cmd.getPing().recycle(); break; case PONG: checkArgument(cmd.hasPong()); handlePong(cmd.getPong()); cmd.getPong().recycle(); break; case REDELIVER_UNACKNOWLEDGED_MESSAGES: checkArgument(cmd.hasRedeliverUnacknowledgedMessages()); handleRedeliverUnacknowledged(cmd.getRedeliverUnacknowledgedMessages()); cmd.getRedeliverUnacknowledgedMessages().recycle(); break; case CONSUMER_STATS: checkArgument(cmd.hasConsumerStats()); handleConsumerStats(cmd.getConsumerStats()); cmd.getConsumerStats().recycle(); break; case CONSUMER_STATS_RESPONSE: checkArgument(cmd.hasConsumerStatsResponse()); handleConsumerStatsResponse(cmd.getConsumerStatsResponse()); cmd.getConsumerStatsResponse().recycle(); break; } } finally { if (cmdBuilder != null) { cmdBuilder.recycle(); } if (cmd != null) { cmd.recycle(); } buffer.release(); } } protected abstract void messageReceived(); protected void handlePartitionMetadataRequest(CommandPartitionedTopicMetadata response) { throw new UnsupportedOperationException(); } protected void handlePartitionResponse(CommandPartitionedTopicMetadataResponse response) { throw new UnsupportedOperationException(); } protected void handleLookup(CommandLookupTopic lookup) { throw new UnsupportedOperationException(); } protected void handleLookupResponse(CommandLookupTopicResponse connection) { throw new UnsupportedOperationException(); } protected void handleConnect(CommandConnect connect) { throw new UnsupportedOperationException(); } protected void handleConnected(CommandConnected connected) { throw new UnsupportedOperationException(); } protected void handleSubscribe(CommandSubscribe subscribe) { throw new UnsupportedOperationException(); } protected void handleProducer(CommandProducer producer) { throw new UnsupportedOperationException(); } protected void handleSend(CommandSend send, ByteBuf headersAndPayload) { throw new UnsupportedOperationException(); } protected void handleSendReceipt(CommandSendReceipt sendReceipt) { throw new UnsupportedOperationException(); } protected void handleSendError(CommandSendError sendError) { throw new UnsupportedOperationException(); } protected void handleMessage(CommandMessage cmdMessage, ByteBuf headersAndPayload) { throw new UnsupportedOperationException(); } protected void handleAck(CommandAck ack) { throw new UnsupportedOperationException(); } protected void handleFlow(CommandFlow flow) { throw new UnsupportedOperationException(); } protected void handleRedeliverUnacknowledged(CommandRedeliverUnacknowledgedMessages redeliver) { throw new UnsupportedOperationException(); } protected void handleUnsubscribe(CommandUnsubscribe unsubscribe) { throw new UnsupportedOperationException(); } protected void handleSuccess(CommandSuccess success) { throw new UnsupportedOperationException(); } protected void handleProducerSuccess(CommandProducerSuccess success) { throw new UnsupportedOperationException(); } protected void handleError(CommandError error) { throw new UnsupportedOperationException(); } protected void handleCloseProducer(CommandCloseProducer closeProducer) { throw new UnsupportedOperationException(); } protected void handleCloseConsumer(CommandCloseConsumer closeConsumer) { throw new UnsupportedOperationException(); } protected void handlePing(CommandPing ping) { throw new UnsupportedOperationException(); } protected void handlePong(CommandPong pong) { throw new UnsupportedOperationException(); } protected void handleConsumerStats(CommandConsumerStats commandConsumerStats) { throw new UnsupportedOperationException(); } protected void handleConsumerStatsResponse(CommandConsumerStatsResponse commandConsumerStatsResponse) { throw new UnsupportedOperationException(); } private static final Logger log = LoggerFactory.getLogger(PulsarDecoder.class); }