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.common.base.Joiner; import com.google.common.base.MoreObjects; import org.apache.commons.cli.CommandLine; import org.apache.commons.cli.GnuParser; import org.apache.commons.cli.HelpFormatter; import org.apache.commons.cli.Options; import org.apache.commons.dbcp2.ConnectionFactory; import org.apache.commons.dbcp2.DriverManagerConnectionFactory; import org.apache.commons.dbcp2.PoolableConnection; import org.apache.commons.dbcp2.PoolableConnectionFactory; import org.apache.commons.dbcp2.PoolingDataSource; import org.apache.commons.pool2.ObjectPool; import org.apache.commons.pool2.impl.GenericObjectPool; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.net.InetSocketAddress; import java.net.URLEncoder; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import javax.management.ObjectName; public class MysqlReplicator extends MysqlLogPuller { private static final Logger LOG = LoggerFactory.getLogger(MysqlReplicator.class); private static final Joiner SPACE = Joiner.on(" "); private static final String ENCODING = "UTF-8"; static { try { Class.forName("com.mysql.jdbc.Driver"); } catch (Exception e) { throw new RuntimeException(e); } } private final String jdbcString; private final String user; private final String password; private MysqlReplicationApplier applier; private PoolingDataSource<PoolableConnection> dataSource; public MysqlReplicator(String database, InetSocketAddress sourceAddress, InetSocketAddress sinkAddress, String jdbcString, String user, String password) { this(database, sourceAddress, sinkAddress, jdbcString, user, password, -1); } /** * Creates an agent to listen to MySQL changes (via binlog). * * @param database * The MySQL database name * @param sourceAddress * The switchboard server address from which to pull events * @param sinkAddress * The local listener port to receive raw binlog data * @param lastIndex * The last index that was processed by whoever is constructing this (if -1, processed none) */ public MysqlReplicator(String database, InetSocketAddress sourceAddress, InetSocketAddress sinkAddress, String jdbcString, String user, String password, long lastIndex) { super(database, sourceAddress, sinkAddress, lastIndex); this.jdbcString = jdbcString; this.user = user; this.password = password; } @Override public String toString() { return MoreObjects.toStringHelper(this).add("database", database).add("sourceAddress", sourceAddress) .add("sinkAddress", sinkAddress).add("jdbcString", jdbcString).add("user", user).toString(); } @Override public void start() throws Exception { // DBCP2 pool ConnectionFactory connectionFactory = new DriverManagerConnectionFactory(jdbcString, user, password); ObjectName poolName = new ObjectName(JdbcBasedLogIndex.class.getCanonicalName(), URLEncoder.encode(jdbcString, ENCODING), "replicatorConnectionPool"); PoolableConnectionFactory poolableConnectionFactory = new PoolableConnectionFactory(connectionFactory, poolName); ObjectPool<PoolableConnection> connectionPool = new GenericObjectPool<PoolableConnection>( poolableConnectionFactory); poolableConnectionFactory.setPool(connectionPool); this.dataSource = new PoolingDataSource<PoolableConnection>(connectionPool); LOG.info("Opened connection pool to {} as {}", jdbcString, user); // Replication applier applier = new MysqlReplicationApplier(inputStream, dataSource); super.start(); } @Override public void shutdown() throws Exception { super.shutdown(); dataSource.close(); applier.shutdown(); } @Override protected Runnable getCallback() { return applier; } /** Main. */ public static void main(String[] args) throws Exception { Options options = new Options(); options.addOption("u", "user", true, "MySQL user"); options.addOption("p", "password", true, "MySQL password"); options.addOption("h", "host", true, "MySQL host"); options.addOption("P", "port", true, "MySQL port"); options.addOption("s", "sinkPort", true, "Local sink port"); options.addOption("l", "lastIndex", true, "Last transaction ID"); options.addOption("h", "help", false, "Prints help message"); CommandLine commandLine = new GnuParser().parse(options, args); if (commandLine.getArgs().length < 2 || commandLine.hasOption("help")) { HelpFormatter helpFormatter = new HelpFormatter(); helpFormatter.printHelp("usage: [opts] switchboardHost:port db1 [db2 ...]", options); System.exit(1); } // Switchboard host String[] hostPort = commandLine.getArgs()[0].split(":"); InetSocketAddress source = new InetSocketAddress(hostPort[0], Integer.valueOf(hostPort[1])); InetSocketAddress sink = new InetSocketAddress( Integer.valueOf(commandLine.getOptionValue("sinkPort", "9090"))); // Databases to replicate String[] databases = Arrays.copyOfRange(commandLine.getArgs(), 1, commandLine.getArgs().length); // JDBC params for local copy String user = commandLine.getOptionValue("user", "root"); String password = commandLine.getOptionValue("password", ""); String jdbcString = String.format("jdbc:mysql://%s:%d", commandLine.getOptionValue("host", "localhost"), Integer.valueOf(commandLine.getOptionValue("port", "3306"))); Long lastIndex = Long.valueOf(commandLine.getOptionValue("lastIndex", "-1")); // Create replicators final List<MysqlReplicator> replicators = new ArrayList<>(); for (String database : databases) { MysqlReplicator replicator = new MysqlReplicator(database, source, sink, jdbcString, user, password, lastIndex); replicators.add(replicator); } // Shutdown hook Runtime.getRuntime().addShutdownHook(new Thread() { @Override public void run() { for (MysqlReplicator replicator : replicators) { try { replicator.shutdown(); } catch (Exception e) { LOG.error("Could not shut down {}", replicator, e); } } } }); for (MysqlReplicator replicator : replicators) { replicator.start(); LOG.info("Started {}", replicator); } } }