docs: final void write(ManagedObject managedObject)
                      throws ObjectManagerException
      {
          final String methodName = "write";
          if (Tracing.isAnyTracingEnabled() && trace.isEntryEnabled())
              trace.entry(this, cclass, methodName, new Object[] { managedObject });
  
          ObjectManagerByteArrayOutputStream byteArrayOutputStream = null;
          byte[] serializedBytes = null;
          int serializedBytesLength = 0;
          if (usesSerializedForm) {
              // Pick up and write the latest serialized bytes.
  
              // It is possible that several threads requested a write one after the
              // other each getting the request added to a different managedObjetsToWrite table,
              // however the first of these through here will clear serializedBytes.
              // It is also possible that the transaction state of the managed object
              // was restored from a checkpoint, in which case the serialized object
              // may already be in the ObjectStore, and the serializedBytes will again be null.
  
              // Is the Object deleted? It may have got deleted after we release the
              // synchronize lock when we captured the tokensToWrite hashtable but before we
              // actually try to write it. We dont strictly need the test for stateDeleted
              // since once in this state the byteArrayOutputStream will be null.
              if (managedObject.state != ManagedObject.stateDeleted)
                  byteArrayOutputStream = managedObject.freeLatestSerializedBytes();
  
          } else {
              // Not logged so use the current serialized bytes, as long as its not part of a transaction.
              // If it is part of a transaction then the transaction will hold the ManagedObject in memory.
              // Not locked because this is only used by SAVE_ONLY_ON_SHUTDOWN stores at shutdown
              // when no appliaction threads are active.
              if (managedObject.state == ManagedObject.stateReady)
                  byteArrayOutputStream = managedObject.getSerializedBytes();
              // The byteArrayOutputStream will have getRleaseSize() == 0;
          } // if ( usesSerializedForm ).
  
          if (byteArrayOutputStream != null) {
              serializedBytes = byteArrayOutputStream.getBuffer();
              serializedBytesLength = byteArrayOutputStream.getCount();
              // For debugging add guardBytes before and after the serialized
              // ManagedObject.
              if (useGuardBytes) {
                  guardByte++;
                  byte[] guardedBytes = new byte[guardBytesLength + serializedBytesLength + guardBytesLength];
                  java.util.Arrays.fill(guardedBytes // Target
                                        , 0 // From index.
                                        , guardBytesLength - 1 // To index.
                                        , guardByte //Fill byte.
                  );
                  System.arraycopy(serializedBytes // Source.
                                   , 0 // Source position.
                                   , guardedBytes // Destination.
                                   , guardBytesLength // Destination position.
                                   , serializedBytesLength// Length.
                  );
                  java.util.Arrays.fill(guardedBytes,
                                        guardBytesLength + serializedBytesLength,
                                        guardBytesLength + serializedBytesLength + guardBytesLength - 1,
                                        guardByte);
                  serializedBytes = guardedBytes;
                  serializedBytesLength = guardedBytes.length;
              } // if (useGuardBytes).
  
              //TODO Consider Rounding to a 4K boundary.
              //!! int lengthRequired = ((serializedBytes.length/4096)+1)*4096;
  
              // Allocate some space for the ManagedObject, if we are recovering, we can reuse already 
              // allocated space and avoid the need to have extra space available in the store. This is 
              // important if we were to crash after flushing the store but before we write checkpoint 
              // end. It that event we would try and write the objects to the store for a second time
              // during recovery and require that it had enough spare space to do this. 
              Directory.StoreArea storeArea;
              boolean addToDirectory = false;
              if (objectManagerState.getObjectManagerStateState() == ObjectManagerState.stateReplayingLog) {
                  storeArea = (Directory.StoreArea) directory.getEntry(new Long(managedObject.owningToken.storedObjectIdentifier));
                  if (storeArea == null
                      || storeArea.length < serializedBytesLength
                      || storeArea.length + minimumFreeSpaceEntrySize > serializedBytesLength) {
                      FreeSpace freeSpace = allocateSpace(serializedBytesLength);
                      storeArea = (Directory.StoreArea) directory.makeStoreArea(managedObject.owningToken.storedObjectIdentifier,
                                                                                freeSpace.address,
                                                                                freeSpace.length);
                      addToDirectory = true;
                  }
  
              } else {
                  FreeSpace freeSpace = allocateSpace(serializedBytesLength);
                  storeArea = (Directory.StoreArea) directory.makeStoreArea(managedObject.owningToken.storedObjectIdentifier,
                                                                            freeSpace.address,
                                                                            freeSpace.length);
                  addToDirectory = true;
              } // if (objectManagerState.
  
              if (gatherStatistics)
                  numberOfManagedObjectsWritten++;
              writeBuffer(serializedBytes,
                          0,
                          serializedBytesLength,
                          storeArea.byteAddress);
  
              if (addToDirectory) {
                  // We assume that even though the buffer has not been forced to disk, the file system will
                  // allow a read of the disk to look into the buffer, hence we go ahead and
                  // update the live directory.
                  Directory.StoreArea existingStoreArea = (Directory.StoreArea) directory.putEntry(storeArea);
  
                  // If this is a replacement we will eventually recover the space the previous object occupies.
                  // Note the free space for now but don't release it until the new Object
                  // has been forced to disk.
                  if (existingStoreArea != null) {
                      newFreeSpace.add(existingStoreArea);
                  } // if (existingStoreArea == null).
              } // if (addToDirectory).
  
              // Keep count of the space we will eventually release now that we have allocated the space.
              checkpointReleaseSize = checkpointReleaseSize + byteArrayOutputStream.getReleaseSize();
              objectManagerState.returnByteArrayOutputStreamToPool(byteArrayOutputStream);
          } // if (byteArrayOutputStream != null ).
  
          if (Tracing.isAnyTracingEnabled() && trace.isEntryEnabled())
              trace.exit(this, cclass, methodName);
      }