package lsr.leader;
import helpers.ProcessCrashController;
import helpers.ProcessCrashController.Reply;
import helpers.ProcessCrashController.Request;
import helpers.ProcessCrashController.Type;
import java.io.FileWriter;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.Random;
import java.util.logging.Logger;
import lsr.common.Configuration;
import lsr.common.ProcessDescriptor;
import lsr.common.SingleThreadDispatcher;
import lsr.leader.BasicLeaderOracle;
import lsr.leader.LatencyLeaderOracle;
import lsr.leader.LeaderOracle;
import lsr.leader.LeaderOracleListener;
import lsr.leader.latency.SimpleLatencyDetector;
import lsr.paxos.network.UdpNetwork;
/**
* Application for simulation purpose.
*
* @author Donz� Benjamin
* @author Nuno Santos
*
*/
public class LeaderOracleSimulation implements LeaderOracleListener {
private static final String ORACLE_TYPE = "simulation.OracleType";
public final Object lock = new Object();
private final UdpNetwork network;
private final ProcessDescriptor p;
private final SingleThreadDispatcher executor;
abstract class LeaderOracleController {
abstract public void start() throws Exception;
abstract public void stop() throws Exception;
abstract public LeaderOracle getLeaderOracle();
public void printRunParameters(PrintWriter pw) throws IOException {
pw.println("N & " + p.config.getN() + "\\\\");
pw.println("Leader & " + getLeaderOracle().getClass().getName() + "\\\\");
pw.println("Delta & " + getLeaderOracle().getDelta() + "\\\\");
}
}
class LatencyLOController extends LeaderOracleController {
public LatencyLeaderOracle leaderOracle;
public SimpleLatencyDetector latencyDetector;
@Override
public void start() throws Exception {
latencyDetector = new SimpleLatencyDetector(p, network, executor);
// Create the leader oracle
leaderOracle = new LatencyLeaderOracle(p, network, executor, latencyDetector);
leaderOracle.registerLeaderOracleListener(LeaderOracleSimulation.this);
latencyDetector.start();
leaderOracle.start();
}
@Override
public void stop() throws Exception {
leaderOracle.stop();
latencyDetector.stop();
leaderOracle = null;
latencyDetector = null;
}
@Override
public void printRunParameters(PrintWriter pw) throws IOException {
super.printRunParameters(pw);
pw.println("Epsilon & " + leaderOracle.getEpsilon() + "\\\\");
pw.println("SendPeriod & " + latencyDetector.getSendPeriod());
}
@Override
public LeaderOracle getLeaderOracle() {
return leaderOracle;
}
}
class BasicLOController extends LeaderOracleController {
public BasicLeaderOracle leaderOracle;
@Override
public void start() throws Exception {
// Create the leader oracle
leaderOracle = new BasicLeaderOracle(p, network, executor);
leaderOracle.start();
}
@Override
public void stop() throws Exception {
leaderOracle.stop();
leaderOracle = null;
}
public LeaderOracle getLeaderOracle() {
return leaderOracle;
}
}
private final LeaderOracleController loController;
public LeaderOracleSimulation(Configuration config, int localId)
throws Exception
{
p = new ProcessDescriptor(config, localId);
// Use UDP to send the leader election messages
network = new UdpNetwork(p);
executor = new SingleThreadDispatcher("leader");
String oracleType = config.getProperty(ORACLE_TYPE, "LatencyLeaderOracle");
if (oracleType.equals("LatencyLeaderOracle")) {
loController = new LatencyLOController();
} else if (oracleType.equals("BasicLeaderOracle")) {
loController = new BasicLOController();
} else {
throw new Exception("Unknown oracle type: " + oracleType);
}
_logger.info("Using " + oracleType);
}
/**
* Called whenever a new leader is elected. This implementation simply
* prints a notification on the screen, but a real protocol would perform
* some action in reaction to the change on leader.
*/
@Override
public void onNewLeaderElected(int leader) {
_logger.info("New leader elected: " + leader);
}
public void runSimulation() throws Exception {
// Connect to the lock server that controls the number of crashes
Socket s = new Socket("localhost", ProcessCrashController.PORT);
ObjectInputStream ois = new ObjectInputStream(s.getInputStream());
ObjectOutputStream oos = new ObjectOutputStream(s.getOutputStream());
// Initialize
System.out.println("Initializing");
oos.writeObject(
new Request(Type.Initialize, p.localID, p.config.getN()));
Reply answer = (Reply) ois.readObject();
System.out.println("Initialized");
if (answer.type != Type.Grant) {
throw new Exception("Could not initialize: " + answer.msg);
}
loController.start();
// Only the first process writes the run parameters
if (p.localID == 0) {
PrintWriter pw = new PrintWriter(new FileWriter("run-params.log"));
loController.printRunParameters(pw);
pw.close();
}
// Random random = new Random();
// int sr = random.nextInt(60000*6);
Random random = new Random();
// long time_next_restart = 60000 * 1;
while(true) {
// Generate a random number with a gaussian of mean 6 minutes and std-dev 1 minute
// double rn = random.nextGaussian();
// rn = rn * 60000 + 60000*6;
// long time_next_crash = Math.round(rn);
// Crash local process every 5 to 10 seconds
long nextCrash = 5000 + random.nextInt(5000);
try {
// Wait until time of crash
Thread.sleep(nextCrash);
_logger.info("Requesting crash");
// Request crash
oos.writeObject(
new Request(Type.Crash, p.localID, p.config.getN()));
oos.flush();
answer = (Reply) ois.readObject();
if (answer.type != Type.Grant) {
_logger.info("Crash denied: " + answer.msg);
// Skip crash
continue;
}
// Stop
loController.stop();
Thread.sleep(10000);
oos.writeObject(
new Request(Type.Recover, p.localID, p.config.getN()));
answer = (Reply) ois.readObject();
if (answer.type != Type.Grant) {
throw new RuntimeException("Could not recover: " + answer.msg);
}
loController.start();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
/**
* Arguments: <repNo> [config.file]
*
* @param args
* @throws Exception
*/
public static void main(String[] args) throws Exception {
if (args.length < 1) {
usage();
System.exit(1);
}
int localID = Integer.parseInt(args[0]);
Configuration p = args.length == 2 ?
new Configuration(args[1]) : new Configuration();
LeaderOracleSimulation lod = new LeaderOracleSimulation(p, localID);
lod.runSimulation();
}
private static void usage() {
System.out.println(
"Invalid arguments. Usage:\n" +
" java lsr.leader.LeaderOracleSimulation <replicaID> [conf directory]");
}
private final static Logger _logger = Logger.getLogger(LeaderOracleSimulation.class.getCanonicalName());
}