[jboss-svn-commits] JBL Code SVN: r25171 - in labs/jbosstm/trunk/ArjunaCore: arjuna/classes/com/arjuna/ats/internal/arjuna and 5 other directories.

jboss-svn-commits at lists.jboss.org jboss-svn-commits at lists.jboss.org
Mon Feb 9 14:13:30 EST 2009


Author: mark.little at jboss.com
Date: 2009-02-09 14:13:30 -0500 (Mon, 09 Feb 2009)
New Revision: 25171

Added:
   labs/jbosstm/trunk/ArjunaCore/arjuna/classes/com/arjuna/ats/internal/arjuna/objectstore/LogStore.java
   labs/jbosstm/trunk/ArjunaCore/arjuna/classes/com/arjuna/ats/internal/arjuna/objectstore/LogStoreSetup.java
   labs/jbosstm/trunk/ArjunaCore/arjuna/tests/classes/com/hp/mwtests/ts/arjuna/objectstore/LogStoreReactivationTest.java
   labs/jbosstm/trunk/ArjunaCore/arjuna/tests/classes/com/hp/mwtests/ts/arjuna/objectstore/LogStoreReactivationTest2.java
   labs/jbosstm/trunk/ArjunaCore/arjuna/tests/classes/com/hp/mwtests/ts/arjuna/objectstore/LogStoreRecoveryTest.java
   labs/jbosstm/trunk/ArjunaCore/arjuna/tests/classes/com/hp/mwtests/ts/arjuna/objectstore/LogStoreRecoveryTest2.java
   labs/jbosstm/trunk/ArjunaCore/arjuna/tests/classes/com/hp/mwtests/ts/arjuna/objectstore/LogStoreTest.java
   labs/jbosstm/trunk/ArjunaCore/arjuna/tests/classes/com/hp/mwtests/ts/arjuna/objectstore/LogStoreTest2.java
   labs/jbosstm/trunk/ArjunaCore/arjuna/tests/classes/com/hp/mwtests/ts/arjuna/objectstore/LogStressTest.java
   labs/jbosstm/trunk/ArjunaCore/arjuna/tests/classes/com/hp/mwtests/ts/arjuna/objectstore/LogStressTest2.java
Modified:
   labs/jbosstm/trunk/ArjunaCore/arjuna/classes/com/arjuna/ats/arjuna/recovery/RecoverAtomicAction.java
   labs/jbosstm/trunk/ArjunaCore/arjuna/classes/com/arjuna/ats/internal/arjuna/Implementations.java
   labs/jbosstm/trunk/ArjunaCore/arjuna/classes/com/arjuna/ats/internal/arjuna/objectstore/FileSystemStore.java
   labs/jbosstm/trunk/ArjunaCore/arjuna/classes/com/arjuna/ats/internal/arjuna/objectstore/ShadowingStore.java
   labs/jbosstm/trunk/ArjunaCore/arjuna/classes/com/arjuna/ats/internal/arjuna/recovery/TransactionStatusManagerItem.java
   labs/jbosstm/trunk/ArjunaCore/arjuna/tests/classes/com/hp/mwtests/ts/arjuna/resources/BasicRecord.java
   labs/jbosstm/trunk/ArjunaCore/docs/user_guide/ProgrammersGuide.odt
Log:
https://jira.jboss.org/jira/browse/JBTM-397

Modified: labs/jbosstm/trunk/ArjunaCore/arjuna/classes/com/arjuna/ats/arjuna/recovery/RecoverAtomicAction.java
===================================================================
--- labs/jbosstm/trunk/ArjunaCore/arjuna/classes/com/arjuna/ats/arjuna/recovery/RecoverAtomicAction.java	2009-02-09 19:12:13 UTC (rev 25170)
+++ labs/jbosstm/trunk/ArjunaCore/arjuna/classes/com/arjuna/ats/arjuna/recovery/RecoverAtomicAction.java	2009-02-09 19:13:30 UTC (rev 25171)
@@ -44,7 +44,7 @@
  * @message com.arjuna.ats.arjuna.recovery.RecoverAtomicAction_1 [com.arjuna.ats.arjuna.recovery.RecoverAtomicAction_1] - RecoverAtomicAction.replayPhase2 recovering {0} ActionStatus is {1}
  * @message com.arjuna.ats.arjuna.recovery.RecoverAtomicAction_2 [com.arjuna.ats.arjuna.recovery.RecoverAtomicAction_2] - RecoverAtomicAction.replayPhase2: Unexpected status: {0}
  * @message com.arjuna.ats.arjuna.recovery.RecoverAtomicAction_3 [com.arjuna.ats.arjuna.recovery.RecoverAtomicAction_3] - RecoverAtomicAction.replayPhase2( {0} )  finished 
- * @message com.arjuna.ats.arjuna.recovery.RecoverAtomicAction_4 [com.arjuna.ats.arjuna.recovery.RecoverAtomicAction_4] - RecoverAtomicAction: transaction {0} not activated, unable to replay phase 2 commit
+ * @message com.arjuna.ats.arjuna.recovery.RecoverAtomicAction_4 [com.arjuna.ats.arjuna.recovery.RecoverAtomicAction_4] - RecoverAtomicAction: transaction {0} not activated, unable to replay phase 2 commit. Check state has not already been completed.
 */
 
 public class RecoverAtomicAction extends AtomicAction

Modified: labs/jbosstm/trunk/ArjunaCore/arjuna/classes/com/arjuna/ats/internal/arjuna/Implementations.java
===================================================================
--- labs/jbosstm/trunk/ArjunaCore/arjuna/classes/com/arjuna/ats/internal/arjuna/Implementations.java	2009-02-09 19:12:13 UTC (rev 25170)
+++ labs/jbosstm/trunk/ArjunaCore/arjuna/classes/com/arjuna/ats/internal/arjuna/Implementations.java	2009-02-09 19:13:30 UTC (rev 25171)
@@ -33,7 +33,6 @@
 
 import com.arjuna.ats.arjuna.gandiva.inventory.Inventory;
 
-import com.arjuna.ats.internal.arjuna.objectstore.CacheStoreSetup;
 import com.arjuna.ats.internal.arjuna.objectstore.*;
 
 import com.arjuna.ats.internal.arjuna.gandiva.nameservice.JNSSetup;
@@ -76,7 +75,8 @@
 	    Inventory.inventory().addToList(new JDBCActionStoreSetup());
 	    Inventory.inventory().addToList(new CacheStoreSetup());
         Inventory.inventory().addToList(new VolatileStoreSetup());
-
+        Inventory.inventory().addToList(new LogStoreSetup());
+        
         Inventory.inventory().addToList(new JNSSetup());
 	    Inventory.inventory().addToList(new PNSSetup());
 

Modified: labs/jbosstm/trunk/ArjunaCore/arjuna/classes/com/arjuna/ats/internal/arjuna/objectstore/FileSystemStore.java
===================================================================
--- labs/jbosstm/trunk/ArjunaCore/arjuna/classes/com/arjuna/ats/internal/arjuna/objectstore/FileSystemStore.java	2009-02-09 19:12:13 UTC (rev 25170)
+++ labs/jbosstm/trunk/ArjunaCore/arjuna/classes/com/arjuna/ats/internal/arjuna/objectstore/FileSystemStore.java	2009-02-09 19:13:30 UTC (rev 25171)
@@ -34,7 +34,6 @@
 import com.arjuna.ats.arjuna.ArjunaNames;
 import com.arjuna.ats.arjuna.objectstore.*;
 import com.arjuna.ats.arjuna.common.*;
-import com.arjuna.common.util.propertyservice.PropertyManager;
 
 import com.arjuna.ats.arjuna.logging.tsLogger;
 import com.arjuna.ats.arjuna.logging.FacilityCode;
@@ -43,13 +42,16 @@
 import com.arjuna.ats.arjuna.gandiva.ClassName;
 import com.arjuna.ats.arjuna.gandiva.ObjectName;
 import com.arjuna.ats.arjuna.utils.FileLock;
-import java.io.*;
+import com.arjuna.ats.arjuna.utils.Utility;
+
 import java.util.Hashtable;
 
 import com.arjuna.ats.arjuna.exceptions.ObjectStoreException;
 import java.lang.NumberFormatException;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
 import java.io.IOException;
-import java.io.FileNotFoundException;
 
 import com.arjuna.common.util.logging.*;
 
@@ -73,6 +75,7 @@
  * @message com.arjuna.ats.internal.arjuna.objectstore.FileSystemStore_6 [com.arjuna.ats.internal.arjuna.objectstore.FileSystemStore_6] - FileSystemStore::setupStore - error from unpack object store.
  * @message com.arjuna.ats.internal.arjuna.objectstore.FileSystemStore_7 [com.arjuna.ats.internal.arjuna.objectstore.FileSystemStore_7] - FileSystemStore::allTypes - could not pack entry string.
  * @message com.arjuna.ats.internal.arjuna.objectstore.FileSystemStore_8 [com.arjuna.ats.internal.arjuna.objectstore.FileSystemStore_8] - FileSystemStore::createHierarchy - null directory name.
+ * @message com.arjuna.ats.internal.arjuna.objectstore.FileSystemStore_20 [com.arjuna.ats.internal.arjuna.objectstore.FileSystemStore_20] - FileSystemStore.renameFromTo - from {0} not present. Possibly renamed by crash recovery.
  */
 
 public abstract class FileSystemStore extends ObjectStoreImple
@@ -467,6 +470,160 @@
 	return fileLock.unlock();
     }
     
+    /**
+     * 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 closeAndUnlock (File fd, FileInputStream ifile, FileOutputStream ofile)
+    {
+        if (tsLogger.arjLogger.debugAllowed())
+        {
+            tsLogger.arjLogger.debug(DebugLevel.FUNCTIONS, VisibilityLevel.VIS_PRIVATE,
+                                     FacilityCode.FAC_OBJECT_STORE, "FileSystemStore.closeAndUnlock("+fd+", "+ifile+", "+ofile+")");
+        }
+        
+        boolean closedOk = unlock(fd);
+
+        try
+        {
+            if (ifile != null)
+                ifile.close();
+            else
+                ofile.close();
+        }
+        catch (Exception e)
+        {
+            closedOk = false;
+        }
+
+        return closedOk;
+    }
+
+    protected File openAndLock (String fname, int lmode, boolean create) throws ObjectStoreException
+    {
+        if (tsLogger.arjLogger.debugAllowed())
+        {
+            tsLogger.arjLogger.debug(DebugLevel.FUNCTIONS, VisibilityLevel.VIS_PRIVATE,
+                                     FacilityCode.FAC_OBJECT_STORE, "FileSystemStore.openAndLock("+fname+", "+FileLock.modeString(lmode)+", "+create+")");
+        }
+        
+        //      File fd = (File) FdCache(fname);
+        File fd = null;
+        
+        if (fd == null)
+        {
+            fd = new File(fname);
+
+            if (!fd.exists())
+            {
+                if (createHierarchy(fname))
+                {
+                    if (!lock(fd, lmode, create))
+                    {
+                        return null;
+                    }
+                    else
+                        return fd;
+                }
+                else
+                    throw new ObjectStoreException("FileSystemStore.openAndLock failed to create hierarchy "+fname);
+            }
+
+            if (!lock(fd, lmode, create))
+                fd = null;
+        }
+
+        return fd;
+    }
+
+    /*
+     * Renaming on Unix works if the file to rename to already exists.
+     * However, on Windows if the file exists then rename fails! So, we
+     * need to delete the file to rename to before we can rename. But, we
+     * must ensure that we don't get into any race conditions, so we
+     * exclusively lock the file to be deleted first. Failure scenarios:
+     *
+     * (i) if we crash after deleting, but before we have had a chance to
+     *     rename the file, then the lock file will exist and prevent anyone
+     *     from deleting the shadow.
+     *
+     * (ii) if we crash after renaming and before the lock file is removed
+     *      then subsequent lock attempts will fail, and the sys admin will
+     *      have to resolve. But consistency will be maintained!
+     *
+     * When JDK 1.4 supports appears we will use the file-locking facility
+     * it provides.
+     *
+     * We have to use locks at deletion, but an implementation such as
+     * ShadowNoFileLockStore can still get away with no locking elsewhere
+     * to improve performance.
+     */
+
+    protected synchronized final boolean renameFromTo (File from, File to)
+    {
+        if (!isWindows)
+            return from.renameTo(to);
+        else
+        {
+            //      FileLock fl = new FileLock(to);
+                
+            if (!from.exists())
+            {
+                /*
+                 * from is in the cache, but not on disk. So, either
+                 *
+                 * (i) two different users are using the same state and
+                 *     should have been using the OS_SHARED flag.
+                 *
+                 * or
+                 *
+                 * (ii) crash recovery has recovered the state.
+                 *
+                 * If (ii) we can't force OS_SHARED on all users for the
+                 * minority case. So, assume (ii) and issue a warning.
+                 */
+
+                removeFromCache(from.toString());
+
+                if (tsLogger.arjLoggerI18N.isWarnEnabled())
+                {
+                    tsLogger.arjLoggerI18N.warn("com.arjuna.ats.internal.arjuna.objectstore.FileSystemStore_20",
+                                                new Object[]{from});
+                }
+
+                return true;
+            }
+            
+            /*
+             * Let let crash recovery deal with this!
+             */
+
+            //      if (fl.lock(FileLock.F_WRLCK))
+            {
+                to.delete();
+
+                boolean res = from.renameTo(to);
+
+                //              fl.unlock();
+                
+                return true;
+            }
+            /*
+            else
+            {
+                if (tsLogger.arjLoggerI18N.isWarnEnabled())
+                {
+                    tsLogger.arjLoggerI18N.warn("com.arjuna.ats.internal.arjuna.objectstore.ShadowingStore_21",
+                                                new Object[]{to});
+                }
+
+                return false;
+                }*/
+        }
+    }
+
     protected FileSystemStore (int ss)
     {
 	super(ss);
@@ -534,13 +691,13 @@
      */
 
     protected String genPathName (Uid objUid,
-				  String tName, int m) throws ObjectStoreException
+				  String tName, int ostype) throws ObjectStoreException
     {
 	if (tsLogger.arjLogger.debugAllowed())
 	{
 	    tsLogger.arjLogger.debug(DebugLevel.FUNCTIONS, VisibilityLevel.VIS_PROTECTED,
 				     FacilityCode.FAC_OBJECT_STORE, 
-				     "FileSystemStore.genPathName("+objUid+", "+tName+", "+m+")");
+				     "FileSystemStore.genPathName("+objUid+", "+tName+", "+ostype+")");
 	}
 	
 	String storeName = locateStore(getStoreName());
@@ -879,5 +1036,7 @@
 	    }
 	}
     }
+    
+    private static boolean isWindows = Utility.isWindows();
 
 }

Added: labs/jbosstm/trunk/ArjunaCore/arjuna/classes/com/arjuna/ats/internal/arjuna/objectstore/LogStore.java
===================================================================
--- labs/jbosstm/trunk/ArjunaCore/arjuna/classes/com/arjuna/ats/internal/arjuna/objectstore/LogStore.java	                        (rev 0)
+++ labs/jbosstm/trunk/ArjunaCore/arjuna/classes/com/arjuna/ats/internal/arjuna/objectstore/LogStore.java	2009-02-09 19:13:30 UTC (rev 25171)
@@ -0,0 +1,1733 @@
+/*
+ * 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.io.FileNotFoundException;
+import java.io.RandomAccessFile;
+import java.io.IOException;
+import java.io.SyncFailedException;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Stack;
+
+import com.arjuna.ats.arjuna.ArjunaNames;
+import com.arjuna.ats.arjuna.common.Environment;
+import com.arjuna.ats.arjuna.common.Uid;
+import com.arjuna.ats.arjuna.common.arjPropertyManager;
+import com.arjuna.ats.arjuna.objectstore.ObjectStore;
+import com.arjuna.ats.arjuna.objectstore.ObjectStoreType;
+import com.arjuna.ats.arjuna.state.*;
+import com.arjuna.ats.arjuna.utils.FileLock;
+import com.arjuna.ats.arjuna.gandiva.ClassName;
+import com.arjuna.ats.arjuna.gandiva.ObjectName;
+
+import com.arjuna.common.util.logging.*;
+
+import com.arjuna.ats.arjuna.exceptions.ObjectStoreException;
+
+import com.arjuna.ats.arjuna.logging.tsLogger;
+import com.arjuna.ats.arjuna.logging.FacilityCode;
+import com.arjuna.ats.internal.arjuna.objectstore.LogInstance.TransactionData;
+
+/**
+ * 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 Mark Little (mark at 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 mlittle
+ *
+ */
+
+class LogInstance
+{	
+	public class TransactionData
+	{
+		TransactionData (final Uid tx, final long off, final LogInstance parent)
+		{
+			txId = tx;
+			offset = off;
+			container = parent;
+		}
+		
+		public final Uid txId;
+		public final long offset;
+		public final LogInstance container;
+	}
+	
+	public LogInstance(String tn, long size)
+	{
+		_logName = new Uid();
+		_typeName = tn;
+		_frozen = false;
+		_totalSize = 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 _frozen;
+	}
+
+	public final void freeze() // one way operation.
+	{
+		_frozen = true;
+	}
+
+	public final int numberOfUsers()
+	{
+		return _transactions.size();
+	}
+
+	public final Uid getName()
+	{
+		return _logName;
+	}
+
+	public final String getTypeName()
+	{
+		return _typeName;
+	}
+	
+	public final InputObjectState allObjUids () throws ObjectStoreException
+	{
+		OutputObjectState state = new OutputObjectState();
+		Iterator<Uid> iter = _ids.keySet().iterator();
+		
+		try
+		{
+			while (iter.hasNext())
+			{
+				iter.next().pack(state);
+			}
+			
+			// don't forget to null terminate
+			
+			Uid.nullUid().pack(state);
+		}
+		catch (final IOException ex)
+		{
+			throw new ObjectStoreException(ex);
+		}
+
+		return new InputObjectState(state);
+	}
+	
+	public final boolean present(Uid id)
+	{
+		return _ids.containsKey(id);
+	}
+	
+	public final TransactionData getTxId (Uid txId)
+	{
+		return new TransactionData(txId, _used, this);
+	}
+	
+	public final TransactionData addTxId (Uid txId, long size)
+	{
+		TransactionData td = new TransactionData(txId, _used, this);
+		
+		_transactions.add(td);  // allow multiple entries in the same log
+		_ids.put(txId, txId);
+		_used += size;
+		
+		return td;
+	}
+	
+	public final long remaining()
+	{
+		return _totalSize - _used;
+	}
+	
+	public final void resize (long size)
+	{
+		_totalSize = size;
+	}
+
+	public String toString()
+	{
+		return "LogInstance < " + _logName + ", " + _typeName + ", "
+				+ numberOfUsers() + ", " + remaining() + " >";
+	}
+
+	private Uid _logName;
+	private String _typeName;
+	private boolean _frozen;
+	private Stack<TransactionData> _transactions = new Stack<TransactionData>();
+	private HashMap<Uid, Uid> _ids = new HashMap<Uid, Uid>();
+	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 t, final Uid u, final int s)
+		{
+			tn = t;
+			uid = u;
+			state = 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, DEFAULT_PURGE_TIME);
+	}
+
+	public LogPurger(LogStore instance, long purgeTime)
+	{
+		_objStore = instance;
+		_purgeTime = purgeTime;
+	}
+
+	public void addRemovedState(final Uid u, final String tn, final int state)
+	{
+		synchronized (_entries)
+		{
+			_entries.put(u, new LogElement(tn, u, state));
+		}
+	}
+
+	public void purge()
+	{
+		try
+		{
+			_objStore.truncateLogs(true);
+		}
+		catch (final Exception ex)
+		{
+		}
+	}
+
+	public void writeRemovalEntries()
+	{
+		synchronized (_entries)
+		{
+			if (_entries.size() > 0)
+			{
+				Collection<LogElement> entries = _entries.values();
+				Iterator<LogElement> iter = entries.iterator();
+
+				while (iter.hasNext())
+				{
+					LogElement val = iter.next();
+
+					try
+					{
+						_objStore.removeState(val.uid, val.tn, val.state);
+					}
+					catch (final Exception ex)
+					{
+						// TODO log warning, but there's nothing else we can do.
+					}
+				}
+
+				_entries.clear();
+			}
+		}
+	}
+
+	/**
+	 * Poke the thread into doing some work even if it normally
+	 * would not.
+	 */
+	
+	public void trigger ()
+	{
+		synchronized (_lock)
+		{
+			if (_status == Status.PASSIVE)
+				_lock.notify();
+		}
+	}
+	
+	public void run()
+	{
+		for (;;)
+		{
+			// TODO activate thread during read and get it to write deleted states
+			
+			try
+			{
+				synchronized (_lock)
+				{
+					_status = Status.PASSIVE;
+					
+					_lock.wait(_purgeTime);
+				}
+			}
+			catch (final Exception ex)
+			{
+				_status = Status.ACTIVE;
+			}
+			
+			/*
+			 * Write any asynchronous delete records.
+			 */
+			
+			System.err.println("**THREAD RUNNING");
+			
+			writeRemovalEntries();
+
+			/*
+			 * Now truncate any logs we've been working on.
+			 */
+			
+			try
+			{
+				_objStore.truncateLogs();
+			}
+			catch (final Exception ex)
+			{
+			}
+		}
+		
+		// _status = Status.TERMINATED;
+	}
+
+	private HashMap<Uid, LogElement> _entries = new HashMap<Uid, LogElement>();
+	private long _purgeTime;
+	private LogStore _objStore;
+	private Status _status;
+	private Object _lock = new Object();
+}
+
+class PurgeShutdownHook extends Thread
+{
+	public PurgeShutdownHook(LogPurger purger)
+	{
+		_purger = purger;
+	}
+
+	public void run()
+	{
+		_purger.writeRemovalEntries(); // flush everything in the cache first.
+		_purger.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";
+
+	public int typeIs()
+	{
+		return ObjectStoreType.ACTIONLOG;
+	}
+
+	/**
+	 * 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 objUid, String 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(tName, ios, ObjectStore.OS_UNKNOWN))
+		{
+			Uid tempUid = new Uid(Uid.nullUid());
+			
+			do
+			{
+				try
+				{
+					tempUid.unpack(ios);
+				}
+				catch (final Exception ex)
+				{
+					ex.printStackTrace();
+					
+					return ObjectStore.OS_UNKNOWN;
+				}
+				
+				if (tempUid.equals(objUid))
+					return ObjectStore.OS_COMMITTED;
+				
+			} while (tempUid.notEquals(Uid.nullUid()));
+			
+			return ObjectStore.OS_UNKNOWN;
+		}
+		else
+			return ObjectStore.OS_UNKNOWN;
+	}
+
+	/**
+	 * 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 objUid, String tName)
+			throws ObjectStoreException
+	{
+		return true;
+	}
+
+	public boolean hide_state(Uid u, String tn) throws ObjectStoreException
+	{
+		if (tsLogger.arjLogger.debugAllowed())
+		{
+			tsLogger.arjLogger.debug(DebugLevel.FUNCTIONS,
+					VisibilityLevel.VIS_PUBLIC, FacilityCode.FAC_OBJECT_STORE,
+					"LogStore.hide_state(" + u + ", " + tn + ")");
+		}
+
+		return false;
+	}
+
+	public boolean reveal_state(Uid u, String tn) throws ObjectStoreException
+	{
+		if (tsLogger.arjLogger.debugAllowed())
+		{
+			tsLogger.arjLogger.debug(DebugLevel.FUNCTIONS,
+					VisibilityLevel.VIS_PUBLIC, FacilityCode.FAC_OBJECT_STORE,
+					"LogStore.reveal_state(" + u + ", " + tn + ")");
+		}
+
+		return false;
+	}
+
+	public InputObjectState read_uncommitted(Uid u, String tn)
+			throws ObjectStoreException
+	{
+		if (tsLogger.arjLogger.debugAllowed())
+		{
+			tsLogger.arjLogger.debug(DebugLevel.FUNCTIONS,
+					VisibilityLevel.VIS_PUBLIC, FacilityCode.FAC_OBJECT_STORE,
+					"LogStore.read_uncommitted(" + u + ", " + tn + ")");
+		}
+
+		return null;
+	}
+
+	public boolean remove_uncommitted(Uid u, String tn)
+			throws ObjectStoreException
+	{
+		if (tsLogger.arjLogger.debugAllowed())
+		{
+			tsLogger.arjLogger.debug(DebugLevel.FUNCTIONS,
+					VisibilityLevel.VIS_PUBLIC, FacilityCode.FAC_OBJECT_STORE,
+					"LogStore.remove_uncommitted(" + u + ", " + tn + ")");
+		}
+
+		return false;
+	}
+
+	public boolean write_committed(Uid storeUid, String tName,
+			OutputObjectState state) throws ObjectStoreException
+	{
+		if (tsLogger.arjLogger.debugAllowed())
+		{
+			tsLogger.arjLogger
+					.debug(DebugLevel.FUNCTIONS, VisibilityLevel.VIS_PUBLIC,
+							FacilityCode.FAC_OBJECT_STORE,
+							"LogStore.write_committed(" + storeUid + ", "
+									+ tName + ")");
+		}
+
+		try
+		{
+			return super.write_committed(storeUid, tName, state);
+		}
+		catch (ObjectStoreException ex)
+		{
+			removeFromLog(storeUid);
+
+			throw ex;
+		}
+	}
+
+	public boolean write_uncommitted(Uid u, String tn, OutputObjectState s)
+			throws ObjectStoreException
+	{
+		if (tsLogger.arjLogger.debugAllowed())
+		{
+			tsLogger.arjLogger.debug(DebugLevel.FUNCTIONS,
+					VisibilityLevel.VIS_PUBLIC, FacilityCode.FAC_OBJECT_STORE,
+					"LogStore.write_uncommitted(" + u + ", " + tn + ", " + s
+							+ ")");
+		}
+
+		return false;
+	}
+
+	public boolean allLogUids (String tName, InputObjectState state, int match) throws ObjectStoreException
+	{
+		return super.allObjUids(tName, state, match);
+	}
+	
+	/**
+	 * 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 tName, InputObjectState state, int 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.
+		
+		_purger.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(tName, logs, match))
+			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.unpack(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<InputObjectState> txs = scanLog(logName, tName);
+	
+						if (txs.size() > 0)
+						{
+							for (int i = 0; i < txs.size(); i++)
+							{
+								txs.get(i).stateUid().pack(objUids);
+							}
+						}
+					}
+				} while (logName.notEquals(Uid.nullUid()));
+				
+				// remember null terminator
+				
+				Uid.nullUid().pack(objUids);
+				
+				state.setBuffer(objUids.buffer());
+			}
+			catch (final IOException ex)
+			{
+				ex.printStackTrace();
+				
+				return false;
+			}
+			
+			return true;
+		}
+	}
+
+	public ClassName className()
+	{
+		return ArjunaNames.Implementation_ObjectStore_ActionLogStore();
+	}
+
+	public static ClassName name()
+	{
+		return ArjunaNames.Implementation_ObjectStore_ActionLogStore();
+	}
+
+	/**
+	 * Have to return as a ShadowingStore because of inheritance.
+	 */
+
+	public static FileSystemStore create()
+	{
+		return new LogStore("");
+	}
+
+	/**
+	 * @message com.arjuna.ats.internal.arjuna.objectstore.LogStore_2
+	 *          [com.arjuna.ats.internal.arjuna.objectstore.LogStore_2] -
+	 *          LogStore.create caught: {0}
+	 * @message com.arjuna.ats.internal.arjuna.objectstore.LogStore_3
+	 *          [com.arjuna.ats.internal.arjuna.objectstore.LogStore_3] - Could
+	 *          not parse {0} as log size: using default value.
+	 */
+
+	public static FileSystemStore create(Object[] param)
+	{
+		if (param == null)
+			return null;
+
+		String location = (String) param[0];
+		Integer shareStatus = (Integer) param[1];
+		int ss = ObjectStore.OS_UNSHARED;
+
+		if (shareStatus != null)
+		{
+			try
+			{
+				if (shareStatus.intValue() == ObjectStore.OS_SHARED)
+					ss = ObjectStore.OS_SHARED;
+			}
+			catch (Exception e)
+			{
+				if (tsLogger.arjLoggerI18N.isWarnEnabled())
+				{
+					tsLogger.arjLoggerI18N
+							.warn(
+									"com.arjuna.ats.internal.arjuna.objectstore.LogStore_2",
+									new Object[]
+									{ e });
+				}
+			}
+		}
+
+		return new LogStore(location, ss);
+	}
+
+	public static FileSystemStore create(ObjectName param)
+	{
+		if (param == null)
+			return null;
+		else
+			return new LogStore(param);
+	}
+
+	protected LogStore(String locationOfStore)
+	{
+		this(locationOfStore, ObjectStore.OS_UNSHARED);
+	}
+
+	protected LogStore(String locationOfStore, int shareStatus)
+	{
+		super(shareStatus);
+
+		try
+		{
+			setupStore(locationOfStore);
+		}
+		catch (ObjectStoreException e)
+		{
+			if (tsLogger.arjLoggerI18N.isWarnEnabled())
+				tsLogger.arjLogger.warn(e.getMessage());
+
+			super.makeInvalid();
+
+			throw new com.arjuna.ats.arjuna.exceptions.FatalError(e.toString());
+		}
+	}
+
+	protected LogStore()
+	{
+		this(ObjectStore.OS_UNSHARED);
+	}
+
+	protected LogStore(int shareStatus)
+	{
+		super(shareStatus);
+	}
+
+	protected LogStore(ObjectName objName)
+	{
+		super(objName);
+	}
+
+	protected synchronized boolean setupStore(String location)
+			throws ObjectStoreException
+	{
+		if (!checkSync)
+		{
+			String syncOpt = arjPropertyManager.propertyManager
+					.getProperty(Environment.TRANSACTION_SYNC);
+
+			if (syncOpt != null)
+			{
+				if (syncOpt.compareTo("OFF") == 0)
+					syncOff();
+				else
+					syncOn();
+			}
+			else
+				syncOn();
+
+			checkSync = true;
+		}
+
+		if (_purger == null)
+		{
+			_purger = new LogPurger(this, _purgeTime);
+			_purger.setDaemon(true);
+
+			Runtime.getRuntime()
+					.addShutdownHook(new PurgeShutdownHook(_purger));
+
+			_purger.start();
+		}
+
+		return super.setupStore(location);
+	}
+
+	/**
+	 * 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 fd, RandomAccessFile rf)
+	{
+		if (tsLogger.arjLogger.debugAllowed())
+		{
+			tsLogger.arjLogger.debug(DebugLevel.FUNCTIONS,
+					VisibilityLevel.VIS_PRIVATE, FacilityCode.FAC_OBJECT_STORE,
+					"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 objUid, String tName,
+			OutputObjectState state, int ft) throws ObjectStoreException
+	{
+		if (tsLogger.arjLogger.debugAllowed())
+		{
+			tsLogger.arjLogger.debug(DebugLevel.FUNCTIONS,
+					VisibilityLevel.VIS_PROTECTED,
+					FacilityCode.FAC_OBJECT_STORE,
+					"ShadowingStore.write_state(" + objUid + ", " + tName
+							+ ", " + ObjectStore.stateTypeString(ft) + ")");
+		}
+
+		if (!storeValid())
+			return false;
+
+		String fname = null;
+		File fd = null;
+		
+		if (tName != null)
+		{
+			int imageSize = (int) state.length();
+			byte[] uidString = objUid.stringForm().getBytes();
+			int buffSize = _redzone.length + 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(objUid, tName, buffSize);		// always adds entry to log
+				LogInstance theLog = theLogEntry.container;
+			
+				if (theLog == null)
+					throw new ObjectStoreException();
+
+				fname = genPathName(theLog.getName(), tName, ft);
+				fd = openAndLock(fname, FileLock.F_WRLCK, true);
+
+				if (fd == null)
+				{
+					if (tsLogger.arjLoggerI18N.isWarnEnabled())
+					{
+						tsLogger.arjLoggerI18N.warn(
+								"com.arjuna.ats.internal.arjuna.objectstore.ShadowingStore_18",
+								new Object[]
+								           { fname });
+					}
+
+					return false;
+				}
+
+				boolean setLength = !fd.exists();
+
+				try
+				{
+					ofile = new RandomAccessFile(fd, FILE_MODE);
+					
+					if (setLength)
+					{
+						ofile.setLength(_maxFileSize);
+					}
+					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(_redzone);
+					buff.putInt(uidString.length);
+					buff.put(uidString);
+					buff.putInt(imageSize);
+					buff.put(state.buffer());
+
+					synchronized (_lock)
+					{
+						ofile.seek(theLogEntry.offset);
+
+						ofile.write(buff.array());
+					}
+				}
+				catch (SyncFailedException e)
+				{
+					unlockAndClose(fd, ofile);
+
+					throw new ObjectStoreException(
+							"ShadowingStore::write_state() - write failed to sync for "
+							+ fname);
+				}
+				catch (FileNotFoundException e)
+				{
+					unlockAndClose(fd, ofile);
+
+					e.printStackTrace();
+
+					throw new ObjectStoreException(
+							"ShadowingStore::write_state() - write failed to locate file "
+							+ fname + ": " + e);
+				}
+				catch (IOException e)
+				{
+					unlockAndClose(fd, ofile);
+
+					e.printStackTrace();
+
+					throw new ObjectStoreException(
+							"ShadowingStore::write_state() - write failed for "
+							+ fname + ": " + e);
+				}
+				finally
+				{
+					try
+					{
+						if (lock != null)
+							lock.release();
+					}
+					catch (IOException ex)
+					{
+						ex.printStackTrace();
+					}
+				}
+			}
+			
+			if (!unlockAndClose(fd, ofile))
+			{
+				if (tsLogger.arjLoggerI18N.isWarnEnabled())
+				{
+					tsLogger.arjLoggerI18N
+							.warn(
+									"com.arjuna.ats.internal.arjuna.objectstore.ShadowingStore_19",
+									new Object[]
+									{ fname });
+				}
+			}
+
+			super.addToCache(fname);
+
+			return true;
+		}
+		else
+			throw new ObjectStoreException(
+					"ShadowStore::write_state - "
+							+ tsLogger.log_mesg
+									.getString("com.arjuna.ats.internal.arjuna.objectstore.notypenameuid")
+							+ objUid);
+	}
+
+	/**
+	 * Shouldn't be called during normal execution only during recovery.
+	 */
+
+	protected InputObjectState read_state(Uid u, String tn, int 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!
+		 */
+		
+		_purger.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(u, tn, -1);
+		
+		if (td == null)
+			throw new ObjectStoreException();
+		
+		ArrayList<InputObjectState> states = 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 u, String tn, int 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 (_synchronousRemoval)
+			{
+				OutputObjectState removalState = new OutputObjectState(u, tn);
+
+				removalState.packBytes(_removedState);
+
+				if (!write_state(u, tn, removalState, s))
+					throw new ObjectStoreException();
+			}
+			else
+				_purger.addRemovedState(u, tn, s);
+		}
+		catch (IOException ex)
+		{
+			throw new ObjectStoreException(ex.toString());
+		}
+		finally
+		{
+			removeFromLog(u);
+		}
+
+		return true;
+	}
+
+	protected boolean lock(File fd, int lmode, boolean create)
+	{
+		return true;
+	}
+
+	protected boolean unlock(File fd)
+	{
+		return true;
+	}
+
+	protected String genPathName (Uid objUid, String tName, int ft) throws ObjectStoreException
+    {
+		String fname = super.genPathName(objUid, tName, ft);
+
+		if (ft == ObjectStore.OS_UNCOMMITTED)
+			fname = fname + HIDDENCHAR;
+
+		return fname;
+    }
+
+	boolean removeState(Uid u, String tn, int s) throws ObjectStoreException
+	{
+		try
+		{
+			OutputObjectState removalState = new OutputObjectState(u, tn);
+
+			removalState.packBytes(_removedState);
+			
+			if (!write_state(u, tn, removalState, s))
+				throw new ObjectStoreException();
+		}
+		catch (IOException ex)
+		{
+			throw new ObjectStoreException(ex.toString());
+		}
+
+		return true;
+	}
+
+	boolean truncateLogs () throws ObjectStoreException
+	{
+		return truncateLogs(false);
+	}
+	
+	boolean truncateLogs (boolean force) throws ObjectStoreException
+	{
+		synchronized (_logNames)
+		{
+			Iterator<LogInstance> iter = _logNames.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(log, force);
+				}
+				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 log, boolean force) throws ObjectStoreException
+	{
+		boolean delete = false;
+		
+		synchronized (_lock)
+		{
+			File fd = new File(genPathName(log.getName(), log.getTypeName(), ObjectStore.OS_COMMITTED));
+
+			try
+			{
+				/*
+				 * Create a list of ObjectState entries.
+				 */
+
+				ArrayList<InputObjectState> objectStates = 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(), ObjectStore.OS_UNCOMMITTED);
+					File fd2 = openAndLock(fname, FileLock.F_WRLCK, true);
+					RandomAccessFile oFile = new RandomAccessFile(fd2, FILE_MODE);
+					int size = 0;
+					
+					oFile.setLength(_maxFileSize);
+
+					for (int i = 0; i < objectStates.size(); i++)
+					{
+						byte[] uidString = objectStates.get(i).stateUid().stringForm().getBytes();
+						int buffSize = _redzone.length + uidString.length + objectStates.get(i).buffer().length + 8;
+						java.nio.ByteBuffer buff = java.nio.ByteBuffer.allocate(buffSize);
+
+						size += buffSize;
+
+						try
+						{
+							buff.put(_redzone);
+							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(fd2, oFile);
+							
+							throw new ObjectStoreException(ex.toString());
+						}
+					}
+
+					try
+					{
+						if (force)
+						{
+							oFile.setLength(size);
+						
+							log.freeze();
+						}
+
+						fd2.renameTo(fd);
+					}
+					catch (final Exception ex)
+					{
+						ex.printStackTrace();
+						
+						// TODO log
+
+						throw new ObjectStoreException(ex.toString());
+					}
+					finally
+					{
+						unlockAndClose(fd2, oFile);
+					}
+				}
+				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());
+			}
+		}
+		
+		return delete;
+	}
+
+	private final ArrayList<InputObjectState> scanLog (final Uid logName, final String typeName) throws ObjectStoreException
+	{
+		/*
+		 * Make sure no new entries can be created while we scan.
+		 */
+		
+		synchronized (_lock)
+		{
+			try
+			{
+				String fname = genPathName(logName, typeName, ObjectStore.OS_COMMITTED);
+				File fd = openAndLock(fname, FileLock.F_WRLCK, true);
+				RandomAccessFile iFile = new RandomAccessFile(fd, FILE_MODE);
+				// iFile.getChannel().lock();
+
+				try
+				{
+					/*
+					 * Create a list of ObjectState entries.
+					 */
+
+					ArrayList<InputObjectState> objectStates = new ArrayList<InputObjectState>();
+
+					iFile.seek(0); // make sure we're at the start
+					
+					while (iFile.getFilePointer() < iFile.length())
+					{
+						byte[] buff = new byte[_redzone.length];
+
+						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)
+							{
+								ex.printStackTrace();
+
+								throw new ObjectStoreException(ex.toString());
+							}
+						}
+					}
+
+					unlockAndClose(fd, iFile);
+					iFile = null;
+
+					/*
+					 * At this stage we now have a list of ObjectState entries.
+					 * Now we need to go through and prune the list. This is
+					 * complicated by the fact that there can be 1.. entries for
+					 * a specific transaction since we continually update the
+					 * log as we drive recovery. If an entry hasn't been deleted
+					 * then we will keep the latest one we find.
+					 */
+
+					/*
+					 * First search for those entries that have been deleted.
+					 */
+
+					ArrayList<InputObjectState> deletedLogs = new ArrayList<InputObjectState>();
+
+					for (int i = 0; i < objectStates.size(); i++)
+					{
+						InputObjectState curr = objectStates.get(i);
+
+						try
+						{
+							if (Arrays.equals(curr.unpackBytes(), _removedState))
+							{
+								deletedLogs.add(curr);
+							}
+							else
+								curr.reread();  // don't forget to reset the read pointer!
+						}
+						catch (final Exception ex)
+						{
+							// if not a delete record then the first entry won't
+							// be an the defined byte array.
+						}
+					}
+
+					if (deletedLogs.size() > 0)
+					{
+						/*
+						 * make sure we remove them from the first list to save time.
+						 */
+						
+						objectStates.removeAll(deletedLogs);
+
+						deleteEntries(objectStates, deletedLogs);
+
+						/*
+						 * At this stage we should only have entries that refer
+						 * to in-flight transactions. Go through the list and
+						 * remove N-1 references for each transaction id.
+						 */
+
+						pruneEntries(objectStates);
+
+						/*
+						 * Now return the list of committed entries.
+						 */
+						
+						return objectStates;
+					}
+					else
+						return objectStates;
+				}
+				finally
+				{
+					if (iFile != null)
+						unlockAndClose(fd, iFile);
+				}
+			}
+			catch (final ObjectStoreException ex)
+			{
+				ex.printStackTrace();
+
+				throw ex;
+			}
+			catch (final Exception ex)
+			{
+				ex.printStackTrace();
+
+				throw new ObjectStoreException(ex.toString());
+			}
+		}
+	}
+	
+	private final boolean redzoneProtected(final byte[] buff)
+	{
+		for (int i = 0; i < _redzone.length; i++)
+		{
+			if (buff[i] != _redzone[i])
+				return false;
+		}
+
+		return true;
+	}
+
+	private final void deleteEntries(ArrayList<InputObjectState> allStates,
+			ArrayList<InputObjectState> deletedStates)
+	{
+		/*
+		 * Look through the remaining states for entries that have been deleted.
+		 */
+
+		for (int i = 0; i < deletedStates.size(); i++)
+		{
+			Uid txId = deletedStates.get(i).stateUid();
+
+			for (int j = 0; j < allStates.size(); j++)
+			{
+				if (allStates.get(j).stateUid().equals(txId))
+					allStates.remove(j);
+			}
+		}
+
+		deletedStates.clear();
+	}
+
+	private final void pruneEntries(ArrayList<InputObjectState> allStates)
+	{
+		/*
+		 * The ArrayList is ordered with the earliest entries first.
+		 */
+
+		for (int j = allStates.size() - 1; j >= 0; j--)
+		{
+			Uid txId = allStates.get(j).stateUid();
+
+			for (int i = 0; i < j; i++)
+			{
+				if (allStates.get(i).stateUid().equals(txId))
+					allStates.remove(i);
+			}
+		}
+	}
+
+	/*
+	 * We maintain a list of log identifiers and the number of threads using
+	 * them. If a log size goes over the maximum allowed, then we swap all
+	 * threads to a new log with the exception of those that are currently using
+	 * the old log.
+	 * 
+	 * We always add a new entry to the log even if one already exists.
+	 * 
+	 * Because normally we are writing to the log we pass in the size that we need to 
+	 * accommodate. However, during recovery we need to read the state yet still
+	 * need the log name. So if we pass a size of -1 this signifies only to
+	 * return the log data and not allocate space for a new instance.
+	 */
+
+	private final TransactionData getLogName (Uid txid, String tName, long size)
+            throws ObjectStoreException
+    {
+        synchronized (_logNames)
+        {
+            Iterator<LogInstance> iter = _logNames.iterator();
+            LogInstance entry = null;
+
+            /*
+             * First check to see if the TxId is in an existing log. Always
+             * return the same log instance for the same txid so we can
+             * keep all data in the same location. This may mean that we have
+             * to extend the size of the log over time to accommodate situations
+             * where the log is modified but not deleted for a while, e.g., during
+             * recovery.
+             */
+            
+            while (iter.hasNext())
+            {
+                entry = (LogInstance) iter.next();
+
+                if (entry.present(txid))
+                {
+                	if (size == -1) // we are reading only
+                		return entry.getTxId(txid);
+                	else
+                		return entry.addTxId(txid, size);
+                }
+            }
+            
+            /*
+             * If we get here then this TxId isn't in one of the
+             * logs we are maintaining currently. So go back through
+             * the list of logs and find one that is small enough
+             * for us to use. The first one with room will do.
+             */
+            
+            iter = _logNames.iterator();
+            
+            while (iter.hasNext())
+            {
+                entry = (LogInstance) iter.next();
+                
+                if (!entry.isFrozen())
+                {
+                	if (entry.remaining() > size)
+                	{
+                        return entry.addTxId(txid, size);
+                    }
+                    else
+                    {
+                    	/*
+                    	 * TODO
+                    	 * 
+                    	 * When can we remove the information about this
+                    	 * log from memory? If we do it too soon then it's possible
+                    	 * that delete entries will not go into the right log. If we
+                    	 * leave it too late then the memory footprint increases. Prune
+                    	 * the entry when we prune the log from disk?
+                    	 */
+                    	
+                    	entry.freeze();
+                    }
+                }
+            }
+
+            // if we get here, then we need to create a new log
+
+            entry = new LogInstance(tName, _maxFileSize);
+            _logNames.add(entry);
+
+            return entry.addTxId(txid, size);
+        }
+    }
+
+	private final void removeFromLog(Uid txid)
+	{
+		if (_synchronousRemoval)
+		{
+			synchronized (_logNames)
+			{
+				Iterator<LogInstance> iter = _logNames.iterator();
+				LogInstance entry = null;
+
+				while (iter.hasNext())
+				{
+					entry = (LogInstance) iter.next();
+
+					if (entry.present(txid))
+					{
+						//entry.removeTxId(txid);
+						break;
+					}
+				}
+			}
+		}
+	}
+
+	private static boolean checkSync = false;
+
+	private static Object _lock = new Object();
+
+	private static ArrayList<LogInstance> _logNames = new ArrayList<LogInstance>();
+
+	private static long _maxFileSize = LOG_SIZE;
+	private static long _purgeTime = LogPurger.DEFAULT_PURGE_TIME;
+
+	private static LogPurger _purger;
+	private static boolean _synchronousRemoval = false;
+
+	private static final byte[] _redzone = { 0x2, 0x4, 0x6, 0x8 };
+
+	private static final byte[] _removedState = { 0xd, 0xe, 0xa, 0xd, 0xb, 0xe, 0xe, 0xf };
+
+	private static final char HIDDENCHAR = '~';
+	
+	static
+	{
+		try
+		{
+			String removeMarkers = arjPropertyManager.propertyManager
+					.getProperty(Environment.TRANSACTION_LOG_SYNC_REMOVAL,
+							"TRUE");
+
+			if (removeMarkers.equalsIgnoreCase("true"))
+				_synchronousRemoval = true;
+
+			String purgeTime = arjPropertyManager.propertyManager.getProperty(Environment.TRANSACTION_LOG_PURGE_TIME);
+			
+			if (purgeTime != null)
+			{
+				try
+				{
+					_purgeTime = Long.parseLong(purgeTime);
+				}
+				catch (final Exception ex)
+				{
+					ex.printStackTrace();
+				}
+			}
+			
+			String logSize = arjPropertyManager.propertyManager.getProperty(Environment.TRANSACTION_LOG_SIZE);
+
+			if (logSize != null)
+			{
+				try
+				{
+					_maxFileSize = Long.parseLong(logSize);
+				}
+				catch (final Exception ex)
+				{
+					if (tsLogger.arjLoggerI18N.isWarnEnabled())
+					{
+						tsLogger.arjLoggerI18N
+								.warn(
+										"com.arjuna.ats.internal.arjuna.objectstore.LogStore_3",
+										new Object[]
+										{ logSize });
+					}
+				}
+			}
+		}
+		catch (final Exception ex)
+		{
+			throw new ExceptionInInitializerError(ex.toString());
+		}
+	}
+}

Added: labs/jbosstm/trunk/ArjunaCore/arjuna/classes/com/arjuna/ats/internal/arjuna/objectstore/LogStoreSetup.java
===================================================================
--- labs/jbosstm/trunk/ArjunaCore/arjuna/classes/com/arjuna/ats/internal/arjuna/objectstore/LogStoreSetup.java	                        (rev 0)
+++ labs/jbosstm/trunk/ArjunaCore/arjuna/classes/com/arjuna/ats/internal/arjuna/objectstore/LogStoreSetup.java	2009-02-09 19:13:30 UTC (rev 25171)
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 1998, 1999, 2000,
+ *
+ * Arjuna Solutions Limited,
+ * Newcastle upon Tyne,
+ * Tyne and Wear,
+ * UK.  
+ *
+ * $Id: ActionStoreSetup.java,v 1.1 2003/06/19 10:50:32 nmcl Exp $
+ */
+
+package com.arjuna.ats.internal.arjuna.objectstore;
+
+import com.arjuna.ats.arjuna.ArjunaNames;
+import com.arjuna.ats.arjuna.gandiva.*;
+import com.arjuna.ats.arjuna.gandiva.inventory.*;
+
+public class LogStoreSetup implements InventoryElement
+{
+
+	public LogStoreSetup()
+	{
+	}
+
+	public synchronized Object createVoid()
+	{
+		return LogStore.create();
+	}
+
+	public synchronized Object createResources(Object[] param)
+	{
+		return LogStore.create(param);
+	}
+
+	public synchronized Object createClassName(ClassName className)
+	{
+		return null;
+	}
+
+	public synchronized Object createObjectName(ObjectName objectName)
+	{
+		return LogStore.create(objectName);
+	}
+
+	public synchronized Object createClassNameResources(ClassName className,
+			Object[] resources)
+	{
+		return null;
+	}
+
+	public synchronized Object createObjectNameResources(ObjectName objectName,
+			Object[] resources)
+	{
+		return null;
+	}
+
+	public ClassName className()
+	{
+		return ArjunaNames.Implementation_ObjectStore_ActionLogStore();
+	}
+
+}

Modified: labs/jbosstm/trunk/ArjunaCore/arjuna/classes/com/arjuna/ats/internal/arjuna/objectstore/ShadowingStore.java
===================================================================
--- labs/jbosstm/trunk/ArjunaCore/arjuna/classes/com/arjuna/ats/internal/arjuna/objectstore/ShadowingStore.java	2009-02-09 19:12:13 UTC (rev 25170)
+++ labs/jbosstm/trunk/ArjunaCore/arjuna/classes/com/arjuna/ats/internal/arjuna/objectstore/ShadowingStore.java	2009-02-09 19:13:30 UTC (rev 25171)
@@ -32,7 +32,6 @@
 package com.arjuna.ats.internal.arjuna.objectstore;
 
 import com.arjuna.ats.arjuna.ArjunaNames;
-import com.arjuna.ats.arjuna.coordinator.*;
 import com.arjuna.ats.arjuna.objectstore.ObjectStore;
 import com.arjuna.ats.arjuna.objectstore.ObjectStoreType;
 import com.arjuna.ats.arjuna.common.*;
@@ -854,165 +853,9 @@
 				     FacilityCode.FAC_OBJECT_STORE, "ShadowingStore.ShadowingStore( "+objName+" )");
 	}
     }
-    
-    /**
-     * 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.
-     */
 
-private boolean closeAndUnlock (File fd, FileInputStream ifile, FileOutputStream ofile)
-    {
-	if (tsLogger.arjLogger.debugAllowed())
-	{
-	    tsLogger.arjLogger.debug(DebugLevel.FUNCTIONS, VisibilityLevel.VIS_PRIVATE,
-				     FacilityCode.FAC_OBJECT_STORE, "ShadowingStore.closeAndUnlock("+fd+", "+ifile+", "+ofile+")");
-	}
-	
-	boolean closedOk = unlock(fd);
-
-	try
-	{
-	    if (ifile != null)
-		ifile.close();
-	    else
-		ofile.close();
-	}
-	catch (Exception e)
-	{
-	    closedOk = false;
-	}
-
-	return closedOk;
-    }
-
-private File openAndLock (String fname, int lmode, boolean create) throws ObjectStoreException
-    {
-	if (tsLogger.arjLogger.debugAllowed())
-	{
-	    tsLogger.arjLogger.debug(DebugLevel.FUNCTIONS, VisibilityLevel.VIS_PRIVATE,
-				     FacilityCode.FAC_OBJECT_STORE, "ShadowingStore.openAndLock("+fname+", "+FileLock.modeString(lmode)+", "+create+")");
-	}
-	
-	//	File fd = (File) FdCache(fname);
-	File fd = null;
-	
-	if (fd == null)
-	{
-	    fd = new File(fname);
-
-	    if (!fd.exists())
-	    {
-		if (createHierarchy(fname))
-		{
-		    if (!lock(fd, lmode, create))
-		    {
-			return null;
-		    }
-		    else
-			return fd;
-		}
-		else
-		    throw new ObjectStoreException("ShadowingStore.openAndLock failed to create hierarchy "+fname);
-	    }
-
-	    if (!lock(fd, lmode, create))
-		fd = null;
-	}
-
-	return fd;
-    }
-
-    /*
-     * Renaming on Unix works if the file to rename to already exists.
-     * However, on Windows if the file exists then rename fails! So, we
-     * need to delete the file to rename to before we can rename. But, we
-     * must ensure that we don't get into any race conditions, so we
-     * exclusively lock the file to be deleted first. Failure scenarios:
-     *
-     * (i) if we crash after deleting, but before we have had a chance to
-     *     rename the file, then the lock file will exist and prevent anyone
-     *     from deleting the shadow.
-     *
-     * (ii) if we crash after renaming and before the lock file is removed
-     *      then subsequent lock attempts will fail, and the sys admin will
-     *      have to resolve. But consistency will be maintained!
-     *
-     * When JDK 1.4 supports appears we will use the file-locking facility
-     * it provides.
-     *
-     * We have to use locks at deletion, but an implementation such as
-     * ShadowNoFileLockStore can still get away with no locking elsewhere
-     * to improve performance.
-     */
-
-private synchronized final boolean renameFromTo (File from, File to)
-    {
-	if (!isWindows)
-	    return from.renameTo(to);
-	else
-	{
-	    //	    FileLock fl = new FileLock(to);
-		
-	    if (!from.exists())
-	    {
-		/*
-		 * from is in the cache, but not on disk. So, either
-		 *
-		 * (i) two different users are using the same state and
-		 *     should have been using the OS_SHARED flag.
-		 *
-		 * or
-		 *
-		 * (ii) crash recovery has recovered the state.
-		 *
-		 * If (ii) we can't force OS_SHARED on all users for the
-		 * minority case. So, assume (ii) and issue a warning.
-		 */
-
-		super.removeFromCache(from.toString());
-
-		if (tsLogger.arjLoggerI18N.isWarnEnabled())
-		{
-		    tsLogger.arjLoggerI18N.warn("com.arjuna.ats.internal.arjuna.objectstore.ShadowingStore_20",
-						new Object[]{from});
-		}
-
-		return true;
-	    }
-	    
-	    /*
-	     * Let let crash recovery deal with this!
-	     */
-
-	    //	    if (fl.lock(FileLock.F_WRLCK))
-	    {
-		to.delete();
-
-		boolean res = from.renameTo(to);
-
-		//		fl.unlock();
-		
-		return true;
-	    }
-	    /*
-	    else
-	    {
-		if (tsLogger.arjLoggerI18N.isWarnEnabled())
-		{
-		    tsLogger.arjLoggerI18N.warn("com.arjuna.ats.internal.arjuna.objectstore.ShadowingStore_21",
-						new Object[]{to});
-		}
-
-		return false;
-		}*/
-	}
-    }
-
 public static final char HIDDINGCHAR = '#';
 public static final char SHADOWCHAR = '!';
-    
-private static boolean isWindows = Utility.isWindows();
 
 
 }

Modified: labs/jbosstm/trunk/ArjunaCore/arjuna/classes/com/arjuna/ats/internal/arjuna/recovery/TransactionStatusManagerItem.java
===================================================================
--- labs/jbosstm/trunk/ArjunaCore/arjuna/classes/com/arjuna/ats/internal/arjuna/recovery/TransactionStatusManagerItem.java	2009-02-09 19:12:13 UTC (rev 25170)
+++ labs/jbosstm/trunk/ArjunaCore/arjuna/classes/com/arjuna/ats/internal/arjuna/recovery/TransactionStatusManagerItem.java	2009-02-09 19:13:30 UTC (rev 25171)
@@ -95,7 +95,7 @@
     {
 	if ( _objectStore == null )
 	    {
-		_objectStore = TxControl.getStore();
+		_objectStore = TxControl.getRecoveryStore();
 	    }
 	return _objectStore;
     }

Added: labs/jbosstm/trunk/ArjunaCore/arjuna/tests/classes/com/hp/mwtests/ts/arjuna/objectstore/LogStoreReactivationTest.java
===================================================================
--- labs/jbosstm/trunk/ArjunaCore/arjuna/tests/classes/com/hp/mwtests/ts/arjuna/objectstore/LogStoreReactivationTest.java	                        (rev 0)
+++ labs/jbosstm/trunk/ArjunaCore/arjuna/tests/classes/com/hp/mwtests/ts/arjuna/objectstore/LogStoreReactivationTest.java	2009-02-09 19:13:30 UTC (rev 25171)
@@ -0,0 +1,67 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2006, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags.
+ * See the copyright.txt in the distribution for a
+ * full listing of individual contributors.
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License, v. 2.1.
+ * This program is distributed in the hope that it will be useful, but WITHOUT A
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ * PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
+ * You should have received a copy of the GNU Lesser General Public License,
+ * v.2.1 along with this distribution; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA  02110-1301, USA.
+ *
+ * (C) 2005-2009,
+ * @author JBoss Inc.
+ */
+/*
+ * Copyright (C) 2004,
+ *
+ * Arjuna Technologies Ltd,
+ * Newcastle upon Tyne,
+ * Tyne and Wear,
+ * UK.
+ *
+ * $Id: Performance2.java 2342 2006-03-30 13:06:17Z  $
+ */
+
+package com.hp.mwtests.ts.arjuna.objectstore;
+
+import com.arjuna.ats.arjuna.ArjunaNames;
+import com.arjuna.ats.arjuna.AtomicAction;
+import com.arjuna.ats.arjuna.common.Environment;
+import com.arjuna.ats.arjuna.common.Uid;
+import com.arjuna.ats.arjuna.coordinator.ActionStatus;
+import com.arjuna.ats.arjuna.recovery.RecoverAtomicAction;
+import com.hp.mwtests.ts.arjuna.resources.BasicRecord;
+
+public class LogStoreReactivationTest
+{
+
+	public static void main (String[] args)
+	{
+		System.setProperty(Environment.COMMIT_ONE_PHASE, "NO");
+		System.setProperty(Environment.OBJECTSTORE_TYPE, ArjunaNames.Implementation_ObjectStore_ActionLogStore().stringForm());
+		System.setProperty(Environment.TRANSACTION_LOG_SYNC_REMOVAL, "false");
+		System.setProperty(Environment.TRANSACTION_LOG_PURGE_TIME, "1000000");  // essentially infinite
+		
+		AtomicAction A = new AtomicAction();
+		Uid txId = A.get_uid();
+		
+		System.err.println("IMPORTANT: ignore warnings about USER_DEF_FIRST0 as they are expected due to BasicRecord usage!");
+		
+		A.begin();
+
+		A.add(new BasicRecord());
+
+		A.commit();
+		
+		RecoverAtomicAction rAA = new RecoverAtomicAction(txId, ActionStatus.COMMITTED);
+		
+		rAA.replayPhase2();
+	}
+}

Added: labs/jbosstm/trunk/ArjunaCore/arjuna/tests/classes/com/hp/mwtests/ts/arjuna/objectstore/LogStoreReactivationTest2.java
===================================================================
--- labs/jbosstm/trunk/ArjunaCore/arjuna/tests/classes/com/hp/mwtests/ts/arjuna/objectstore/LogStoreReactivationTest2.java	                        (rev 0)
+++ labs/jbosstm/trunk/ArjunaCore/arjuna/tests/classes/com/hp/mwtests/ts/arjuna/objectstore/LogStoreReactivationTest2.java	2009-02-09 19:13:30 UTC (rev 25171)
@@ -0,0 +1,67 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2006, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags.
+ * See the copyright.txt in the distribution for a
+ * full listing of individual contributors.
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License, v. 2.1.
+ * This program is distributed in the hope that it will be useful, but WITHOUT A
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ * PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
+ * You should have received a copy of the GNU Lesser General Public License,
+ * v.2.1 along with this distribution; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA  02110-1301, USA.
+ *
+ * (C) 2005-2009,
+ * @author JBoss Inc.
+ */
+/*
+ * Copyright (C) 2004,
+ *
+ * Arjuna Technologies Ltd,
+ * Newcastle upon Tyne,
+ * Tyne and Wear,
+ * UK.
+ *
+ * $Id: Performance2.java 2342 2006-03-30 13:06:17Z  $
+ */
+
+package com.hp.mwtests.ts.arjuna.objectstore;
+
+import com.arjuna.ats.arjuna.ArjunaNames;
+import com.arjuna.ats.arjuna.AtomicAction;
+import com.arjuna.ats.arjuna.common.Environment;
+import com.arjuna.ats.arjuna.common.Uid;
+import com.arjuna.ats.arjuna.coordinator.ActionStatus;
+import com.arjuna.ats.arjuna.recovery.RecoverAtomicAction;
+import com.hp.mwtests.ts.arjuna.resources.BasicRecord;
+
+public class LogStoreReactivationTest2
+{
+
+	public static void main (String[] args)
+	{
+		System.setProperty(Environment.COMMIT_ONE_PHASE, "NO");
+		System.setProperty(Environment.OBJECTSTORE_TYPE, ArjunaNames.Implementation_ObjectStore_ActionLogStore().stringForm());
+		System.setProperty(Environment.TRANSACTION_LOG_SYNC_REMOVAL, "true");
+		System.setProperty(Environment.TRANSACTION_LOG_PURGE_TIME, "1000000");  // essentially infinite
+		
+		AtomicAction A = new AtomicAction();
+		Uid txId = A.get_uid();
+		
+		System.err.println("IMPORTANT: if there are warnings about USER_DEF_FIRST0 then the test has failed!");
+		
+		A.begin();
+
+		A.add(new BasicRecord());
+
+		A.commit();
+		
+		RecoverAtomicAction rAA = new RecoverAtomicAction(txId, ActionStatus.COMMITTED);
+		
+		rAA.replayPhase2();
+	}
+}

Added: labs/jbosstm/trunk/ArjunaCore/arjuna/tests/classes/com/hp/mwtests/ts/arjuna/objectstore/LogStoreRecoveryTest.java
===================================================================
--- labs/jbosstm/trunk/ArjunaCore/arjuna/tests/classes/com/hp/mwtests/ts/arjuna/objectstore/LogStoreRecoveryTest.java	                        (rev 0)
+++ labs/jbosstm/trunk/ArjunaCore/arjuna/tests/classes/com/hp/mwtests/ts/arjuna/objectstore/LogStoreRecoveryTest.java	2009-02-09 19:13:30 UTC (rev 25171)
@@ -0,0 +1,171 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2006, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags.
+ * See the copyright.txt in the distribution for a
+ * full listing of individual contributors.
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License, v. 2.1.
+ * This program is distributed in the hope that it will be useful, but WITHOUT A
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ * PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
+ * You should have received a copy of the GNU Lesser General Public License,
+ * v.2.1 along with this distribution; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA  02110-1301, USA.
+ *
+ * (C) 2005-2006,
+ * @author JBoss Inc.
+ */
+/*
+ * Copyright (C) 2004,
+ *
+ * Arjuna Technologies Ltd,
+ * Newcastle upon Tyne,
+ * Tyne and Wear,
+ * UK.
+ *
+ * $Id: AllObjUidsTest.java 2342 2006-03-30 13:06:17Z  $
+ */
+
+package com.hp.mwtests.ts.arjuna.objectstore;
+
+import com.arjuna.ats.arjuna.ArjunaNames;
+import com.arjuna.ats.arjuna.objectstore.ObjectStore;
+import com.arjuna.ats.arjuna.state.*;
+import com.arjuna.ats.arjuna.common.Environment;
+import com.arjuna.ats.arjuna.common.Uid;
+import com.arjuna.ats.arjuna.coordinator.TxControl;
+
+public class LogStoreRecoveryTest
+{
+	public static void main (String[] args)
+	{
+		System.setProperty(Environment.OBJECTSTORE_TYPE, ArjunaNames.Implementation_ObjectStore_ActionLogStore().stringForm());
+		System.setProperty(Environment.TRANSACTION_LOG_PURGE_TIME, "10000");
+		
+		ObjectStore objStore = TxControl.getStore();
+		final int numberOfTransactions = 1000;
+		final Uid[] ids = new Uid[numberOfTransactions];
+		final int fakeData = 0xdeedbaaf;
+		final String type = "/StateManager/BasicAction/TwoPhaseCoordinator/AtomicAction/LogStoreRecoveryTest";
+		
+		for (int i = 0; i < numberOfTransactions; i++)
+		{
+			OutputObjectState dummyState = new OutputObjectState();
+			
+			try
+			{
+				dummyState.packInt(fakeData);			
+				ids[i] = new Uid();
+				objStore.write_committed(ids[i], type, dummyState);
+			}
+			catch (final Exception ex)
+			{
+				ex.printStackTrace();
+			}
+		}
+		
+		/*
+		 * Remove 50% of the entries, simulating a crash during
+		 * normal execution.
+		 * 
+		 * Q: why not just write 50% in the first place?
+		 * A: because we will extend this test to allow the recovery
+		 *    system to run in between writing and removing.
+		 */
+		
+		for (int i = 0; i < numberOfTransactions / 2; i++)
+		{
+			try
+			{
+				objStore.remove_committed(ids[i], type);
+			}
+			catch (final Exception ex)
+			{
+				ex.printStackTrace();
+			}
+		}
+		
+		try
+		{
+			/*
+			 * Give the purger thread a chance to run and delete
+			 * the entries we've "removed" (really only marked as
+			 * being removable.)
+			 */
+			
+			Thread.sleep(12000);
+		}
+		catch (final Exception ex)
+		{
+		}
+		
+		/*
+		 * Now get a list of entries to work on.
+		 */
+		
+		InputObjectState ios = new InputObjectState();
+		boolean passed = true;
+		
+		try
+		{
+			if (objStore.allObjUids(type, ios, ObjectStore.OS_UNKNOWN))
+			{
+				Uid id = new Uid(Uid.nullUid());
+				int numberOfEntries = 0;
+				
+				do
+				{
+					try
+					{
+						id.unpack(ios);
+					}
+					catch (Exception ex)
+					{
+						id = Uid.nullUid();
+					}
+
+					if (id.notEquals(Uid.nullUid()))
+					{
+						numberOfEntries++;
+						
+						boolean found = false;
+						
+						for (int i = 0; i < ids.length; i++)
+						{
+							if (id.equals(ids[i]))
+								found = true;
+						}
+						
+						if (passed && !found)
+						{
+							passed = false;
+
+							System.err.println("Found unexpected transaction!");
+						}
+					}
+				}
+				while (id.notEquals(Uid.nullUid()));
+				
+				if ((numberOfEntries == numberOfTransactions / 2) && passed)
+				{
+					System.err.println("Would attempt recovery on "+numberOfEntries+" dead transactions.");
+				}
+				else
+				{
+					passed = false;
+
+					System.err.println("Expected "+(numberOfTransactions / 2)+" and got "+numberOfEntries);
+				}
+			}
+		}
+		catch (final Exception ex)
+		{
+			ex.printStackTrace();
+		}
+		
+		System.err.println("Test "+((passed) ? "passed" : "failed"));
+	}
+}

Added: labs/jbosstm/trunk/ArjunaCore/arjuna/tests/classes/com/hp/mwtests/ts/arjuna/objectstore/LogStoreRecoveryTest2.java
===================================================================
--- labs/jbosstm/trunk/ArjunaCore/arjuna/tests/classes/com/hp/mwtests/ts/arjuna/objectstore/LogStoreRecoveryTest2.java	                        (rev 0)
+++ labs/jbosstm/trunk/ArjunaCore/arjuna/tests/classes/com/hp/mwtests/ts/arjuna/objectstore/LogStoreRecoveryTest2.java	2009-02-09 19:13:30 UTC (rev 25171)
@@ -0,0 +1,118 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2006, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags.
+ * See the copyright.txt in the distribution for a
+ * full listing of individual contributors.
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License, v. 2.1.
+ * This program is distributed in the hope that it will be useful, but WITHOUT A
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ * PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
+ * You should have received a copy of the GNU Lesser General Public License,
+ * v.2.1 along with this distribution; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA  02110-1301, USA.
+ *
+ * (C) 2005-2009,
+ * @author JBoss Inc.
+ */
+/*
+ * Copyright (C) 2004,
+ *
+ * Arjuna Technologies Ltd,
+ * Newcastle upon Tyne,
+ * Tyne and Wear,
+ * UK.
+ *
+ * $Id: Performance2.java 2342 2006-03-30 13:06:17Z  $
+ */
+
+package com.hp.mwtests.ts.arjuna.objectstore;
+
+import com.hp.mwtests.ts.arjuna.resources.*;
+
+import com.arjuna.ats.arjuna.ArjunaNames;
+import com.arjuna.ats.arjuna.AtomicAction;
+import com.arjuna.ats.arjuna.common.*;
+import com.arjuna.ats.arjuna.recovery.RecoveryManager;
+
+class TestWorker extends Thread
+{
+
+	public TestWorker (int iters)
+	{
+		_iters = iters;
+	}
+
+	public void run ()
+	{
+		for (int i = 0; i < _iters; i++)
+		{
+			try
+			{
+				AtomicAction A = new AtomicAction();
+
+				A.begin();
+
+				A.add(new BasicRecord());
+
+				A.commit();
+			}
+			catch (Exception e)
+			{
+				e.printStackTrace();
+			}
+			
+			Thread.yield();
+		}
+	}
+
+	private int _iters;
+}
+
+public class LogStoreRecoveryTest2
+{
+
+	public static void main (String[] args)
+	{
+		int threads = 10;
+		int work = 100;
+		
+		System.setProperty(Environment.COMMIT_ONE_PHASE, "NO");
+		System.setProperty(Environment.OBJECTSTORE_TYPE, ArjunaNames.Implementation_ObjectStore_ActionLogStore().stringForm());
+		System.setProperty(Environment.TRANSACTION_LOG_SYNC_REMOVAL, "false");
+		System.setProperty(Environment.TRANSACTION_LOG_PURGE_TIME, "1000000");  // essentially infinite
+
+		TestWorker[] workers = new TestWorker[threads];
+
+		for (int i = 0; i < threads; i++)
+		{
+			workers[i] = new TestWorker(work);
+
+			workers[i].start();
+		}
+		
+		for (int j = 0; j < threads; j++)
+		{
+			try
+			{
+				workers[j].join();
+				System.err.println("**terminated "+j);
+			}
+			catch (final Exception ex)
+			{
+			}
+		}
+
+		/*
+		 * Now have a log that hasn't been deleted. Run recovery and see
+		 * what happens!
+		 */
+		
+		RecoveryManager manager = RecoveryManager.manager(RecoveryManager.DIRECT_MANAGEMENT);
+		
+		manager.scan();
+	}
+}

Added: labs/jbosstm/trunk/ArjunaCore/arjuna/tests/classes/com/hp/mwtests/ts/arjuna/objectstore/LogStoreTest.java
===================================================================
--- labs/jbosstm/trunk/ArjunaCore/arjuna/tests/classes/com/hp/mwtests/ts/arjuna/objectstore/LogStoreTest.java	                        (rev 0)
+++ labs/jbosstm/trunk/ArjunaCore/arjuna/tests/classes/com/hp/mwtests/ts/arjuna/objectstore/LogStoreTest.java	2009-02-09 19:13:30 UTC (rev 25171)
@@ -0,0 +1,131 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2006, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags.
+ * See the copyright.txt in the distribution for a
+ * full listing of individual contributors.
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License, v. 2.1.
+ * This program is distributed in the hope that it will be useful, but WITHOUT A
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ * PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
+ * You should have received a copy of the GNU Lesser General Public License,
+ * v.2.1 along with this distribution; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA  02110-1301, USA.
+ *
+ * (C) 2005-2006,
+ * @author JBoss Inc.
+ */
+/*
+ * Copyright (C) 2004,
+ *
+ * Arjuna Technologies Ltd,
+ * Newcastle upon Tyne,
+ * Tyne and Wear,
+ * UK.
+ *
+ * $Id: AllObjUidsTest.java 2342 2006-03-30 13:06:17Z  $
+ */
+
+package com.hp.mwtests.ts.arjuna.objectstore;
+
+import com.arjuna.ats.arjuna.ArjunaNames;
+import com.arjuna.ats.arjuna.objectstore.ObjectStore;
+import com.arjuna.ats.arjuna.state.*;
+import com.arjuna.ats.arjuna.common.Environment;
+import com.arjuna.ats.arjuna.common.Uid;
+import com.arjuna.ats.arjuna.coordinator.TxControl;
+
+public class LogStoreTest
+{
+	public static void main (String[] args)
+	{
+		System.setProperty(Environment.OBJECTSTORE_TYPE, ArjunaNames.Implementation_ObjectStore_ActionLogStore().stringForm());
+		
+		ObjectStore objStore = TxControl.getStore();
+		final int numberOfTransactions = 1000;
+		final Uid[] ids = new Uid[numberOfTransactions];
+		final int fakeData = 0xdeedbaaf;
+		final String type = "/StateManager/BasicAction/TwoPhaseCoordinator/AtomicAction/Test";
+		
+		for (int i = 0; i < numberOfTransactions; i++)
+		{
+			OutputObjectState dummyState = new OutputObjectState();
+			
+			try
+			{
+				dummyState.packInt(fakeData);			
+				ids[i] = new Uid();
+				objStore.write_committed(ids[i], type, dummyState);
+			}
+			catch (final Exception ex)
+			{
+				ex.printStackTrace();
+			}
+		}
+		
+		InputObjectState ios = new InputObjectState();
+		boolean passed = false;
+		
+		try
+		{
+			if (objStore.allObjUids(type, ios, ObjectStore.OS_UNKNOWN))
+			{
+				Uid id = new Uid(Uid.nullUid());
+				int numberOfEntries = 0;
+				
+				do
+				{
+					try
+					{
+						id.unpack(ios);
+					}
+					catch (Exception ex)
+					{
+						id = Uid.nullUid();
+					}
+
+					if (id.notEquals(Uid.nullUid()))
+					{
+						passed = true;
+						
+						System.err.println("Located transaction log "+id+" in object store.");
+						
+						numberOfEntries++;
+						
+						boolean found = false;
+						
+						for (int i = 0; i < ids.length; i++)
+						{
+							if (id.equals(ids[i]))
+								found = true;
+						}
+						
+						if (passed && !found)
+						{
+							passed = false;
+
+							System.err.println("Found unexpected transaction!");
+						}
+					}
+				}
+				while (id.notEquals(Uid.nullUid()));
+				
+				if ((numberOfEntries != ids.length) && passed)
+				{
+					passed = false;
+					
+					System.err.println("Expected "+ids.length+" and got "+numberOfEntries);
+				}
+			}
+		}
+		catch (final Exception ex)
+		{
+			ex.printStackTrace();
+		}
+		
+		System.err.println("Test "+((passed) ? "passed" : "failed"));
+	}
+}

Added: labs/jbosstm/trunk/ArjunaCore/arjuna/tests/classes/com/hp/mwtests/ts/arjuna/objectstore/LogStoreTest2.java
===================================================================
--- labs/jbosstm/trunk/ArjunaCore/arjuna/tests/classes/com/hp/mwtests/ts/arjuna/objectstore/LogStoreTest2.java	                        (rev 0)
+++ labs/jbosstm/trunk/ArjunaCore/arjuna/tests/classes/com/hp/mwtests/ts/arjuna/objectstore/LogStoreTest2.java	2009-02-09 19:13:30 UTC (rev 25171)
@@ -0,0 +1,162 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2006, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags.
+ * See the copyright.txt in the distribution for a
+ * full listing of individual contributors.
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License, v. 2.1.
+ * This program is distributed in the hope that it will be useful, but WITHOUT A
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ * PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
+ * You should have received a copy of the GNU Lesser General Public License,
+ * v.2.1 along with this distribution; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA  02110-1301, USA.
+ *
+ * (C) 2005-2006,
+ * @author JBoss Inc.
+ */
+/*
+ * Copyright (C) 2004,
+ *
+ * Arjuna Technologies Ltd,
+ * Newcastle upon Tyne,
+ * Tyne and Wear,
+ * UK.
+ *
+ * $Id: AllObjUidsTest.java 2342 2006-03-30 13:06:17Z  $
+ */
+
+package com.hp.mwtests.ts.arjuna.objectstore;
+
+import com.arjuna.ats.arjuna.ArjunaNames;
+import com.arjuna.ats.arjuna.objectstore.ObjectStore;
+import com.arjuna.ats.arjuna.state.*;
+import com.arjuna.ats.arjuna.common.Environment;
+import com.arjuna.ats.arjuna.common.Uid;
+import com.arjuna.ats.arjuna.coordinator.TxControl;
+
+public class LogStoreTest2
+{
+	public static void main (String[] args)
+	{
+		System.setProperty(Environment.OBJECTSTORE_TYPE, ArjunaNames.Implementation_ObjectStore_ActionLogStore().stringForm());
+		System.setProperty(Environment.TRANSACTION_LOG_PURGE_TIME, "10000");
+		
+		ObjectStore objStore = TxControl.getStore();
+		final int numberOfTransactions = 1000;
+		final Uid[] ids = new Uid[numberOfTransactions];
+		final int fakeData = 0xdeedbaaf;
+		final String type = "/StateManager/BasicAction/TwoPhaseCoordinator/AtomicAction/Test";
+		
+		for (int i = 0; i < numberOfTransactions; i++)
+		{
+			OutputObjectState dummyState = new OutputObjectState();
+			
+			try
+			{
+				dummyState.packInt(fakeData);			
+				ids[i] = new Uid();
+				objStore.write_committed(ids[i], type, dummyState);
+			}
+			catch (final Exception ex)
+			{
+				ex.printStackTrace();
+			}
+		}
+		
+		try
+		{
+			objStore.remove_committed(ids[0], type);
+		}
+		catch (final Exception ex)
+		{
+			ex.printStackTrace();
+		}
+		
+		try
+		{
+			/*
+			 * Give the purger thread a chance to run and delete
+			 * the entry.
+			 */
+			
+			Thread.sleep(12000);
+		}
+		catch (final Exception ex)
+		{
+		}
+		
+		InputObjectState ios = new InputObjectState();
+		boolean passed = false;
+		
+		try
+		{
+			if (objStore.allObjUids(type, ios, ObjectStore.OS_UNKNOWN))
+			{
+				Uid id = new Uid(Uid.nullUid());
+				int numberOfEntries = 0;
+				
+				do
+				{
+					try
+					{
+						id.unpack(ios);
+					}
+					catch (Exception ex)
+					{
+						id = Uid.nullUid();
+					}
+
+					if (id.notEquals(Uid.nullUid()))
+					{
+						passed = true;
+						
+						numberOfEntries++;
+						
+						boolean found = false;
+						
+						for (int i = 0; i < ids.length; i++)
+						{
+							if (id.equals(ids[i]))
+								found = true;
+						}
+						
+						if (passed && !found)
+						{
+							passed = false;
+
+							System.err.println("Found unexpected transaction!");
+						}
+					}
+				}
+				while (id.notEquals(Uid.nullUid()));
+				
+				if ((numberOfEntries == ids.length -1) && passed)
+				{
+					if (objStore.currentState(ids[0], type) != ObjectStore.OS_UNKNOWN)
+						passed = false;
+					else
+					{
+						if (objStore.currentState(ids[1], type) != ObjectStore.OS_COMMITTED)
+							passed = false;
+					}
+				}
+				else
+				{
+					passed = false;
+
+					System.err.println("Expected "+ids.length+" and got "+numberOfEntries);
+				}
+			}
+		}
+		catch (final Exception ex)
+		{
+			ex.printStackTrace();
+		}
+		
+		System.err.println("Test "+((passed) ? "passed" : "failed"));
+	}
+}

Added: labs/jbosstm/trunk/ArjunaCore/arjuna/tests/classes/com/hp/mwtests/ts/arjuna/objectstore/LogStressTest.java
===================================================================
--- labs/jbosstm/trunk/ArjunaCore/arjuna/tests/classes/com/hp/mwtests/ts/arjuna/objectstore/LogStressTest.java	                        (rev 0)
+++ labs/jbosstm/trunk/ArjunaCore/arjuna/tests/classes/com/hp/mwtests/ts/arjuna/objectstore/LogStressTest.java	2009-02-09 19:13:30 UTC (rev 25171)
@@ -0,0 +1,171 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2006, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags.
+ * See the copyright.txt in the distribution for a
+ * full listing of individual contributors.
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License, v. 2.1.
+ * This program is distributed in the hope that it will be useful, but WITHOUT A
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ * PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
+ * You should have received a copy of the GNU Lesser General Public License,
+ * v.2.1 along with this distribution; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA  02110-1301, USA.
+ *
+ * (C) 2005-2009,
+ * @author JBoss Inc.
+ */
+/*
+ * Copyright (C) 2004,
+ *
+ * Arjuna Technologies Ltd,
+ * Newcastle upon Tyne,
+ * Tyne and Wear,
+ * UK.
+ *
+ * $Id: Performance2.java 2342 2006-03-30 13:06:17Z  $
+ */
+
+package com.hp.mwtests.ts.arjuna.objectstore;
+
+import com.hp.mwtests.ts.arjuna.resources.*;
+
+import com.arjuna.ats.arjuna.ArjunaNames;
+import com.arjuna.ats.arjuna.AtomicAction;
+import com.arjuna.ats.arjuna.common.*;
+import com.arjuna.ats.arjuna.coordinator.TxControl;
+import com.arjuna.ats.arjuna.objectstore.ObjectStore;
+import com.arjuna.ats.arjuna.state.InputObjectState;
+
+class StressWorker extends Thread
+{
+
+	public StressWorker (int iters, int thread)
+	{
+		_iters = iters;
+		_thread = thread;
+	}
+
+	public void run ()
+	{
+		for (int i = 0; i < _iters; i++)
+		{
+			try
+			{
+				AtomicAction A = new AtomicAction();
+
+				A.begin();
+
+				A.add(new BasicRecord());
+
+				A.commit();
+			}
+			catch (Exception e)
+			{
+				e.printStackTrace();
+			}
+			
+			Thread.yield();
+		}
+	}
+
+	private int _iters;
+	private int _thread;
+}
+
+public class LogStressTest
+{
+
+	public static void main (String[] args)
+	{
+		int threads = 10;
+		int work = 100;
+
+		System.setProperty(Environment.COMMIT_ONE_PHASE, "NO");
+		System.setProperty(Environment.OBJECTSTORE_TYPE, ArjunaNames.Implementation_ObjectStore_ActionLogStore().stringForm());
+		System.setProperty(Environment.TRANSACTION_LOG_PURGE_TIME, "10000");
+		
+		for (int i = 0; i < args.length; i++)
+		{
+			if (args[i].compareTo("-threads") == 0)
+			{
+				try
+				{
+					Integer v = new Integer(args[i + 1]);
+
+					threads = v.intValue();
+				}
+				catch (Exception e)
+				{
+					System.err.println(e);
+				}
+			}
+			if (args[i].compareTo("-work") == 0)
+			{
+				try
+				{
+					Integer v = new Integer(args[i + 1]);
+
+					work = v.intValue();
+				}
+				catch (Exception e)
+				{
+					System.err.println(e);
+				}
+			}
+			if (args[i].compareTo("-help") == 0)
+			{
+				System.out
+						.println("Usage: LogStressTest [-help] [-threads <number>] [-work <number>]");
+				System.exit(0);
+			}
+		}
+
+		StressWorker[] workers = new StressWorker[threads];
+
+		for (int i = 0; i < threads; i++)
+		{
+			workers[i] = new StressWorker(work, i);
+
+			workers[i].start();
+		}
+		
+		for (int j = 0; j < threads; j++)
+		{
+			try
+			{
+				workers[j].join();
+			}
+			catch (final Exception ex)
+			{
+			}
+		}
+
+		InputObjectState ios = new InputObjectState();
+		boolean passed = false;
+		
+		try
+		{
+			TxControl.getStore().allObjUids(new AtomicAction().type(), ios, ObjectStore.OS_UNKNOWN);
+			
+			Uid tempUid = new Uid(Uid.nullUid());
+
+			tempUid.unpack(ios);
+			
+			// there should be no entries left
+				
+			if (tempUid.equals(Uid.nullUid()))
+			{
+				passed = true;
+			}
+		}
+		catch (final Exception ex)
+		{
+		}
+		
+		System.err.println("Test "+((passed) ? "passed" : "failed"));
+	}
+}

Added: labs/jbosstm/trunk/ArjunaCore/arjuna/tests/classes/com/hp/mwtests/ts/arjuna/objectstore/LogStressTest2.java
===================================================================
--- labs/jbosstm/trunk/ArjunaCore/arjuna/tests/classes/com/hp/mwtests/ts/arjuna/objectstore/LogStressTest2.java	                        (rev 0)
+++ labs/jbosstm/trunk/ArjunaCore/arjuna/tests/classes/com/hp/mwtests/ts/arjuna/objectstore/LogStressTest2.java	2009-02-09 19:13:30 UTC (rev 25171)
@@ -0,0 +1,104 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2006, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags.
+ * See the copyright.txt in the distribution for a
+ * full listing of individual contributors.
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License, v. 2.1.
+ * This program is distributed in the hope that it will be useful, but WITHOUT A
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ * PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
+ * You should have received a copy of the GNU Lesser General Public License,
+ * v.2.1 along with this distribution; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA  02110-1301, USA.
+ *
+ * (C) 2005-2009,
+ * @author JBoss Inc.
+ */
+/*
+ * Copyright (C) 2004,
+ *
+ * Arjuna Technologies Ltd,
+ * Newcastle upon Tyne,
+ * Tyne and Wear,
+ * UK.
+ *
+ * $Id: Performance2.java 2342 2006-03-30 13:06:17Z  $
+ */
+
+package com.hp.mwtests.ts.arjuna.objectstore;
+
+import com.arjuna.ats.arjuna.ArjunaNames;
+import com.arjuna.ats.arjuna.AtomicAction;
+import com.arjuna.ats.arjuna.common.*;
+import com.hp.mwtests.ts.arjuna.resources.BasicRecord;
+
+/*
+ * Run with the log store for N hours and make sure there are
+ * no logs left at the end.
+ */
+
+public class LogStressTest2
+{
+
+	public static void main (String[] args)
+	{
+		System.setProperty(Environment.COMMIT_ONE_PHASE, "NO");
+		System.setProperty(Environment.OBJECTSTORE_TYPE, ArjunaNames.Implementation_ObjectStore_ActionLogStore().stringForm());
+		System.setProperty(Environment.TRANSACTION_LOG_SIZE, "10000");
+		
+		int timeLimit = 4; // hours
+		
+		for (int i = 0; i < args.length; i++)
+		{
+			if (args[i].compareTo("-time") == 0)
+			{
+				try
+				{
+					Integer v = new Integer(args[i + 1]);
+
+					timeLimit = v.intValue();
+				}
+				catch (Exception e)
+				{
+					System.err.println(e);
+				}
+			}
+			if (args[i].compareTo("-help") == 0)
+			{
+				System.out
+						.println("Usage: LogStressTest2 [-help] [-time <hours>]");
+				System.exit(0);
+			}
+		}
+
+		System.err.println("WARNING: this test will run for "+timeLimit+" hours.");
+		
+		final long stime = System.currentTimeMillis();
+		final long endTime = timeLimit * 60 * 60 * 1000;
+		long ftime;
+		
+		do
+		{
+			try
+			{
+				AtomicAction A = new AtomicAction();
+				
+				A.begin();
+				
+				A.add(new BasicRecord());
+				
+				A.commit();
+			}
+			catch (final Exception ex)
+			{
+			}
+			
+			ftime = System.currentTimeMillis();
+			
+		} while ((ftime - stime) < endTime);
+	}
+}

Modified: labs/jbosstm/trunk/ArjunaCore/arjuna/tests/classes/com/hp/mwtests/ts/arjuna/resources/BasicRecord.java
===================================================================
--- labs/jbosstm/trunk/ArjunaCore/arjuna/tests/classes/com/hp/mwtests/ts/arjuna/resources/BasicRecord.java	2009-02-09 19:12:13 UTC (rev 25170)
+++ labs/jbosstm/trunk/ArjunaCore/arjuna/tests/classes/com/hp/mwtests/ts/arjuna/resources/BasicRecord.java	2009-02-09 19:13:30 UTC (rev 25171)
@@ -35,13 +35,8 @@
 import com.arjuna.ats.arjuna.common.*;
 import com.arjuna.ats.arjuna.state.*;
 import com.arjuna.ats.arjuna.gandiva.ClassName;
-import com.arjuna.ats.arjuna.objectstore.ObjectStoreType;
 import java.io.PrintWriter;
 
-import java.io.IOException;
-
-import com.arjuna.common.util.logging.*;
-
 public class BasicRecord extends AbstractRecord
 {
 

Modified: labs/jbosstm/trunk/ArjunaCore/docs/user_guide/ProgrammersGuide.odt
===================================================================
(Binary files differ)




More information about the jboss-svn-commits mailing list