diff --git a/src/main/java/group25/Server/Common/AbstractRMHashMapManager.java b/src/main/java/group25/Server/Common/AbstractRMHashMapManager.java index 76059a3..ae3d03a 100644 --- a/src/main/java/group25/Server/Common/AbstractRMHashMapManager.java +++ b/src/main/java/group25/Server/Common/AbstractRMHashMapManager.java @@ -15,7 +15,7 @@ public abstract class AbstractRMHashMapManager { private String m_name = ""; - protected RMHashMap globalState; + protected RMHashMap globalState = new RMHashMap(); protected HashMap transactionStates = new HashMap<>(); protected XMLPersistor xmlPersistor = new XMLPersistor(); protected final String filename1, filename2, pointerFile, logFile; @@ -25,7 +25,6 @@ public abstract class AbstractRMHashMapManager { private CrashMode crashMode = CrashMode.NO_CRASH; - // TODO: give lock to correct transaction on wakeup from failure public AbstractRMHashMapManager(String p_name, String filename1, String filename2, String pointerFile, String logFile, IMiddlewareResourceManager middlewareRM) { this.m_name = p_name; @@ -34,14 +33,38 @@ public AbstractRMHashMapManager(String p_name, String filename1, String filename this.pointerFile = pointerFile; this.logFile = logFile; this.middlewareRM = middlewareRM; - + this.currentCommitFile = filename2; + } + + public void recover() { currentCommitFile = xmlPersistor.readObject(pointerFile); if (currentCommitFile == null) { - currentCommitFile = filename1; + currentCommitFile = filename2; // first commit should be to other file, filename1 globalState = new RMHashMap(); } else { globalState = xmlPersistor.readObject(currentCommitFile); } + + Integer transactionVotedYes = xmlPersistor.readObject(logFile); + if (transactionVotedYes != null && transactionVotedYes != -1) { + int xid = transactionVotedYes; + String workingFile = currentCommitFile.equals(filename1)? filename2: filename1; + transactionStates.put(xid, xmlPersistor.readObject(workingFile)); + globalLock.lock(xid); + try { + if (middlewareRM.commited(xid)) { + System.out.println("middleware says to commit recovered transaction so committing"); + doCommit(xid); + } else { + System.out.println("middleware doesn't know about transaction so aborting"); + abort(xid); + } + } catch (Exception e) { + System.out.println("Failed to complete recovery on doCommit/abort "+xid); + } + } else { + System.out.println("did not record voting yes"); + } } public void crashResourceManager(CrashMode cm) { @@ -63,6 +86,7 @@ private void crashIf(CrashMode cm) { public void vote(int xid) throws RemoteException { System.out.println(getName() + " got vote request."); + xmlPersistor.writeObject(-1, logFile); // do this all in a new thread since we want vote() to return immediately in the TM new Thread(() -> { @@ -99,6 +123,7 @@ public void vote(int xid) throws RemoteException { updateThenPersistGlobalState(xid); // TODO log YES + xmlPersistor.writeObject(xid, logFile); crashIf(CrashMode.RM_AFTER_DECIDING_VOTE); // uncertainty phase. Send Yes, and wait for decision @@ -107,6 +132,7 @@ public void vote(int xid) throws RemoteException { System.out.println(GREEN.colorString("Voting yes.")); try { middlewareRM.receiveVote(xid, true, this.m_name); + xmlPersistor.writeObject(xid, logFile); crashIf(CrashMode.RM_AFTER_VOTING); return; } catch (InvalidTransactionException ite) { @@ -121,6 +147,7 @@ public void vote(int xid) throws RemoteException { try { System.out.println(GREEN.colorString("Try again to vote yes.")); middlewareRM.receiveVote(xid, true, this.m_name); + xmlPersistor.writeObject(xid, logFile); crashIf(CrashMode.RM_AFTER_VOTING); return; } catch (InvalidTransactionException ite) { @@ -154,6 +181,8 @@ public boolean doCommit(int xid) throws RemoteException { currentCommitFile = filename1; } + xmlPersistor.writeObject(-1, logFile); + // destroy transaction-specific state removeTransactionState(xid); @@ -166,6 +195,7 @@ public boolean doCommit(int xid) throws RemoteException { public boolean abort(int xid) throws RemoteException { new Thread(() -> { + xmlPersistor.writeObject(-1, logFile); System.out.println(m_name + " aborting"); crashIf(CrashMode.RM_AFTER_RECEIVING_DECISION); if (globalLock.getLockOwner() == xid) { @@ -229,7 +259,10 @@ public void updateThenPersistGlobalState(int xid) { } } - public RMItem readData(int xid, String key) { + public RMItem readData(int xid, String key) throws UnsupportedOperationException { + if (globalLock.getLockOwner() == xid) + throw new UnsupportedOperationException(); + RMHashMap m_data = getTransactionState(xid); if (m_data == null) { synchronized(globalState) { @@ -249,7 +282,10 @@ public RMItem readData(int xid, String key) { } // Writes a data item - public void writeData(int xid, String key, RMItem value) { + public void writeData(int xid, String key, RMItem value) throws UnsupportedOperationException { + if (globalLock.getLockOwner() == xid) + throw new UnsupportedOperationException(); + RMHashMap m_data = getTransactionState(xid); if (m_data == null) { synchronized(globalState) { @@ -272,7 +308,10 @@ public void removeData(int xid, String key) { } } - public boolean deleteItem(int xid, String key) { + public boolean deleteItem(int xid, String key) throws UnsupportedOperationException { + if (globalLock.getLockOwner() == xid) + throw new UnsupportedOperationException(); + Trace.info("RM::deleteItem(" + xid + ", " + key + ") called"); ReservableItem curObj = (ReservableItem) readData(xid, key); // Check if there is such an item in the storage @@ -292,7 +331,7 @@ public boolean deleteItem(int xid, String key) { } // Query the number of available seats/rooms/cars - public int queryNum(int xid, String key) { + public int queryNum(int xid, String key) throws UnsupportedOperationException { Trace.info("RM::queryNum(" + xid + ", " + key + ") called"); ReservableItem curObj = (ReservableItem) readData(xid, key); int value = 0; @@ -304,7 +343,7 @@ public int queryNum(int xid, String key) { } // Query the price of an item - public int queryPrice(int xid, String key) { + public int queryPrice(int xid, String key) throws UnsupportedOperationException { Trace.info("RM::queryPrice(" + xid + ", " + key + ") called"); ReservableItem curObj = (ReservableItem) readData(xid, key); int value = 0; diff --git a/src/main/java/group25/Server/Common/MiddlewareResourceManager.java b/src/main/java/group25/Server/Common/MiddlewareResourceManager.java index 69bd8d6..18dfd28 100644 --- a/src/main/java/group25/Server/Common/MiddlewareResourceManager.java +++ b/src/main/java/group25/Server/Common/MiddlewareResourceManager.java @@ -188,6 +188,10 @@ public boolean bundle(int xid, int customerID, Vector flightNumbers, St return true; } + public boolean commited(int xid) { + return transactionManager.commited(xid); + } + public void receiveVote(int xid, boolean voteYes, String rmName) throws InvalidTransactionException, RemoteException { transactionManager.receiveVote(xid, voteYes, rmName); } diff --git a/src/main/java/group25/Server/Common/TransactionManager.java b/src/main/java/group25/Server/Common/TransactionManager.java index b3cf715..c79f8eb 100644 --- a/src/main/java/group25/Server/Common/TransactionManager.java +++ b/src/main/java/group25/Server/Common/TransactionManager.java @@ -36,6 +36,7 @@ public class TransactionManager implements Remote private CrashMode crashMode = CrashMode.NO_CRASH; private HashMap> resourceManagerRecorder = new HashMap<>(); + private ArrayList commitedTransactions = new ArrayList<>(); private HashMap transactionAges = new HashMap<>(); @@ -54,6 +55,10 @@ public TransactionManager( setUpTimeToLiveThread(); } + public boolean commited(int xid) { + return commitedTransactions.contains(xid); + } + public void reconnect(String rmName, String hostname, int port, String objName) throws RemoteException { switch (rmName) { case "car": { @@ -197,7 +202,7 @@ public synchronized boolean commit(int xid) throws InvalidTransactionException, } try { sem.acquire(); // wait to be awoken by receiveVote() - boolean receivedAllVotes = sem.tryAcquire(25, TimeUnit.SECONDS); // all participants must respond within 25 seconds + boolean receivedAllVotes = sem.tryAcquire(30, TimeUnit.SECONDS); // all participants must respond within 25 seconds if (!receivedAllVotes) { System.out.println("Timed out waiting for votes. Aborting"); abort(xid, false); @@ -284,6 +289,8 @@ public void receiveVote(int xid, boolean voteYes, String rmName) throws InvalidT crashIf(CrashMode.TM_BEFORE_SENDING_LAST_DECISION); } + commitedTransactions.add(xid); + Name_RM_Vote name_rm_vote = resourceManagers.get(i); try { name_rm_vote.rm.doCommit(xid); @@ -292,8 +299,9 @@ public void receiveVote(int xid, boolean voteYes, String rmName) throws InvalidT new Thread(() -> { long startTime = System.currentTimeMillis(); boolean commited = false; - while (System.currentTimeMillis() - startTime < 10 * 1000) { + while (System.currentTimeMillis() - startTime < 30 * 1000) { try { + name_rm_vote.rm = rmFromRMName(name_rm_vote.rmName); name_rm_vote.rm.doCommit(xid); commited = true; break; @@ -364,7 +372,7 @@ public boolean abort(int xid, boolean fromCommit) throws InvalidTransactionExcep return abort(xid); } else { synchronized(transactionAges) { - if (transactionAges.remove(xid) == null) throw new InvalidTransactionException(); + if (transactionAges.get(xid) == null) throw new InvalidTransactionException(); } return abort(xid); } @@ -825,35 +833,23 @@ public boolean shutdownAllResourceManagers(){ } return true; } -} - -class BeforeImage { - IAbstractRMHashMapManager rm; - String dataKey; - RMItem rItem; - BeforeImage(IAbstractRMHashMapManager rm, String dataKey, RMItem rItem) { - this.rm = rm; - this.dataKey = dataKey; - this.rItem = rItem; - } - - void restore(int xid) throws RemoteException { - if (rItem == null) { - rm.removeData(xid, dataKey); - } else { - rm.writeData(xid, dataKey, rItem); - } - } - - @Override - public boolean equals(Object other) { - if (!(other instanceof BeforeImage)) { - return false; + private IAbstractRMHashMapManager rmFromRMName(String rmName) { + switch (rmName) { + case "CarSever": { + return carRM; + } + case "RoomServer": { + return roomRM; + } + case "FlightServer": { + return flightRM; + } + case "CustomerServer": { + return customerRM; + } } - BeforeImage bOther = (BeforeImage) other; - boolean isEqual = bOther.dataKey.equals(this.dataKey); - return isEqual; + return null; } } diff --git a/src/main/java/group25/Server/Interface/IMiddlewareResourceManager.java b/src/main/java/group25/Server/Interface/IMiddlewareResourceManager.java index f01abdb..9aff368 100644 --- a/src/main/java/group25/Server/Interface/IMiddlewareResourceManager.java +++ b/src/main/java/group25/Server/Interface/IMiddlewareResourceManager.java @@ -235,4 +235,7 @@ public void crashResourceManager(String rmName, int mode) public void reconnect(String rmName, String hostname, int port, String objName) throws RemoteException; + + public boolean commited(int xid) + throws RemoteException; } diff --git a/src/main/java/group25/Server/RMI/RMICarResourceManager.java b/src/main/java/group25/Server/RMI/RMICarResourceManager.java index e36a49c..51fbc77 100644 --- a/src/main/java/group25/Server/RMI/RMICarResourceManager.java +++ b/src/main/java/group25/Server/RMI/RMICarResourceManager.java @@ -37,7 +37,7 @@ public class RMICarResourceManager extends CarResourceManager { private static boolean should_recover = false; public RMICarResourceManager(String name, IMiddlewareResourceManager midRM) { - super(name, "carData1.xml", "carData2.xml", "carMasterRecord.xml", "carLogFile.txt", midRM); + super(name, "carData1.xml", "carData2.xml", "carMasterRecord.xml", "carLogFile.xml", midRM); } public static void main(String args[]) { @@ -93,9 +93,12 @@ public static void main(String args[]) { System.out.println("'" + s_serverName + "' resource manager server ready and bound to '" + s_serverName + "'"); - // TODO: recover state + // recover state if (should_recover) { + server.recover(); System.out.println(BLUE.colorString("recovering state from files")); + } else { + // delete files? } // for recovery, force middleware to reconnect to carRM diff --git a/src/main/java/group25/Server/RMI/RMICustomerResourceManager.java b/src/main/java/group25/Server/RMI/RMICustomerResourceManager.java index 6cb3f25..4d8270d 100644 --- a/src/main/java/group25/Server/RMI/RMICustomerResourceManager.java +++ b/src/main/java/group25/Server/RMI/RMICustomerResourceManager.java @@ -32,7 +32,7 @@ public class RMICustomerResourceManager extends CustomerResourceManager { private static boolean should_recover = false; public RMICustomerResourceManager(String name, IMiddlewareResourceManager midRM) { - super(name, "customerData1.xml", "customerData2.xml", "customerMasterRecord.xml", "customerLogFile.txt", midRM); + super(name, "customerData1.xml", "customerData2.xml", "customerMasterRecord.xml", "customerLogFile.xml", midRM); } public static void main(String args[]) { @@ -84,9 +84,11 @@ public static void main(String args[]) { System.setSecurityManager(new SecurityManager()); } - // TODO: recover state if (should_recover) { + server.recover(); System.out.println(BLUE.colorString("recovering state from files")); + } else { + // delete files? } // for recovery, force middleware to reconnect to carRM diff --git a/src/main/java/group25/Server/RMI/RMIFlightResourceManager.java b/src/main/java/group25/Server/RMI/RMIFlightResourceManager.java index 64ae504..29b7bf1 100644 --- a/src/main/java/group25/Server/RMI/RMIFlightResourceManager.java +++ b/src/main/java/group25/Server/RMI/RMIFlightResourceManager.java @@ -29,7 +29,7 @@ public class RMIFlightResourceManager extends FlightResourceManager { private static boolean should_recover = false; public RMIFlightResourceManager(String name, IMiddlewareResourceManager midRM) { - super(name, "flightData1.xml", "flightData2.xml", "flightMasterRecord.xml", "flightLogFile.txt", midRM); + super(name, "flightData1.xml", "flightData2.xml", "flightMasterRecord.xml", "flightLogFile.xml", midRM); } public static void main(String args[]) { @@ -88,9 +88,11 @@ public static void main(String args[]) { System.setSecurityManager(new SecurityManager()); } - // TODO: recover state if (should_recover) { + server.recover(); System.out.println(BLUE.colorString("recovering global state from files")); + } else { + // delete files? } // for recovery, force middleware to reconnect to carRM diff --git a/src/main/java/group25/Server/RMI/RMIRoomResourceManager.java b/src/main/java/group25/Server/RMI/RMIRoomResourceManager.java index 97fc1ca..5035567 100644 --- a/src/main/java/group25/Server/RMI/RMIRoomResourceManager.java +++ b/src/main/java/group25/Server/RMI/RMIRoomResourceManager.java @@ -31,7 +31,7 @@ public class RMIRoomResourceManager extends RoomResourceManager { private static boolean should_recover = false; public RMIRoomResourceManager(String name, IMiddlewareResourceManager midRM) { - super(name, "roomData1.xml", "roomData2.xml", "roomMasterRecord.xml", "roomLogFile.txt", midRM); + super(name, "roomData1.xml", "roomData2.xml", "roomMasterRecord.xml", "roomLogFile.xml", midRM); } public static void main(String args[]) { @@ -85,9 +85,11 @@ public static void main(String args[]) { System.out.println("'"+s_serverName+"' resource manager server ready and bound to '"+s_serverName+"'"); - // TODO: recover state if (should_recover) { + server.recover(); System.out.println(BLUE.colorString("recovering state from files")); + } else { + // delete files? } // for recovery, force middleware to reconnect to carRM diff --git a/src/main/java/group25/Utils/XMLPersistor.java b/src/main/java/group25/Utils/XMLPersistor.java index c66dd9d..cf888ce 100644 --- a/src/main/java/group25/Utils/XMLPersistor.java +++ b/src/main/java/group25/Utils/XMLPersistor.java @@ -80,7 +80,13 @@ public T readObject(String filepath) { return null; // file not found } - return (T)this.xstream.fromXML(str); + T retval = null; + try { + retval = (T) this.xstream.fromXML(str); + } catch (Exception e) { + } + + return retval; } public ObjectOutputStream getWriteAppendStream(String filepath) {