void abortAll() throws IOException {
    Iterator myTxns = theTxns.keySet().iterator();

    while (myTxns.hasNext()) {
      TxnId myId = (TxnId) myTxns.next();

      TxnState myState = getState(myId);

      try {
        int myStatus = myState.getStatus();

        if ((myStatus == TransactionConstants.PREPARED)
            || (myStatus == TransactionConstants.ACTIVE)) {

          /*
           *  AbortAll is a naive operation in that it has no
           *  awareness of a specific transaction thus it cannot
           *  explicitly vote one of them off so we must do it
           *  ourselves
           */
          myState.vote();
          myState.abort();
          myTxns.remove();
        }

      } catch (TransactionException aTE) {
        // Whoops, got nailed checking status, logged in the call
        // nothing to do.
      }
    }
  }
  /** Do not call this method directly - it should only be invoked from a Prevayler command. */
  void abort(TxnId anId) throws UnknownTransactionException, IOException {

    TxnState myState = getTxnFor(anId, null, true);

    myState.abort();
    removeTxn(anId);
  }
  /** 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();
  }
  private void writeObject(ObjectOutputStream anOut) throws IOException {
    anOut.writeObject(LogVersion.VERSION);

    /*
      We only save PREPARED transactions, ignoring ACTIVES because
      they are transient and their state changes won't be applied
      until we've issued prepare and then commit or abort.  The ACTIVES
      will either die due to failure or, post the sync, add operations
      to the log.  Note that, whilst a transaction is active, it generates
      no log records at all hence the reason we don't need to save them.
      Commited or aborted updates in cache which need flushing to disk
      should have already been sync'd before we get this far.
    */
    ArrayList myPrepared = new ArrayList();

    // Write out clock
    //
    anOut.writeObject(theClock);

    Iterator myTxns = theTxns.keySet().iterator();

    while (myTxns.hasNext()) {
      TxnId myId = (TxnId) myTxns.next();

      TxnState myState = getState(myId);

      try {
        int myStatus = myState.getStatus();

        if (myStatus == TransactionConstants.PREPARED) {
          myPrepared.add(myState);
        }

      } catch (TransactionException aTE) {
        // Whoops, got nailed checking status, logged in the call
        // nothing to do.
      }
    }

    anOut.writeInt(myPrepared.size());

    for (int i = 0; i < myPrepared.size(); i++) {
      anOut.writeObject(myPrepared.get(i));
    }

    /*
     Write out any user-code snapshot contributions
    */
    ArrayList myContributions = new ArrayList();

    synchronized (theSnapshotContributors) {
      for (int i = 0; i < theSnapshotContributors.size(); i++) {
        myContributions.add(
            ((SnapshotContributor) theSnapshotContributors.get(i)).getContribution());
      }
    }

    Serializable[] myUserData = new Serializable[myContributions.size()];
    myUserData = (Serializable[]) myContributions.toArray(myUserData);

    anOut.writeObject(myUserData);
  }