package com.techq.available.quorum;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.rmi.UnexpectedException;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import javax.print.attribute.Size2DSyntax;
import org.omg.CORBA.TIMEOUT;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.techq.available.App;
import com.techq.available.quorum.handler.Follower;
import com.techq.available.quorum.handler.Leader;
/**
*
* @author CHQ
* 2012-2-3
*/
public class QuorumPeer extends Thread {
private static final Logger LOG = LoggerFactory.getLogger(App.class);
ProposalVote curVote;
long logicalClock = 0;
private long id = -1;
private long zxid = 0;
public long startTime = 0;
volatile boolean isGoingOn = true;
public Map<Long, QuorumServer> quorumPeers;
public InetSocketAddress electionAddr;
volatile ServerState state;
Election election;
boolean isRunning = true;
private int initLimit = 5;
public InetSocketAddress addr;
public Election getElection() {
return election;
}
public ProposalVote getCurVote() {
return curVote;
}
public long getLogicalClock() {
return logicalClock;
}
public void setLogicalClock(long logicalClock) {
this.logicalClock = logicalClock;
}
public int getInitLimit() {
return initLimit;
}
public void setInitLimit(int initLimit) {
this.initLimit = initLimit;
}
public QuorumPeer(Map quorumPeers, ProposalVote curVote, long id) {
super("QuorumPeer[myid=" + id + "]");
this.quorumPeers = quorumPeers;
this.curVote = curVote;
this.id = id;
this.setPeerState(ServerState.LOOKING);
election = new LeaderElection(this);
}
public long getTickTime() {
return tickTime;
}
public void setTickTime(long tickTime) {
this.tickTime = tickTime;
}
public long getSyncLimit() {
return syncLimit;
}
public void setSyncLimit(long syncLimit) {
this.syncLimit = syncLimit;
}
private long tickTime = 1000;
private long syncLimit;
volatile protected int tick;
public long getId() {
return id;
}
public long getZxid() {
return zxid;
}
public Map<Long, QuorumPeer.QuorumServer> getVotingViews() {
return Collections.unmodifiableMap(this.quorumPeers);
}
public Map<Long, QuorumPeer.QuorumServer> getViews() {
return Collections.unmodifiableMap(this.quorumPeers);
}
public static class QuorumServer {
public QuorumServer(long id, InetSocketAddress addr, InetSocketAddress electionAddr) {
this.id = id;
this.addr = addr;
this.electionAddr = electionAddr;
}
public QuorumServer(long id, InetSocketAddress addr) {
this.id = id;
this.addr = addr;
this.electionAddr = null;
}
public QuorumServer(long id, InetSocketAddress addr, InetSocketAddress electionAddr,
LearnerType type) {
this.id = id;
this.addr = addr;
this.electionAddr = electionAddr;
this.type = type;
}
public InetSocketAddress addr;
public InetSocketAddress electionAddr;
public long id;
public LearnerType type = LearnerType.PARTICIPANT;
}
public enum LearnerType {
PARTICIPANT, OBSERVER;
}
public ServerState getPeerState() {
return state;
}
public void setPeerState(ServerState newState) {
state = newState;
}
@Override
public void run() {
LOG.info("Starting quorum peer");
while (isRunning) {
switch (getPeerState()) {
case LOOKING:
try {
Vote candidate = election.lookForLeader();
} catch (Exception e) {
LOG.warn("Unexpected exception", e);
setPeerState(ServerState.LOOKING);
}
break;
case FOLLOWING:
try {
LOG.info("i am follower, i have nothing to do but following");
QuorumServer leaderServer = quorumPeers.get(curVote.proposedLeader);
Follower handler;
handler = new Follower(curVote.proposedLeader, curVote.proposedZxid, id, leaderServer.addr);
boolean isOK = handler.followLeader();
if (!isOK) {
setPeerState(ServerState.LOOKING);
LOG.info("can't sync with leader, and restart");
//TimeUnit.SECONDS.sleep(10);
setPeerState(ServerState.LOOKING);
} else {
LOG.info("follow[id=" +id+"] exit");
return;
}
} catch (InterruptedException e1) {
LOG.warn("Unexpected InterruptedException", e1);
setPeerState(ServerState.LOOKING);
} catch (IOException e) {
LOG.warn("Unexpected IOException", e);
setPeerState(ServerState.LOOKING);
}
break;
case LEADING:
LOG.info("i am leader, i have nothing to do but leading");
Set<Long> views = this.getViews().keySet();
QuorumServer leaderServer = quorumPeers.get(curVote.proposedLeader);
try {
Leader leaderHandler = new Leader(curVote.proposedLeader, views, leaderServer.addr);
//block here
boolean isOK = leaderHandler.leading();
if (isOK) {
LOG.warn("leader[id="+id+"] exit!!!");
return;
} else {
setPeerState(ServerState.LOOKING);
LOG.error("can't sync with followers, and restart");
//TimeUnit.SECONDS.sleep(10);
}
} catch (InterruptedException e1) {
LOG.error("can't sync with followers, and restart", e1);
setPeerState(ServerState.LOOKING);
} catch (IOException e) {
LOG.error("can't sync with followers, and restart", e);
setPeerState(ServerState.LOOKING);
} catch (Exception e) {
LOG.error("unexpected happened", e);
setPeerState(ServerState.LOOKING);
}
break;
}
}
}
public boolean synced() {
return this.tick <= this.syncLimit;
}
}