docs: protected void optimisticReplace(java.util.List managedObjectsToAdd,
                                       java.util.List managedObjectsToReplace,
                                       java.util.List managedObjectsToDelete,
                                       java.util.List tokensToNotify,
                                       Transaction transaction,
                                       long logSpaceReservedDelta)
                      throws ObjectManagerException {
          final String methodName = "optimisticReplace";
          if (Tracing.isAnyTracingEnabled() && trace.isEntryEnabled())
              trace.entry(this, cclass, methodName, new Object[] { managedObjectsToAdd,
                                                                  managedObjectsToReplace,
                                                                  managedObjectsToDelete,
                                                                  tokensToNotify,
                                                                  transaction,
                                                                  new Long(logSpaceReservedDelta) });
  
          // Keep a subset list of Tokens for persistent ManagedObjects to add.
          java.util.List persistentTokensToAdd = new java.util.ArrayList();
          // Keep a subset list of Tokens for persistent ManagedObjects to optimistically replace.
          java.util.List persistentTokensToReplace = new java.util.ArrayList();
          // Also update the list of the serializedBytes.
          java.util.List persistentSerializedBytes = new java.util.ArrayList();
          // A local Map of serializedBytes, so that we can add it to the set known to be logged,
          // once we have written the transactionOptimisticReplaceLogRecord.
          java.util.Map newLoggedSerializedBytes = new java.util.HashMap();
          // Keep a subset list of persistent tokens to delete.
          java.util.List persistentTokensToDelete = new java.util.ArrayList();
          // Keep a subset list of persistent tokens to notify.
          java.util.List persistentTokensToNotify = new java.util.ArrayList();
          long logSequenceNumber; // Sequence number of the log record written.
  
          // This try block catches any fatal error that causes us to fail to write the transaction log or reserve space
          // in the Objectstores or make the preAdd/Replace/OptimisticReplace/Delete calls to the ManagedObjects.
          try {
              if (managedObjectsToAdd != null) {
                  for (java.util.Iterator managedObjectIterator = managedObjectsToAdd.iterator(); managedObjectIterator.hasNext();) {
                      ManagedObject managedObject = (ManagedObject) managedObjectIterator.next();
  
                      // Make the ManagedObjects ready for an optimisticReplace,
                      // give it a chance to blow up in anything is wrong.
                      managedObject.preAdd(transaction);
  
                      // Build the subset of persistent objects.
                      if (managedObject.owningToken.getObjectStore().getPersistence()) {
                          persistentTokensToAdd.add(managedObject.owningToken);
                          // We have to allow for some other transaction to modify the object between the end of this transaction and
                          // when we actually write it to the ObjectStore.
                          // We address this by keeping a copy of the serialised immage of the object when we create the
                          // log record along with the logSequenceNumber when we write it.
                          // A higher level lock, such as synchronizing on LinkedList must protect against our capturing the wrong
                          // serialized version of the ManagedObject.
                          ObjectManagerByteArrayOutputStream serializedBytes = managedObject.getSerializedBytes();
                          persistentSerializedBytes.add(serializedBytes);
                          // Remember what we loggged in case we commit this version of the ManagedObject.
                          newLoggedSerializedBytes.put(managedObject.owningToken,
                                                       serializedBytes);
  
                      } else if (managedObject.owningToken.getObjectStore().getUsesSerializedForm()) {
                          // We need to capture the serialized form of the ManagedObject for the ObjectStore?
                          ObjectManagerByteArrayOutputStream serializedBytes = managedObject.getSerializedBytes();
                          newLoggedSerializedBytes.put(managedObject.owningToken,
                                                       serializedBytes);
                      } // if ... persistent.
                  } // for ... managedObjectsToAdd.
              } // if (managedObjectsToAdd != null).
  
              if (managedObjectsToReplace != null) {
                  for (java.util.Iterator managedObjectIterator = managedObjectsToReplace.iterator(); managedObjectIterator.hasNext();) {
                      ManagedObject managedObject = (ManagedObject) managedObjectIterator.next();
  
                      // Make the ManagedObjects ready for an optimisticReplace,
                      // give it a chance to blow up in anything is wrong.
                      managedObject.preOptimisticReplace(transaction);
  
                      // Build the subset of persistent objects.
                      if (managedObject.owningToken.getObjectStore().getPersistence()) {
                          persistentTokensToReplace.add(managedObject.owningToken);
                          // We have to allow for some other transaction to modify the object between the end of this transaction and
                          // when we actually write it to the ObjectStore.
                          // We address this by keeping a copy of the serialised immage of the object when we create the
                          // log record along with the logSequenceNumber when we write it.
                          // A higher level lock, such as synchronizing on LinkedList must protect against our capturing the wrong
                          // serialized version of the ManagedObject.
                          ObjectManagerByteArrayOutputStream serializedBytes = managedObject.getSerializedBytes();
                          persistentSerializedBytes.add(serializedBytes);
                          // Remember what we loggged in case we commit this version of the ManagedObject.
                          newLoggedSerializedBytes.put(managedObject.owningToken,
                                                       serializedBytes);
  
                      } else if (managedObject.owningToken.getObjectStore().getUsesSerializedForm()) {
                          // We need to capture the serialized form of the ManagedObject for the ObjectStore?
                          ObjectManagerByteArrayOutputStream serializedBytes = managedObject.getSerializedBytes();
                          newLoggedSerializedBytes.put(managedObject.owningToken,
                                                       serializedBytes);
                      } // if ... persistent.
                  } // for ... managedObjectsToReplace.
              } // if (managedObjectsToReplace != null).
  
              if (managedObjectsToDelete != null) {
                  for (java.util.Iterator managedObjectIterator = managedObjectsToDelete.iterator(); managedObjectIterator.hasNext();) {
                      ManagedObject managedObject = (ManagedObject) managedObjectIterator.next();
  
                      // Make the ManagedObject ready for an deletion,
                      // give it a chance to blow up in anything is wrong.
                      managedObject.preDelete(transaction);
                      // Build the subset of persistent objects.
                      if (managedObject.owningToken.getObjectStore().getPersistence()) {
                          persistentTokensToDelete.add(managedObject.owningToken);
                      } // if ... persistent.
                  } // for ... managedObjectsToDelete.
              } // if (managedObjectsToDelete != null).
  
              if (tokensToNotify != null) {
                  for (java.util.Iterator tokenIterator = tokensToNotify.iterator(); tokenIterator.hasNext();) {
                      Token token = (Token) tokenIterator.next();
                      // Build the subset of persistent objects.
                      if (token.getObjectStore().getPersistence()) {
                          persistentTokensToNotify.add(token);
                      } // if (token.getObjectStore().persistent).
                  } // for ... tokenToNotify.
              } // if (tokensToNotify != null).
          } catch (ObjectManagerException exception) {
              // The write was not done.
              // No FFDC Code Needed.
              ObjectManager.ffdc.processException(this, cclass, methodName, exception, "1:1305:1.41");
  
              postOptmisticReplace(managedObjectsToAdd, managedObjectsToReplace, managedObjectsToDelete, transaction);
  
              if (Tracing.isAnyTracingEnabled() && trace.isEntryEnabled())
                  trace.exit(this, cclass, methodName, new Object[] { exception });
              throw exception;
          } // catch (ObjectManagerException... 
  
          synchronized (this) {
              // To defend against two application threads completing the same transaction and trying to
              // continue with it at the same time we check that the Transaction still refers to this one,
              // now that we are synchronized on the InternalTransaction.
              if (transaction.internalTransaction != this) {
                  postOptmisticReplace(managedObjectsToAdd, managedObjectsToReplace, managedObjectsToDelete, transaction);
  
                  if (Tracing.isAnyTracingEnabled() && trace.isEntryEnabled())
                      trace.exit(this, cclass,
                                 methodName,
                                 new Object[] { "via InvalidTransactionException", transaction.internalTransaction }
                                      );
                  // Same behaviour as if the transaction was completed and replaced by
                  // objectManagerState.dummyInternalTransaction.
                  throw new InvalidStateException(this,
                                                  InternalTransaction.stateTerminated,
                                                  InternalTransaction.stateNames[InternalTransaction.stateTerminated]);
  
              } // if (transaction.internalTransaction != this).
              try {
                  // Is data logging required for this object in order to recover it?
                  if (!persistentTokensToAdd.isEmpty()
                      || !persistentTokensToReplace.isEmpty()
                      || !persistentTokensToDelete.isEmpty()
                      || !persistentTokensToNotify.isEmpty()) {
                      testState(nextStateForInvolveOptimisticPersistentObject);
  
                      // Over estimate of reserved space. If we replace a ManagedObject previously involved in this
                      // Transaction we will have already reserved some space.
                      logSpaceReservedDelta = logSpaceReservedDelta
                                              + (+persistentTokensToAdd.size() + persistentTokensToReplace.size() + persistentTokensToDelete.size() + persistentTokensToNotify.size())
                                              * Token.maximumSerializedSize();
                      if (logSpaceReserved == 0)
                          logSpaceReservedDelta = logSpaceReservedDelta + logSpaceReservedOverhead;
                      TransactionOptimisticReplaceLogRecord transactionOptimisticReplaceLogRecord = new TransactionOptimisticReplaceLogRecord(this,
                                                                                                                                              persistentTokensToAdd,
                                                                                                                                              persistentTokensToReplace,
                                                                                                                                              persistentSerializedBytes,
                                                                                                                                              persistentTokensToDelete,
                                                                                                                                              persistentTokensToNotify);
                      // If we throw an exception in here no state change has been done.
                      logSequenceNumber = objectManagerState.logOutput.writeNext(transactionOptimisticReplaceLogRecord,
                                                                                 logSpaceReservedDelta, true, false);
  
                      // The previous testState() call means we should not fail in here.
                      setState(nextStateForInvolveOptimisticPersistentObject); // Make the state change.
  
                      logSpaceReserved = logSpaceReserved + logSpaceReservedDelta;
  
                  } else { // No persistent tokens mentioned then.
                      testState(nextStateForInvolveOptimisticNonPersistentObject);
                      setState(nextStateForInvolveOptimisticNonPersistentObject);
                      logSequenceNumber = objectManagerState.getDummyLogSequenceNumber();
                  } // else non Persistent.
  
              } catch (ObjectManagerException exception) {
                  // The write was not done.
                  // No FFDC Code Needed.
                  ObjectManager.ffdc.processException(this, cclass, "optimisticReplace", exception, "1:1371:1.41");
  
                  postOptmisticReplace(managedObjectsToAdd, managedObjectsToReplace, managedObjectsToDelete, transaction);
  
                  if (Tracing.isAnyTracingEnabled() && trace.isEntryEnabled())
                      trace.exit(this, cclass, methodName, new Object[] { exception });
                  throw exception;
              } // catch (ObjectManagerException... 
  
              // Any logging must be done before the following operations on the ManagedObjects as we will
              // redrive these after recovery from the log on a restart.
  
              // We have now sucessfully logged the serialized bytes.
              loggedSerializedBytes.putAll(newLoggedSerializedBytes);
  
              // Drive the postAdd method for the ManagedObjects.
              if (managedObjectsToAdd != null) {
                  for (java.util.Iterator managedObjectIterator = managedObjectsToAdd.iterator(); managedObjectIterator.hasNext();) {
                      ManagedObject managedObject = (ManagedObject) managedObjectIterator.next();
                      // The ManagedObject is now included in the transaction.
                      includedManagedObjects.put(managedObject.owningToken,
                                                 managedObject);
                      // Remember which logSequenceNumber when we last logged it.
                      logSequenceNumbers.put(managedObject.owningToken,
                                             new Long(logSequenceNumber));
                      managedObjectSequenceNumbers.put(managedObject.owningToken,
                                                       new Long(managedObject.getUpdateSequence()));
                      managedObject.postAdd(transaction,
                                            true);
                  } // for ... managedObjectsToAdd.
              } // if (managedObjectsToAdd != null).
  
              if (managedObjectsToReplace != null) {
                  for (java.util.Iterator managedObjectIterator = managedObjectsToReplace.iterator(); managedObjectIterator.hasNext();) {
                      ManagedObject managedObject = (ManagedObject) managedObjectIterator.next();
                      // The ManagedObject is now included in the transaction.
                      includedManagedObjects.put(managedObject.owningToken,
                                                 managedObject);
                      // Remember which logSequenceNumber when we last logged it.
                      logSequenceNumbers.put(managedObject.owningToken,
                                             new Long(logSequenceNumber));
                      managedObjectSequenceNumbers.put(managedObject.owningToken,
                                                       new Long(managedObject.getUpdateSequence()));
                      // See comment in ManagedObject.optimisticReplaceCommit().
                      // In principle the managed object here could
                      // also have been deleted by a subsequent transaction. But so far I have not seen this happen.
                      managedObject.postOptimisticReplace(transaction,
                                                          true);
                  } // for ... managedObjectsToReplace.
              } // if (managedObjectsToReplace != null).
  
              // Drive the postDelete method for the ManagedObjects.
              if (managedObjectsToDelete != null) {
                  for (java.util.Iterator managedObjectIterator = managedObjectsToDelete.iterator(); managedObjectIterator.hasNext();) {
                      ManagedObject managedObject = (ManagedObject) managedObjectIterator.next();
                      // The ManagedObject is now included in the transaction.
                      includedManagedObjects.put(managedObject.owningToken,
                                                 managedObject);
                      // Remember which logSequenceNumber when we last logged it.
                      logSequenceNumbers.put(managedObject.owningToken,
                                             new Long(logSequenceNumber));
                      managedObjectSequenceNumbers.put(managedObject.owningToken,
                                                       new Long(managedObject.getUpdateSequence()));
                      managedObject.postDelete(transaction,
                                               true);
                  } // for ... managedObjectsToDelete.
              } // if (managedObjectsToDelete != null).
  
              // Drive the optimisticReplaceLogged method for any ManagedObjects to Notify.
              if (tokensToNotify != null) {
                  for (java.util.Iterator tokenIterator = tokensToNotify.iterator(); tokenIterator.hasNext();) {
                      Token token = (Token) tokenIterator.next();
                      if (token.getObjectStore()
                                      .getPersistence())
                          allPersistentTokensToNotify.add(token);
                      token.getManagedObject()
                                      .optimisticReplaceLogged(transaction);
                  } // for ... tokensToNotify.
              } // if (tokensToReplace != null).
          } // synchronized (this).
  
          if (Tracing.isAnyTracingEnabled() && trace.isEntryEnabled())
              trace.exit(this, cclass, methodName);
      }