Start line:  
End line:  

Snippet Preview

Snippet HTML Code

Stack Overflow Questions
   /*
    * Copyright (C) 1998, 1999, 2000-2009,
    *
    * Arjuna Solutions Limited,
    * Newcastle upon Tyne,
    * Tyne and Wear,
    * UK.
    *
    * $Id: LogStore.java,v 1.4 2004/11/11 12:22:21 nmcl Exp $
   */
  
  package com.arjuna.ats.internal.arjuna.objectstore;
  
  import java.io.File;
  import java.util.Arrays;
  import java.util.HashMap;
  import java.util.Stack;
  
This is the transaction log implementation. It is optimised for the typical mode of the coordinator: write-once and never read or update. Reads or updates occur only in the case of failures, which hopefully are rare; hence the reason we optimise for the non-failure case. This does mean that recovery may take longer than when using other log implementations. There are several implementations of this approach, some of which perform better on one operating system than another. We may put them in to the source eventually and make it clear for which OS combination they are best suited. However, this implementation works well on all operating systems we have tested so is a good default.

Author(s):
Mark Little (mark@arjuna.com)
Version:
$Id: LogStore.java,v 1.4 2004/11/11 12:22:21 nmcl Exp $
Since:
JTS 1.0.
  

Algorithm used: During normal execution of a transaction, we only ever write and then remove the log entry; we never read it. Therefore, optimise for that situation. The log continually builds up in size until a maximum capacity is reached and in which case, we switch to another log. Meanwhile, the recovery manager periodically runs through completed logs and removes those that are no longer needed, truncating those that require recovery (which cannot complete at this time). When writing the initial log entry, we write a redzone marker, followed by the entry size and then the actual entry. Since a log is never shared between VMs, we only need to synchronize between the threads within a given VM: the recovery manager never works on a log that is being used by another VM anyway. The end of a log is marked with a termination record. Obviously if a crash occurs, then no such record will have been written and in which case, the recovery manager determines that the log is no longer required via timeout heuristics. The implementation normally writes removal records to the end of the log when an entry is deleted. This can be disabled and in which case we end up in the same situation as if a failure occurred as the removal record was being written or a crash happened before remove_committed could succeed on any of the other file-based object store implementations: we potentially try to commit transactions that have terminated (either committed or rolled back). In which case we ... (i) call commit on a state that has already been committed and fail to do so. Will eventually move the log record elsewhere and the administrator can deal with it. (ii) call commit on a state that has already been rolled back and again fail to do so. Will eventually move the log record elsewhere as above. If we do not write removal records then we would end up in a situation of trying to commit every log instance multiple times. As such we always try to write records but do them either synchronously or asynchronously (periodically). Of course there's still the chance that a failure will cause problems in both sync and async cases, but we have reduced the probability as well as the number of such problem items. The periodicity of this is the same as pruning the log, i.e., the same thread does both jobs. By default we synchronously add the removal marker to the log, i.e., when remove_committed returns, the marker entry has been appended to the log. NOTE: there is a race where we terminate the log instance and yet transactions may still be using it. This happens with other object store implementations too. However, in this case we could end up with a log that should be deleted because all of the entries have gone. We try to fix this up through allObjUids. If recovery works correctly then these states will eventually get deleted. TODO When truncating logs we write a shadow and then overwrite the original with the shadow when finished. If there is a crash we could end up with the shadow as well as the original. Recovery could tidy this up for us - as long as we have the original then we can continue to recover - the shadow instance may be corrupted so best to ignore it and simply delete it. But we would need to ensure that we didn't delete a shadow that is actually still active. Also we do not use a primary and backup log approach. Whenever we need a new log instance we create one. This means that there could be many logs being used at the same time, which could be a problem for disk space (unlikely these days, but possible). If this approach gets to be an issue then we can limit the number of log instances created.
 

Represents a specific log instance.

Author(s):
mlittle
 
 
 {
     public class TransactionData
     {
         TransactionData (final Uid txfinal long offfinal LogInstance parent)
         {
              = tx;
              = off;
              = parent;
         }
 
         public final Uid txId;
         public final long offset;
         public final LogInstance container;
     }
 
     public LogInstance(String tnlong size)
     {
          = new Uid();
          = tn;
          = false;
          = size;
     }
 
     /*
       * Once frozen we will not use the log again except for recovery and
       * pruning.
       *
       * We could consider another algorithm that reuses the log once it has
       * dropped below a threshold size. Probably not worth it at the moment.
       */
 
     public final boolean isFrozen()
     {
         return ;
     }
 
     public final void freeze() // one way operation.
     {
          = true;
     }
 
     public final int numberOfUsers()
     {
         return .size();
     }
 
     public final Uid getName()
     {
         return ;
     }
 
     public final String getTypeName()
     {
         return ;
     }
 
     public final InputObjectState allObjUids () throws ObjectStoreException
     {
         OutputObjectState state = new OutputObjectState();
         Iterator<Uiditer = .keySet().iterator();
 
         try
         {
             while (iter.hasNext())
             {
                 UidHelper.packInto(iter.next(), state);
             }
 
             // don't forget to null terminate
 
             UidHelper.packInto(Uid.nullUid(), state);
         }
         catch (final IOException ex)
         {
             throw new ObjectStoreException(ex);
         }
 
         return new InputObjectState(state);
     }
 
     public final boolean present(Uid id)
     {
         return .containsKey(id);
     }
 
     public final TransactionData getTxId (Uid txId)
     {
         return new TransactionData(txIdthis);
     }
 
     public final TransactionData addTxId (Uid txIdlong size)
     {
         TransactionData td = new TransactionData(txIdthis);
 
         .add(td);  // allow multiple entries in the same log
         .put(txIdtxId);
          += size;
 
         return td;
     }
 
     public final long remaining()
     {
         return  - ;
     }
 
     public final void resize (long size)
     {
          = size;
     }
 
     public String toString()
     {
         return "LogInstance < " +  + ", " +  + ", "
                 + numberOfUsers() + ", " + remaining() + " >";
     }
 
     private Uid _logName;
     private String _typeName;
     private boolean _frozen;
     private Stack<TransactionData_transactions = new Stack<TransactionData>();
     private HashMap<UidUid_ids = new HashMap<UidUid>();
     private long _used = 0;
     private long _totalSize;
 }
 
 /*
  * Time based, but it would be good to have it triggered on the number of
  * entries that need to be added.
  */
 
 class LogPurger extends Thread
 {
     private enum Status {ACTIVE, PASSIVE, TERMINATED};
 
     class LogElement
     {
         public LogElement(final String tfinal Uid ufinal int s)
         {
              = t;
              = u;
              = s;
         }
 
         public String tn;
         public Uid uid;
         public int state;
     };
 
     /*
       * Purge every N seconds.
       *
       * TODO purge after number of logs > M
       */
 
     public static final long DEFAULT_PURGE_TIME = 100000; // 100 seconds
 
     public LogPurger(LogStore instance)
     {
         this(instance);
     }
 
     public LogPurger(LogStore instancelong purgeTime)
     {
         super("Log Purger");
          = instance;
          = purgeTime;
     }
 
     public void addRemovedState(final Uid ufinal String tnfinal int state)
     {
         synchronized ()
         {
             .put(unew LogElement(tnustate));
         }
     }
 
     public void purge()
     {
         try
         {
             .truncateLogs(true);
         }
         catch (final Exception ex)
         {
         }
     }
 
     public void writeRemovalEntries()
     {
         synchronized ()
         {
             if (.size() > 0)
             {
                 Collection<LogElemententries = .values();
                 Iterator<LogElementiter = entries.iterator();
 
                 while (iter.hasNext())
                 {
                     LogElement val = iter.next();
 
                     try
                     {
                         .removeState(val.uidval.tnval.state);
                     }
                     catch (final Exception ex)
                     {
                         // TODO log warning, but there's nothing else we can do.
                     }
                 }
 
                 .clear();
             }
         }
     }

    
Poke the thread into doing some work even if it normally would not.
 
 
     public void trigger ()
     {
         synchronized ()
         {
             if ( == .)
                 .notify();
         }
     }
 
     public void run()
     {
         for (;;)
         {
             // TODO activate thread during read and get it to write deleted states
 
             try
             {
                 synchronized ()
                 {
                      = .;
 
                     .wait();
                 }
             }
             catch (final Exception ex)
             {
                  = .;
             }
 
             /*
                 * Write any asynchronous delete records.
                 */
 
             writeRemovalEntries();
 
             /*
                 * Now truncate any logs we've been working on.
                 */
 
             try
             {
                 .truncateLogs();
             }
             catch (final Exception ex)
             {
             }
         }
 
         // _status = Status.TERMINATED;
     }
 
     private HashMap<UidLogElement_entries = new HashMap<UidLogElement>();
     private long _purgeTime;
     private LogStore _objStore;
     private Status _status;
     private Object _lock = new Object();
 }
 
 class PurgeShutdownHook extends Thread
 {
     public PurgeShutdownHook(LogPurger purger)
     {
          = purger;
     }
 
     public void run()
     {
         .writeRemovalEntries(); // flush everything in the cache first.
         .purge();
     }
 
     private LogPurger _purger;
 }
 
 /*
  * Derive it directly from FSStore for now, simply because we are unlikely to
  * have many log instances in the store. However, if it becomes a problem, then
  * we can simply derive from the HashedActionStore.
  */
 
 public class LogStore extends FileSystemStore
 {
     public static final long LOG_SIZE = 10 * 1024 * 1024;  // default maximum log size in bytes
 
     private static final String FILE_MODE = "rwd";

    
Normally returns the current state of the log entry. However, this is never called during normal (non-recovery) execution. Therefore, the overhead of having to scan all of the logs (if it's not one we're using) is minimal.
 
 
     public int currentState(Uid objUidString tName)
             throws ObjectStoreException
     {
         InputObjectState ios = new InputObjectState();
 
         /*
            * TODO
            *
            * It's possible that the entry has been marked to be deleted but
            * that the removal entry hasn't been written yet. We could check the
            * async cache. However, since we really only care about this during
            * recovery, it's not going to cause us  problems anyway.
            */
 
         if (allObjUids(tNameios.))
         {
             Uid tempUid = new Uid(Uid.nullUid());
 
             do
             {
                 try
                 {
                     tempUid = UidHelper.unpackFrom(ios);
                 }
                 catch (final Exception ex)
                 {
                     ex.printStackTrace();
 
                     return .;
                 }
 
                 if (tempUid.equals(objUid))
                     return .;
 
             } while (tempUid.notEquals(Uid.nullUid()));
 
             return .;
         }
         else
             return .;
     }

    
Commit a previous write_state operation which was made with the SHADOW StateType argument. This is achieved by renaming the shadow and removing the hidden version.
 
 
     public boolean commit_state(Uid objUidString tName)
             throws ObjectStoreException
     {
         return true;
     }
 
     public boolean hide_state(Uid uString tnthrows ObjectStoreException
     {
         if (..isTraceEnabled()) {
             ..trace("LogStore.hide_state(" + u + ", " + tn + ")");
         }
 
         return false;
     }
 
     public boolean reveal_state(Uid uString tnthrows ObjectStoreException
     {
         if (..isTraceEnabled()) {
             ..trace("LogStore.reveal_state(" + u + ", " + tn + ")");
         }
 
         return false;
     }
 
     public InputObjectState read_uncommitted(Uid uString tn)
             throws ObjectStoreException
     {
         if (..isTraceEnabled()) {
             ..trace("LogStore.read_uncommitted(" + u + ", " + tn + ")");
         }
 
         return null;
     }
 
     public boolean remove_uncommitted(Uid uString tn)
             throws ObjectStoreException
     {
         if (..isTraceEnabled()) {
             ..trace("LogStore.remove_uncommitted(" + u + ", " + tn + ")");
         }
 
         return false;
     }
 
     public boolean write_committed(Uid storeUidString tName,
                                    OutputObjectState statethrows ObjectStoreException
     {
         if (..isTraceEnabled()) {
             ..trace("LogStore.write_committed(" + storeUid + ", "
                     + tName + ")");
         }
 
         try
         {
             return super.write_committed(storeUidtNamestate);
         }
         catch (ObjectStoreException ex)
         {
             removeFromLog(storeUid);
 
             throw ex;
         }
     }
 
     public boolean write_uncommitted(Uid uString tnOutputObjectState s)
             throws ObjectStoreException
     {
         if (..isTraceEnabled()) {
             ..trace("LogStore.write_uncommitted(" + u + ", " + tn + ", " + s
                     + ")");
         }
 
         return false;
     }
 
     public boolean allLogUids (String tNameInputObjectState stateint matchthrows ObjectStoreException
     {
         return super.allObjUids(tNamestatematch);
     }

    
This is a recovery-only method and should not be called during normal execution. As such we need to load in all of the logs we can find that aren't already loaded (or activated).
 
 
     public boolean allObjUids(String tNameInputObjectState stateint match)
             throws ObjectStoreException
     {
         /*
            * match will always be OS_COMMITTED since that's all we ever write for
            * the logs.
            */
 
         // in case of asynchronous removals trigger the purger now.
 
         .trigger();
 
         /*
            * Get a list of logs. Load them in to memory if we aren't already
            * working on them/it. But we can prune the entry once we're
            * finished or the memory footprint will grow. We should do this
            * for all frozen entries eventually too.
            */
 
         InputObjectState logs = new InputObjectState();
         OutputObjectState objUids = new OutputObjectState();
 
         /*
            * We never call this method except during recovery. As such we shouldn't
            * need to worry about optimizations such as checking whether or not the
            * log is in current working memory.
            */
 
         if (!super.allObjUids(tNamelogsmatch))
             return false;
         else
         {
             /*
                 * Now we have all of the log names let's attach to each one
                 * and locate the committed instances (not deleted.)
                 */
 
             Uid logName = new Uid(Uid.nullUid());
 
             try
             {
                 do
                 {
                     logName = UidHelper.unpackFrom(logs);
 
                     if (logName.notEquals(Uid.nullUid()))
                     {
                         /*
                                * Could check to see if log is in current working memory.
                                */
 
                         /*
                                * TODO
                                *
                                * First purge the log if we can, but we need to know that
                                * we're not playing with an instance that is being manipulated
                                * from another VM instance.
                                */
 
                         ArrayList<InputObjectStatetxs = scanLog(logNametName);
 
                         if (txs.size() > 0)
                         {
                             for (int i = 0; i < txs.size(); i++)
                             {
                                 UidHelper.packInto(txs.get(i).stateUid(), objUids);
                             }
                         }
                     }
                 } while (logName.notEquals(Uid.nullUid()));
 
                 // remember null terminator
 
                 UidHelper.packInto(Uid.nullUid(), objUids);
 
                 state.setBuffer(objUids.buffer());
             }
             catch (final IOException ex)
             {
                 ex.printStackTrace();
 
                 return false;
             }
 
             return true;
         }
     }
 
     public LogStore(ObjectStoreEnvironmentBean objectStoreEnvironmentBeanthrows ObjectStoreException
     {
         super(objectStoreEnvironmentBean);
 
         // overrides parents use of isObjectStoreSync
          = objectStoreEnvironmentBean.isTransactionSync();
 
          = objectStoreEnvironmentBean.isSynchronousRemoval();
 
          = objectStoreEnvironmentBean.getPurgeTime();
 
          = objectStoreEnvironmentBean.getTxLogSize();
 
          = new LogPurger(this);
         .setDaemon(true);
 
         Runtime.getRuntime().addShutdownHook(new PurgeShutdownHook());
 
         .start();
     }

    
Unlock and close the file. Note that if the unlock fails we set the return value to false to indicate an error but rely on the close to really do the unlock.
 
 
     protected boolean unlockAndClose(File fdRandomAccessFile rf)
     {
         if (..isTraceEnabled()) {
             ..trace("RandomAccessFile.unlockAndClose(" + fd + ", " + rf + ")");
         }
 
         boolean closedOk = unlock(fd);
 
         try
         {
             rf.close();
         }
         catch (Exception e)
         {
             closedOk = false;
         }
 
         return closedOk;
     }

    
write_state saves the ObjectState in a file named by the type and Uid of the ObjectState. If the second argument is SHADOW, then the file name is different so that a subsequent commit_state invocation will rename the file. We need to make sure that each entry is written to the next empty location in the log even if there's already an entry for this tx.
 
 
     protected boolean write_state(Uid objUidString tName,
                                   OutputObjectState stateint ftthrows ObjectStoreException
     {
         if (..isTraceEnabled()) {
             ..trace("ShadowingStore.write_state(" + objUid + ", " + tName
                     + ", " + StateType.stateTypeString(ft) + ")");
         }
 
         String fname = null;
         File fd = null;
 
         if (tName != null)
         {
             int imageSize = (intstate.length();
             byte[] uidString = objUid.stringForm().getBytes();
             int buffSize = . + uidString.length + imageSize + 8;  // don't put in endOfLog since we keep overwriting that.
             RandomAccessFile ofile = null;
             java.nio.channels.FileLock lock = null;
 
             if (imageSize > 0)
             {
                 TransactionData theLogEntry = getLogName(objUidtNamebuffSize);		// always adds entry to log
                 LogInstance theLog = theLogEntry.container;
 
                 if (theLog == null)
                     throw new ObjectStoreException();
 
                 fname = genPathName(theLog.getName(), tNameft);
                 fd = openAndLock(fname.true);
 
                 if (fd == null) {
                     ..warn_objectstore_ShadowingStore_18(fname);
 
                     return false;
                 }
 
                 boolean setLength = !fd.exists();
 
                 try
                 {
                     ofile = new RandomAccessFile(fd);
 
                     if (setLength)
                     {
                         ofile.setLength();
                     }
                     else
                     {
                         // may have to resize file if we keep updating this transaction info
 
                         if (theLog.remaining() < buffSize)
                         {
                             long size = ofile.length() + buffSize - theLog.remaining();
 
                             ofile.setLength(size);
 
                             theLog.resize(size);
                         }
                     }
 
                     java.nio.ByteBuffer buff = java.nio.ByteBuffer.allocate(buffSize);
 
                     buff.put();
                     buff.putInt(uidString.length);
                     buff.put(uidString);
                     buff.putInt(imageSize);
                     buff.put(state.buffer());
 
                     synchronized ()
                     {
                         ofile.seek(theLogEntry.offset);
 
                         ofile.write(buff.array());
                     }
                 }
                 catch (SyncFailedException e)
                 {
                     unlockAndClose(fdofile);
 
                     throw new ObjectStoreException(
                             "ShadowingStore::write_state() - write failed to sync for "
                                     + fnamee);
                 }
                 catch (FileNotFoundException e)
                 {
                     unlockAndClose(fdofile);
 
                     e.printStackTrace();
 
                     throw new ObjectStoreException(
                             "ShadowingStore::write_state() - write failed to locate file "
                                     + fname + ": " + ee);
                 }
                 catch (IOException e)
                 {
                     unlockAndClose(fdofile);
 
                     e.printStackTrace();
 
                     throw new ObjectStoreException(
                             "ShadowingStore::write_state() - write failed for "
                                     + fname + ": " + ee);
                 }
                 finally
                 {
                     try
                     {
                         if (lock != null)
                             lock.release();
                     }
                     catch (IOException ex)
                     {
                         ex.printStackTrace();
                     }
                 }
             }
 
             if (!unlockAndClose(fdofile)) {
                 ..warn_objectstore_ShadowingStore_19(fname);
             }
 
             super.addToCache(fname);
 
             return true;
         }
         else
             throw new ObjectStoreException(
                     "ShadowStore::write_state - "
                             + ..get_objectstore_notypenameuid()
                             + objUid);
     }

    
Shouldn't be called during normal execution only during recovery.
 
 
     protected InputObjectState read_state(Uid uString tnint s)
             throws ObjectStoreException
     {
         /*
            * In case of asynchronous removals of state, let's trigger the purger
            * thread to flush its cache now. Try to avoid false positives during
            * recovery wherever possible!
            */
 
         .trigger();
 
         /*
            * It's possible that recovery got hold of a state id while it was
            * being deleted (marker written and pruning thread not yet active).
            * In which case when it comes to do a read it's not going to find
            * the state there any longer. Conversely it's possible that it could do
            * a read on a state that is about to be deleted. Recovery should be
            * able to cope with these edge cases.
            */
 
         TransactionData td = getLogName(utn, -1);
 
         if (td == null)
             throw new ObjectStoreException();
 
         ArrayList<InputObjectStatestates = scanLog(td.container.getName(), tn);
 
         if ((states == null) || (states.size() == 0))
             return null;
 
         for (int i = 0; i < states.size(); i++)
         {
             if (states.get(i).stateUid().equals(u))
                 return states.get(i);
         }
 
         /*
            * Not in the log, so probably removed by now.
            */
 
         return null;
     }

    
Does nothing except indicate that this thread is finished with the log on behalf of this transaction.
 
 
     protected boolean remove_state(Uid uString tnint s)
             throws ObjectStoreException
     {
         // maybe write a removal entry into the log.
 
         try
         {
             /*
                 * If we don't add a removal entry then recovery has to work a
                 * little harder to figure things out. But it has to cater for the
                 * situation where a removal record write fails anyway, so this
                 * shouldn't be a big deal. On the up side it improves performance
                 * by 30% for this implementation, which is a 40% improvement over
                 * the basic file-based log!
                 */
 
             /*
                 * If we write a removal record as a separate entity to the original
                 * data item then we cannot ensure that they will go into the same
                 * log with a pre-set size for the log. Therefore, we have two
                 * options:
                 *
                 * (i) find the old entry in the log and mark it as deleted.
                 * (ii) increase the size of the log to accommodate the removal entry.
                 *
                 * We currently go for option (ii) as this is the quickest.
                 */
 
             if ()
             {
                 OutputObjectState removalState = new OutputObjectState(utn);
 
                 removalState.packBytes();
 
                 if (!write_state(utnremovalStates))
                     throw new ObjectStoreException();
             }
             else
                 .addRemovedState(utns);
         }
         catch (IOException ex)
         {
             throw new ObjectStoreException(ex.toString(), ex);
         }
         catch (final Throwable ex)
         {
             ex.printStackTrace();
 
             throw new ObjectStoreException(ex.toString(), ex);
         }
         finally
         {
             removeFromLog(u);
         }
 
         return true;
     }
 
     protected boolean lock(File fdint lmodeboolean create)
     {
         return true;
     }
 
     protected boolean unlock(File fd)
     {
         return true;
     }
 
     protected String genPathName (Uid objUidString tNameint ftthrows ObjectStoreException
     {
         String fname = super.genPathName(objUidtNameft);
 
         if (ft == .)
             fname = fname + ;
 
         return fname;
     }
 
     boolean removeState(Uid uString tnint sthrows ObjectStoreException
     {
         try
         {
             OutputObjectState removalState = new OutputObjectState(utn);
 
             removalState.packBytes();
 
             if (!write_state(utnremovalStates))
                 throw new ObjectStoreException();
         }
         catch (IOException ex)
         {
             throw new ObjectStoreException(ex.toString(), ex);
         }
 
         return true;
     }
 
     boolean truncateLogs () throws ObjectStoreException
     {
        return truncateLogs(false);
    }
    boolean truncateLogs (boolean forcethrows ObjectStoreException
    {
        synchronized ()
        {
            Iterator<LogInstanceiter = .iterator();
            /*
                * Only do this for logs that are full to save time,
                * except if we are terminating.
                */
            while (iter.hasNext())
            {
                boolean delete = false;
                LogInstance log = null;
                try
                {
                    log = iter.next();
                    if (log.isFrozen() || force)
                        delete = truncateLog(logforce);
                }
                catch (final Exception ex)
                {
                    // TODO log
                }
                if (delete)
                    iter.remove();
            }
        }
        return true;
    }
    /*
      * Return true if the log needs to be deleted.
      */
    private final boolean truncateLog(final LogInstance logboolean forcethrows ObjectStoreException
    {
        boolean delete = false;
        synchronized ()
        {
            File fd = new File(genPathName(log.getName(), log.getTypeName(), .));
            try
            {
                /*
                     * Create a list of ObjectState entries.
                     */
                ArrayList<InputObjectStateobjectStates = scanLog(log.getName(), log.getTypeName());
                /*
                     * At this stage we should now have a list of unique
                     * entries. Write them back to the log. Do this
                     * atomically! If the list is empty then delete the
                     * file!
                     */
                if ((objectStates != null) && (objectStates.size() > 0))
                {
                    /*
                          * If we are terminating then we can truncate the log to the
                          * real size needed to contain the existing entries since we
                          * will not use it again within another VM except for
                          * recovery purposes.
                          */
                    String fname = genPathName(log.getName(), log.getTypeName(), .);
                    File fd2 = openAndLock(fname.true);
                    RandomAccessFile oFile = new RandomAccessFile(fd2);
                    int size = 0;
                    oFile.setLength();
                    for (int i = 0; i < objectStates.size(); i++)
                    {
                        byte[] uidString = objectStates.get(i).stateUid().stringForm().getBytes();
                        int buffSize = . + uidString.length + objectStates.get(i).buffer().length + 8;
                        java.nio.ByteBuffer buff = java.nio.ByteBuffer.allocate(buffSize);
                        size += buffSize;
                        try
                        {
                            buff.put();
                            buff.putInt(uidString.length);
                            buff.put(uidString);
                            buff.putInt(objectStates.get(i).buffer().length);
                            buff.put(objectStates.get(i).buffer(),0, objectStates.get(i).buffer().length);
                        }
                        catch (final Exception ex)
                        {
                            ex.printStackTrace();
                            // TODO log
                            fd2.delete();
                            unlockAndClose(fd2oFile);
                            throw new ObjectStoreException(ex.toString(), ex);
                        }
                    }
                    try
                    {
                        if (force)
                        {
                            oFile.setLength(size);
                            log.freeze();
                        }
                        fd2.renameTo(fd);
                    }
                    catch (final Exception ex)
                    {
                        ex.printStackTrace();
                        // TODO log
                        throw new ObjectStoreException(ex.toString(), ex);
                    }
                    finally
                    {
                        unlockAndClose(fd2oFile);
                    }
                }
                else
                {
                    /*
                          * Delete the log if there are no states in it. We could
                          * keep the file around and reuse it, but the advantage of
                          * this is small compared to having to cope with reusing old
                          * log instances.
                          */
                    fd.delete();
                    /*
                          * Remember to remove the information from the memory cache.
                          */
                    delete = true;
                }
            }
            catch (final ObjectStoreException ex)
            {
                ex.printStackTrace();
                throw ex;
            }
            catch (final Exception ex)
            {
                ex.printStackTrace();
                throw new ObjectStoreException(ex.toString(), ex);
            }
        }
        return delete;
    }
    private final ArrayList<InputObjectStatescanLog (final Uid logNamefinal String typeNamethrows ObjectStoreException
    {
        /*
           * Make sure no new entries can be created while we scan.
           */
        synchronized ()
        {
            try
            {
                String fname = genPathName(logNametypeName.);
                File fd = openAndLock(fname.true);
                RandomAccessFile iFile = new RandomAccessFile(fd);
                // iFile.getChannel().lock();
                try
                {
                    /*
                          * Create a list of ObjectState entries.
                          */
                    ArrayList<InputObjectStateobjectStates = new ArrayList<InputObjectState>();
                    iFile.seek(0); // make sure we're at the start
                    while (iFile.getFilePointer() < iFile.length())
                    {
                        byte[] buff = new byte[.];
                        iFile.read(buff);
                        if (!redzoneProtected(buff))
                        {
                            // end
                            break;
                            /*
                                    * TODO add an end-of-log entry and check for that. Currently just assume
                                    * that no RZ means end, rather than corruption.
                                    */
                        }
                        else
                        {
                            int uidSize = iFile.readInt();
                            byte[] uidString = new byte[uidSize];
                            iFile.read(uidString);
                            Uid txId = new Uid(new String(uidString));
                            int imageSize = iFile.readInt();
                            byte[] imageState = new byte[imageSize];
                            iFile.read(imageState);
                            try
                            {
                                InputObjectState state = new InputObjectState(
                                        txId""imageState);
                                objectStates.add(state);
                            }
                            catch (final Exception ex)
                            {