/**
* Copyright (c) 2007-2009 Alysson Bessani, Eduardo Alchieri, Paulo Sousa, and the authors indicated in the @author tags
*
* This file is part of SMaRt.
*
* SMaRt is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* SMaRt 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with SMaRt. If not, see <http://www.gnu.org/licenses/>.
*/
package bftsmart.statemanagment;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Queue;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.locks.ReentrantLock;
import bftsmart.paxosatwar.executionmanager.ExecutionManager;
import bftsmart.paxosatwar.messages.PaxosMessage;
import bftsmart.reconfiguration.ServerViewManager;
import bftsmart.reconfiguration.views.View;
import bftsmart.tom.core.DeliveryThread;
import bftsmart.tom.core.TOMLayer;
import bftsmart.tom.leaderchange.LCManager;
import bftsmart.tom.util.Logger;
import bftsmart.tom.util.TOMUtil;
/**
* TODO: Don't know if this class will be used. For now, leave it here
*
* Check if the changes for supporting dynamicity are correct
* @author Joao Sousa
*/
public class StateManager {
private HashSet<SenderEid> senderEids = null;
private HashSet<SenderState> senderStates = null;
private HashSet<SenderView> senderViews = null;
private HashSet<SenderRegency> senderRegencies = null;
private HashSet<SenderLeader> senderLeaders = null;
//private ReentrantLock lockState = new ReentrantLock();
private ReentrantLock lockTimer = new ReentrantLock();
private Timer stateTimer = null;
private int lastEid;
private int waitingEid;
private int replica;
private ApplicationState state;
private ServerViewManager SVManager;
private TOMLayer tomLayer;
private DeliveryThread dt;
private LCManager lcManager;
private ExecutionManager execManager;
private boolean appStateOnly;
private final static long INIT_TIMEOUT = 5000;
private long timeout = INIT_TIMEOUT;
public StateManager(ServerViewManager manager, TOMLayer tomLayer, DeliveryThread dt, LCManager lcManager, ExecutionManager execManager) {
//******* EDUARDO BEGIN **************//
this.SVManager = manager;
//******* EDUARDO END **************//
this.tomLayer = tomLayer;
this.dt = dt;
this.lcManager = lcManager;
this.execManager = execManager;
senderEids = new HashSet<SenderEid>();
senderStates = new HashSet<SenderState>();
senderViews = new HashSet<SenderView>();
senderRegencies = new HashSet<SenderRegency>();
senderLeaders = new HashSet<SenderLeader>();
this.replica = 0;
if (replica == manager.getStaticConf().getProcessId()) changeReplica();
this.state = null;
this.lastEid = -1;
this.waitingEid = -1;
appStateOnly = false;
}
public int getReplica() {
return replica;
}
public void changeReplica() {
//******* EDUARDO BEGIN **************//
int pos = -1;
do {
//TODO: Check if still correct
pos = this.SVManager.getCurrentViewPos(replica);
replica = this.SVManager.getCurrentViewProcesses()[(pos + 1) % SVManager.getCurrentViewN()];
//******* EDUARDO END **************//
} while (replica == SVManager.getStaticConf().getProcessId());
}
public void setReplicaState(ApplicationState state) {
this.state = state;
}
public ApplicationState getReplicaState() {
return state;
}
public void addEID(int sender, int eid) {
senderEids.add(new SenderEid(sender, eid));
}
public void emptyEIDs() {
senderEids.clear();
}
public void emptyEIDs(int eid) {
for (SenderEid m : senderEids)
if (m.eid <= eid) senderEids.remove(m);
}
public boolean moreThanF_EIDs(int eid) {
int count = 0;
HashSet<Integer> replicasCounted = new HashSet<Integer>();
for (SenderEid m : senderEids) {
if (m.eid == eid && !replicasCounted.contains(m.sender)) {
replicasCounted.add(m.sender);
count++;
}
}
//******* EDUARDO BEGIN **************//
return count > SVManager.getCurrentViewF();
//******* EDUARDO END **************//
}
public void addRegency(int sender, int regency) {
senderRegencies.add(new SenderRegency(sender, regency));
}
public void addLeader(int sender, int leader) {
senderLeaders.add(new SenderLeader(sender, leader));
}
public void addView(int sender, View view) {
senderViews.add(new SenderView(sender, view));
}
public void emptyRegencies() {
senderRegencies.clear();
}
public void emptyViews() {
senderViews.clear();
}
public void emptyLeaders() {
senderLeaders.clear();
}
public void emptyRegencies(int regency) {
for (SenderRegency m : senderRegencies)
if (m.regency <= regency) senderRegencies.remove(m);
}
public boolean moreThan2F_Regencies(int regency) {
int count = 0;
HashSet<Integer> replicasCounted = new HashSet<Integer>();
for (SenderRegency m : senderRegencies) {
if (m.regency == regency && !replicasCounted.contains(m.sender)) {
replicasCounted.add(m.sender);
count++;
}
}
//******* EDUARDO BEGIN **************//
return count > SVManager.getQuorum2F();
//******* EDUARDO END **************//
}
public boolean moreThan2F_Leaders(int leader) {
int count = 0;
HashSet<Integer> replicasCounted = new HashSet<Integer>();
for (SenderLeader m : senderLeaders) {
if (m.leader == leader && !replicasCounted.contains(m.sender)) {
replicasCounted.add(m.sender);
count++;
}
}
//******* EDUARDO BEGIN **************//
return count > SVManager.getQuorum2F();
//******* EDUARDO END **************//
}
public boolean moreThan2F_Views(View view) {
int count = 0;
HashSet<Integer> replicasCounted = new HashSet<Integer>();
for (SenderView m : senderViews) {
if (m.view.equals(view) && !replicasCounted.contains(m.sender)) {
replicasCounted.add(m.sender);
count++;
}
}
//******* EDUARDO BEGIN **************//
return count > SVManager.getQuorum2F();
//******* EDUARDO END **************//
}
public void addState(int sender, ApplicationState state) {
senderStates.add(new SenderState(sender, state));
}
public void emptyStates() {
senderStates.clear();
}
public int getWaiting() {
return waitingEid;
}
public void setWaiting(int wait) {
this.waitingEid = wait;
}
public void setLastEID(int eid) {
lastEid = eid;
}
public int getLastEID() {
return lastEid;
}
public boolean moreThanF_Replies() {
int count = 0;
HashSet<Integer> replicasCounted = new HashSet<Integer>();
for (SenderState m : senderStates) {
if (!replicasCounted.contains(m.sender)) {
replicasCounted.add(m.sender);
count++;
}
}
//******* EDUARDO BEGIN **************//
return count > SVManager.getCurrentViewF();
//******* EDUARDO END **************//
}
private ApplicationState getValidHash() {
SenderState[] st = new SenderState[senderStates.size()];
senderStates.toArray(st);
int count = 0;
for (int i = 0; i < st.length; i++, count = 0) {
for (int j = 0; j < st.length; j++) {
System.out.println("PID " + st[j].sender + " sent EID " + st[j].state.getLastEid());
//System.out.println(st[i].state.equals(st[j].state) + " && " + st[j].state.hasState());
if (st[i].state.equals(st[j].state) && st[j].state.hasState()) count++;
System.out.println("Count: " + count);
//******* EDUARDO BEGIN **************//
if (count > SVManager.getCurrentViewF()) return st[j].state;
//******* EDUARDO END **************//
}
}
return null;
}
public int getNumValidHashes() {
SenderState[] st = new SenderState[senderStates.size()];
senderStates.toArray(st);
int count = 0;
for (int i = 0; i < st.length; i++) {
for (int j = 0; j < st.length; j++) {
if (st[i].state.equals(st[j].state) && st[j].state.hasState()) count++;
}
}
return count;
}
public int getReplies() {
return senderStates.size();
}
public void requestAppState(int eid) {
setLastEID(eid + 1);
setWaiting(eid);
appStateOnly = true;
requestState();
}
public void analyzeState(int eid) {
Logger.println("(TOMLayer.analyzeState) The state transfer protocol is enabled");
if (getWaiting() == -1) {
Logger.println("(TOMLayer.analyzeState) I'm not waiting for any state, so I will keep record of this message");
//addEID(sender, eid);
if (tomLayer.execManager.isDecidable(eid)) {
Logger.println("(TOMLayer.analyzeState) I have now more than " + SVManager.getCurrentViewF() + " messages for EID " + eid + " which are beyond EID " + getLastEID());
setLastEID(eid);
setWaiting(eid - 1);
requestState();
}
}
/************************* TESTE *************************
System.out.println("[/TOMLayer.requestState]");
/************************* TESTE *************************/
}
private void requestState() {
if (tomLayer.requestsTimer != null) tomLayer.requestsTimer.clearAll();
//stateManager.emptyReplicas(eid);// this causes an exception
SMMessage smsg = new SMMessage(SVManager.getStaticConf().getProcessId(),
getWaiting(), TOMUtil.SM_REQUEST, getReplica(), null, null, -1, -1);
tomLayer.getCommunication().send(SVManager.getCurrentViewOtherAcceptors(), smsg);
Logger.println("(TOMLayer.requestState) I just sent a request to the other replicas for the state up to EID " + getWaiting());
TimerTask stateTask = new TimerTask() {
public void run() {
int[] myself = new int[1];
myself[0] = SVManager.getStaticConf().getProcessId();
tomLayer.getCommunication().send(myself, new SMMessage(-1, getWaiting(), TOMUtil.TRIGGER_SM_LOCALLY, -1, null, null, -1, -1));
}
};
stateTimer = new Timer("state timer");
timeout = timeout * 2;
stateTimer.schedule(stateTask,timeout);
}
public void stateTimeout() {
lockTimer.lock();
Logger.println("(StateManager.stateTimeout) Timeout for the replica that was supposed to send the complete state. Changing desired replica.");
System.out.println("Timeout no timer do estado!");
if (stateTimer != null) stateTimer.cancel();
//setWaiting(-1);
changeReplica();
emptyStates();
emptyEIDs();
emptyLeaders();
emptyRegencies();
emptyViews();
setReplicaState(null);
requestState();
lockTimer.unlock();
}
public void SMRequestDeliver(SMMessage msg) {
Logger.println("(TOMLayer.SMRequestDeliver) invoked method");
//******* EDUARDO BEGIN **************//
if (SVManager.getStaticConf().isStateTransferEnabled() && dt.getRecoverer() != null) {
//******* EDUARDO END **************//
Logger.println("(TOMLayer.SMRequestDeliver) The state transfer protocol is enabled");
//lockState.lock();
Logger.println("(TOMLayer.SMRequestDeliver) I received a state request for EID " + msg.getEid() + " from replica " + msg.getSender());
boolean sendState = msg.getReplica() == SVManager.getStaticConf().getProcessId();
if (sendState) Logger.println("(TOMLayer.SMRequestDeliver) I should be the one sending the state");
//TransferableState thisState = getLog().getTransferableState(msg.getEid(), sendState);
ApplicationState thisState = dt.getRecoverer().getState(msg.getEid(), sendState);
//lockState.unlock();
if (thisState == null) {
Logger.println("(TOMLayer.SMRequestDeliver) I don't have the state requested :-(");
thisState = dt.getRecoverer().getState(-1, sendState);
}
int[] targets = { msg.getSender() };
SMMessage smsg = new SMMessage(SVManager.getStaticConf().getProcessId(),
msg.getEid(), TOMUtil.SM_REPLY, -1, thisState, SVManager.getCurrentView(), lcManager.getLastReg(), tomLayer.lm.getCurrentLeader());
// malicious code, to force the replica not to send the state
//if (reconfManager.getStaticConf().getProcessId() != 0 || !sendState)
tomLayer.getCommunication().send(targets, smsg);
Logger.println("(TOMLayer.SMRequestDeliver) I sent the state until EID " + thisState.getLastEid());
}
}
public void SMReplyDeliver(SMMessage msg) {
//******* EDUARDO BEGIN **************//
lockTimer.lock();
if (SVManager.getStaticConf().isStateTransferEnabled()) {
//******* EDUARDO END **************//
Logger.println("(TOMLayer.SMReplyDeliver) The state transfer protocol is enabled");
Logger.println("(TOMLayer.SMReplyDeliver) I received a state reply for EID " + msg.getEid() + " from replica " + msg.getSender());
if (getWaiting() != -1 && msg.getEid() == getWaiting()) {
int currentRegency = -1;
int currentLeader = -1;
View currentView = null;
if (!appStateOnly) {
addRegency(msg.getSender(), msg.getRegency());
addLeader(msg.getSender(), msg.getLeader());
addView(msg.getSender(), msg.getView());
if (moreThan2F_Regencies(msg.getRegency())) currentRegency = msg.getRegency();
if (moreThan2F_Leaders(msg.getLeader())) currentLeader = msg.getLeader();
if (moreThan2F_Views(msg.getView())) {
currentView = msg.getView();
if (!currentView.isMember(SVManager.getStaticConf().getProcessId())) {
System.out.println("Not a member!");
}
}
} else {
currentLeader = tomLayer.lm.getCurrentLeader();
currentRegency = lcManager.getLastReg();
currentView = SVManager.getCurrentView();
}
Logger.println("(TOMLayer.SMReplyDeliver) The reply is for the EID that I want!");
if (msg.getSender() == getReplica() && msg.getState().getSerializedState() != null) {
Logger.println("(TOMLayer.SMReplyDeliver) I received the state, from the replica that I was expecting");
setReplicaState(msg.getState());
if (stateTimer != null) stateTimer.cancel();
}
addState(msg.getSender(),msg.getState());
if (moreThanF_Replies()) {
Logger.println("(TOMLayer.SMReplyDeliver) I have more than " + SVManager.getCurrentViewF() + " replies!");
Logger.println("[StateManager.getValidHash]");
ApplicationState recvState = getValidHash();
Logger.println("[/StateManager.getValidHash]");
int haveState = 0;
if (getReplicaState() != null) {
byte[] hash = null;
hash = tomLayer.computeHash(getReplicaState().getSerializedState());
if (recvState != null) {
if (Arrays.equals(hash, recvState.getStateHash())) haveState = 1;
else if (getNumValidHashes() > SVManager.getCurrentViewF()) haveState = -1;
}
}
if (recvState != null && haveState == 1 && currentRegency > -1 &&
currentLeader > -1 && currentView != null) {
Logger.println("(TOMLayer.SMReplyDeliver) The state of those replies is good!");
Logger.println("(TOMLayer.SMReplyDeliver) EID State requested: " + msg.getEid());
Logger.println("(TOMLayer.SMReplyDeliver) EID State received: " + recvState.getLastEid());
lcManager.setLastReg(currentRegency);
lcManager.setNextReg(currentRegency);
lcManager.setNewLeader(currentLeader);
tomLayer.lm.setNewLeader(currentLeader);
recvState.setSerializedState(getReplicaState().getSerializedState());
// ISTO E PRECISO METER NA APLICACAO!!!!!!
/*lockState.lock();
getLog().update(recvState);
lockState.unlock();*/
dt.deliverLock();
//ot.OutOfContextLock();
setWaiting(-1);
//Logger.debug = true;
dt.update(recvState);
//Deal with stopped messages that may come from synchronization phase
if (!appStateOnly && execManager.stopped()) {
Queue<PaxosMessage> stoppedMsgs = execManager.getStoppedMsgs();
for (PaxosMessage stopped : stoppedMsgs) {
if (stopped.getNumber() > recvState.getLastEid() /*msg.getEid()*/)
execManager.addOutOfContextMessage(stopped);
}
execManager.clearStopped();
execManager.restart();
}
Logger.println("Processing out of context messages");
tomLayer.processOutOfContext();
if (SVManager.getCurrentViewId() != currentView.getId()) {
System.out.println("Installing current view!");
SVManager.reconfigureTo(currentView);
}
dt.canDeliver();
//ot.OutOfContextUnlock();
dt.deliverUnlock();
emptyStates();
emptyEIDs();
emptyLeaders();
emptyRegencies();
emptyViews();
setReplicaState(null);
System.out.println("I updated the state!");
tomLayer.requestsTimer.Enabled(true);
tomLayer.requestsTimer.startTimer();
if (stateTimer != null) stateTimer.cancel();
if (appStateOnly) {
appStateOnly = false;
tomLayer.resumeLC();
}
//******* EDUARDO BEGIN **************//
} else if (recvState == null && (SVManager.getCurrentViewN() / 2) < getReplies()) {
//******* EDUARDO END **************//
Logger.println("(TOMLayer.SMReplyDeliver) I have more than " +
(SVManager.getCurrentViewN() / 2) + " messages that are no good!");
setWaiting(-1);
emptyStates();
emptyEIDs();
emptyLeaders();
emptyRegencies();
emptyViews();
setReplicaState(null);
//requestState();
if (stateTimer != null) stateTimer.cancel();
if (appStateOnly) {
requestState();
}
} else if (haveState == -1) {
Logger.println("(TOMLayer.SMReplyDeliver) The replica from which I expected the state, sent one which doesn't match the hash of the others, or it never sent it at all");
//setWaiting(-1);
changeReplica();
emptyStates();
emptyEIDs();
emptyLeaders();
emptyRegencies();
emptyViews();
setReplicaState(null);
requestState();
if (stateTimer != null) stateTimer.cancel();
}
}
}
}
lockTimer.unlock();
}
private class SenderRegency {
private int sender;
private int regency;
SenderRegency(int sender, int regency) {
this.sender = sender;
this.regency = regency;
}
@Override
public boolean equals(Object obj) {
if (obj instanceof SenderRegency) {
SenderRegency m = (SenderRegency) obj;
return (m.regency == this.regency && m.sender == this.sender);
}
return false;
}
@Override
public int hashCode() {
int hash = 1;
hash = hash * 31 + this.sender;
hash = hash * 31 + this.regency;
return hash;
}
}
private class SenderLeader {
private int sender;
private int leader;
SenderLeader(int sender, int leader) {
this.sender = sender;
this.leader = leader;
}
@Override
public boolean equals(Object obj) {
if (obj instanceof SenderLeader) {
SenderLeader m = (SenderLeader) obj;
return (m.leader == this.leader && m.sender == this.sender);
}
return false;
}
@Override
public int hashCode() {
int hash = 1;
hash = hash * 31 + this.sender;
hash = hash * 31 + this.leader;
return hash;
}
}
private class SenderEid {
private int sender;
private int eid;
SenderEid(int sender, int eid) {
this.sender = sender;
this.eid = eid;
}
@Override
public boolean equals(Object obj) {
if (obj instanceof SenderEid) {
SenderEid m = (SenderEid) obj;
return (m.eid == this.eid && m.sender == this.sender);
}
return false;
}
@Override
public int hashCode() {
int hash = 1;
hash = hash * 31 + this.sender;
hash = hash * 31 + this.eid;
return hash;
}
}
private class SenderState {
private int sender;
private ApplicationState state;
SenderState(int sender, ApplicationState state) {
this.sender = sender;
this.state = state;
}
@Override
public boolean equals(Object obj) {
if (obj instanceof SenderState) {
SenderState m = (SenderState) obj;
return (this.state.equals(m.state) && m.sender == this.sender);
}
return false;
}
@Override
public int hashCode() {
int hash = 1;
hash = hash * 31 + this.sender;
if (this.state != null) {
hash = hash * 31 + this.state.hashCode();
}
else hash = hash * 31 + 0;
return hash;
}
}
private class SenderView {
private int sender;
private View view;
SenderView(int sender, View view) {
this.sender = sender;
this.view = view;
}
@Override
public boolean equals(Object obj) {
if (obj instanceof SenderView) {
SenderView m = (SenderView) obj;
return (this.view.equals(m.view) && m.sender == this.sender);
}
return false;
}
@Override
public int hashCode() {
int hash = 1;
hash = hash * 31 + this.sender;
hash = hash * 31 + this.view.hashCode();
return hash;
}
}
}