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.fasterxml.jackson.databind.ObjectMapper; import io.netty.channel.EventLoopGroup; import io.netty.channel.nio.NioEventLoopGroup; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.PipedInputStream; import java.io.PipedOutputStream; import java.net.InetSocketAddress; import java.util.concurrent.Executors; import java.util.concurrent.ExecutorService; import java.util.concurrent.atomic.AtomicBoolean; public abstract class MysqlLogPuller { private static final String ENCODING = "UTF-8"; private static final Logger LOG = LoggerFactory.getLogger(MysqlLogPuller.class); private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper(); protected final String database; protected final long lastIndex; protected final InetSocketAddress sourceAddress; protected final InetSocketAddress sinkAddress; protected final PipedInputStream inputStream; protected final PipedOutputStream outputStream; protected final AtomicBoolean isStarted; protected EventLoopGroup eventExecutors; protected ExecutorService callbackExecutor; protected ExecutorService refreshExecutor; protected LogReceiver logReceiver; protected LogPuller logPuller; public MysqlLogPuller(String database, InetSocketAddress sourceAddress, InetSocketAddress sinkAddress) { this(database, sourceAddress, sinkAddress, -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 MysqlLogPuller(String database, InetSocketAddress sourceAddress, InetSocketAddress sinkAddress, long lastIndex) { this.database = database; this.lastIndex = lastIndex; this.sourceAddress = sourceAddress; this.sinkAddress = sinkAddress; this.inputStream = new PipedInputStream(); this.outputStream = new PipedOutputStream(); this.isStarted = new AtomicBoolean(); } protected abstract Runnable getCallback(); /** * Starts pulling events from switchboard server. */ public void start() throws Exception { if (!isStarted.getAndSet(true)) { outputStream.connect(inputStream); callbackExecutor = Executors.newSingleThreadExecutor(); refreshExecutor = Executors.newSingleThreadExecutor(); eventExecutors = new NioEventLoopGroup(); // Start server to receive log segments logReceiver = new LogReceiver(sinkAddress, eventExecutors, outputStream); logReceiver.start(); // Iterates through events and fires appropriate callbacks callbackExecutor.submit(getCallback()); // Pulls log indexes logPuller = new LogPuller(sourceAddress, sinkAddress, database, lastIndex); logReceiver.registerListener(logPuller); refreshExecutor.submit(logPuller); } } /** * Stops pulling events from switchboard server. */ public void shutdown() throws Exception { if (isStarted.getAndSet(false)) { logPuller.shutdown(); try { logReceiver.shutdown(); } catch (Exception e) { LOG.error("Exception while shutting down log receiver", e); } eventExecutors.shutdownGracefully(); callbackExecutor.shutdown(); refreshExecutor.shutdown(); } } }