docs: public void performRecovery(ObjectManagerState objectManagerState)
                      throws ObjectManagerException {
          if (Tracing.isAnyTracingEnabled() && trace.isEntryEnabled())
              trace.entry(this,
                          cclass,
                          "performRecovery",
                          new Object[] { objectManagerState, logicalUnitOfWork, new Integer(transactionState)
                                        , tokensToOptimisticReplace, serializedManagedObjectBytes, tokensToDelete, tokensToNotify }
                              );
  
          // Replace the objects using their original transaction.
          Transaction transactionForRecovery = objectManagerState.getTransaction(logicalUnitOfWork);
  
          java.util.Iterator serializedObjectIterator = serializedManagedObjectBytes.iterator();
  
          // A list of ManagedObjects to add, this lists forces the ManagedObjects to
          // remain in storage while we give them to the Transaction.
          java.util.List managedObjectsToAdd = new java.util.ArrayList(tokensToAdd.size());
          for (java.util.Iterator tokenIterator = tokensToAdd.iterator(); tokenIterator.hasNext();) {
              Token token = (Token) tokenIterator.next();
  
              // Recover the ManagedObject from the serialized bytes.
              byte managedObjectBytes[] = (byte[]) serializedObjectIterator.next();
              ManagedObject managedObject = ManagedObject.restoreFromSerializedBytes(managedObjectBytes,
                                                                                     objectManagerState);
  
              ManagedObject managedObjectToAdd = token.setManagedObject(managedObject);
              managedObjectToAdd.state = ManagedObject.stateConstructed;
              managedObjectsToAdd.add(managedObjectToAdd);
          } // for ...tokensToAdd.
  
          // A list of ManagedObjects to optimistic replace, this lists forces the ManagedObjects to
          // remain in storage while we give them to the Transaction.
          java.util.List managedObjectsToOptimisticReplace = new java.util.ArrayList(tokensToOptimisticReplace.size());
  
          for (java.util.Iterator tokenIterator = tokensToOptimisticReplace.iterator(); tokenIterator.hasNext();) {
              Token token = (Token) tokenIterator.next();
  
              // Recover the ManagedObject from the serialized bytes.
              byte managedObjectBytes[] = (byte[]) serializedObjectIterator.next();
              ManagedObject managedObject = ManagedObject.restoreFromSerializedBytes(managedObjectBytes,
                                                                                     objectManagerState);
  
              ManagedObject managedObjectToReplace = token.setManagedObject(managedObject);
              managedObjectsToOptimisticReplace.add(managedObjectToReplace);
          } // for ...tokensToOptimisticReplace.
  
          // A list of ManagedObjects to delete, this lists forces the ManagedObjects to
          // remain in storage while we give them to the Transaction.
          java.util.List managedObjectsToDelete = new java.util.ArrayList(tokensToDelete.size());
          // Check that the tokensToDelete all have an underlying managedObject because they may have been deleted
          // from the ObjectStore already.
  
          for (java.util.Iterator tokenIterator = tokensToDelete.iterator(); tokenIterator.hasNext();) {
              Token token = (Token) tokenIterator.next();
  
              ManagedObject existingManagedObject = token.getManagedObject();
              if (existingManagedObject == null) {
                  // The object may have already been deleted from the ObjectStore,
                  // so create a dummy object to keep the transaction happy.
                  // The Token will have the ObjecStore and storedObjectIdentifier so the the correct delete in the
                  // ObjectStore can take place.
                  DummyManagedObject dummyManagedObject = new DummyManagedObject("Created by TransactionOptimisticReplaceLogRecord.performRecovery() (Delete)");
                  existingManagedObject = token.setManagedObject(dummyManagedObject);
                  existingManagedObject.state = ManagedObject.stateReady;
              } // if(existingManagedObject == null).
              managedObjectsToDelete.add(existingManagedObject);
          } // for tokensToDelete...
  
          // Check that the tokensToNotify all have an underlying managedObject because they may have been deleted
          // by a subsequent transaction. If we create any dummy objects we must hold a strong reference to them
          // until after the transactionForRecovery.optimisticReplace method has run, otherwise the garbage
          // collector might remove them from the weak reference in Token, and it wont be in. the ObjectStore.
          java.util.List dummyManagedObjectsToNotify = new java.util.ArrayList();
  
          for (java.util.Iterator tokenIterator = tokensToNotify.iterator(); tokenIterator.hasNext();) {
              Token token = (Token) tokenIterator.next();
  
              ManagedObject existingManagedObject = token.getManagedObject();
              if (existingManagedObject == null) {
                  // The object may have already been deleted from the ObjectStore,
                  // so create a dummy object to keep the transaction happy.
                  // The Token will have the ObjecStore and storedObjectIdentifier so the the correct delete in the
                  // ObjectStore can take place.
                  DummyManagedObject dummyManagedObject = new DummyManagedObject("Created by TransactionOptimisticReplaceLogRecord.performRecovery() (Notify)");
                  dummyManagedObject.state = ManagedObject.stateReady;
                  dummyManagedObjectsToNotify.add(dummyManagedObject);
                  token.setManagedObject(dummyManagedObject);
              } // if(existingManagedObject == null).
          } // for tokensToNotify...
  
          transactionForRecovery.optimisticReplace(managedObjectsToAdd,
                                                   managedObjectsToOptimisticReplace,
                                                   managedObjectsToDelete,
                                                   tokensToNotify);
          transactionForRecovery.internalTransaction.resetState(transactionState);
  
          if (Tracing.isAnyTracingEnabled() && trace.isEntryEnabled())
              trace.exit(this,
                         cclass,
                         "performRecovery");
      }