/** Do not call this method directly - it should only be invoked from a Prevayler command. */
  int prepare(TxnState aState) throws UnknownTransactionException, IOException {

    /*
      Do we know about this transaction?

      If we don't we've failed and are now doing recovery so we must
      re-insert the state.
    */
    boolean needsRestore = (getState(aState.getId()) == null);

    if (needsRestore) {
      theTxns.put(aState.getId(), aState);
    }

    return aState.prepare(needsRestore);
  }
  private void readObject(ObjectInputStream anIn) throws IOException, ClassNotFoundException {

    boolean isUpgrade = false;

    theTxns = new ConcurrentHashMap();
    theSnapshotContributors = new ArrayList();

    Object myFirst = anIn.readObject();

    /*
     If there's no LogVersion, chances are we're looking at a pre 1.13
     log format - upgrade is simple as there's no LogVersion and there
     will be no user checkpoint data so we just ignore those fields.
    */
    if (!(myFirst instanceof LogVersion)) {
      TxnDispatcher.theLogger.log(Level.SEVERE, "Upgrading old transaction log");
      isUpgrade = true;
      theClock = (AlarmClock) myFirst;
    } else {
      LogVersion myVersion = (LogVersion) myFirst;

      if (!myVersion.equals(LogVersion.VERSION))
        throw new IOException("Yikes - log versions don't match - upgrade?" + myVersion);

      theClock = (AlarmClock) anIn.readObject();
    }

    int myNumRecords = anIn.readInt();

    for (int i = 0; i < myNumRecords; i++) {
      TxnState myState = (TxnState) anIn.readObject();

      try {
        myState.prepare(true);
      } catch (UnknownTransactionException aUTE) {
        IOException anIOE = new IOException("Failed to recover prepare");
        anIOE.initCause(aUTE);
        throw anIOE;
      }

      theTxns.put(myState.getId(), myState);
    }

    if (isUpgrade) theSnapshotContributions = new Serializable[0];
    else theSnapshotContributions = (Serializable[]) anIn.readObject();
  }