package bftsmart.tom.server.defaultservices; /** * 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/>. */ import java.util.Arrays; import bftsmart.statemanagment.ApplicationState; /** * This classe represents a state tranfered from a replica to another. The state associated with the last * checkpoint together with all the batches of messages received do far, comprises the sender's * current state * * @author Jo�o Sousa */ public class DefaultApplicationState implements ApplicationState { private static final long serialVersionUID = 6771081456095596363L; protected byte[] state; // State associated with the last checkpoint protected byte[] stateHash; // Hash of the state associated with the last checkpoint protected int lastEid = -1; // Execution ID for the last messages batch delivered to the application protected boolean hasState; // indicates if the replica really had the requested state private CommandsInfo[] messageBatches; // batches received since the last checkpoint. private int lastCheckpointEid; // Execution ID for the last checkpoint private int lastCheckpointRound; // Round for the last checkpoint private int lastCheckpointLeader; // Leader for the last checkpoint /** * Constructs a TansferableState * This constructor should be used when there is a valid state to construct the object with * @param messageBatches Batches received since the last checkpoint. * @param state State associated with the last checkpoint * @param stateHash Hash of the state associated with the last checkpoint */ public DefaultApplicationState(CommandsInfo[] messageBatches, int lastCheckpointEid, int lastCheckpointRound, int lastCheckpointLeader, int lastEid, byte[] state, byte[] stateHash) { this.messageBatches = messageBatches; // batches received since the last checkpoint. this.lastCheckpointEid = lastCheckpointEid; // Execution ID for the last checkpoint this.lastCheckpointRound = lastCheckpointRound; // Round for the last checkpoint this.lastCheckpointLeader = lastCheckpointLeader; // Leader for the last checkpoint this.lastEid = lastEid; // Execution ID for the last messages batch delivered to the application this.state = state; // State associated with the last checkpoint this.stateHash = stateHash; this.hasState = true; } /** * Constructs a TansferableState * This constructor should be used when there isn't a valid state to construct the object with */ public DefaultApplicationState() { this.messageBatches = null; // batches received since the last checkpoint. this.lastCheckpointEid = -1; // Execution ID for the last checkpoint this.lastCheckpointRound = -1; // Round for the last checkpoint this.lastCheckpointLeader = -1; // Leader for the last checkpoint this.lastEid = -1; this.state = null; // State associated with the last checkpoint this.stateHash = null; this.hasState = false; } public void setSerializedState(byte[] state) { this.state = state; } public byte[] getSerializedState() { return state; } /** * Indicates if the TransferableState object has a valid state * @return true if it has a valid state, false otherwise */ public boolean hasState() { return hasState; } /** * Retrieves the execution ID for the last messages batch delivered to the application * @return Execution ID for the last messages batch delivered to the application */ public int getLastEid() { return lastEid; } /** * Retrieves the state associated with the last checkpoint * @return State associated with the last checkpoint */ public byte[] getState() { return state; } /** * Retrieves the hash of the state associated with the last checkpoint * @return Hash of the state associated with the last checkpoint */ public byte[] getStateHash() { return stateHash; } /** * Sets the state associated with the last checkpoint * @param state State associated with the last checkpoint */ public void setState(byte[] state) { this.state = state; } /** * Retrieves all batches of messages * @return Batch of messages */ public CommandsInfo[] getMessageBatches() { return messageBatches; } /** * Retrieves the specified batch of messages * @param eid Execution ID associated with the batch to be fetched * @return The batch of messages associated with the batch correspondent execution ID */ public CommandsInfo getMessageBatch(int eid) { if (eid >= lastCheckpointEid && eid <= lastEid) { return messageBatches[eid - lastCheckpointEid - 1]; } else return null; } /** * Retrieves the execution ID for the last checkpoint * @return Execution ID for the last checkpoint, or -1 if no checkpoint was yet executed */ public int getLastCheckpointEid() { return lastCheckpointEid; } /** * Retrieves the decision round for the last checkpoint * @return Decision round for the last checkpoint, or -1 if no checkpoint was yet executed */ public int getLastCheckpointRound() { return lastCheckpointRound; } /** * Retrieves the leader for the last checkpoint * @return Leader for the last checkpoint, or -1 if no checkpoint was yet executed */ public int getLastCheckpointLeader() { return lastCheckpointLeader; } @Override public boolean equals(Object obj) { if (obj instanceof DefaultApplicationState) { DefaultApplicationState tState = (DefaultApplicationState) obj; if ((this.messageBatches != null && tState.messageBatches == null) || (this.messageBatches == null && tState.messageBatches != null)) { //System.out.println("[DefaultApplicationState] returing FALSE1!"); return false; } if (this.messageBatches != null && tState.messageBatches != null) { if (this.messageBatches.length != tState.messageBatches.length) { //System.out.println("[DefaultApplicationState] returing FALSE2!"); return false; } for (int i = 0; i < this.messageBatches.length; i++) { if (this.messageBatches[i] == null && tState.messageBatches[i] != null) { //System.out.println("[DefaultApplicationState] returing FALSE3!"); return false; } if (this.messageBatches[i] != null && tState.messageBatches[i] == null) { //System.out.println("[DefaultApplicationState] returing FALSE4!"); return false; } if (!(this.messageBatches[i] == null && tState.messageBatches[i] == null) && (!this.messageBatches[i].equals(tState.messageBatches[i]))) { //System.out.println("[DefaultApplicationState] returing FALSE5!" + (this.messageBatches[i] == null) + " " + (tState.messageBatches[i] == null)); return false; } } } return (Arrays.equals(this.stateHash, tState.stateHash) && tState.lastCheckpointEid == this.lastCheckpointEid && tState.lastCheckpointRound == this.lastCheckpointRound && tState.lastCheckpointLeader == this.lastCheckpointLeader && tState.lastEid == this.lastEid && tState.hasState == this.hasState); } //System.out.println("[DefaultApplicationState] returing FALSE!"); return false; } @Override public int hashCode() { int hash = 1; hash = hash * 31 + this.lastCheckpointEid; hash = hash * 31 + this.lastCheckpointRound; hash = hash * 31 + this.lastCheckpointLeader; hash = hash * 31 + this.lastEid; hash = hash * 31 + (this.hasState ? 1 : 0); if (this.stateHash != null) { for (int i = 0; i < this.stateHash.length; i++) hash = hash * 31 + (int) this.stateHash[i]; } else { hash = hash * 31 + 0; } if (this.messageBatches != null) { for (int i = 0; i < this.messageBatches.length; i++) { if (this.messageBatches[i] != null) { hash = hash * 31 + this.messageBatches[i].hashCode(); } else { hash = hash * 31 + 0; } } } else { hash = hash * 31 + 0; } return hash; } }