Java tutorial
/** * Copyright (C) 2015 Greg Brandt (brandt.greg@gmail.com) * * 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.github.brandtg.switchboard; import com.google.code.or.binlog.BinlogEventParser; import com.google.code.or.binlog.BinlogEventV4; import com.google.code.or.binlog.impl.event.FormatDescriptionEvent; import com.google.code.or.binlog.impl.event.GtidEvent; import com.google.code.or.binlog.impl.event.QueryEvent; import com.google.code.or.binlog.impl.event.RawEvent; import com.google.code.or.binlog.impl.event.XidEvent; import com.google.code.or.binlog.impl.parser.FormatDescriptionEventParser; import com.google.code.or.binlog.impl.parser.GtidEventParser; import com.google.code.or.binlog.impl.parser.QueryEventParser; import com.google.code.or.binlog.impl.parser.RawEventParser; import com.google.code.or.binlog.impl.parser.XidEventParser; import com.google.common.base.Joiner; import com.google.common.collect.ImmutableSet; import org.apache.commons.codec.binary.Base64; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.ByteArrayOutputStream; import java.io.InputStream; import java.sql.Connection; import java.sql.Statement; import java.util.ArrayList; import java.util.Collection; import java.util.Iterator; import java.util.List; import java.util.concurrent.atomic.AtomicBoolean; import javax.sql.DataSource; public class MysqlReplicationApplier implements Runnable { private static final Logger LOG = LoggerFactory.getLogger(MysqlReplicationApplier.class); private static final Joiner SPACE = Joiner.on(" "); private final InputStream inputStream; private final DataSource dataSource; private final AtomicBoolean isShutdown; /** * Applies a stream of MySQL binary log events to a MySQL instance. * * @param inputStream * An input stream containing raw MySQL binary log. * @param dataSource * A data source for the database to which replicated events are to be applied. */ public MysqlReplicationApplier(InputStream inputStream, DataSource dataSource) { this.inputStream = inputStream; this.dataSource = dataSource; this.isShutdown = new AtomicBoolean(false); } /** Shuts down this applier. */ public void shutdown() { if (!isShutdown.getAndSet(true)) { LOG.info("Shut down MySQL replication applier"); } } @Override public void run() { while (!isShutdown.get()) { List<String> sqls = new ArrayList<>(); List<String> rawSqls = new ArrayList<>(); try { Collection<BinlogEventParser> eventParsers = ImmutableSet.of( (BinlogEventParser) new QueryEventParser(), (BinlogEventParser) new GtidEventParser(), (BinlogEventParser) new XidEventParser(), (BinlogEventParser) new FormatDescriptionEventParser()); MysqlLogParser logParser = new MysqlLogParser(inputStream, eventParsers, new RawEventParser()); Iterator<BinlogEventV4> itr = new MysqlLogIterator(logParser); while (itr.hasNext()) { BinlogEventV4 event = itr.next(); if (event instanceof FormatDescriptionEvent) { FormatDescriptionEvent formatDescriptionEvent = (FormatDescriptionEvent) event; ByteArrayOutputStream baos = new ByteArrayOutputStream(); baos.write(formatDescriptionEvent.getHeader().getBytes()); baos.write(formatDescriptionEvent.getBytes()); byte[] bytes = baos.toByteArray(); String binlogSql = String.format("BINLOG '%s'", Base64.encodeBase64String(bytes)); try (Connection conn = dataSource.getConnection(); Statement stmt = conn.createStatement()) { stmt.execute("ROLLBACK"); stmt.execute(binlogSql); LOG.info("Executed FormatDescriptionEvent SQL: {}", binlogSql); } } else if (event instanceof GtidEvent) { GtidEvent gtidEvent = (GtidEvent) event; sqls.clear(); rawSqls.clear(); sqls.add(String.format("SET @@SESSION.GTID_NEXT = '%s:%d'", gtidEvent.getSourceIdAsUuid(), gtidEvent.getTransactionId())); sqls.add(String.format("SET TIMESTAMP=%d", gtidEvent.getHeader().getTimestamp() / 1000)); } else if (event instanceof QueryEvent) { QueryEvent queryEvent = (QueryEvent) event; sqls.add(queryEvent.getSql().toString()); } else if (event instanceof RawEvent) { RawEvent rawEvent = (RawEvent) event; rawSqls.add(Base64.encodeBase64String(rawEvent.getBytesWithHeader())); } else if (event instanceof XidEvent) { try (Connection conn = dataSource.getConnection(); Statement stmt = conn.createStatement()) { for (String sql : sqls) { stmt.execute(sql); } String joinedRaw = SPACE.join(rawSqls); String binlogSql = String.format("BINLOG '\n%s\n'", joinedRaw); stmt.execute(binlogSql); stmt.execute("COMMIT"); } LOG.info("Last GTID statement: {}", sqls.get(0)); } } } catch (Exception e) { LOG.error("Error in replication applier", e); throw new RuntimeException(e); } } } }