package org.neo4j.onlinebackup.zha; import java.io.IOException; import java.nio.ByteBuffer; import java.util.List; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.WatchedEvent; import org.apache.zookeeper.Watcher; import org.apache.zookeeper.ZooDefs; import org.apache.zookeeper.ZooKeeper; public class ZooClient implements Watcher { private final ZooKeeper zooKeeper; private final long storeCreationTime; private final long storeId; private final int machineId; private final String sequenceNr; private int committedTx; private int globalCommittedTx; public ZooClient( String servers, int machineId, long storeCreationTime, long storeId, int committedTx ) { try { this.zooKeeper = new ZooKeeper( servers, 5000, this ); } catch ( IOException e ) { throw new ZooKeeperException( "Unable to create zoo keeper client", e ); } this.machineId = machineId; this.storeCreationTime = storeCreationTime; this.storeId = storeId; this.committedTx = committedTx; sequenceNr = setup(); } private String getRoot() { String rootPath = "/" + storeCreationTime + "_" + storeId; byte[] rootData = null; do { try { rootData = zooKeeper.getData( rootPath, false, null ); ByteBuffer buf = ByteBuffer.wrap( rootData ); globalCommittedTx = buf.getInt(); if ( globalCommittedTx < committedTx ) { throw new IllegalStateException( "Global committed tx " + globalCommittedTx + " while machine[" + machineId + "] @" + committedTx ); } // ok we got the root return rootPath; } catch ( KeeperException e ) { if ( e.code() != KeeperException.Code.NONODE ) { throw new ZooKeeperException( "Unable to get root node", e ); } } catch ( InterruptedException e ) { Thread.interrupted(); throw new ZooKeeperException( "Got interrupted", e ); } // try create root try { byte data[] = new byte[4]; ByteBuffer buf = ByteBuffer.wrap( data ); buf.putInt( committedTx ); zooKeeper.create( rootPath, data, ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT ); } catch ( KeeperException e ) { if ( e.code() != KeeperException.Code.NODEEXISTS ) { throw new ZooKeeperException( "Unable to create root", e ); } } catch ( InterruptedException e ) { Thread.interrupted(); throw new ZooKeeperException( "Got interrupted", e ); } } while ( rootData == null ); throw new IllegalStateException(); } private String setup() { try { String path = getRoot() + "/" + machineId + "_"; byte[] data = new byte[4]; ByteBuffer buf = ByteBuffer.wrap( data ); buf.putInt( committedTx ); String created = zooKeeper.create( path, data, ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL ); return created.substring( created.lastIndexOf( "_" ) + 1 ); } catch ( KeeperException e ) { throw new ZooKeeperException( "Unable to setup", e ); } catch ( InterruptedException e ) { Thread.interrupted(); throw new ZooKeeperException( "Setup got interrupted", e ); } } public void process( WatchedEvent event ) { System.out.println( "Got event: " + event ); } public synchronized int getMaster() { try { String root = getRoot(); List<String> children = zooKeeper.getChildren( root, false ); int currentMasterId = -1; int lowestSeq = Integer.MAX_VALUE; for ( String child : children ) { int index = child.indexOf( '_' ); int id = Integer.parseInt( child.substring( 0, index ) ); int seq = Integer.parseInt( child.substring( index + 1 ) ); try { byte[] data = zooKeeper.getData( root + "/" + child, false, null ); ByteBuffer buf = ByteBuffer.wrap( data ); int tx = buf.getInt(); if ( tx == globalCommittedTx ) { if ( seq < lowestSeq ) { currentMasterId = id; lowestSeq = seq; } } } catch ( KeeperException inner ) { if ( inner.code() != KeeperException.Code.NONODE ) { throw new ZooKeeperException( "Unabe to get master.", inner ); } } } return currentMasterId; } catch ( KeeperException e ) { throw new ZooKeeperException( "Unable to get master", e ); } catch ( InterruptedException e ) { Thread.interrupted(); throw new ZooKeeperException( "Interrupted.", e ); } } public synchronized void setCommittedTx( int tx ) { if ( tx <= committedTx ) { throw new IllegalArgumentException( "tx=" + tx + " but committedTx is " + committedTx ); } int masterId = getMaster(); String root = getRoot(); String path = root + "/" + machineId + "_" + sequenceNr; byte[] data = new byte[4]; ByteBuffer buf = ByteBuffer.wrap( data ); buf.putInt( tx ); try { zooKeeper.setData( path, data, -1 ); if ( masterId == machineId ) { zooKeeper.setData( root, data, -1 ); } } catch ( KeeperException e ) { throw new ZooKeeperException( "Unable to set current tx", e ); } catch ( InterruptedException e ) { Thread.interrupted(); throw new ZooKeeperException( "Interrupted...", e ); } } public void shutdown() { try { zooKeeper.close(); } catch ( InterruptedException e ) { throw new ZooKeeperException( "Error closing zookeeper connection", e ); } } public static void main( String args[] ) { String servers = "localhost:2181,localhost:2182,localhost:2183"; ZooClient client = new ZooClient( servers, 1, 2, 1, 0 ); System.out.println( client.getMaster() ); client.setCommittedTx( 1 ); System.out.println( client.getMaster() ); client.shutdown(); } }