com.tesora.dve.db.mysql.portal.protocol.MSPProtocolDecoder.java Source code

Java tutorial

Introduction

Here is the source code for com.tesora.dve.db.mysql.portal.protocol.MSPProtocolDecoder.java

Source

package com.tesora.dve.db.mysql.portal.protocol;

/*
 * #%L
 * Tesora Inc.
 * Database Virtualization Engine
 * %%
 * Copyright (C) 2011 - 2014 Tesora Inc.
 * %%
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License, version 3,
 * as published by the Free Software Foundation.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU Affero General Public License for more details.
 * 
 * You should have received a copy of the GNU Affero General Public License
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
 * #L%
 */

import com.tesora.dve.db.mysql.libmy.MyMessage;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelDuplexHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelPromise;
import io.netty.handler.codec.ByteToMessageDecoder;

import java.util.List;

import com.tesora.dve.exceptions.PECodingException;
import com.tesora.dve.exceptions.PEException;
import org.slf4j.LoggerFactory;

public class MSPProtocolDecoder extends ChannelDuplexHandler {

    private final static MSPMessage mspMessages[] = { MSPComQueryRequestMessage.PROTOTYPE,
            MSPComFieldListRequestMessage.PROTOTYPE, MSPComQuitRequestMessage.PROTOTYPE,
            MSPComSetOptionRequestMessage.PROTOTYPE, MSPComPingRequestMessage.PROTOTYPE,
            MSPComInitDBRequestMessage.PROTOTYPE, MSPComPrepareStmtRequestMessage.PROTOTYPE,
            MSPComStmtExecuteRequestMessage.PROTOTYPE, MSPComStmtCloseRequestMessage.PROTOTYPE,
            MSPComProcessInfoRequestMessage.PROTOTYPE, MSPComStatisticsRequestMessage.PROTOTYPE };

    private final static MSPMessage[] messageMap = new MSPMessage[256];
    static {
        for (final MSPMessage m : mspMessages) {
            messageMap[m.getMysqlMessageType()] = m;
        }
    }

    private final MSPMessage[] messageExecutor;

    private MyDecoderState currentState;

    private Packet mspPacket;
    public boolean firstPacket = true;

    //TODO: tracking this as a single entry assumes we won't get pipelined requests from the client. -sgossard
    byte nextSequence = 1;

    CachedAppendBuffer cachedAppendBuffer = new CachedAppendBuffer();
    private final ByteToMessageDecoder decoder = new ByteToMessageDecoder() {
        @Override
        public void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
            MSPProtocolDecoder.this.decode(ctx, in, out);
        }

        @Override
        protected void decodeLast(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
            MSPProtocolDecoder.this.decode(ctx, in, out);
        }
    };

    public MSPProtocolDecoder(MyDecoderState initialState) throws PEException {
        super();
        this.messageExecutor = messageMap;
        this.currentState = initialState;
    }

    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        decoder.channelInactive(ctx);
        cachedAppendBuffer.releaseSlab();
        if (mspPacket != null) {
            mspPacket.release();
            mspPacket = null;
        }
    }

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        decoder.channelRead(ctx, msg);
    }

    @Override
    public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
        if (msg instanceof MyMessage) {
            MyMessage message = (MyMessage) msg;
            ByteBuf appendableView = cachedAppendBuffer.startAppend(ctx);
            nextSequence = (byte) (0xFF & Packet.encodeFullMessage(nextSequence, message, appendableView));
            ByteBuf writableSlice = cachedAppendBuffer.sliceWritableData();
            ctx.write(writableSlice, promise);
        } else {
            //forward packet along.
            ctx.write(msg, promise);
        }
    }

    protected void decode(final ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
        try {

            if (mspPacket == null) {
                //assumes next full inbound request will have a starting sequence (true except for auth handshake)
                //also assumes we only get one request at a time (synchronous), which is typical of most mysql clients
                int seq = firstPacket ? 1 : 0;
                mspPacket = new Packet(ctx.alloc(), seq, Packet.Modifier.HEAPCOPY_ON_READ, "frontend");
            }

            //deals with the handshake packet
            firstPacket = false;

            if (!mspPacket.decodeMore(in))
                return;

            int sequenceId = mspPacket.getSequenceNumber();
            this.nextSequence = (byte) (0xFF & mspPacket.getNextSequenceNumber()); //save off the sequence for our outbound response.

            ByteBuf payload = mspPacket.unwrapPayload().retain();//retain a separate reference to the payload.
            mspPacket.release();
            mspPacket = null;

            final MyDecoderState state = currentState;
            switch (state) {
            case READ_SERVER_GREETING:
            case READ_CLIENT_AUTH: {
                MSPMessage authMessage;
                if (state == MyDecoderState.READ_CLIENT_AUTH) {
                    authMessage = MSPAuthenticateV10MessageMessage.newMessage(payload);
                } else if (state == MyDecoderState.READ_SERVER_GREETING) {
                    authMessage = new MSPServerGreetingRequestMessage(payload);
                } else {
                    throw new PECodingException("Unexpected state in packet decoding, " + state);
                }
                out.add(authMessage);
                this.currentState = MyDecoderState.READ_PACKET;
                break;
            }
            case READ_PACKET:
                out.add(buildMessage(payload, (byte) sequenceId));
                break;
            }
        } catch (Exception e) {
            LoggerFactory.getLogger(MSPProtocolDecoder.class).warn("problem decoding frame", e);
        } finally {
        }
    }

    private MSPMessage buildMessage(final ByteBuf payload, final byte sequenceId) {
        final byte messageType = payload.getByte(0); //peek at the first byte in the payload to determine message type.
        try {
            return this.messageExecutor[messageType].newPrototype(payload);
        } catch (final Exception e) {
            return new MSPUnknown(messageType, payload);
        }
    }

    public enum MyDecoderState {
        READ_SERVER_GREETING, READ_CLIENT_AUTH, READ_PACKET
    }
}