Java tutorial
/*------------------------------------------------------------------------------------------------- _______ __ _ _______ _______ ______ ______ |_____| | \ | | |______ | \ |_____] | | | \_| | ______| |_____/ |_____] Copyright (c) 2016, antsdb.com and/or its affiliates. All rights reserved. *-xguo0<@ 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. You should have received a copy of the GNU Affero General Public License along with this program. If not, see <https://www.gnu.org/licenses/agpl-3.0.txt> -------------------------------------------------------------------------------------------------*/ package com.antsdb.saltedfish.server.mysql; import java.util.BitSet; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.LinkedList; import java.util.List; import org.slf4j.Logger; import com.antsdb.saltedfish.nosql.CheckPoint; import com.antsdb.saltedfish.server.SaltedFish; import com.antsdb.saltedfish.server.mysql.packet.replication.GenericPacket; import com.antsdb.saltedfish.server.mysql.packet.replication.ReplicationPacket; import com.antsdb.saltedfish.server.mysql.packet.replication.RotatePacket; import com.antsdb.saltedfish.server.mysql.packet.replication.RowsEventV2Packet; import com.antsdb.saltedfish.server.mysql.packet.replication.StateIndicator; import com.antsdb.saltedfish.server.mysql.packet.replication.StopPacket; import com.antsdb.saltedfish.server.mysql.packet.replication.TableMapPacket; import com.antsdb.saltedfish.server.mysql.packet.replication.XIDPacket; import com.antsdb.saltedfish.server.mysql.util.BindValue; import com.antsdb.saltedfish.server.mysql.util.BindValueUtil; import com.antsdb.saltedfish.server.mysql.util.ReplicatedRow; import com.antsdb.saltedfish.sql.ConfigService; import com.antsdb.saltedfish.sql.Session; import com.antsdb.saltedfish.util.UberUtil; import io.netty.buffer.ByteBuf; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelInboundHandlerAdapter; public class MysqlClientHandler extends ChannelInboundHandlerAdapter { static Logger _log = UberUtil.getThisLogger(); SaltedFish fish; Session session; TableMapPacket currentTableMap; public String masterUser; public String masterPassword; public String masterBinlog; public long masterLogPos; public String currentBinlog; public long currentPos = -1; public PacketEncoder packetEncoder; public MysqlClientHandler() { this.fish = SaltedFish.getInstance(); masterUser = getConfig().getSlaveUser(); masterPassword = getConfig().getSlavePassword(); masterBinlog = getCheckPoint().getSlaveLogFile(); masterLogPos = getCheckPoint().getSlaveLogPosition(); packetEncoder = new PacketEncoder(); } @Override public void channelActive(ChannelHandlerContext ctx) throws Exception { } @Override public void channelInactive(ChannelHandlerContext ctx) throws Exception { if (this.session != null) { this.fish.getOrca().closeSession(this.session); } _log.info("Replication slave closed."); super.channelInactive(ctx); } @Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { if (_log.isTraceEnabled()) { _log.debug(msg.toString()); } // process the request with error handling try { run(ctx, msg); } catch (Exception x) { if (_log.isDebugEnabled()) { _log.error("error detail: \n {}", msg, x); } } } private void run(ChannelHandlerContext ctx, Object msg) throws Exception { if (msg instanceof StateIndicator) { state(ctx, (StateIndicator) msg); } else if (msg instanceof RotatePacket) { RotatePacket pkt = (RotatePacket) msg; currentBinlog = pkt.binlog; _log.info("Binlog file:" + currentBinlog); currentPos = pkt.nextPosition; getCheckPoint().setSlaveLogFile(currentBinlog); } else if (msg instanceof TableMapPacket) { currentTableMap = (TableMapPacket) msg; _log.trace("TableMapPacket:" + currentTableMap.schemaName + "." + currentTableMap.tableName); currentPos = currentTableMap.nextPosition; } else if (msg instanceof RowsEventV2Packet) { RowsEventV2Packet pkt = (RowsEventV2Packet) msg; _log.trace("RowsEventV2Packet:"); processRows(currentTableMap, pkt); currentPos = pkt.nextPosition; } else if (msg instanceof XIDPacket) { XIDPacket pkt = (XIDPacket) msg; _log.trace("XIDPacket:" + pkt.xid); currentPos = pkt.nextPosition; } else if (msg instanceof StopPacket) { StopPacket pkt = (StopPacket) msg; // should we close or keep going? // close(ctx); _log.trace("StopPacket"); currentPos = pkt.nextPosition; } else if (msg instanceof GenericPacket) { GenericPacket pkt = (GenericPacket) msg; currentPos = pkt.nextPosition; _log.trace("GenericPacket type: {}", ((GenericPacket) msg).eventType); } else { _log.warn("unknown event type: {}", msg.getClass().toString()); } getCheckPoint().setSlaveLogPosition(this.currentPos); _log.trace("Binlog file position: {}", currentPos); } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable x) throws Exception { System.out.println(x.getMessage()); } public void processRows(TableMapPacket map, RowsEventV2Packet msg) { List<ReplicatedRow> rows = new LinkedList<ReplicatedRow>(); HashMap<Integer, Byte> usedCols = new LinkedHashMap<Integer, Byte>(); HashMap<Integer, Byte> usedUpdateCols = new LinkedHashMap<Integer, Byte>(); BitSet usedBits = msg.colPresentBitmap; BitSet usedUpdateBits = msg.colPresentBitmapAftImg; for (int i = 0; i < map.colCount; i++) { if (usedBits.get(i)) { usedCols.put(i, map.colTypeDef[i]); } if (msg.eventType == ReplicationPacket.UPDATE_ROWS_EVENT) { if (usedUpdateBits.get(i)) { usedUpdateCols.put(i, map.colTypeDef[i]); } } } while (msg.rawRows.isReadable()) { int presentCount = (usedCols.size() + 7) / 8; ReplicatedRow row = new ReplicatedRow(); byte[] nullBitmap = new byte[presentCount]; msg.rawRows.readBytes(nullBitmap); BitSet nullBits = BitSet.valueOf(nullBitmap); int i = 0; for (Integer key : usedCols.keySet()) { BindValue value = new BindValue(); value.type = usedCols.get(key); value.isNull = nullBits.get(i); if (!value.isNull) { BindValueUtil.read(msg.rawRows, value); } row.colValue.put(key, value); i++; } if (msg.eventType == ReplicationPacket.UPDATE_ROWS_EVENT) { int presentUpdateCount = (usedUpdateCols.size() + 7) / 8; byte[] nullBitmapAftImg = new byte[presentUpdateCount]; msg.rawRows.readBytes(nullBitmapAftImg); BitSet nullBitsAfter = BitSet.valueOf(nullBitmapAftImg); int j = 0; for (Integer key : usedUpdateCols.keySet()) { BindValue value = new BindValue(); value.type = usedUpdateCols.get(key); value.isNull = nullBitsAfter.get(j); if (!value.isNull) { BindValueUtil.read(msg.rawRows, value); } row.colValueAftImg.put(key, value); j++; } } rows.add(row); _log.trace("table name: {}.{}", map.schemaName, map.tableName); _log.trace("event type: {}", msg.eventType); _log.trace("row: {}", row.colValue); if (msg.eventType == ReplicationPacket.UPDATE_ROWS_EVENT) _log.trace("rowAftImg: {}", row.colValueAftImg); } } public void state(ChannelHandlerContext ctx, StateIndicator msg) { ByteBuf buf = ctx.alloc().buffer(); if (msg.eventType == StateIndicator.INITIAL_STATE) { PacketEncoder.writePacket(buf, (byte) 1, () -> packetEncoder.writeHandshakeResponse(buf, MysqlServerHandler.getServerCapabilities(), masterUser, masterPassword)); ctx.writeAndFlush(buf); } else if (msg.eventType == StateIndicator.HANDSHAKEN_STATE) { PacketEncoder.writePacket(buf, (byte) 0, () -> packetEncoder.writeRegisterSlave(buf, getConfig().getSlaveServerId())); ctx.writeAndFlush(buf); _log.info("Replication handshaken"); } else if (msg.eventType == StateIndicator.REGISTERED_STATE) { this.session = this.fish.getOrca().createSession(masterUser); PacketEncoder.writePacket(buf, (byte) 0, () -> packetEncoder.writeBinlogDump(buf, masterLogPos, getConfig().getSlaveServerId(), masterBinlog)); ctx.writeAndFlush(buf); _log.info("Slave registered"); } else if (msg.eventType == StateIndicator.HANDSHAKE_FAIL_STATE || msg.eventType == StateIndicator.REGISTER_FAIL_STATE) { _log.error("Replication failed to start"); close(ctx); } } private ConfigService getConfig() { return this.fish.getOrca().getConfigService(); } public void close(ChannelHandlerContext ctx) { ctx.channel().close(); } CheckPoint getCheckPoint() { return this.fish.getOrca().getHumpback().getCheckPoint(); } }