com.tesora.dve.mysqlapi.repl.messages.MyUserVarLogEvent.java Source code

Java tutorial

Introduction

Here is the source code for com.tesora.dve.mysqlapi.repl.messages.MyUserVarLogEvent.java

Source

package com.tesora.dve.mysqlapi.repl.messages;

/*
 * #%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 io.netty.buffer.ByteBuf;
import io.netty.buffer.PooledByteBufAllocator;
import io.netty.buffer.Unpooled;
import io.netty.util.CharsetUtil;

import java.math.BigDecimal;
import java.nio.ByteOrder;

import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;

import com.tesora.dve.db.mysql.common.MysqlAPIUtils;
import com.tesora.dve.exceptions.PEException;

public class MyUserVarLogEvent extends MyLogEventPacket {
    private static final Logger logger = Logger.getLogger(MyUserVarLogEvent.class);

    private static final int DIG_PER_DEC1 = 9;
    private static final int dig2bytes[] = { 0, 1, 1, 2, 2, 3, 3, 4, 4, 4 };

    int variableNameLen;
    String variableName;
    byte nullByte;
    MyItemResultCode valueType;
    int valueCharSet;
    int valueLen;
    ByteBuf valueBytes;

    String variableValue;

    public enum MyItemResultCode {
        STRING_RESULT((byte) 0x00), REAL_RESULT((byte) 0x01), INT_RESULT((byte) 0x02), ROW_RESULT(
                (byte) 0x03), DECIMAL_RESULT((byte) 0x04);

        private final byte code;

        MyItemResultCode(byte b) {
            code = b;
        }

        public static MyItemResultCode fromByte(byte b) {
            for (MyItemResultCode mt : values()) {
                if (mt.code == b) {
                    return mt;
                }
            }
            return null;
        }

        public byte getByteValue() {
            return code;
        }
    }

    public MyUserVarLogEvent(MyReplEventCommonHeader ch) {
        super(ch);
    }

    @Override
    public void accept(ReplicationVisitorTarget visitorTarget) throws PEException {
        visitorTarget.visit((MyUserVarLogEvent) this);
    }

    @Override
    public void unmarshallMessage(ByteBuf cb) throws PEException {
        variableNameLen = cb.readInt();
        variableName = MysqlAPIUtils.readBytesAsString(cb, variableNameLen, CharsetUtil.UTF_8);
        nullByte = cb.readByte();
        if (nullByte != 1) {
            variableValue = processVariableValue(cb);
        } else {
            variableValue = "NULL";
        }
    }

    String processVariableValue(ByteBuf cb) throws PEException {
        String value = StringUtils.EMPTY;

        valueType = MyItemResultCode.fromByte(cb.readByte());
        valueCharSet = cb.readInt();
        valueLen = cb.readInt();
        valueBytes = Unpooled.buffer(cb.readableBytes()).order(ByteOrder.LITTLE_ENDIAN);
        valueBytes.writeBytes(cb);

        switch (valueType) {
        case DECIMAL_RESULT:
            value = processDecimalValue(valueBytes, valueLen);
            break;
        case INT_RESULT:
            value = processIntValue(valueBytes, valueLen);
            break;
        case REAL_RESULT:
            value = Double.toString(valueBytes.readDouble());
            break;
        case STRING_RESULT:
            value = "'" + StringUtils.replace(
                    MysqlAPIUtils.readBytesAsString(valueBytes, valueLen, CharsetUtil.UTF_8), "'", "''") + "'";
            break;
        case ROW_RESULT:
        default:
            throw new PEException(
                    "Unsupported variable type '" + valueType + "' for variable '" + variableName + "'");
        }
        return value;
    }

    String processDecimalValue(ByteBuf cb, int valueLen) throws PEException {
        String value = StringUtils.EMPTY;

        byte precision = cb.readByte();
        byte scale = cb.readByte();

        int intg = (int) precision - (int) scale;
        int intg0 = intg / DIG_PER_DEC1;
        int frac0 = (int) scale / DIG_PER_DEC1;
        int intg0x = intg - intg0 * DIG_PER_DEC1;
        int frac0x = (int) scale - frac0 * DIG_PER_DEC1;

        int firstValue = intg0 * 4 + dig2bytes[intg0x];
        int secondValue = frac0 * 4 + dig2bytes[frac0x];

        int binSize = firstValue + secondValue;

        int readableBytes = cb.readableBytes();
        if ((firstValue < 1 && secondValue < 1) || readableBytes < binSize) {
            throw new PEException("Cannot decode binary decimal");
        }

        ByteBuf chunk = PooledByteBufAllocator.DEFAULT.heapBuffer(binSize);
        cb.readBytes(chunk);

        // 1st byte is special cause it determines the sign
        byte firstByte = chunk.getByte(0);
        int sign = (firstByte & 0x80) == 0x80 ? 1 : -1;
        // invert sign
        chunk.setByte(0, (firstByte ^ 0x80));

        if (sign == -1) {
            // invert all the bytes
            for (int i = 0; i < binSize; i++) {
                chunk.setByte(i, ~chunk.getByte(i));
            }
        }

        BigDecimal integerPortion = decodeBinDecimal(chunk, firstValue, true);
        BigDecimal fractionPortion = decodeBinDecimal(chunk, secondValue, false);

        value = ((sign == -1) ? "-" : StringUtils.EMPTY) + integerPortion.toPlainString() + "."
                + fractionPortion.toPlainString();

        return value;
    }

    BigDecimal decodeBinDecimal(ByteBuf cb, int bufferLen, boolean isIntegerPortion) throws PEException {
        BigDecimal decimalPortion = new BigDecimal(0);
        if (bufferLen > 0) {

            ByteBuf decimalPortionBuf = cb.readBytes(bufferLen);

            if (isIntegerPortion) {
                int initialBytes = bufferLen % 4;
                if (initialBytes > 0) {
                    long intValue = readValue(decimalPortionBuf, initialBytes);
                    decimalPortion = BigDecimal.valueOf(intValue);
                }
            }

            int decimalPortionLen = decimalPortionBuf.readableBytes();

            while (decimalPortionLen > 0) {
                int nextLen = (decimalPortionLen < 4) ? decimalPortionLen : 4;
                long intValue = readValue(decimalPortionBuf, nextLen);

                if (intValue > 0) {
                    if (decimalPortion.longValue() == 0) {
                        decimalPortion = decimalPortion.add(BigDecimal.valueOf(intValue));
                    } else {
                        int digits = (int) (Math.log10(intValue) + 1);
                        decimalPortion = decimalPortion.movePointRight(digits).add(BigDecimal.valueOf(intValue));
                    }
                }

                decimalPortionLen = decimalPortionBuf.readableBytes();
            }
        }
        return decimalPortion;
    }

    long readValue(ByteBuf decimalPortionBuf, int valueLen) throws PEException {
        if (valueLen < 1 || valueLen > 4)
            throw new PEException("Cannot decode decimal buffer.  Invalid read length of " + valueLen);

        long value = 0;
        if (valueLen == 4) {
            value = decimalPortionBuf.readUnsignedInt();
        } else if (valueLen == 3) {
            value = decimalPortionBuf.readUnsignedMedium();
        } else if (valueLen == 2) {
            value = decimalPortionBuf.readUnsignedShort();
        } else if (valueLen == 1) {
            value = decimalPortionBuf.readUnsignedByte();
        }
        return value;
    }

    String processIntValue(ByteBuf cb, int valueLen) throws PEException {
        String value = StringUtils.EMPTY;

        switch (valueLen) {
        case 8:
            value = Long.toString(cb.readLong());
            break;
        case 7:
        case 6:
        case 5:
            throw new PEException(
                    "Cannot decode INT value of length '" + valueLen + "' for variable '" + variableName + "'");
        case 4:
            value = Long.toString(cb.readInt());
            break;
        case 3:
            value = Long.toString(cb.readMedium());
            break;
        case 2:
            value = Long.toString(cb.readShort());
            break;
        case 1:
            value = Byte.toString(cb.readByte());
            break;
        }
        return value;
    }

    @Override
    public void marshallMessage(ByteBuf cb) {
        cb.writeInt(variableNameLen);
        cb.writeBytes(variableName.getBytes(CharsetUtil.UTF_8));
        cb.writeByte(nullByte);
        if (nullByte != 1) {
            cb.writeByte(valueType.getByteValue());
            cb.writeInt(valueCharSet);
            cb.writeInt(valueLen);
            cb.writeBytes(valueBytes);
        }
    }

    public String getVariableName() {
        return variableName;
    }

    public String getVariableValue() {
        return variableValue;
    }

}