package org.neo4j.onlinebackup.ha; import java.io.IOException; import java.net.InetSocketAddress; import java.nio.channels.ReadableByteChannel; import java.nio.channels.ServerSocketChannel; import java.util.List; import java.util.Map; import java.util.concurrent.CopyOnWriteArrayList; import org.neo4j.graphdb.GraphDatabaseService; import org.neo4j.kernel.EmbeddedGraphDatabase; import org.neo4j.kernel.impl.transaction.XaDataSourceManager; import org.neo4j.kernel.impl.transaction.xaframework.XaDataSource; import org.neo4j.onlinebackup.net.AcceptJob; import org.neo4j.onlinebackup.net.Callback; import org.neo4j.onlinebackup.net.Connection; import org.neo4j.onlinebackup.net.HandleIncommingSlaveJob; import org.neo4j.onlinebackup.net.HandleSlaveConnection; import org.neo4j.onlinebackup.net.Job; import org.neo4j.onlinebackup.net.JobEater; import org.neo4j.onlinebackup.net.SocketException; public class Master implements Callback { private final EmbeddedGraphDatabase graphDb; private final XaDataSourceManager xaDsMgr; private final JobEater jobEater; private final ServerSocketChannel serverChannel; private final int port; private List<HandleSlaveConnection> slaveList = new CopyOnWriteArrayList<HandleSlaveConnection>(); public Master( String path, Map<String,String> params, int listenPort ) { this.graphDb = new EmbeddedGraphDatabase( path, params ); this.xaDsMgr = graphDb.getConfig().getTxModule() .getXaDataSourceManager(); for ( XaDataSource xaDs : xaDsMgr.getAllRegisteredDataSources() ) { xaDs.keepLogicalLogs( true ); } this.port = listenPort; try { serverChannel = ServerSocketChannel.open(); serverChannel.configureBlocking( false ); serverChannel.socket().bind( new InetSocketAddress( listenPort ) ); } catch ( IOException e ) { throw new SocketException( "Unable to bind at port[" + listenPort + "]", e ); } jobEater = new JobEater(); jobEater.addJob( new AcceptJob( this, serverChannel ) ); jobEater.start(); } public GraphDatabaseService getGraphDbService() { return graphDb; } public int getPort() { return this.port; } public void jobExecuted( Job job ) { if ( job instanceof AcceptJob ) { // handle incomming slave AcceptJob acceptJob = (AcceptJob) job; if ( acceptJob.getAcceptedChannel() != null ) { Connection connection = new Connection( ((AcceptJob) job).getAcceptedChannel() ); jobEater.addJob( new HandleIncommingSlaveJob( connection, this ) ); } } else if ( job instanceof HandleIncommingSlaveJob ) { HandleSlaveConnection chainJob = (HandleSlaveConnection) job.getChainJob(); if ( chainJob != null ) { slaveList.add( chainJob ); } else { System.out.println( "null chain job" ); } } } public void shutdown() { jobEater.stopEating(); try { serverChannel.close(); } catch ( IOException e ) { e.printStackTrace(); } graphDb.shutdown(); } public long getIdentifier( String xaDsName ) { XaDataSource xaDs = xaDsMgr.getXaDataSource( xaDsName ); if ( xaDs != null ) { return xaDs.getRandomIdentifier(); } return -1; } public long getCreationTime( String xaDsName ) { XaDataSource xaDs = xaDsMgr.getXaDataSource( xaDsName ); if ( xaDs != null ) { return xaDs.getCreationTime(); } return -1; } public long getVersion( String xaDsName ) { XaDataSource xaDs = xaDsMgr.getXaDataSource( xaDsName ); if ( xaDs != null ) { return xaDs.getCurrentLogVersion(); } return -1; } public ReadableByteChannel getLog( String xaDsName, long version ) throws IOException { XaDataSource xaDs = xaDsMgr.getXaDataSource( xaDsName ); if ( xaDs != null ) { return xaDs.getLogicalLog( version ); } return null; } public long getLogLength( String xaDsName, long version ) { XaDataSource xaDs = xaDsMgr.getXaDataSource( xaDsName ); if ( xaDs != null ) { return xaDs.getLogicalLogLength( version ); } return -1l; } public boolean hasLog( String xaDsName, long version ) { XaDataSource xaDs = xaDsMgr.getXaDataSource( xaDsName ); if ( xaDs != null ) { return xaDs.hasLogicalLog( version ); } return false; } public synchronized void rotateLogAndPushToSlaves() throws IOException { if ( slaveList.size() == 0 ) { return; } for ( XaDataSource xaDs : xaDsMgr.getAllRegisteredDataSources() ) { xaDs.rotateLogicalLog(); } List<HandleSlaveConnection> newList = new CopyOnWriteArrayList<HandleSlaveConnection>(); for ( HandleSlaveConnection slave : slaveList ) { XaDataSource xaDs = xaDsMgr.getXaDataSource( slave.getXaDsName() ); if ( xaDs != null ) { long version = xaDs.getCurrentLogVersion() - 1; if ( !slave.offerLogToSlave( version ) ) { System.out.println( "Failed to offer log to slave: " + slave ); } else { newList.add( slave ); } } } slaveList = newList; } }