/** * Copyright (c) 2002-2012 "Neo Technology," * Network Engine for Objects in Lund AB [http://neotechnology.com] * * This file is part of Neo4j. * * Neo4j is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package org.neo4j.cluster.protocol.atomicbroadcast.multipaxos; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.net.URI; import java.net.URISyntaxException; import java.util.List; import ch.qos.logback.classic.Level; import ch.qos.logback.classic.Logger; import ch.qos.logback.classic.LoggerContext; import org.neo4j.cluster.BindingListener; import org.neo4j.cluster.ClusterSettings; import org.neo4j.cluster.MultiPaxosServerFactory; import org.neo4j.cluster.NetworkedServerFactory; import org.neo4j.cluster.ProtocolServer; import org.neo4j.cluster.com.NetworkInstance; import org.neo4j.cluster.protocol.atomicbroadcast.AtomicBroadcast; import org.neo4j.cluster.protocol.atomicbroadcast.AtomicBroadcastListener; import org.neo4j.cluster.protocol.atomicbroadcast.AtomicBroadcastSerializer; import org.neo4j.cluster.protocol.atomicbroadcast.Payload; import org.neo4j.cluster.protocol.cluster.Cluster; import org.neo4j.cluster.protocol.cluster.ClusterConfiguration; import org.neo4j.cluster.protocol.cluster.ClusterContext; import org.neo4j.cluster.protocol.cluster.ClusterListener; import org.neo4j.cluster.protocol.cluster.ClusterMessage; import org.neo4j.cluster.protocol.election.Election; import org.neo4j.cluster.protocol.election.ServerIdElectionCredentialsProvider; import org.neo4j.cluster.protocol.heartbeat.Heartbeat; import org.neo4j.cluster.protocol.heartbeat.HeartbeatContext; import org.neo4j.cluster.protocol.heartbeat.HeartbeatListener; import org.neo4j.cluster.protocol.heartbeat.HeartbeatMessage; import org.neo4j.cluster.timeout.FixedTimeoutStrategy; import org.neo4j.cluster.timeout.MessageTimeoutStrategy; import org.neo4j.helpers.collection.MapUtil; import org.neo4j.kernel.configuration.Config; import org.neo4j.kernel.configuration.ConfigurationDefaults; import org.neo4j.kernel.lifecycle.LifeSupport; import org.neo4j.kernel.logging.LogbackService; import org.slf4j.impl.StaticLoggerBinder; /** * Multi Paxos test server */ public class MultiPaxosServer { private AtomicBroadcastSerializer broadcastSerializer; private ProtocolServer server; private Election election; public static void main( String[] args ) throws IOException, InvocationTargetException, IllegalAccessException { new MultiPaxosServer().start(); } protected Cluster cluster; protected AtomicBroadcast broadcast; public void start() throws IOException { broadcastSerializer = new AtomicBroadcastSerializer(); final LifeSupport life = new LifeSupport(); try { MessageTimeoutStrategy timeoutStrategy = new MessageTimeoutStrategy( new FixedTimeoutStrategy( 5000 ) ) .timeout( HeartbeatMessage.sendHeartbeat, 200 ); NetworkedServerFactory serverFactory = new NetworkedServerFactory( life, new MultiPaxosServerFactory( new ClusterConfiguration( "default" ), new LogbackService( null, null ) ), timeoutStrategy, new LogbackService( null, null ) ); ServerIdElectionCredentialsProvider electionCredentialsProvider = new ServerIdElectionCredentialsProvider(); server = serverFactory.newNetworkedServer( new Config( new ConfigurationDefaults( NetworkInstance.Configuration.class ).apply( MapUtil.stringMap( ClusterSettings.cluster_server.name(), ":5001-5003" ) ) ), new InMemoryAcceptorInstanceStore(), electionCredentialsProvider ); server.addBindingListener( electionCredentialsProvider ); server.addBindingListener( new BindingListener() { @Override public void listeningAt( URI me ) { System.out.println( "Listening at:" + me ); } } ); cluster = server.newClient( Cluster.class ); cluster.addClusterListener( new ClusterListener() { @Override public void enteredCluster( ClusterConfiguration clusterConfiguration ) { System.out.println( "Entered cluster:" + clusterConfiguration ); } @Override public void joinedCluster( URI member ) { System.out.println( "Joined cluster:" + member ); } @Override public void leftCluster( URI member ) { System.out.println( "Left cluster:" + member ); } @Override public void leftCluster() { System.out.println( "Left cluster" ); } @Override public void elected( String role, URI electedMember ) { System.out.println( electedMember + " was elected as " + role ); } } ); Heartbeat heartbeat = server.newClient( Heartbeat.class ); heartbeat.addHeartbeatListener( new HeartbeatListener() { @Override public void failed( URI server ) { System.out.println( server + " failed" ); } @Override public void alive( URI server ) { System.out.println( server + " alive" ); } } ); election = server.newClient( Election.class ); broadcast = server.newClient( AtomicBroadcast.class ); broadcast.addAtomicBroadcastListener( new AtomicBroadcastListener() { @Override public void receive( Payload value ) { try { System.out.println( broadcastSerializer.receive( value ) ); } catch ( IOException e ) { e.printStackTrace(); } catch ( ClassNotFoundException e ) { e.printStackTrace(); } } } ); life.start(); String command; BufferedReader reader = new BufferedReader( new InputStreamReader( System.in ) ); while ( !(command = reader.readLine()).equals( "quit" ) ) { String[] arguments = command.split( " " ); Method method = getCommandMethod( arguments[0] ); if ( method != null ) { String[] realArgs = new String[arguments.length - 1]; System.arraycopy( arguments, 1, realArgs, 0, realArgs.length ); try { method.invoke( this, realArgs ); } catch ( IllegalAccessException e ) { e.printStackTrace(); } catch ( IllegalArgumentException e ) { e.printStackTrace(); } catch ( InvocationTargetException e ) { e.printStackTrace(); } } } cluster.leave(); } finally { life.shutdown(); System.out.println( "Done" ); } } public void create( String name ) { cluster.create( name ); } public void join( String nodeUri ) throws URISyntaxException { cluster.join( new URI( nodeUri ) ); } public void leave() { cluster.leave(); } public void broadcast( String value ) throws IOException { broadcast.broadcast( broadcastSerializer.broadcast( value ) ); } public void demote( String nodeUri ) throws URISyntaxException { election.demote( new URI( nodeUri ) ); } public void promote( String nodeUri, String role ) throws URISyntaxException { election.promote( new URI( nodeUri ), role ); } public void logging( String name, String level ) { LoggerContext loggerContext = (LoggerContext) StaticLoggerBinder.getSingleton().getLoggerFactory(); List<Logger> loggers = loggerContext.getLoggerList(); for ( Logger logger : loggers ) { if ( logger.getName().startsWith( name ) ) { logger.setLevel( Level.toLevel( level ) ); } } } public void config() { ClusterConfiguration configuration = ((ClusterContext) server.getConnectedStateMachines() .getStateMachine( ClusterMessage.class ) .getContext()).getConfiguration(); List<URI> failed = ((HeartbeatContext) server.getConnectedStateMachines().getStateMachine( HeartbeatMessage .class ).getContext()).getFailed(); System.out.println( configuration + " Failed:" + failed ); } private Method getCommandMethod( String name ) { for ( Method method : MultiPaxosServer.class.getMethods() ) { if ( method.getName().equals( name ) ) { return method; } } return null; } }