[jboss-cvs] JBoss Messaging SVN: r4008 - in trunk: src/main/org/jboss/messaging/core/config and 7 other directories.

jboss-cvs-commits at lists.jboss.org jboss-cvs-commits at lists.jboss.org
Fri Apr 4 10:25:21 EDT 2008


Author: timfox
Date: 2008-04-04 10:25:20 -0400 (Fri, 04 Apr 2008)
New Revision: 4008

Added:
   trunk/src/main/org/jboss/messaging/core/journal/TestableJournal.java
   trunk/src/main/org/jboss/messaging/core/journal/impl/JournalFileImpl.java
   trunk/src/main/org/jboss/messaging/core/journal/impl/Reclaimer.java
   trunk/tests/src/org/jboss/messaging/core/journal/impl/test/timing/
   trunk/tests/src/org/jboss/messaging/core/journal/impl/test/timing/FakeJournalImplTest.java
   trunk/tests/src/org/jboss/messaging/core/journal/impl/test/timing/JournalImplTestUnit.java
   trunk/tests/src/org/jboss/messaging/core/journal/impl/test/timing/RealJournalImplTest.java
   trunk/tests/src/org/jboss/messaging/core/journal/impl/test/unit/FakeJournalImplTest.java
   trunk/tests/src/org/jboss/messaging/core/journal/impl/test/unit/JournalImplTestUnit.java
   trunk/tests/src/org/jboss/messaging/core/journal/impl/test/unit/ReclaimerTest.java
Removed:
   trunk/src/main/org/jboss/messaging/core/journal/impl/JournalFile.java
   trunk/tests/src/org/jboss/messaging/core/journal/impl/test/unit/JournalImplTest.java
Modified:
   trunk/src/etc/server/default/deploy/jbm-configuration.xml
   trunk/src/main/org/jboss/messaging/core/config/Configuration.java
   trunk/src/main/org/jboss/messaging/core/config/impl/ConfigurationImpl.java
   trunk/src/main/org/jboss/messaging/core/config/impl/FileConfiguration.java
   trunk/src/main/org/jboss/messaging/core/journal/Journal.java
   trunk/src/main/org/jboss/messaging/core/journal/impl/JournalImpl.java
   trunk/src/main/org/jboss/messaging/core/journal/impl/TransactionHolder.java
   trunk/src/main/org/jboss/messaging/core/persistence/impl/journal/JournalStorageManager.java
   trunk/tests/src/org/jboss/messaging/core/journal/impl/test/unit/JournalImplTestBase.java
   trunk/tests/src/org/jboss/messaging/core/journal/impl/test/unit/RealJournalImplTest.java
Log:
More journal work


Modified: trunk/src/etc/server/default/deploy/jbm-configuration.xml
===================================================================
--- trunk/src/etc/server/default/deploy/jbm-configuration.xml	2008-04-04 11:01:48 UTC (rev 4007)
+++ trunk/src/etc/server/default/deploy/jbm-configuration.xml	2008-04-04 14:25:20 UTC (rev 4008)
@@ -69,8 +69,6 @@
       
       <journal-min-files>10</journal-min-files>
       
-      <journal-min-available-files>10</journal-min-available-files>
-      
       <journal-task-period>5000</journal-task-period>
       
    </configuration>

Modified: trunk/src/main/org/jboss/messaging/core/config/Configuration.java
===================================================================
--- trunk/src/main/org/jboss/messaging/core/config/Configuration.java	2008-04-04 11:01:48 UTC (rev 4007)
+++ trunk/src/main/org/jboss/messaging/core/config/Configuration.java	2008-04-04 14:25:20 UTC (rev 4008)
@@ -138,10 +138,6 @@
 
 //   public void setJournalMinFiles(int journalMinFiles);
 
-   public int getJournalMinAvailableFiles();
-
-//   public void setJournalMinAvailableFiles(int journalMinAvailableFiles);
-
    public long getJournalTaskPeriod();
 
 //   public void setJournalTaskPeriod(long journalTaskPeriod);

Modified: trunk/src/main/org/jboss/messaging/core/config/impl/ConfigurationImpl.java
===================================================================
--- trunk/src/main/org/jboss/messaging/core/config/impl/ConfigurationImpl.java	2008-04-04 11:01:48 UTC (rev 4007)
+++ trunk/src/main/org/jboss/messaging/core/config/impl/ConfigurationImpl.java	2008-04-04 14:25:20 UTC (rev 4008)
@@ -89,8 +89,6 @@
    
    protected int journalMinFiles;
    
-   protected int journalMinAvailableFiles;
-   
    protected long journalTaskPeriod;
 
    // remoting config
@@ -424,17 +422,7 @@
 	{
 		this.journalMinFiles = journalMinFiles;
 	}
-
-	public int getJournalMinAvailableFiles()
-	{
-		return journalMinAvailableFiles;
-	}
-
-	public void setJournalMinAvailableFiles(int journalMinAvailableFiles)
-	{
-		this.journalMinAvailableFiles = journalMinAvailableFiles;
-	}
-
+	
 	public long getJournalTaskPeriod()
 	{
 		return journalTaskPeriod;

Modified: trunk/src/main/org/jboss/messaging/core/config/impl/FileConfiguration.java
===================================================================
--- trunk/src/main/org/jboss/messaging/core/config/impl/FileConfiguration.java	2008-04-04 11:01:48 UTC (rev 4007)
+++ trunk/src/main/org/jboss/messaging/core/config/impl/FileConfiguration.java	2008-04-04 14:25:20 UTC (rev 4008)
@@ -127,8 +127,6 @@
       
       this.journalMinFiles = getInteger(e, "journal-min-files", 10);
       
-      this.journalMinAvailableFiles = getInteger(e, "journal-min-available-files", 10);
-      
       this.journalTaskPeriod = getLong(e, "journal-task-period", 5000L);
       
       

Modified: trunk/src/main/org/jboss/messaging/core/journal/Journal.java
===================================================================
--- trunk/src/main/org/jboss/messaging/core/journal/Journal.java	2008-04-04 11:01:48 UTC (rev 4007)
+++ trunk/src/main/org/jboss/messaging/core/journal/Journal.java	2008-04-04 14:25:20 UTC (rev 4008)
@@ -63,4 +63,10 @@
 	void load(List<RecordInfo> committedRecords,
 			    List<PreparedTransactionInfo> preparedTransactions) throws Exception;
 	
+	// Start and stop reclaimer
+	
+	void startReclaimer();
+	
+	void stopReclaimer();
+	
 }

Added: trunk/src/main/org/jboss/messaging/core/journal/TestableJournal.java
===================================================================
--- trunk/src/main/org/jboss/messaging/core/journal/TestableJournal.java	                        (rev 0)
+++ trunk/src/main/org/jboss/messaging/core/journal/TestableJournal.java	2008-04-04 14:25:20 UTC (rev 4008)
@@ -0,0 +1,22 @@
+package org.jboss.messaging.core.journal;
+
+/**
+ * 
+ * A TestableJournal
+ * 
+ * @author <a href="mailto:tim.fox at jboss.com">Tim Fox</a>
+ *
+ */
+public interface TestableJournal extends Journal
+{
+	void checkAndReclaimFiles() throws Exception;
+	
+	int getDataFilesCount();
+	
+	int getFreeFilesCount();
+	
+	int getIDMapSize();
+	
+	//void dump();
+	
+}

Deleted: trunk/src/main/org/jboss/messaging/core/journal/impl/JournalFile.java
===================================================================
--- trunk/src/main/org/jboss/messaging/core/journal/impl/JournalFile.java	2008-04-04 11:01:48 UTC (rev 4007)
+++ trunk/src/main/org/jboss/messaging/core/journal/impl/JournalFile.java	2008-04-04 14:25:20 UTC (rev 4008)
@@ -1,89 +0,0 @@
-/*
-  * JBoss, Home of Professional Open Source
-  * Copyright 2005, JBoss Inc., and individual contributors as indicated
-  * by the @authors tag. See the copyright.txt in the distribution for a
-  * full listing of individual contributors.
-  *
-  * This is free software; you can redistribute it and/or modify it
-  * under the terms of the GNU Lesser General Public License as
-  * published by the Free Software Foundation; either version 2.1 of
-  * the License, or (at your option) any later version.
-  *
-  * This software is distributed in the hope that it will be useful,
-  * but WITHOUT ANY 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 along with this software; if not, write to the Free
-  * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
-  * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
-  */
-package org.jboss.messaging.core.journal.impl;
-
-import java.util.HashSet;
-import java.util.Set;
-
-import org.jboss.messaging.core.journal.SequentialFile;
-
-/**
- * 
- * A JournalFile
- * 
- * @author <a href="mailto:tim.fox at jboss.com">Tim Fox</a>
- *
- */
-public class JournalFile
-{
-	private final SequentialFile file;
-	
-	private final long orderingID;
-	
-	private int offset;
-	
-	private final Set<Long> positives = new HashSet<Long>();
-	
-	private final Set<Long> negatives = new HashSet<Long>();
-		
-	public JournalFile(final SequentialFile file, final long orderingID)
-	{
-		this.file = file;
-		
-		this.orderingID = orderingID;
-	}
-	
-	public void extendOffset(final int delta)
-	{
-		offset += delta;
-	}
-	
-	public int getOffset()
-	{
-		return offset;
-	}
-	
-	public long getOrderingID()
-	{
-		return orderingID;
-	}
-	
-	public void setOffset(final int offset)
-	{
-		this.offset = offset;
-	}
-	
-	public SequentialFile getFile()
-	{
-		return file;
-	}	
-	
-	public void addPositive(final long id)
-	{
-		this.positives.add(id);
-	}
-	
-	public void addNegative(final long id)
-	{
-		this.negatives.add(id);
-	}
-}

Copied: trunk/src/main/org/jboss/messaging/core/journal/impl/JournalFileImpl.java (from rev 3897, trunk/src/main/org/jboss/messaging/core/journal/impl/JournalFile.java)
===================================================================
--- trunk/src/main/org/jboss/messaging/core/journal/impl/JournalFileImpl.java	                        (rev 0)
+++ trunk/src/main/org/jboss/messaging/core/journal/impl/JournalFileImpl.java	2008-04-04 14:25:20 UTC (rev 4008)
@@ -0,0 +1,134 @@
+/*
+  * JBoss, Home of Professional Open Source
+  * Copyright 2005, JBoss Inc., and individual contributors as indicated
+  * by the @authors tag. See the copyright.txt in the distribution for a
+  * full listing of individual contributors.
+  *
+  * This is free software; you can redistribute it and/or modify it
+  * under the terms of the GNU Lesser General Public License as
+  * published by the Free Software Foundation; either version 2.1 of
+  * the License, or (at your option) any later version.
+  *
+  * This software is distributed in the hope that it will be useful,
+  * but WITHOUT ANY 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 along with this software; if not, write to the Free
+  * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+  * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+  */
+package org.jboss.messaging.core.journal.impl;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.jboss.messaging.core.journal.SequentialFile;
+import org.jboss.messaging.core.logging.Logger;
+
+/**
+ * 
+ * A JournalFileImpl
+ * 
+ * @author <a href="mailto:tim.fox at jboss.com">Tim Fox</a>
+ *
+ */
+public class JournalFileImpl implements JournalFile
+{			
+	private static final Logger log = Logger.getLogger(JournalFileImpl.class);
+		
+	private final SequentialFile file;
+	
+	private final long orderingID;
+	
+	private int offset;
+			
+	private int posCount;
+	
+	private boolean canReclaim;
+	
+	private Map<JournalFile, Integer> negCounts = new HashMap<JournalFile, Integer>();
+	
+	public JournalFileImpl(final SequentialFile file, final long orderingID)
+	{
+		this.file = file;
+		
+		this.orderingID = orderingID;
+	}
+	
+	public int getPosCount()
+	{
+		return posCount;
+	}
+
+	public boolean isCanReclaim()
+	{
+		return canReclaim;
+	}
+
+	public void setCanReclaim(final boolean canReclaim)
+	{
+		this.canReclaim = canReclaim;
+	}
+
+	public void incNegCount(final JournalFile file)
+	{
+		Integer count = negCounts.get(file);
+		
+		int c = count == null ? 1 : count.intValue() + 1;
+		
+		negCounts.put(file, c);
+	}
+	
+	public int getNegCount(final JournalFile file)
+	{		
+		Integer count =  negCounts.get(file);
+		
+		if (count == null)
+		{
+			return 0;
+		}
+		else
+		{
+			return count.intValue();
+		}
+	}
+
+	public void incPosCount()
+	{
+		posCount++;
+	}
+	
+	public void decPosCount()
+	{
+		posCount--;
+	}
+		
+	public void extendOffset(final int delta)
+	{
+		offset += delta;
+	}
+	
+	public int getOffset()
+	{
+		return offset;
+	}
+	
+	public long getOrderingID()
+	{
+		return orderingID;
+	}
+	
+	public void setOffset(final int offset)
+	{
+		this.offset = offset;
+	}
+	
+	public SequentialFile getFile()
+	{
+		return file;
+	}	
+	
+
+}

Modified: trunk/src/main/org/jboss/messaging/core/journal/impl/JournalImpl.java
===================================================================
--- trunk/src/main/org/jboss/messaging/core/journal/impl/JournalImpl.java	2008-04-04 11:01:48 UTC (rev 4007)
+++ trunk/src/main/org/jboss/messaging/core/journal/impl/JournalImpl.java	2008-04-04 14:25:20 UTC (rev 4008)
@@ -26,6 +26,7 @@
 import java.util.Collections;
 import java.util.Comparator;
 import java.util.HashSet;
+import java.util.Iterator;
 import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
@@ -33,16 +34,18 @@
 import java.util.Set;
 import java.util.Timer;
 import java.util.TimerTask;
+import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ConcurrentLinkedQueue;
 import java.util.concurrent.Semaphore;
 import java.util.concurrent.atomic.AtomicLong;
 
-import org.jboss.messaging.core.journal.Journal;
 import org.jboss.messaging.core.journal.PreparedTransactionInfo;
 import org.jboss.messaging.core.journal.RecordInfo;
 import org.jboss.messaging.core.journal.SequentialFile;
 import org.jboss.messaging.core.journal.SequentialFileFactory;
+import org.jboss.messaging.core.journal.TestableJournal;
 import org.jboss.messaging.core.logging.Logger;
+import org.jboss.messaging.util.Pair;
 
 /**
  * 
@@ -51,7 +54,7 @@
  * @author <a href="mailto:tim.fox at jboss.com">Tim Fox</a>
  *
  */
-public class JournalImpl implements Journal
+public class JournalImpl implements TestableJournal
 {
 	private static final Logger log = Logger.getLogger(JournalImpl.class);
 	
@@ -71,7 +74,7 @@
    
    public static final int MIN_FILE_SIZE = 1024;
    
-   public static final int MIN_TASK_PERIOD = 5000;
+   public static final int MIN_TASK_PERIOD = 1000;
    
    //Record markers - they must be all unique
    
@@ -102,8 +105,6 @@
 	
 	private final int minFiles;
 	
-	private final int minAvailableFiles;
-	
 	private final boolean sync;
 	
 	private final SequentialFileFactory fileFactory;
@@ -115,10 +116,15 @@
 	public final String fileExtension;
 	 
 	
-	private final Queue<JournalFile> files = new ConcurrentLinkedQueue<JournalFile>();
+	private final Queue<JournalFile> dataFiles = new ConcurrentLinkedQueue<JournalFile>();
 	
-	private final Queue<JournalFile> availableFiles = new ConcurrentLinkedQueue<JournalFile>();
+	private final Queue<JournalFile> freeFiles = new ConcurrentLinkedQueue<JournalFile>();
 	
+	private Map<Long, PosFiles> posFilesMap = new ConcurrentHashMap<Long, PosFiles>();
+	
+	private Map<Long, TransactionNegPos> transactionInfos = new ConcurrentHashMap<Long, TransactionNegPos>();
+
+		
 	/*
 	 * We use a semaphore rather than synchronized since it performs better when contended
 	 */
@@ -136,11 +142,11 @@
 	
 	private TimerTask reclaimerTask;
 	
-	private TimerTask availableFilesTask;
-	
 	private final AtomicLong transactionIDSequence = new AtomicLong(0);
 	
-	public JournalImpl(final int fileSize, final int minFiles, final int minAvailableFiles,
+	private Reclaimer reclaimer = new Reclaimer();
+	
+	public JournalImpl(final int fileSize, final int minFiles,
 			             final boolean sync, final SequentialFileFactory fileFactory, final long taskPeriod,
 			             final String filePrefix, final String fileExtension)
 	{
@@ -152,10 +158,6 @@
 		{
 			throw new IllegalArgumentException("minFiles cannot be less than 2");
 		}
-		if (minAvailableFiles < 2)
-		{
-			throw new IllegalArgumentException("minAvailableFiles cannot be less than 2");
-		}
 		if (fileFactory == null)
 		{
 			throw new NullPointerException("fileFactory is null");
@@ -177,8 +179,6 @@
 		
 		this.minFiles = minFiles;
 		
-		this.minAvailableFiles = minAvailableFiles;
-		
 		this.sync = sync;
 		
 		this.fileFactory = fileFactory;
@@ -216,6 +216,8 @@
 		bb.flip();
 		
 		appendRecord(bb, true);
+		
+		posFilesMap.put(id, new PosFiles(currentFile));
 	}
 			
 	public void appendUpdateRecord(final long id, final byte[] record) throws Exception
@@ -224,6 +226,13 @@
 		{
 			throw new IllegalStateException("Journal must be loaded first");
 		}
+		
+		PosFiles posFiles = posFilesMap.get(id);
+		
+		if (posFiles == null)
+		{
+			throw new IllegalStateException("Cannot find add info " + id);
+		}
 			
 		int size = SIZE_BYTE + SIZE_LONG + SIZE_INT + record.length + SIZE_BYTE;
 		
@@ -236,9 +245,11 @@
 		bb.put(DONE);		
 		bb.flip();
 		
-		appendRecord(bb, true);			
+		appendRecord(bb, true);		
+		
+		posFiles.addUpdateFile(currentFile);
 	}
-			
+		
 	public void appendDeleteRecord(long id) throws Exception
 	{
 		if (state != STATE_LOADED)
@@ -246,6 +257,15 @@
 			throw new IllegalStateException("Journal must be loaded first");
 		}
 		
+		PosFiles posFiles = posFilesMap.remove(id);
+		
+		if (posFiles == null)
+		{
+			throw new IllegalStateException("Cannot find add info " + id);
+		}
+		
+		posFiles.addDelete(currentFile);
+		
 		int size = SIZE_BYTE + SIZE_LONG + SIZE_BYTE;
 		
 		ByteBuffer bb = ByteBuffer.wrap(new byte[size]);
@@ -255,7 +275,7 @@
 		bb.put(DONE);		
 		bb.flip();
 								
-		appendRecord(bb, true);			
+		appendRecord(bb, true);							
 	}		
 	
 	public long getTransactionID()
@@ -270,7 +290,7 @@
 		{
 			throw new IllegalStateException("Journal must be loaded first");
 		}
-
+		
 		int size = SIZE_BYTE + SIZE_LONG + SIZE_LONG + SIZE_INT + record.length + SIZE_BYTE;
 
 		ByteBuffer bb = ByteBuffer.wrap(new byte[size]);
@@ -284,8 +304,12 @@
 		bb.flip();
 		
 		appendRecord(bb, false);
+		
+		TransactionNegPos tx = getTransactionInfo(txID);
+		
+		tx.addPos(currentFile, id);
 	}
-			
+	
 	public void appendUpdateRecordTransactional(final long txID, final long id,
 			final byte[] record) throws Exception
 	{
@@ -293,7 +317,7 @@
 		{
 			throw new IllegalStateException("Journal must be loaded first");
 		}
-
+		
 		int size = SIZE_BYTE + SIZE_LONG + SIZE_LONG + SIZE_INT + record.length + SIZE_BYTE;
 		
 		ByteBuffer bb = ByteBuffer.wrap(new byte[size]);
@@ -306,7 +330,11 @@
 		bb.put(DONE);		
 		bb.flip();
 		
-		appendRecord(bb, false);			
+		appendRecord(bb, false);
+		
+		TransactionNegPos tx = getTransactionInfo(txID);
+		
+		tx.addPos(currentFile, id);
 	}
 	
 	public void appendDeleteRecordTransactional(final long txID, final long id) throws Exception
@@ -326,9 +354,12 @@
 		bb.put(DONE);			
 		bb.flip();
 								
-		appendRecord(bb, false);				
+		appendRecord(bb, false);		
+		
+		TransactionNegPos tx = getTransactionInfo(txID);
+		
+		tx.addNeg(currentFile, id);		
 	}	
-	
 		
 	public void appendPrepareRecord(final long txID) throws Exception
 	{
@@ -337,6 +368,13 @@
 			throw new IllegalStateException("Journal must be loaded first");
 		}
 		
+		TransactionNegPos tx = transactionInfos.get(txID);
+		
+		if (tx == null)
+		{
+			throw new IllegalStateException("Cannot find tx with id " + txID);
+		}
+		
 		int size = SIZE_BYTE + SIZE_LONG + SIZE_BYTE;
 		
 		ByteBuffer bb = ByteBuffer.wrap(new byte[size]);
@@ -347,6 +385,8 @@
 		bb.flip();
 		
 		appendRecord(bb, true);		
+		
+		tx.prepare(currentFile);
 	}
 	
 	public void appendCommitRecord(final long txID) throws Exception
@@ -356,6 +396,13 @@
 			throw new IllegalStateException("Journal must be loaded first");
 		}
 		
+		TransactionNegPos tx = transactionInfos.remove(txID);
+		
+		if (tx == null)
+		{
+			throw new IllegalStateException("Cannot find tx with id " + txID);
+		}
+				
 		int size = SIZE_BYTE + SIZE_LONG + SIZE_BYTE;
 		
 		ByteBuffer bb = ByteBuffer.wrap(new byte[size]);
@@ -366,6 +413,8 @@
 		bb.flip();
 		
 		appendRecord(bb, true);	
+		
+		tx.commit(currentFile);				
 	}
 	
 	public void appendRollbackRecord(final long txID) throws Exception
@@ -375,6 +424,13 @@
 			throw new IllegalStateException("Journal must be loaded first");
 		}
 		
+		TransactionNegPos tx = transactionInfos.remove(txID);
+		
+		if (tx == null)
+		{
+			throw new IllegalStateException("Cannot find tx with id " + txID);
+		}
+				
 		int size = SIZE_BYTE + SIZE_LONG + SIZE_BYTE;
 		
 		ByteBuffer bb = ByteBuffer.wrap(new byte[size]);
@@ -385,10 +441,12 @@
 		bb.flip();
 								
 		appendRecord(bb, true);			
+		
+		tx.rollback(currentFile);
 	}
 		
-	public void load(final List<RecordInfo> committedRecords,
-		              final List<PreparedTransactionInfo> preparedTransactions) throws Exception
+	public synchronized void load(final List<RecordInfo> committedRecords,
+		                           final List<PreparedTransactionInfo> preparedTransactions) throws Exception
 	{
 		if (state != STATE_STARTED)
 		{
@@ -419,25 +477,13 @@
 			
 			long orderingID = bb.getLong();
 						
-			orderedFiles.add(new JournalFile(file, orderingID));
+			orderedFiles.add(new JournalFileImpl(file, orderingID));
 			
 			file.close();
 		}
 		
-		int createNum = minFiles - orderedFiles.size();
+		//Now order them by ordering id - we can't use the file name for ordering since we can re-use dataFiles
 		
-		//Preallocate some more if necessary
-		for (int i = 0; i < createNum; i++)
-		{
-			JournalFile file = createFile();
-			
-			orderedFiles.add(file);
-			
-			file.getFile().close();
-		}
-			
-		//Now order them by ordering id - we can't use the file name for ordering since we can re-use files
-		
 		class JournalFileComparator implements Comparator<JournalFile>
 		{
 			public int compare(JournalFile f1, JournalFile f2)
@@ -489,6 +535,7 @@
 					case ADD_RECORD:
 					{									
 						long id = bb.getLong();				
+						
 						int size = bb.getInt();						
 						byte[] record = new byte[size];						
 						bb.get(record);						
@@ -501,7 +548,9 @@
 						else
 						{																				
 							records.add(new RecordInfo(id, record, false));
-							hasData = true;							
+							hasData = true;						
+
+							posFilesMap.put(id, new PosFiles(file));
 						}
 												
 						break;
@@ -509,6 +558,7 @@
 					case UPDATE_RECORD:						
 					{
 						long id = bb.getLong();		
+						
 						int size = bb.getInt();						
 						byte[] record = new byte[size];						
 						bb.get(record);						
@@ -521,7 +571,18 @@
 						else
 						{					
 							records.add(new RecordInfo(id, record, true));							
-							hasData = true;							
+							hasData = true;		
+							file.incPosCount();
+							
+						   PosFiles posFiles = posFilesMap.get(id);
+							
+							if (posFiles != null)
+							{
+								//It's legal for this to be null. The file(s) with the  may have been deleted
+								//just leaving some updates in this file
+								
+								posFiles.addUpdateFile(file);
+							}
 						}
 												
 						break;
@@ -538,7 +599,14 @@
 						else
 						{						
 							recordsToDelete.add(id);							
-							hasData = true;							
+							hasData = true;
+							
+							PosFiles posFiles = posFilesMap.remove(id);
+							
+							if (posFiles != null)
+							{
+								posFiles.addDelete(file);
+							}							
 						}
 						
 						break;
@@ -568,7 +636,19 @@
 							}
 							
 							tx.recordInfos.add(new RecordInfo(id, record, false));							
-							hasData = true;							
+							
+							TransactionNegPos tnp = transactionInfos.get(txID);
+							
+							if (tnp == null)
+							{
+								tnp = new TransactionNegPos();
+								
+								transactionInfos.put(txID, tnp);
+							}
+							
+							tnp.addPos(file, id);
+							
+							hasData = true;														
 						}
 					
 						break;
@@ -598,6 +678,17 @@
 							}
 							
 							tx.recordInfos.add(new RecordInfo(id, record, true));
+							
+							TransactionNegPos tnp = transactionInfos.get(txID);
+							
+							if (tnp == null)
+							{
+								tnp = new TransactionNegPos();
+								
+								transactionInfos.put(txID, tnp);
+							}
+							
+							tnp.addPos(file, id);
 
 							hasData = true;							
 						}
@@ -626,6 +717,18 @@
 							}
 							
 							tx.recordsToDelete.add(id);							
+							
+							TransactionNegPos tnp = transactionInfos.get(txID);
+							
+							if (tnp == null)
+							{
+								tnp = new TransactionNegPos();
+								
+								transactionInfos.put(txID, tnp);
+							}
+							
+							tnp.addNeg(file, id);
+							
 							hasData = true;							
 						}
 											
@@ -651,6 +754,15 @@
 							}
 														
 							tx.prepared = true;
+							
+							TransactionNegPos tnp = transactionInfos.get(txID);
+							
+							if (tnp == null)
+							{
+								throw new IllegalStateException("Cannot find tx " + txID);
+							}
+							
+							tnp.prepare(file);		
 						}
 						
 						break;
@@ -675,7 +787,16 @@
 							}
 							
 							records.addAll(tx.recordInfos);							
-							recordsToDelete.addAll(tx.recordsToDelete);														
+							recordsToDelete.addAll(tx.recordsToDelete);	
+							
+							TransactionNegPos tnp = transactionInfos.remove(txID);
+							
+							if (tnp == null)
+							{
+								throw new IllegalStateException("Cannot find tx " + txID);
+							}
+							
+							tnp.commit(file);							
 						}
 						
 						break;
@@ -697,7 +818,16 @@
 							if (tx == null)
 							{
 								throw new IllegalStateException("Cannot find tx with id " + txID);
-							}						
+							}				
+							
+							TransactionNegPos tnp = transactionInfos.remove(txID);
+							
+							if (tnp == null)
+							{
+								throw new IllegalStateException("Cannot find tx " + txID);
+							}
+							
+							tnp.rollback(file);	
 						}
 						
 						break;
@@ -733,16 +863,14 @@
 						
 			if (hasData)
 			{			
-				files.add(file);
+				dataFiles.add(file);
 				
-				//Files are always maintained closed - there may be a lot of them and we don't want to run out
-				//of file handles
 				file.getFile().close();				
 			}
 			else
 			{				
-				//Empty files with no data
-				availableFiles.add(file);
+				//Empty dataFiles with no data
+				freeFiles.add(file);
 				
 				//Position it ready for writing
 				file.getFile().position(SIZE_LONG);
@@ -750,20 +878,31 @@
 		}			
 		
 		transactionIDSequence.set(maxTransactionID + 1);
-									
-		//Now it's possible that some of the files are no longer needed
 		
-		checkFilesForReclamation();
+		//Create any more files we need
 				
-		//Check we have enough available files
+		//FIXME - size() involves a scan
+		int filesToCreate = minFiles - (dataFiles.size() + freeFiles.size());
 		
-		checkAndCreateAvailableFiles();
-						
-		for (JournalFile file: files)
+		for (int i = 0; i < filesToCreate; i++)
 		{
-			currentFile = file;						
+			freeFiles.add(createFile());
 		}
+												
+		//The current file is the last one
 		
+		Iterator<JournalFile> iter = dataFiles.iterator();
+		
+		while (iter.hasNext())
+		{
+			currentFile = iter.next();
+			
+			if (!iter.hasNext())
+			{
+				iter.remove();
+			}
+		}
+		
 		if (currentFile != null)
 		{		
 			currentFile.getFile().open();
@@ -774,13 +913,9 @@
 		}
 		else
 		{
-			currentFile = availableFiles.remove();
-			
-			files.add(currentFile);
+			currentFile = freeFiles.remove();
 		}				
-		
-		startTasks();
-				
+								
 		for (RecordInfo record: records)
 		{
 			if (!recordsToDelete.contains(record.id))
@@ -794,6 +929,16 @@
 			if (!transaction.prepared)
 			{
 				log.warn("Uncommitted transaction with id " + transaction.transactionID + " found and discarded");
+				
+				TransactionNegPos transactionInfo = this.transactionInfos.get(transaction.transactionID);
+				
+				if (transactionInfo == null)
+				{
+					throw new IllegalStateException("Cannot find tx " + transaction.transactionID);
+				}
+				
+				//Reverse the refs
+				transactionInfo.forget();
 			}
 			else
 			{
@@ -809,19 +954,79 @@
 				
 		state = STATE_LOADED;
 	}
+	
+	// TestableJournal implementation --------------------------------------------------------------
 			
-	public void checkAndCreateAvailableFiles() throws Exception
+	public synchronized void checkAndReclaimFiles() throws Exception
 	{		
-		int filesToCreate = minAvailableFiles - availableFiles.size();
+		JournalFile[] files = new JournalFile[dataFiles.size()];
 		
-		for (int i = 0; i < filesToCreate; i++)
-		{
-			JournalFile file = createFile();
-
-			availableFiles.add(file);
+		reclaimer.scan(dataFiles.toArray(files));
+				
+		for (JournalFile file: dataFiles)
+		{		
+   		if (file.isCanReclaim())
+   		{
+   			//File can be reclaimed or deleted
+   			
+   			dataFiles.remove(file);
+   			
+   			//FIXME - size() involves a scan!!!
+   			if (freeFiles.size() + dataFiles.size() + 1 < minFiles)
+   			{      			
+      			//Re-initialise it
+      			
+      			long newOrderingID = generateOrderingID();
+      			
+      			ByteBuffer bb = ByteBuffer.wrap(new byte[SIZE_LONG]);
+      			
+      			bb.putLong(newOrderingID);
+      			
+      			SequentialFile sf = file.getFile();
+      			
+      			sf.open();
+      			
+      			//Note we MUST re-fill it - otherwise we won't be able to detect corrupt records
+      			
+      			//TODO - if we can avoid this somehow would be good, since filling the file is a heavyweight
+      			//operation and can impact other IO operations on the disk
+      			sf.fill(0, fileSize, FILL_CHARACTER);
+      			
+      			sf.write(bb, true);
+      			
+      			JournalFile jf = new JournalFileImpl(sf, newOrderingID);
+      			
+      			sf.position(SIZE_LONG);
+      			
+      			jf.setOffset(SIZE_LONG);
+      			
+      			freeFiles.add(jf);   
+   			}
+   			else
+   			{
+   				file.getFile().open();
+   				
+   				file.getFile().delete();
+   			}
+   		}
 		}
 	}
 	
+	public int getDataFilesCount()
+	{
+		return dataFiles.size();
+	}
+	
+	public int getFreeFilesCount()
+	{
+		return freeFiles.size();
+	}
+	
+	public int getIDMapSize()
+	{
+		return posFilesMap.size();
+	}
+	
 	// MessagingComponent implementation ---------------------------------------------------
 	
 	public synchronized void start()
@@ -841,93 +1046,54 @@
 			throw new IllegalStateException("Journal is already stopped");
 		}
 		
-		if (reclaimerTask != null)
-		{
-			reclaimerTask.cancel();
-		}
+		stopReclaimer();
 		
-		if (availableFilesTask != null)
-		{
-			availableFilesTask.cancel();
-		}
-		
 		if (currentFile != null)
 		{
 			currentFile.getFile().close();
 		}
 		
-		for (JournalFile file: availableFiles)
+		for (JournalFile file: freeFiles)
 		{
 			file.getFile().close();
 		}
 
 		currentFile = null;
 		
-		files.clear();
+		dataFiles.clear();
 		
-		availableFiles.clear();		
+		freeFiles.clear();		
 		
 		state = STATE_STOPPED;
 	}
 	
-	public void startTasks()
+	public void startReclaimer()
 	{
-//		reclaimerTask = new ReclaimerTask();
-//		timer.schedule(reclaimerTask, taskPeriod, taskPeriod);
-//		
-//		availableFilesTask = new AvailableFilesTask();
-//		timer.schedule(availableFilesTask, taskPeriod, taskPeriod);
+		if (state == STATE_STOPPED)
+		{
+			throw new IllegalStateException("Journal is stopped");
+		}
+		
+		reclaimerTask = new ReclaimerTask();
+		
+		timer.schedule(reclaimerTask, taskPeriod, taskPeriod);
 	}
 	
-	// Public -----------------------------------------------------------------------------
-	
-	public Queue<JournalFile> getFiles()
+	public void stopReclaimer()
 	{
-		return files;
+		if (state == STATE_STOPPED)
+		{
+			throw new IllegalStateException("Journal is already stopped");
+		}
+		
+		if (reclaimerTask != null)
+		{
+			reclaimerTask.cancel();
+		}
 	}
 	
-	public Queue<JournalFile> getAvailableFiles()
-	{
-		return availableFiles;
-	}
-	
-	public void checkFilesForReclamation() throws Exception
-	{		
-		for (JournalFile file: files)
-		{		
-			//TODO reclamation
-   		if (false && file != currentFile)
-   		{
-   			//File can be reclaimed
-   			
-   			files.remove(file);
-   			
-   			//Re-initialise it
-   			
-   			long newOrderingID = generateOrderingID();
-   			
-   			ByteBuffer bb = ByteBuffer.wrap(new byte[SIZE_LONG]);
-   			
-   			bb.putLong(newOrderingID);
-   			
-   			SequentialFile sf = file.getFile();
-   			
-   			//Note we MUST re-fill it - otherwise we won't be able to detect corrupt records
-   			sf.fill(0, fileSize, FILL_CHARACTER);
-   			
-   			sf.write(bb, true);
-   			
-   			JournalFile jf = new JournalFile(sf, newOrderingID);
-   			
-   			sf.position(SIZE_LONG);
-   			
-   			jf.setOffset(SIZE_LONG);
-   			
-   			availableFiles.add(jf);   		
-   		}
-		}
-	}
-		
+	// Public -----------------------------------------------------------------------------
+			
 	// Private -----------------------------------------------------------------------------
 		
 	private void appendRecord(ByteBuffer bb, boolean sync) throws Exception
@@ -981,7 +1147,7 @@
 		
 		sequentialFile.position(SIZE_LONG);
 		
-		JournalFile info = new JournalFile(sequentialFile, orderingID);
+		JournalFile info = new JournalFileImpl(sequentialFile, orderingID);
 		
 		info.extendOffset(SIZE_LONG);
 		
@@ -1018,62 +1184,225 @@
 		}
 
 		if (currentFile == null || fileSize - currentFile.getOffset() < size)
+		{					
+			currentFile.getFile().close();
+			
+			dataFiles.add(currentFile);
+			
+			//FIXME - isEmpty() involves a scan!!
+			if (!freeFiles.isEmpty())
+			{
+				currentFile = freeFiles.remove();
+			}
+			else
+			{
+				currentFile = createFile();
+			}
+		}		
+	}
+	
+	private TransactionNegPos getTransactionInfo(final long txID)
+	{
+		TransactionNegPos tx = transactionInfos.get(txID);
+		
+		if (tx == null)
 		{
-			checkAndCreateAvailableFiles();
+			tx = new TransactionNegPos();
 			
-			currentFile.getFile().close();
-			
-		   currentFile = availableFiles.remove();			
-
-			files.add(currentFile);
+			transactionInfos.put(txID, tx);
 		}
+		
+		return tx;
 	}
+				
 	
+	// Inner classes ---------------------------------------------------------------------------
+	
 	private class ReclaimerTask extends TimerTask
 	{
-		public boolean cancel()
+		public synchronized boolean cancel()
 		{
 			timer.cancel();
 			
 			return super.cancel();
 		}
 
-		public void run()
+		public synchronized void run()
 		{
 			try
 			{
-				//checkFilesForReclamation();
+				checkAndReclaimFiles();		
 			}
 			catch (Exception e)
 			{
-				log.error("Failure in running reclaimer", e);
+				log.error("Failure in running ReclaimerTask", e);
 				
 				cancel();
 			}
 		}		
-	}
+	}	
 	
-	private class AvailableFilesTask extends TimerTask
+	private static class PosFiles
 	{
-		public boolean cancel()
+		private final JournalFile addFile;
+		
+		private List<JournalFile> updateFiles;
+		
+		PosFiles(final JournalFile addFile)
 		{
-			timer.cancel();
+			this.addFile = addFile;
 			
-			return super.cancel();
+			addFile.incPosCount();
 		}
-
-		public void run()
+		
+		void addUpdateFile(final JournalFile updateFile)
 		{
-			try
+			if (updateFiles == null)
 			{
-				//checkAndCreateAvailableFiles();
+				updateFiles = new ArrayList<JournalFile>();
 			}
-			catch (Exception e)
+			
+			updateFiles.add(updateFile);
+			
+			updateFile.incPosCount();
+		}
+		
+		void addDelete(final JournalFile file)
+		{
+			file.incNegCount(addFile);
+			
+			if (updateFiles != null)
 			{
-				log.error("Failure in running availableFileChecker", e);
+				for (JournalFile jf: updateFiles)
+				{
+					file.incNegCount(jf);
+				}
+			}
+		}
+	}
+		
+	private class TransactionNegPos
+	{
+		private List<Pair<JournalFile, Long>> pos;
+		
+		private List<Pair<JournalFile, Long>> neg;
+		
+		private Set<JournalFile> transactionPos;
+		
+		void addTXPosCount(final JournalFile file)
+		{
+			if (transactionPos == null)
+			{
+				transactionPos = new HashSet<JournalFile>();
+			}
+						
+			if (!transactionPos.contains(file))
+			{
+				transactionPos.add(file);
 				
-				cancel();
+				//We add a pos for the transaction itself in the file - this prevents any transactional operations
+				//being deleted before a commit or rollback is written
+				file.incPosCount();
+			}	
+		}
+		
+		void addPos(final JournalFile file, final long id)
+		{		
+			addTXPosCount(file);				
+			
+			if (pos == null)
+			{
+				pos = new ArrayList<Pair<JournalFile, Long>>();
 			}
-		}		
-	}	
+
+			pos.add(new Pair<JournalFile, Long>(file, id));
+		}
+		
+		void addNeg(final JournalFile file, final long id)
+		{			
+			addTXPosCount(file);		
+			
+			if (neg == null)
+			{
+				neg = new ArrayList<Pair<JournalFile, Long>>();
+			}
+			
+			neg.add(new Pair<JournalFile, Long>(file, id));			
+		}
+		
+		void commit(final JournalFile file)
+		{			
+			if (pos != null)
+			{
+				for (Pair<JournalFile, Long> p: pos)
+	   		{
+					PosFiles posFiles = posFilesMap.get(p.b);
+					
+					if (posFiles == null)
+					{
+						posFiles = new PosFiles(p.a);
+						
+						posFilesMap.put(p.b, posFiles);
+					}
+					else
+					{					
+					   posFiles.addUpdateFile(p.a);
+					}
+	   		}
+			}
+			
+			if (neg != null)
+			{
+   			for (Pair<JournalFile, Long> n: neg)
+   			{
+   				PosFiles posFiles = posFilesMap.remove(n.b);
+   				
+   				if (posFiles == null)
+   				{
+   					throw new IllegalStateException("Cannot find add info " + n.b);
+   				}
+   				
+   				posFiles.addDelete(n.a);
+   			}
+			}
+			
+			//Now add negs for the pos we added in each file in which there were transactional operations
+			
+			for (JournalFile jf: transactionPos)
+			{
+				file.incNegCount(jf);
+			}			
+		}
+		
+		void rollback(JournalFile file)
+		{		
+			//Now add negs for the pos we added in each file in which there were transactional operations
+			//Note that we do this on rollback as we do on commit, since we need to ensure the file containing
+			//the rollback record doesn't get deleted before the files with the transactional operations are deleted
+			//Otherwise we may run into problems especially with XA where we are just left with a prepare when the tx
+			//has actually been rolled back
+			
+			for (JournalFile jf: transactionPos)
+			{
+				file.incNegCount(jf);
+			}
+		}
+		
+		void prepare(JournalFile file)
+		{
+			//We don't want the prepare record getting deleted before time
+			
+			addTXPosCount(file);
+		}
+		
+		void forget()
+		{
+			//The transaction was not committed or rolled back in the file, so we reverse any pos counts we added
+			
+			for (JournalFile jf: transactionPos)
+			{
+				jf.decPosCount();
+			}
+		}
+	}
 }

Added: trunk/src/main/org/jboss/messaging/core/journal/impl/Reclaimer.java
===================================================================
--- trunk/src/main/org/jboss/messaging/core/journal/impl/Reclaimer.java	                        (rev 0)
+++ trunk/src/main/org/jboss/messaging/core/journal/impl/Reclaimer.java	2008-04-04 14:25:20 UTC (rev 4008)
@@ -0,0 +1,101 @@
+/*
+  * JBoss, Home of Professional Open Source
+  * Copyright 2005, JBoss Inc., and individual contributors as indicated
+  * by the @authors tag. See the copyright.txt in the distribution for a
+  * full listing of individual contributors.
+  *
+  * This is free software; you can redistribute it and/or modify it
+  * under the terms of the GNU Lesser General Public License as
+  * published by the Free Software Foundation; either version 2.1 of
+  * the License, or (at your option) any later version.
+  *
+  * This software is distributed in the hope that it will be useful,
+  * but WITHOUT ANY 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 along with this software; if not, write to the Free
+  * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+  * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+  */
+package org.jboss.messaging.core.journal.impl;
+
+import org.jboss.messaging.core.logging.Logger;
+
+
+/**
+ * 
+ * A ReclaimerTest
+ * 
+ * The journal consists of an ordered list of journal files Fn where 0 <= n <= N
+ * 
+ * A journal file can contain either positives (pos) or negatives (neg)
+ * 
+ * (Positives correspond either to adds or updates, and negatives correspond to deletes).
+ * 
+ * A file Fn can be deleted if, and only if the following criteria are satisified
+ * 
+ * 1) All pos in a file Fn, must have corresponding neg in any file Fm where m >= n.
+ * 
+ * 2) All pos that correspond to any neg in file Fn, must all live in any file Fm where 0 <= m <= n
+ * which are also marked for deletion in the same pass of the algorithm.
+ * 
+ * @author <a href="mailto:tim.fox at jboss.com">Tim Fox</a>
+ *
+ */
+public class Reclaimer
+{
+	private static final Logger log = Logger.getLogger(Reclaimer.class);
+		
+	public void scan(final JournalFile[] files)
+	{
+		for (int i = 0; i < files.length; i++)
+		{
+			//First we evaluate criterion 1)
+			
+			JournalFile currentFile = files[i];
+
+			int posCount = currentFile.getPosCount();
+
+			int totNeg = 0;
+			
+			for (int j = i; j < files.length; j++)
+			{
+				totNeg += files[j].getNegCount(currentFile);
+			}
+			
+			currentFile.setCanReclaim(true);
+			
+			if (posCount <= totNeg)
+			{   		
+	   		//Now we evaluate criterion 2)
+				
+	   		for (int j = 0; j <= i; j++)
+	   		{
+	   			JournalFile file = files[j];
+	   			
+	   			int negCount = currentFile.getNegCount(file);
+	   			
+	   			if (negCount != 0)
+	   			{
+	   				if (file.isCanReclaim())
+	   				{
+	   					//Ok
+	   				}
+	   				else
+	   				{
+	   					currentFile.setCanReclaim(false);
+	   					
+	   					break;
+	   				}
+	   			}
+	   		}   		
+			}
+			else
+			{
+				currentFile.setCanReclaim(false);
+			}			
+		}			
+	}
+}

Modified: trunk/src/main/org/jboss/messaging/core/journal/impl/TransactionHolder.java
===================================================================
--- trunk/src/main/org/jboss/messaging/core/journal/impl/TransactionHolder.java	2008-04-04 11:01:48 UTC (rev 4007)
+++ trunk/src/main/org/jboss/messaging/core/journal/impl/TransactionHolder.java	2008-04-04 14:25:20 UTC (rev 4008)
@@ -14,6 +14,7 @@
  * @author <a href="mailto:tim.fox at jboss.com">Tim Fox</a>
  *
  */
+
 public class TransactionHolder
 {
 	public TransactionHolder(final long id)

Modified: trunk/src/main/org/jboss/messaging/core/persistence/impl/journal/JournalStorageManager.java
===================================================================
--- trunk/src/main/org/jboss/messaging/core/persistence/impl/journal/JournalStorageManager.java	2008-04-04 11:01:48 UTC (rev 4007)
+++ trunk/src/main/org/jboss/messaging/core/persistence/impl/journal/JournalStorageManager.java	2008-04-04 14:25:20 UTC (rev 4008)
@@ -100,7 +100,7 @@
 			
 	   SequentialFileFactory bindingsFF = new NIOSequentialFileFactory(bindingsDir);
 	      
-	   bindingsJournal = new JournalImpl(1024 * 1024, 2, 2, true, bindingsFF, 30000, "jbm-bindings", "bindings");
+	   bindingsJournal = new JournalImpl(1024 * 1024, 2, true, bindingsFF, 10000, "jbm-bindings", "bindings");
 	      
 	   String journalDir = config.getJournalDirectory();
 	   
@@ -113,8 +113,8 @@
 	       
 	   SequentialFileFactory journalFF = new NIOSequentialFileFactory(journalDir);
 	      
-	   messageJournal = new JournalImpl(config.getJournalFileSize(), config.getJournalMinFiles(),
-	   		config.getJournalMinAvailableFiles(), config.isJournalSync(), journalFF,
+	   messageJournal = new JournalImpl(config.getJournalFileSize(), 
+	   		config.getJournalMinFiles(), config.isJournalSync(), journalFF,
 	   		config.getJournalTaskPeriod(), "jbm-data", "jbm");
 	}
 	
@@ -514,8 +514,12 @@
 		
 		bindingsJournal.start();
 		
+		bindingsJournal.startReclaimer();
+		
 		messageJournal.start();
 		
+		messageJournal.startReclaimer();
+		
 		started = true;
 	}
 

Added: trunk/tests/src/org/jboss/messaging/core/journal/impl/test/timing/FakeJournalImplTest.java
===================================================================
--- trunk/tests/src/org/jboss/messaging/core/journal/impl/test/timing/FakeJournalImplTest.java	                        (rev 0)
+++ trunk/tests/src/org/jboss/messaging/core/journal/impl/test/timing/FakeJournalImplTest.java	2008-04-04 14:25:20 UTC (rev 4008)
@@ -0,0 +1,41 @@
+/*
+  * JBoss, Home of Professional Open Source
+  * Copyright 2005, JBoss Inc., and individual contributors as indicated
+  * by the @authors tag. See the copyright.txt in the distribution for a
+  * full listing of individual contributors.
+  *
+  * This is free software; you can redistribute it and/or modify it
+  * under the terms of the GNU Lesser General Public License as
+  * published by the Free Software Foundation; either version 2.1 of
+  * the License, or (at your option) any later version.
+  *
+  * This software is distributed in the hope that it will be useful,
+  * but WITHOUT ANY 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 along with this software; if not, write to the Free
+  * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+  * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+  */
+package org.jboss.messaging.core.journal.impl.test.timing;
+
+import org.jboss.messaging.core.journal.SequentialFileFactory;
+import org.jboss.messaging.core.journal.impl.test.unit.fakes.FakeSequentialFileFactory;
+
+/**
+ * 
+ * A FakeJournalImplTest
+ * 
+ * @author <a href="mailto:tim.fox at jboss.com">Tim Fox</a>
+ *
+ */
+public class FakeJournalImplTest extends JournalImplTestUnit
+{
+	protected SequentialFileFactory getFileFactory() throws Exception
+	{
+		return new FakeSequentialFileFactory();
+	}
+}
+

Added: trunk/tests/src/org/jboss/messaging/core/journal/impl/test/timing/JournalImplTestUnit.java
===================================================================
--- trunk/tests/src/org/jboss/messaging/core/journal/impl/test/timing/JournalImplTestUnit.java	                        (rev 0)
+++ trunk/tests/src/org/jboss/messaging/core/journal/impl/test/timing/JournalImplTestUnit.java	2008-04-04 14:25:20 UTC (rev 4008)
@@ -0,0 +1,176 @@
+/*
+  * JBoss, Home of Professional Open Source
+  * Copyright 2005, JBoss Inc., and individual contributors as indicated
+  * by the @authors tag. See the copyright.txt in the distribution for a
+  * full listing of individual contributors.
+  *
+  * This is free software; you can redistribute it and/or modify it
+  * under the terms of the GNU Lesser General Public License as
+  * published by the Free Software Foundation; either version 2.1 of
+  * the License, or (at your option) any later version.
+  *
+  * This software is distributed in the hope that it will be useful,
+  * but WITHOUT ANY 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 along with this software; if not, write to the Free
+  * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+  * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+  */
+package org.jboss.messaging.core.journal.impl.test.timing;
+
+import java.util.ArrayList;
+
+import org.jboss.messaging.core.journal.Journal;
+import org.jboss.messaging.core.journal.RecordInfo;
+import org.jboss.messaging.core.journal.impl.JournalImpl;
+import org.jboss.messaging.core.journal.impl.NIOSequentialFileFactory;
+import org.jboss.messaging.core.journal.impl.test.unit.JournalImplTestBase;
+import org.jboss.messaging.core.logging.Logger;
+
+/**
+ * 
+ * A RealJournalImplTest
+ * 
+ * @author <a href="mailto:tim.fox at jboss.com">Tim Fox</a>
+ *
+ */
+public abstract class JournalImplTestUnit extends JournalImplTestBase
+{
+	private static final Logger log = Logger.getLogger(JournalImplTestUnit.class);
+	
+	
+	
+	public void testAddUpdateDeleteManyLargeFileSize() throws Exception
+	{
+		final int numberAdds = 10000;
+		
+		final int numberUpdates = 5000;
+		
+		final int numberDeletes = 3000;
+						
+		long[] adds = new long[numberAdds];
+		
+		for (int i = 0; i < numberAdds; i++)
+		{
+			adds[i] = i;
+		}
+		
+		long[] updates = new long[numberUpdates];
+		
+		for (int i = 0; i < numberUpdates; i++)
+		{
+			updates[i] = i;
+		}
+		
+		long[] deletes = new long[numberDeletes];
+		
+		for (int i = 0; i < numberDeletes; i++)
+		{
+			deletes[i] = i;
+		}
+		
+		setup(10, 10 * 1024 * 1024, true);
+		createJournal();
+		startJournal();
+		load();
+		add(adds);
+		update(updates);
+		delete(deletes);
+		stopJournal();
+		createJournal();
+		startJournal();
+		loadAndCheck();
+		
+	}
+	
+	public void testAddUpdateDeleteManySmallFileSize() throws Exception
+	{
+		final int numberAdds = 10000;
+		
+		final int numberUpdates = 5000;
+		
+		final int numberDeletes = 3000;
+						
+		long[] adds = new long[numberAdds];
+		
+		for (int i = 0; i < numberAdds; i++)
+		{
+			adds[i] = i;
+		}
+		
+		long[] updates = new long[numberUpdates];
+		
+		for (int i = 0; i < numberUpdates; i++)
+		{
+			updates[i] = i;
+		}
+		
+		long[] deletes = new long[numberDeletes];
+		
+		for (int i = 0; i < numberDeletes; i++)
+		{
+			deletes[i] = i;
+		}
+		
+		setup(10, 10 * 1024, true);
+		createJournal();
+		startJournal();
+		load();
+		add(adds);
+		update(updates);
+		delete(deletes);
+		stopJournal();
+		createJournal();
+		startJournal();
+		loadAndCheck();
+		
+	}
+	
+	public void testReclaimAndReload() throws Exception
+	{
+		setup(5, 10 * 1024 * 1024, true);
+		createJournal();
+		startJournal();
+		load();
+		
+		journal.startReclaimer();
+		
+		long start = System.currentTimeMillis();
+						
+		for (int count = 0; count < 100000; count++)
+		{
+			add(count);
+			
+			if (count >= 5000)
+			{
+				delete(count - 5000);
+			}
+			
+			if (count % 10000 == 0)
+			{
+				log.info("Done: " + count);
+			}
+		}
+		
+		long end = System.currentTimeMillis();
+		
+		double rate = 1000 * ((double)100000) / (end - start);
+		
+		log.info("Rate of " + rate + " adds/removes per sec");
+					
+		stopJournal();
+		createJournal();
+		startJournal();
+		loadAndCheck();
+		
+		assertEquals(5000, journal.getIDMapSize());
+		
+		stopJournal();
+	}
+	
+}
+
+

Added: trunk/tests/src/org/jboss/messaging/core/journal/impl/test/timing/RealJournalImplTest.java
===================================================================
--- trunk/tests/src/org/jboss/messaging/core/journal/impl/test/timing/RealJournalImplTest.java	                        (rev 0)
+++ trunk/tests/src/org/jboss/messaging/core/journal/impl/test/timing/RealJournalImplTest.java	2008-04-04 14:25:20 UTC (rev 4008)
@@ -0,0 +1,121 @@
+/*
+  * JBoss, Home of Professional Open Source
+  * Copyright 2005, JBoss Inc., and individual contributors as indicated
+  * by the @authors tag. See the copyright.txt in the distribution for a
+  * full listing of individual contributors.
+  *
+  * This is free software; you can redistribute it and/or modify it
+  * under the terms of the GNU Lesser General Public License as
+  * published by the Free Software Foundation; either version 2.1 of
+  * the License, or (at your option) any later version.
+  *
+  * This software is distributed in the hope that it will be useful,
+  * but WITHOUT ANY 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 along with this software; if not, write to the Free
+  * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+  * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+  */
+package org.jboss.messaging.core.journal.impl.test.timing;
+
+import java.io.File;
+import java.util.ArrayList;
+
+import org.jboss.messaging.core.journal.Journal;
+import org.jboss.messaging.core.journal.RecordInfo;
+import org.jboss.messaging.core.journal.SequentialFileFactory;
+import org.jboss.messaging.core.journal.impl.JournalImpl;
+import org.jboss.messaging.core.journal.impl.NIOSequentialFileFactory;
+import org.jboss.messaging.core.logging.Logger;
+
+/**
+ * 
+ * A RealJournalImplTest
+ * 
+ * @author <a href="mailto:tim.fox at jboss.com">Tim Fox</a>
+ *
+ */
+public class RealJournalImplTest extends JournalImplTestUnit
+{
+	private static final Logger log = Logger.getLogger(RealJournalImplTest.class);
+	
+	protected String journalDir = System.getProperty("user.home") + "/journal-test";
+		
+	protected SequentialFileFactory getFileFactory() throws Exception
+	{
+		File file = new File(journalDir);
+		
+		log.info("deleting directory " + journalDir);
+		
+		deleteDirectory(file);
+		
+		file.mkdir();		
+		
+		return new NIOSequentialFileFactory(journalDir);
+	}
+	
+	public void testSpeedNonTransactional() throws Exception
+	{
+		Journal journal =
+			new JournalImpl(10 * 1024 * 1024, 10, true, new NIOSequentialFileFactory(journalDir),
+					5000, "jbm-data", "jbm");
+		
+		journal.start();
+		
+		journal.load(new ArrayList<RecordInfo>(), null);
+		
+		final int numMessages = 10000;
+		
+		byte[] data = new byte[1024];
+		
+		long start = System.currentTimeMillis();
+		
+		for (int i = 0; i < numMessages; i++)
+		{
+			journal.appendAddRecord(i, data);
+		}
+		
+		long end = System.currentTimeMillis();
+		
+		double rate = 1000 * (double)numMessages / (end - start);
+		
+		log.info("Rate " + rate + " records/sec");
+
+	}
+	
+	public void testSpeedTransactional() throws Exception
+	{
+		Journal journal =
+			new JournalImpl(10 * 1024 * 1024, 10, true, new NIOSequentialFileFactory(journalDir),
+					5000, "jbm-data", "jbm");
+		
+		journal.start();
+		
+		journal.load(new ArrayList<RecordInfo>(), null);
+		
+		final int numMessages = 10000;
+		
+		byte[] data = new byte[1024];
+		
+		long start = System.currentTimeMillis();
+		
+		int count = 0;
+		for (int i = 0; i < numMessages; i++)
+		{
+			journal.appendAddRecordTransactional(i, count++, data);
+			
+			journal.appendCommitRecord(i);
+		}
+		
+		long end = System.currentTimeMillis();
+		
+		double rate = 1000 * (double)numMessages / (end - start);
+		
+		log.info("Rate " + rate + " records/sec");
+
+	}
+}
+

Copied: trunk/tests/src/org/jboss/messaging/core/journal/impl/test/unit/FakeJournalImplTest.java (from rev 3884, trunk/tests/src/org/jboss/messaging/core/journal/impl/test/unit/JournalImplTest.java)
===================================================================
--- trunk/tests/src/org/jboss/messaging/core/journal/impl/test/unit/FakeJournalImplTest.java	                        (rev 0)
+++ trunk/tests/src/org/jboss/messaging/core/journal/impl/test/unit/FakeJournalImplTest.java	2008-04-04 14:25:20 UTC (rev 4008)
@@ -0,0 +1,40 @@
+/*
+  * JBoss, Home of Professional Open Source
+  * Copyright 2005, JBoss Inc., and individual contributors as indicated
+  * by the @authors tag. See the copyright.txt in the distribution for a
+  * full listing of individual contributors.
+  *
+  * This is free software; you can redistribute it and/or modify it
+  * under the terms of the GNU Lesser General Public License as
+  * published by the Free Software Foundation; either version 2.1 of
+  * the License, or (at your option) any later version.
+  *
+  * This software is distributed in the hope that it will be useful,
+  * but WITHOUT ANY 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 along with this software; if not, write to the Free
+  * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+  * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+  */
+package org.jboss.messaging.core.journal.impl.test.unit;
+
+import org.jboss.messaging.core.journal.SequentialFileFactory;
+import org.jboss.messaging.core.journal.impl.test.unit.fakes.FakeSequentialFileFactory;
+
+/**
+ * 
+ * A FakeJournalImplTest
+ * 
+ * @author <a href="mailto:tim.fox at jboss.com">Tim Fox</a>
+ *
+ */
+public class FakeJournalImplTest extends JournalImplTestUnit
+{
+	protected SequentialFileFactory getFileFactory() throws Exception
+	{
+		return new FakeSequentialFileFactory();
+	}
+}

Deleted: trunk/tests/src/org/jboss/messaging/core/journal/impl/test/unit/JournalImplTest.java
===================================================================
--- trunk/tests/src/org/jboss/messaging/core/journal/impl/test/unit/JournalImplTest.java	2008-04-04 11:01:48 UTC (rev 4007)
+++ trunk/tests/src/org/jboss/messaging/core/journal/impl/test/unit/JournalImplTest.java	2008-04-04 14:25:20 UTC (rev 4008)
@@ -1,45 +0,0 @@
-/*
-  * JBoss, Home of Professional Open Source
-  * Copyright 2005, JBoss Inc., and individual contributors as indicated
-  * by the @authors tag. See the copyright.txt in the distribution for a
-  * full listing of individual contributors.
-  *
-  * This is free software; you can redistribute it and/or modify it
-  * under the terms of the GNU Lesser General Public License as
-  * published by the Free Software Foundation; either version 2.1 of
-  * the License, or (at your option) any later version.
-  *
-  * This software is distributed in the hope that it will be useful,
-  * but WITHOUT ANY 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 along with this software; if not, write to the Free
-  * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
-  * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
-  */
-package org.jboss.messaging.core.journal.impl.test.unit;
-
-import org.jboss.messaging.core.journal.SequentialFileFactory;
-import org.jboss.messaging.core.journal.impl.test.unit.fakes.FakeSequentialFileFactory;
-
-/**
- * 
- * A JournalImplTest
- * 
- * @author <a href="mailto:tim.fox at jboss.com">Tim Fox</a>
- *
- */
-public class JournalImplTest extends JournalImplTestBase
-{
-	protected void prepareDirectory() throws Exception
-	{				
-		//NOOP
-	}
-	
-	protected SequentialFileFactory getFileFactory() throws Exception
-	{
-		return new FakeSequentialFileFactory();
-	}
-}

Modified: trunk/tests/src/org/jboss/messaging/core/journal/impl/test/unit/JournalImplTestBase.java
===================================================================
--- trunk/tests/src/org/jboss/messaging/core/journal/impl/test/unit/JournalImplTestBase.java	2008-04-04 11:01:48 UTC (rev 4007)
+++ trunk/tests/src/org/jboss/messaging/core/journal/impl/test/unit/JournalImplTestBase.java	2008-04-04 14:25:20 UTC (rev 4008)
@@ -21,7 +21,6 @@
   */
 package org.jboss.messaging.core.journal.impl.test.unit;
 
-import java.nio.ByteBuffer;
 import java.util.ArrayList;
 import java.util.Iterator;
 import java.util.LinkedHashMap;
@@ -30,10 +29,10 @@
 import java.util.ListIterator;
 import java.util.Map;
 
-import org.jboss.messaging.core.journal.Journal;
 import org.jboss.messaging.core.journal.PreparedTransactionInfo;
 import org.jboss.messaging.core.journal.RecordInfo;
 import org.jboss.messaging.core.journal.SequentialFileFactory;
+import org.jboss.messaging.core.journal.TestableJournal;
 import org.jboss.messaging.core.journal.impl.JournalImpl;
 import org.jboss.messaging.core.logging.Logger;
 import org.jboss.messaging.test.unit.RandomUtil;
@@ -50,45 +49,31 @@
 {
 	private static final Logger log = Logger.getLogger(JournalImplTestBase.class);
 	
-	private List<RecordInfo> records = new LinkedList<RecordInfo>();
+	protected List<RecordInfo> records = new LinkedList<RecordInfo>();
 	
-	private Journal journal;
+	protected TestableJournal journal;
 	
-	private int recordLength = 1024;
+	protected int recordLength = 1024;
 	
-	private Map<Long, TransactionHolder> transactions = new LinkedHashMap<Long, TransactionHolder>();
+	protected Map<Long, TransactionHolder> transactions = new LinkedHashMap<Long, TransactionHolder>();
 	
-	private int minFiles;
+	protected int minFiles;
 	
-	private int minAvailableFiles;
+	protected int fileSize;
 	
-	private int fileSize;
+	protected boolean sync;
 	
-	private boolean sync;
+	protected String filePrefix = "jbm";
 	
-	private String filePrefix = "jbm";
+	protected String fileExtension = "jbm";
 	
-	private String fileExtension = "jbm";
-	
-	private SequentialFileFactory fileFactory;
-	
-	private void logThem()
-	{
-		log.info("**** loggingg attributes***");
-		log.info("recordlength:" + recordLength);
-		log.info("minfiles:" + minFiles);
-		log.info("minavailableFiles:" + minAvailableFiles);
-		log.info("filesize:" + fileSize);
-		log.info("sync:" + sync);
-	}
-						
+	protected SequentialFileFactory fileFactory;
+					
 	protected void setUp() throws Exception
 	{
 		super.setUp();
 		
-		prepareDirectory();
-		
-		fileFactory = getFileFactory();
+		resetFileFactory();
 
 		transactions.clear();
 		
@@ -115,1021 +100,18 @@
 		journal = null;;
 	}
 	
-	protected abstract void prepareDirectory() throws Exception;
-	
-	protected abstract SequentialFileFactory getFileFactory() throws Exception;
-	
-	// General tests
-	// =============
-	
-	public void testState() throws Exception
+	protected void resetFileFactory() throws Exception
 	{
-		setup(10, 10, 10 * 1024, true);
-		createJournal();
-		try
-		{
-			load();
-			fail("Should throw exception");
-		}
-		catch (IllegalStateException e)
-		{
-			//OK
-		}
-		try
-		{
-			stopJournal();
-			fail("Should throw exception");
-		}
-		catch (IllegalStateException e)
-		{
-			//OK
-		}
-		startJournal();
-		try
-		{
-			startJournal();
-			fail("Should throw exception");
-		}
-		catch (IllegalStateException e)
-		{
-			//OK
-		}
-		stopJournal();
-		startJournal();
-		load();
-		try
-		{
-			load();
-			fail("Should throw exception");
-		}
-		catch (IllegalStateException e)
-		{
-			//OK
-		}
-		try
-		{
-			startJournal();
-			fail("Should throw exception");
-		}
-		catch (IllegalStateException e)
-		{
-			//OK
-		}
-		stopJournal();		
+		fileFactory = getFileFactory();
 	}
 	
-	public void testParams() throws Exception
-	{
-		try
-		{
-			new JournalImpl(JournalImpl.MIN_FILE_SIZE - 1, 10, 10, true, fileFactory, 5000, filePrefix, fileExtension);
-			
-			fail("Should throw exception");
-		}
-		catch (IllegalArgumentException e)
-		{
-			//Ok
-		}
+	protected abstract SequentialFileFactory getFileFactory() throws Exception;
 		
-		try
-		{
-			new JournalImpl(10 * 1024, 1, 10, true, fileFactory, 5000, filePrefix, fileExtension);
-			
-			fail("Should throw exception");
-		}
-		catch (IllegalArgumentException e)
-		{
-			//Ok
-		}
-		
-		try
-		{
-			new JournalImpl(10 * 1024, 10, 1, true, fileFactory, 5000, filePrefix, fileExtension);
-			
-			fail("Should throw exception");
-		}
-		catch (IllegalArgumentException e)
-		{
-			//Ok
-		}
-		
-		try
-		{
-			new JournalImpl(10 * 1024, 10, 10, true, null, 5000, filePrefix, fileExtension);
-			
-			fail("Should throw exception");
-		}
-		catch (NullPointerException e)
-		{
-			//Ok
-		}
-		
-		try
-		{
-			new JournalImpl(10 * 1024, 10, 10, true, fileFactory, JournalImpl.MIN_TASK_PERIOD - 1, filePrefix, fileExtension);
-			
-			fail("Should throw exception");
-		}
-		catch (IllegalArgumentException e)
-		{
-			//Ok
-		}
-		
-		try
-		{
-			new JournalImpl(10 * 1024, 10, 10, true, fileFactory, 5000, null, fileExtension);
-			
-			fail("Should throw exception");
-		}
-		catch (NullPointerException e)
-		{
-			//Ok
-		}
-		
-		try
-		{
-			new JournalImpl(10 * 1024, 10, 10, true, fileFactory, 5000, filePrefix, null);
-			
-			fail("Should throw exception");
-		}
-		catch (NullPointerException e)
-		{
-			//Ok
-		}
-		
-	}
-
-	// Non transactional tests
-	// =======================
-	
-	public void testSimpleAdd() throws Exception
-	{
-		setup(10, 10, 10 * 1024, true);
-		createJournal();
-		startJournal();
-		load();
-		add(1);	
-		stopJournal();
-		createJournal();
-		startJournal();
-		loadAndCheck();
-	}
-	
-	public void testMultipleAdd() throws Exception
-	{
-		setup(10, 10, 10 * 1024, true);
-		createJournal();
-		startJournal();
-		load();
-		add(1,2,3,4,5,6,7,8,9,10);		
-		stopJournal();
-		createJournal();
-		startJournal();
-		loadAndCheck();
-	}
-	
-	public void testMultipleAddNonContiguous() throws Exception
-	{
-		setup(10, 10, 10 * 1024, true);
-		createJournal();
-		startJournal();
-		load();
-		add(1,3,5,7,10,13,56,100,102,200,201,202,203);		
-		stopJournal();
-		createJournal();
-		startJournal();
-		loadAndCheck();
-	}
-	
-	public void testSimpleAddUpdate() throws Exception
-	{
-		setup(10, 10, 10 * 1024, true);
-		createJournal();
-		startJournal();
-		load();
-		add(1);		
-		update(1);
-		stopJournal();
-		createJournal();
-		startJournal();
-		loadAndCheck();
-	}
-	
-	public void testMultipleAddUpdate() throws Exception
-	{
-		setup(10, 10, 10 * 1024, true);
-		createJournal();
-		startJournal();
-		load();
-		add(1,2,3,4,5,6,7,8,9,10);		
-		update(1,2,4,7,9,10);
-		stopJournal();
-		createJournal();
-		startJournal();
-		loadAndCheck();
-	}
-	
-	public void testMultipleAddUpdateAll() throws Exception
-	{
-		setup(10, 10, 10 * 1024, true);
-		createJournal();
-		startJournal();
-		load();
-		add(1,2,3,4,5,6,7,8,9,10);		
-		update(1,2,3,4,5,6,7,8,9,10);
-		stopJournal();
-		createJournal();
-		startJournal();
-		loadAndCheck();
-	}
-	
-	public void testMultipleAddUpdateNonContiguous() throws Exception
-	{
-		setup(10, 10, 10 * 1024, true);
-		createJournal();
-		startJournal();
-		load();
-		add(1,3,5,7,10,13,56,100,102,200,201,202,203);	
-		add(3,7,10,13,56,100,200,202,203);	
-		stopJournal();
-		createJournal();
-		startJournal();
-		loadAndCheck();
-	}
-	
-	public void testMultipleAddUpdateAllNonContiguous() throws Exception
-	{
-		setup(10, 10, 10 * 1024, true);
-		createJournal();
-		startJournal();
-		load();
-		add(1,3,5,7,10,13,56,100,102,200,201,202,203);
-		update(1,3,5,7,10,13,56,100,102,200,201,202,203);
-		stopJournal();
-		createJournal();
-		startJournal();
-		loadAndCheck();
-	}
-		
-	public void testSimpleAddUpdateDelete() throws Exception
-	{
-		setup(10, 10, 10 * 1024, true);
-		createJournal();
-		startJournal();
-		load();
-		add(1);		
-		update(1);
-		delete(1);
-		stopJournal();
-		createJournal();
-		startJournal();
-		loadAndCheck();
-	}
-	
-	public void testMultipleAddUpdateDelete() throws Exception
-	{
-		setup(10, 10, 10 * 1024, true);
-		createJournal();
-		startJournal();
-		load();
-		add(1,2,3,4,5,6,7,8,9,10);		
-		update(1,2,4,7,9,10);
-		delete(1,4,7,9,10);
-		stopJournal();
-		createJournal();
-		startJournal();
-		loadAndCheck();
-	}
-	
-	public void testMultipleAddUpdateDeleteAll() throws Exception
-	{
-		setup(10, 10, 10 * 1024, true);
-		createJournal();
-		startJournal();
-		load();
-		add(1,2,3,4,5,6,7,8,9,10);		
-		update(1,2,3,4,5,6,7,8,9,10);
-		update(1,2,3,4,5,6,7,8,9,10);
-		stopJournal();
-		createJournal();
-		startJournal();
-		loadAndCheck();
-	}
-	
-	public void testMultipleAddUpdateDeleteNonContiguous() throws Exception
-	{
-		setup(10, 10, 10 * 1024, true);
-		createJournal();
-		startJournal();
-		load();
-		add(1,3,5,7,10,13,56,100,102,200,201,202,203);	
-		add(3,7,10,13,56,100,200,202,203);	
-		delete(3,10,56,100,200,203);	
-		stopJournal();
-		createJournal();
-		startJournal();
-		loadAndCheck();
-	}
-	
-	public void testMultipleAddUpdateDeleteAllNonContiguous() throws Exception
-	{
-		setup(10, 10, 10 * 1024, true);
-		createJournal();
-		startJournal();
-		load();
-		add(1,3,5,7,10,13,56,100,102,200,201,202,203);
-		update(1,3,5,7,10,13,56,100,102,200,201,202,203);
-		delete(1,3,5,7,10,13,56,100,102,200,201,202,203);
-		stopJournal();
-		createJournal();
-		startJournal();
-		loadAndCheck();		
-	}
-	
-	public void testMultipleAddUpdateDeleteDifferentOrder() throws Exception
-	{
-		setup(10, 10, 10 * 1024, true);
-		createJournal();
-		startJournal();
-		load();
-		add(1,3,5,7,10,13,56,100,102,200,201,202,203);
-		update(203, 202, 201, 200, 102, 100, 1, 3, 5, 7, 10, 13, 56);
-		delete(56, 13, 10, 7, 5, 3, 1, 203, 202, 201, 200, 102, 100);
-		stopJournal();
-		createJournal();
-		startJournal();
-		loadAndCheck();		
-	}
-		
-	public void testMultipleAddUpdateDeleteDifferentRecordLengths() throws Exception
-	{
-		setup(10, 10, 2048, true);
-		createJournal();
-		startJournal();
-		load();
-		
-		for (int i = 0; i < 1000; i++)
-		{
-			byte[] record = generateRecord(10 + (int)(1500 * Math.random()));
-			
-			journal.appendAddRecord(i, record);
-			
-			records.add(new RecordInfo(i, record, false));
-		}
-		
-		for (int i = 0; i < 1000; i++)
-		{
-			byte[] record = generateRecord(10 + (int)(1024 * Math.random()));
-			
-			journal.appendUpdateRecord(i, record);
-			
-			records.add(new RecordInfo(i, record, true));
-		}
-		
-		for (int i = 0; i < 1000; i++)
-		{
-			journal.appendDeleteRecord(i);
-			
-			removeRecordsForID(i);
-		}
-		
-		stopJournal();
-		createJournal();
-		startJournal();
-		loadAndCheck();
-		stopJournal();			
-	}
-	
-	public void testAddUpdateDeleteManySmallFileSize() throws Exception
-	{
-		final int numberAdds = 10000;
-		
-		final int numberUpdates = 5000;
-		
-		final int numberDeletes = 3000;
-						
-		long[] adds = new long[numberAdds];
-		
-		for (int i = 0; i < numberAdds; i++)
-		{
-			adds[i] = i;
-		}
-		
-		long[] updates = new long[numberUpdates];
-		
-		for (int i = 0; i < numberUpdates; i++)
-		{
-			updates[i] = i;
-		}
-		
-		long[] deletes = new long[numberDeletes];
-		
-		for (int i = 0; i < numberDeletes; i++)
-		{
-			deletes[i] = i;
-		}
-		
-		setup(10, 10, 10 * 1024, true);
-		createJournal();
-		startJournal();
-		load();
-		add(adds);
-		update(updates);
-		delete(deletes);
-		stopJournal();
-		createJournal();
-		startJournal();
-		loadAndCheck();
-		
-	}
-	
-	public void testAddUpdateDeleteManyLargeFileSize() throws Exception
-	{
-		final int numberAdds = 10000;
-		
-		final int numberUpdates = 5000;
-		
-		final int numberDeletes = 3000;
-						
-		long[] adds = new long[numberAdds];
-		
-		for (int i = 0; i < numberAdds; i++)
-		{
-			adds[i] = i;
-		}
-		
-		long[] updates = new long[numberUpdates];
-		
-		for (int i = 0; i < numberUpdates; i++)
-		{
-			updates[i] = i;
-		}
-		
-		long[] deletes = new long[numberDeletes];
-		
-		for (int i = 0; i < numberDeletes; i++)
-		{
-			deletes[i] = i;
-		}
-		
-		setup(10, 10, 10 * 1024 * 1024, true);
-		createJournal();
-		startJournal();
-		load();
-		add(adds);
-		update(updates);
-		delete(deletes);
-		stopJournal();
-		createJournal();
-		startJournal();
-		loadAndCheck();
-		
-	}
-	
-	public void testAddUpdateDeleteRestartAndContinue() throws Exception
-	{
-		setup(10, 10, 10 * 1024, true);
-		createJournal();
-		startJournal();
-		load();
-		add(1, 2, 3);
-		update(1, 2);
-		stopJournal();
-		createJournal();
-		startJournal();
-		loadAndCheck();
-		add(4, 5, 6);
-		update(5);
-		delete(3);
-		stopJournal();
-		createJournal();
-		startJournal();
-		loadAndCheck();
-		add(7, 8);
-		delete(1, 2);
-		delete(4, 5, 6);
-		stopJournal();
-		createJournal();
-		startJournal();
-		loadAndCheck();
-	}
-	
-	public void testAddUpdateDeleteTransactionalRestartAndContinue() throws Exception
-	{
-		setup(10, 10, 10 * 1024, true);
-		createJournal();
-		startJournal();
-		load();
-		add(1, 1, 2, 3);
-		updateTx(1, 1, 2);
-		commit(1);
-		stopJournal();
-		createJournal();
-		startJournal();
-		loadAndCheck();
-		addTx(2, 4, 5, 6);
-		update(2, 5);
-		delete(2, 3);
-		commit(2);
-		stopJournal();
-		createJournal();
-		startJournal();
-		loadAndCheck();
-		addTx(3, 7, 8);
-		deleteTx(3, 1, 2);
-		deleteTx(3, 4, 5, 6);
-		commit(3);
-		stopJournal();
-		createJournal();
-		startJournal();
-		loadAndCheck();
-	}
-	
-	public void testFillFileExactly() throws Exception
-	{		
-		this.recordLength = 500;
-		
-		int numRecords = 2;
-		
-		//The real appended record size in the journal file = SIZE_BYTE + SIZE_LONG + SIZE_INT + recordLength + SIZE_BYTE
-		
-		int realLength = 1 + 8 + 4 + this.recordLength + 1;
-		
-		int fileSize = numRecords * realLength + 8; //8 for timestamp
-						
-		setup(10, 10, fileSize, true);
-		
-		logThem();
-		
-		createJournal();
-		startJournal();
-		load();
-		
-		add(1, 2);
-		
-		stopJournal();
-		createJournal();
-		startJournal();
-		loadAndCheck();
-		
-		add(3, 4);
-		
-		stopJournal();
-		createJournal();
-		startJournal();
-		loadAndCheck();
-		
-		add(4, 5, 6, 7, 8, 9, 10);
-		
-		stopJournal();
-		createJournal();
-		startJournal();
-		loadAndCheck();		
-	}
-	
-	// Transactional tests
-	// ===================
-	
-	public void testSimpleTransaction() throws Exception
-	{
-		setup(10, 10, 10 * 1024, true);
-		createJournal();
-		startJournal();
-		load();
-		addTx(1, 1);
-		updateTx(1, 1);		
-		deleteTx(1, 1);	
-		commit(1);
-		stopJournal();
-		createJournal();
-		startJournal();
-		loadAndCheck();		
-	}
-	
-	public void testTransactionDontDeleteAll() throws Exception
-	{
-		setup(10, 10, 10 * 1024, true);
-		createJournal();
-		startJournal();
-		load();
-		addTx(1, 1, 2, 3);
-		updateTx(1, 1, 2);		
-		deleteTx(1, 1);	
-		commit(1);
-		stopJournal();
-		createJournal();
-		startJournal();
-		loadAndCheck();		
-	}
-	
-	public void testTransactionDeleteAll() throws Exception
-	{
-		setup(10, 10, 10 * 1024, true);
-		createJournal();
-		startJournal();
-		load();
-		addTx(1, 1, 2, 3);
-		updateTx(1, 1, 2);		
-		deleteTx(1, 1, 2, 3);
-		commit(1);
-		stopJournal();
-		createJournal();
-		startJournal();
-		loadAndCheck();		
-	}
-	
-	public void testTransactionUpdateFromBeforeTx() throws Exception
-	{
-		setup(10, 10, 10 * 1024, true);
-		createJournal();
-		startJournal();
-		load();
-		add(1, 2, 3);
-		addTx(1, 4, 5, 6);
-		updateTx(1, 1, 5);
-		commit(1);
-		stopJournal();
-		createJournal();
-		startJournal();
-		loadAndCheck();		
-	}
-	
-	public void testTransactionDeleteFromBeforeTx() throws Exception
-	{
-		setup(10, 10, 10 * 1024, true);
-		createJournal();
-		startJournal();
-		load();
-		add(1, 2, 3);
-		addTx(1, 4, 5, 6);
-		deleteTx(1, 1, 2, 3, 4, 5, 6);
-		commit(1);
-		stopJournal();
-		createJournal();
-		startJournal();
-		loadAndCheck();		
-	}
-	
-	public void testTransactionChangesNotVisibleOutsideTX() throws Exception
-	{
-		setup(10, 10, 10 * 1024, true);
-		createJournal();
-		startJournal();
-		load();
-		add(1, 2, 3);
-		addTx(1, 4, 5, 6);
-		updateTx(1, 1, 2, 4, 5);
-		deleteTx(1, 1, 2, 3, 4, 5, 6);
-		stopJournal();
-		createJournal();
-		startJournal();
-		loadAndCheck();		
-	}
-	
-	public void testMultipleTransactionsDifferentIDs() throws Exception
-	{
-		setup(10, 10, 10 * 1024, true);
-		createJournal();
-		startJournal();
-		load();
-		
-		addTx(1, 1, 2, 3, 4, 5, 6);
-		updateTx(1, 1, 3, 5);
-		deleteTx(1, 1, 2, 3, 4, 5, 6);
-		commit(1);
-		
-		addTx(2, 11, 12, 13, 14, 15, 16);
-		updateTx(2, 11, 13, 15);
-		deleteTx(2, 11, 12, 13, 14, 15, 16);
-		commit(2);
-		
-		addTx(3, 21, 22, 23, 24, 25, 26);
-		updateTx(3, 21, 23, 25);
-		deleteTx(3, 21, 22, 23, 24, 25, 26);
-		commit(3);
-		
-		stopJournal();
-		createJournal();
-		startJournal();
-		loadAndCheck();
-	}
-	
-	public void testMultipleInterleavedTransactionsDifferentIDs() throws Exception
-	{
-		setup(10, 10, 10 * 1024, true);
-		createJournal();
-		startJournal();
-		load();
-		
-		addTx(1, 1, 2, 3, 4, 5, 6);		
-		addTx(3, 21, 22, 23, 24, 25, 26);				
-		updateTx(1, 1, 3, 5);		
-		addTx(2, 11, 12, 13, 14, 15, 16);				
-		deleteTx(1, 1, 2, 3, 4, 5, 6);						
-		updateTx(2, 11, 13, 15);		
-		updateTx(3, 21, 23, 25);			
-		deleteTx(2, 11, 12, 13, 14, 15, 16);		
-		deleteTx(3, 21, 22, 23, 24, 25, 26);
-		
-		commit(1);
-		commit(2);
-		commit(3);
-		
-		stopJournal();
-		createJournal();
-		startJournal();
-		loadAndCheck();
-	}
-	
-	public void testMultipleInterleavedTransactionsSameIDs() throws Exception
-	{
-		setup(10, 10, 10 * 1024, true);
-		createJournal();
-		startJournal();
-		load();
-				
-		add(1, 2, 3, 4, 5, 6, 7, 8);		
-		addTx(1, 9, 10, 11, 12);		
-		addTx(2, 13, 14, 15, 16, 17);		
-		addTx(3, 18, 19, 20, 21, 22);		
-		updateTx(1, 1, 2, 3);		
-		updateTx(2, 4, 5, 6);		
-		commit(2);		
-		updateTx(3, 7, 8);		
-		deleteTx(1, 1, 2);		
-		commit(1);		
-		deleteTx(3, 7, 8);		
-		commit(3);
-		
-		stopJournal();
-		createJournal();
-		startJournal();
-		loadAndCheck();
-	}
-	
-	public void testTransactionMixed() throws Exception
-	{
-		setup(10, 10, 10 * 1024, true);
-		createJournal();
-		startJournal();
-		load();
-		add(1,3,5,7,10,13,56,100,102,200,201,202,203);		
-		addTx(1, 675, 676, 677, 700, 703);
-		update(1,3,5,7,10,13,56,100,102,200,201,202,203);		
-		updateTx(1, 677, 700);		
-		delete(1,3,5,7,10,13,56,100,102,200,201,202,203);		
-		deleteTx(1, 703, 675, 1,3,5,7,10);		
-		commit(1);
-		stopJournal();
-		createJournal();
-		startJournal();
-		loadAndCheck();		
-	}
-	
-	public void testTransactionAddDeleteDifferentOrder() throws Exception
-	{
-		setup(10, 10, 10 * 1024, true);
-		createJournal();
-		startJournal();
-		load();		
-		addTx(1, 1, 2, 3, 4, 5, 6, 7, 8, 9);					
-		deleteTx(1, 9, 8, 5, 3, 7, 6, 2, 1, 4);	
-		commit(1);
-		stopJournal();
-		createJournal();
-		startJournal();
-		loadAndCheck();		
-	}
-	
-	public void testAddOutsideTXThenUpdateInsideTX() throws Exception
-	{
-		setup(10, 10, 10 * 1024, true);
-		createJournal();
-		startJournal();
-		load();		
-		add(1, 2, 3);
-		updateTx(1, 1, 2, 3);
-		commit(1);
-		stopJournal();
-		createJournal();
-		startJournal();
-		loadAndCheck();
-	}
-	
-	public void testAddOutsideTXThenDeleteInsideTX() throws Exception
-	{
-		setup(10, 10, 10 * 1024, true);
-		createJournal();
-		startJournal();
-		load();		
-		add(1, 2, 3);
-		deleteTx(1, 1, 2, 3);
-		commit(1);
-		stopJournal();
-		createJournal();
-		startJournal();
-		loadAndCheck();
-	}
-	
-	public void testRollback() throws Exception
-	{
-		setup(10, 10, 10 * 1024, true);
-		createJournal();
-		startJournal();
-		load();		
-		add(1, 2, 3);
-		deleteTx(1, 1, 2, 3);
-		rollback(1);
-		stopJournal();
-		createJournal();
-		startJournal();
-		loadAndCheck();
-	}
-	
-	public void testRollbackMultiple() throws Exception
-	{
-		setup(10, 10, 10 * 1024, true);
-		createJournal();
-		startJournal();
-		load();		
-		add(1, 2, 3);
-		deleteTx(1, 1, 2, 3);
-		addTx(2, 4, 5, 6);
-		rollback(1);
-		rollback(2);
-		stopJournal();
-		createJournal();
-		startJournal();
-		loadAndCheck();
-	}
-	
-	// XA tests
-	// ========
-	
-	public void testXASimpleNotPrepared() throws Exception
-	{
-		setup(10, 10, 10 * 1024, true);
-		createJournal();
-		startJournal();
-		load();		
-		addTx(1, 1, 2, 3, 4, 5, 6, 7, 8, 9);					
-		updateTx(1, 1, 2, 3, 4, 7, 8);
-		deleteTx(1, 1, 2, 3, 4, 5);		
-		stopJournal();
-		createJournal();
-		startJournal();
-		loadAndCheck();		
-	}
-	
-	public void testXASimplePrepared() throws Exception
-	{
-		setup(10, 10, 10 * 1024, true);
-		createJournal();
-		startJournal();
-		load();		
-		addTx(1, 1, 2, 3, 4, 5, 6, 7, 8, 9);					
-		updateTx(1, 1, 2, 3, 4, 7, 8);
-		deleteTx(1, 1, 2, 3, 4, 5);	
-		prepare(1);
-		stopJournal();
-		createJournal();
-		startJournal();
-		loadAndCheck();		
-	}
-	
-	public void testXASimpleCommit() throws Exception
-	{
-		setup(10, 10, 10 * 1024, true);
-		createJournal();
-		startJournal();
-		load();		
-		addTx(1, 1, 2, 3, 4, 5, 6, 7, 8, 9);					
-		updateTx(1, 1, 2,3, 4, 7, 8);
-		deleteTx(1, 1, 2, 3, 4, 5);	
-		prepare(1);
-		commit(1);
-		stopJournal();
-		createJournal();
-		startJournal();
-		loadAndCheck();		
-	}
-	
-	public void testXASimpleRollback() throws Exception
-	{
-		setup(10, 10, 10 * 1024, true);
-		createJournal();
-		startJournal();
-		load();		
-		addTx(1, 1, 2, 3, 4, 5, 6, 7, 8, 9);					
-		updateTx(1, 1, 2,3, 4, 7, 8);
-		deleteTx(1, 1, 2, 3, 4, 5);	
-		prepare(1);
-		rollback(1);
-		stopJournal();
-		createJournal();
-		startJournal();
-		loadAndCheck();		
-	}
-	
-	public void testXAChangesNotVisibleNotPrepared() throws Exception
-	{
-		setup(10, 10, 10 * 1024, true);
-		createJournal();
-		startJournal();
-		load();		
-		add(1, 2, 3, 4, 5, 6);
-		addTx(1, 7, 8, 9, 10);					
-		updateTx(1, 1, 2, 3, 7, 8, 9);
-		deleteTx(1, 1, 2, 3, 4, 5);	
-		stopJournal();
-		createJournal();
-		startJournal();
-		loadAndCheck();		
-	}
-	
-	public void testXAChangesNotVisiblePrepared() throws Exception
-	{
-		setup(10, 10, 10 * 1024, true);
-		createJournal();
-		startJournal();
-		load();		
-		add(1, 2, 3, 4, 5, 6);
-		addTx(1, 7, 8, 9, 10);					
-		updateTx(1, 1, 2, 3, 7, 8, 9);
-		deleteTx(1, 1, 2, 3, 4, 5);	
-		prepare(1);
-		stopJournal();
-		createJournal();
-		startJournal();
-		loadAndCheck();		
-	}
-	
-	public void testXAChangesNotVisibleRollback() throws Exception
-	{
-		setup(10, 10, 10 * 1024, true);
-		createJournal();
-		startJournal();
-		load();		
-		add(1, 2, 3, 4, 5, 6);
-		addTx(1, 7, 8, 9, 10);					
-		updateTx(1, 1, 2, 3, 7, 8, 9);
-		deleteTx(1, 1, 2, 3, 4, 5);	
-		prepare(1);
-		rollback(1);
-		stopJournal();
-		createJournal();
-		startJournal();
-		loadAndCheck();		
-	}
-	
-	public void testXAChangesisibleCommit() throws Exception
-	{
-		setup(10, 10, 10 * 1024, true);
-		createJournal();
-		startJournal();
-		load();		
-		add(1, 2, 3, 4, 5, 6);
-		addTx(1, 7, 8, 9, 10);					
-		updateTx(1, 1, 2, 3, 7, 8, 9);
-		deleteTx(1, 1, 2, 3, 4, 5);	
-		prepare(1);
-		commit(1);
-		stopJournal();
-		createJournal();
-		startJournal();
-		loadAndCheck();		
-	}
-	
-	public void testXAMultiple() throws Exception
-	{
-		setup(10, 10, 10 * 1024, true);
-		createJournal();
-		startJournal();
-		load();		
-		add(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
-		addTx(1, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20);
-		addTx(2, 21, 22, 23, 24, 25, 26, 27);
-		updateTx(1, 1, 3, 6, 11, 14, 17);
-		addTx(3, 28, 29, 30, 31, 32, 33, 34, 35);
-		updateTx(3, 7, 8, 9, 10);
-		deleteTx(2, 4, 5, 6, 23, 25, 27);
-		prepare(2);
-		deleteTx(1, 1, 2, 11, 14, 15);
-		prepare(1);
-		deleteTx(3, 28, 31, 32, 9);
-		prepare(3);
-		
-		commit(1);
-		rollback(2);
-		commit(3);
-	}
-	
 	// Private ---------------------------------------------------------------------------------
 	
-	void setup(int minFiles, int minAvailableFiles, int fileSize, boolean sync)
+	protected void setup(int minFreeFiles, int fileSize, boolean sync)
 	{		
-		this.minFiles = minFiles;
-		this.minAvailableFiles = minAvailableFiles;
+		this.minFiles = minFreeFiles;
 		this.fileSize = fileSize;
 		this.sync = sync;
 	}
@@ -1137,20 +119,23 @@
 	public void createJournal() throws Exception
 	{		
 		journal =
-			new JournalImpl(fileSize, minFiles, minAvailableFiles, sync, fileFactory, 5000, filePrefix, fileExtension);
+			new JournalImpl(fileSize, minFiles, sync, fileFactory, 1000, filePrefix, fileExtension);
 	}
 		
-	private void startJournal() throws Exception
+	protected void startJournal() throws Exception
 	{
 		journal.start();
 	}
 	
-	private void stopJournal() throws Exception
+	protected void stopJournal() throws Exception
 	{
+		//We do a reclaim in here
+		journal.checkAndReclaimFiles();
+		
 		journal.stop();		
 	}
 	
-	private void loadAndCheck() throws Exception
+	protected void loadAndCheck() throws Exception
 	{
 		List<RecordInfo> committedRecords = new ArrayList<RecordInfo>();
 		
@@ -1181,12 +166,12 @@
 		checkTransactionsEquivalent(prepared, preparedTransactions);
 	}		
 	
-	private void load() throws Exception
+	protected void load() throws Exception
 	{
 		journal.load(null, null);
 	}
 	
-	private void add(long... arguments) throws Exception
+	protected void add(long... arguments) throws Exception
 	{
 		for (int i = 0; i < arguments.length; i++)
 		{		
@@ -1198,7 +183,7 @@
 		}
 	}
 	
-	private void update(long... arguments) throws Exception
+	protected void update(long... arguments) throws Exception
 	{
 		for (int i = 0; i < arguments.length; i++)
 		{		
@@ -1210,7 +195,7 @@
 		}
 	}
 	
-	private void delete(long... arguments) throws Exception
+	protected void delete(long... arguments) throws Exception
 	{
 		for (int i = 0; i < arguments.length; i++)
 		{		
@@ -1220,7 +205,7 @@
 		}
 	}
 			
-	private void addTx(long txID, long... arguments) throws Exception
+	protected void addTx(long txID, long... arguments) throws Exception
 	{
 		TransactionHolder tx = getTransaction(txID);
 		
@@ -1235,7 +220,7 @@
 		}		
 	}
 	
-	private void updateTx(long txID, long... arguments) throws Exception
+	protected void updateTx(long txID, long... arguments) throws Exception
 	{
 		TransactionHolder tx = getTransaction(txID);
 		
@@ -1249,7 +234,7 @@
 		}		
 	}
 
-	private void deleteTx(long txID, long... arguments) throws Exception
+	protected void deleteTx(long txID, long... arguments) throws Exception
 	{
 		TransactionHolder tx = getTransaction(txID);
 		
@@ -1262,7 +247,7 @@
 		
 	}
 	
-	private void prepare(long txID) throws Exception
+	protected void prepare(long txID) throws Exception
 	{
 		TransactionHolder tx = transactions.get(txID);
 		
@@ -1281,7 +266,7 @@
 		tx.prepared = true;
 	}
 	
-	private void commit(long txID) throws Exception
+	protected void commit(long txID) throws Exception
 	{
 		TransactionHolder tx = transactions.get(txID);
 		
@@ -1295,7 +280,7 @@
 		this.commitTx(txID);
 	}
 	
-	private void rollback(long txID) throws Exception
+	protected void rollback(long txID) throws Exception
 	{
 		TransactionHolder tx = transactions.remove(txID);
 		
@@ -1324,7 +309,7 @@
 		}
 	}
 	
-	private void removeRecordsForID(long id)
+	protected void removeRecordsForID(long id)
 	{
 		for (ListIterator<RecordInfo> iter = records.listIterator(); iter.hasNext();)
 		{
@@ -1337,7 +322,7 @@
 		}
 	}
 	
-	private TransactionHolder getTransaction(long txID)
+	protected TransactionHolder getTransaction(long txID)
 	{
 		TransactionHolder tx = transactions.get(txID);
 		
@@ -1351,7 +336,7 @@
 		return tx;
 	}
 			
-	private void checkTransactionsEquivalent(List<PreparedTransactionInfo> expected, List<PreparedTransactionInfo> actual)
+	protected void checkTransactionsEquivalent(List<PreparedTransactionInfo> expected, List<PreparedTransactionInfo> actual)
 	{
 		assertEquals("Lists not same length", expected.size(), actual.size());
 		
@@ -1386,7 +371,7 @@
 		}
 	}
 	
-	private void checkRecordsEquivalent(List<RecordInfo> expected, List<RecordInfo> actual)
+	protected void checkRecordsEquivalent(List<RecordInfo> expected, List<RecordInfo> actual)
 	{
 		assertEquals("Lists not same length", expected.size(), actual.size());
 		
@@ -1408,7 +393,7 @@
 		}		
 	}
 	
-	private byte[] generateRecord(int length)
+	protected byte[] generateRecord(int length)
 	{
 		byte[] record = new byte[length];
 		for (int i = 0; i < length; i++)

Added: trunk/tests/src/org/jboss/messaging/core/journal/impl/test/unit/JournalImplTestUnit.java
===================================================================
--- trunk/tests/src/org/jboss/messaging/core/journal/impl/test/unit/JournalImplTestUnit.java	                        (rev 0)
+++ trunk/tests/src/org/jboss/messaging/core/journal/impl/test/unit/JournalImplTestUnit.java	2008-04-04 14:25:20 UTC (rev 4008)
@@ -0,0 +1,2131 @@
+/*
+  * JBoss, Home of Professional Open Source
+  * Copyright 2005, JBoss Inc., and individual contributors as indicated
+  * by the @authors tag. See the copyright.txt in the distribution for a
+  * full listing of individual contributors.
+  *
+  * This is free software; you can redistribute it and/or modify it
+  * under the terms of the GNU Lesser General Public License as
+  * published by the Free Software Foundation; either version 2.1 of
+  * the License, or (at your option) any later version.
+  *
+  * This software is distributed in the hope that it will be useful,
+  * but WITHOUT ANY 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 along with this software; if not, write to the Free
+  * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+  * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+  */
+package org.jboss.messaging.core.journal.impl.test.unit;
+
+import java.util.List;
+
+import org.jboss.messaging.core.journal.RecordInfo;
+import org.jboss.messaging.core.journal.impl.JournalImpl;
+import org.jboss.messaging.core.logging.Logger;
+
+/**
+ * 
+ * A JournalImplTestBase
+ * 
+ * @author <a href="mailto:tim.fox at jboss.com">Tim Fox</a>
+ *
+ */
+public abstract class JournalImplTestUnit extends JournalImplTestBase
+{
+	private static final Logger log = Logger.getLogger(JournalImplTestUnit.class);
+	
+	// General tests
+	// =============
+	
+	public void testState() throws Exception
+	{
+		setup(10, 10 * 1024, true);
+		createJournal();
+		try
+		{
+			load();
+			fail("Should throw exception");
+		}
+		catch (IllegalStateException e)
+		{
+			//OK
+		}
+		try
+		{
+			stopJournal();
+			fail("Should throw exception");
+		}
+		catch (IllegalStateException e)
+		{
+			//OK
+		}
+		startJournal();
+		try
+		{
+			startJournal();
+			fail("Should throw exception");
+		}
+		catch (IllegalStateException e)
+		{
+			//OK
+		}
+		stopJournal();
+		startJournal();
+		load();
+		try
+		{
+			load();
+			fail("Should throw exception");
+		}
+		catch (IllegalStateException e)
+		{
+			//OK
+		}
+		try
+		{
+			startJournal();
+			fail("Should throw exception");
+		}
+		catch (IllegalStateException e)
+		{
+			//OK
+		}
+		stopJournal();		
+	}
+	
+	public void testParams() throws Exception
+	{
+		try
+		{
+			new JournalImpl(JournalImpl.MIN_FILE_SIZE - 1, 10, true, fileFactory, 5000, filePrefix, fileExtension);
+			
+			fail("Should throw exception");
+		}
+		catch (IllegalArgumentException e)
+		{
+			//Ok
+		}
+		
+		try
+		{
+			new JournalImpl(10 * 1024, 1, true, fileFactory, 5000, filePrefix, fileExtension);
+			
+			fail("Should throw exception");
+		}
+		catch (IllegalArgumentException e)
+		{
+			//Ok
+		}
+				
+		try
+		{
+			new JournalImpl(10 * 1024, 10, true, null, 5000, filePrefix, fileExtension);
+			
+			fail("Should throw exception");
+		}
+		catch (NullPointerException e)
+		{
+			//Ok
+		}
+		
+		try
+		{
+			new JournalImpl(10 * 1024, 10, true, fileFactory, JournalImpl.MIN_TASK_PERIOD - 1, filePrefix, fileExtension);
+			
+			fail("Should throw exception");
+		}
+		catch (IllegalArgumentException e)
+		{
+			//Ok
+		}
+		
+		try
+		{
+			new JournalImpl(10 * 1024, 10, true, fileFactory, 5000, null, fileExtension);
+			
+			fail("Should throw exception");
+		}
+		catch (NullPointerException e)
+		{
+			//Ok
+		}
+		
+		try
+		{
+			new JournalImpl(10 * 1024, 10, true, fileFactory, 5000, filePrefix, null);
+			
+			fail("Should throw exception");
+		}
+		catch (NullPointerException e)
+		{
+			//Ok
+		}
+		
+	}
+	
+	public void testFilesImmediatelyAfterload() throws Exception
+	{
+		try
+		{
+   		setup(10, 10 * 1024, true);
+   		createJournal();
+   		startJournal();
+   		load();
+   		
+   		List<String> files = fileFactory.listFiles(fileExtension);
+   		
+   		assertEquals(10, files.size());
+   		
+   		for (String file: files)
+   		{
+   			assertTrue(file.startsWith(filePrefix));
+   		}
+   		
+   		stopJournal();
+   		
+   		resetFileFactory();
+   		
+   		setup(20, 10 * 1024, true);
+   		createJournal();
+   		startJournal();
+   		load();
+   		
+   		files = fileFactory.listFiles(fileExtension);
+   		
+   		assertEquals(20, files.size());
+   		
+   		for (String file: files)
+   		{
+   			assertTrue(file.startsWith(filePrefix));
+   		}
+   						
+   		stopJournal();	
+   		
+   		fileExtension = "tim";
+   		
+   		resetFileFactory();
+   		
+   		setup(17, 10 * 1024, true);
+   		createJournal();
+   		startJournal();
+   		load();
+   		
+   		files = fileFactory.listFiles(fileExtension);
+   		
+   		assertEquals(17, files.size());
+   		
+   		for (String file: files)
+   		{
+   			assertTrue(file.startsWith(filePrefix));
+   		}
+   		
+   		stopJournal();	
+   		
+   		filePrefix = "echidna";
+   		
+   		resetFileFactory();
+   		
+   		setup(11, 10 * 1024, true);
+   		createJournal();
+   		startJournal();
+   		load();
+   		
+   		files = fileFactory.listFiles(fileExtension);
+   		
+   		assertEquals(11, files.size());
+   		
+   		for (String file: files)
+   		{
+   			assertTrue(file.startsWith(filePrefix));
+   		}
+   		
+   		stopJournal();	
+		}
+		finally
+		{
+			filePrefix = "jbm";
+			
+			fileExtension = "jbm";
+		}
+	}
+	
+	public void testCreateFilesOnLoad() throws Exception
+	{
+		setup(10, 10 * 1024, true);
+		createJournal();
+		startJournal();
+		load();
+		
+		List<String> files1 = fileFactory.listFiles(fileExtension);
+		
+		assertEquals(10, files1.size());
+		
+		stopJournal();
+		
+		//Now restart with different number of minFiles - should create 10 more
+		
+		setup(20, 10 * 1024, true);
+		createJournal();
+		startJournal();
+		load();
+		
+		List<String> files2 = fileFactory.listFiles(fileExtension);
+		
+		assertEquals(20, files2.size());
+		
+		for (String file: files1)
+		{
+			assertTrue(files2.contains(file));
+		}
+				
+		stopJournal();	
+	}
+	
+	public void testReduceFreeFiles() throws Exception
+	{
+		setup(10, 10 * 1024, true);
+		createJournal();
+		startJournal();
+		load();
+		
+		List<String> files1 = fileFactory.listFiles(fileExtension);
+		
+		assertEquals(10, files1.size());
+		
+		stopJournal();
+		
+		setup(5, 10 * 1024, true);
+		createJournal();
+		startJournal();
+		load();
+		
+		List<String> files2 = fileFactory.listFiles(fileExtension);
+		
+		assertEquals(10, files2.size());
+		
+		for (String file: files1)
+		{
+			assertTrue(files2.contains(file));
+		}
+				
+		stopJournal();	
+	}
+			
+	public void testCheckCreateMoreFiles() throws Exception
+	{
+		setup(10, 10 * 1024, true);
+		createJournal();
+		startJournal();
+		load();
+		
+		List<String> files1 = fileFactory.listFiles(fileExtension);
+		
+		assertEquals(10, files1.size());
+		
+		assertEquals(0, journal.getDataFilesCount());
+		assertEquals(9, journal.getFreeFilesCount());
+		assertEquals(0, journal.getIDMapSize());
+				
+		//Fill all the files
+		
+		for (int i = 0; i < 90; i++)
+		{
+			add(i);
+		}
+		
+		assertEquals(9, journal.getDataFilesCount());
+		assertEquals(0, journal.getFreeFilesCount());
+		assertEquals(90, journal.getIDMapSize());
+				
+		List<String> files2 = fileFactory.listFiles(fileExtension);
+		
+		assertEquals(10, files2.size());
+				
+		for (String file: files1)
+		{
+			assertTrue(files2.contains(file));
+		}
+		
+		//Now add some more
+		
+		for (int i = 90; i < 95; i++)
+		{
+			add(i);
+		}
+		
+		assertEquals(10, journal.getDataFilesCount());
+		assertEquals(0, journal.getFreeFilesCount());
+		assertEquals(95, journal.getIDMapSize());
+		
+		List<String> files3 = fileFactory.listFiles(fileExtension);
+		
+		assertEquals(11, files3.size());
+				
+		for (String file: files1)
+		{
+			assertTrue(files3.contains(file));
+		}
+		
+		//And a load more
+		
+		for (int i = 95; i < 200; i++)
+		{
+			add(i);
+		}
+		
+		assertEquals(22, journal.getDataFilesCount());
+		assertEquals(0, journal.getFreeFilesCount());
+		assertEquals(200, journal.getIDMapSize());
+		
+		List<String> files4 = fileFactory.listFiles(fileExtension);
+		
+		assertEquals(23, files4.size());
+				
+		for (String file: files1)
+		{
+			assertTrue(files4.contains(file));
+		}
+						
+		stopJournal();	
+	}
+	
+	public void testReclaim() throws Exception
+	{
+		setup(10, 10 * 1024, true);
+		createJournal();
+		startJournal();
+		load();
+		
+		List<String> files1 = fileFactory.listFiles(fileExtension);
+		
+		assertEquals(10, files1.size());
+		
+		assertEquals(0, journal.getDataFilesCount());
+		assertEquals(9, journal.getFreeFilesCount());
+		assertEquals(0, journal.getIDMapSize());
+				
+		for (int i = 0; i < 100; i++)
+		{
+			add(i);
+		}
+		
+		assertEquals(11, journal.getDataFilesCount());
+		assertEquals(0, journal.getFreeFilesCount());
+		assertEquals(100, journal.getIDMapSize());
+		
+		List<String> files4 = fileFactory.listFiles(fileExtension);
+		
+		assertEquals(12, files4.size());
+				
+		for (String file: files1)
+		{
+			assertTrue(files4.contains(file));
+		}
+		
+		//Now delete half of them
+		
+		for (int i = 0; i < 50; i++)
+		{
+			delete(i);
+		}
+		
+		assertEquals(11, journal.getDataFilesCount());
+		assertEquals(0, journal.getFreeFilesCount());
+		assertEquals(50, journal.getIDMapSize());
+		
+		//Make sure the deletes aren't in the current file
+		
+		for (int i = 100; i < 110; i++)
+		{
+			add(i);
+		}
+		
+		assertEquals(12, journal.getDataFilesCount());
+		assertEquals(0, journal.getFreeFilesCount());
+		assertEquals(60, journal.getIDMapSize());
+		
+		journal.checkAndReclaimFiles();
+		
+		//Several of them should be reclaimed - and others deleted - the total number of files should not drop below
+		//10
+		
+		assertEquals(7, journal.getDataFilesCount());
+		assertEquals(2, journal.getFreeFilesCount());
+		assertEquals(60, journal.getIDMapSize());
+		
+		List<String> files5 = fileFactory.listFiles(fileExtension);
+		
+		assertEquals(10, files5.size());
+		
+		//Now delete the rest
+		
+		for (int i = 50; i < 110; i++)
+		{
+			delete(i);
+		}
+		
+		//And fill the current file
+		
+		for (int i = 110; i < 120; i++)
+		{
+			add(i);
+			delete(i);
+		}
+		
+		journal.checkAndReclaimFiles();
+		
+		assertEquals(0, journal.getDataFilesCount());
+		assertEquals(9, journal.getFreeFilesCount());
+		assertEquals(0, journal.getIDMapSize());
+		
+		List<String> files6 = fileFactory.listFiles(fileExtension);
+		
+		assertEquals(10, files6.size());
+										
+		stopJournal();	
+	}
+			
+	public void testReclaimAddUpdateDeleteDifferentFiles1() throws Exception
+	{
+		setup(2, 1046, true); //Make sure there is one record per file
+		createJournal();
+		startJournal();
+		load();
+		
+		add(1);
+		update(1);
+		delete(1);
+		
+		List<String> files1 = fileFactory.listFiles(fileExtension);
+		
+		assertEquals(3, files1.size());
+		
+		assertEquals(2, journal.getDataFilesCount());
+		assertEquals(0, journal.getFreeFilesCount());
+		assertEquals(0, journal.getIDMapSize());
+		
+		journal.checkAndReclaimFiles();
+		
+		List<String> files2 = fileFactory.listFiles(fileExtension);
+		
+		assertEquals(2, files2.size());
+		
+		//1 gets deleted and 1 gets reclaimed
+		
+		assertEquals(0, journal.getDataFilesCount());
+		assertEquals(1, journal.getFreeFilesCount());
+		assertEquals(0, journal.getIDMapSize());
+		
+		stopJournal();
+	}
+	
+	public void testReclaimAddUpdateDeleteDifferentFiles2() throws Exception
+	{
+		setup(2, 1046, true); //Make sure there is one record per file
+		createJournal();
+		startJournal();
+		load();
+		
+		add(1);
+		update(1);
+		add(2);
+		
+		List<String> files1 = fileFactory.listFiles(fileExtension);
+		
+		assertEquals(3, files1.size());
+		
+		assertEquals(2, journal.getDataFilesCount());
+		assertEquals(0, journal.getFreeFilesCount());
+		assertEquals(2, journal.getIDMapSize());
+		
+		journal.checkAndReclaimFiles();
+		
+		List<String> files2 = fileFactory.listFiles(fileExtension);
+		
+		assertEquals(3, files2.size());
+		
+		assertEquals(2, journal.getDataFilesCount());
+		assertEquals(0, journal.getFreeFilesCount());
+		assertEquals(2, journal.getIDMapSize());
+		
+		
+		stopJournal();
+	}
+	
+	public void testReclaimTransactionalAddCommit() throws Exception
+	{
+		testReclaimTransactionalAdd(true);
+	}
+	
+	public void testReclaimTransactionalAddRollback() throws Exception
+	{
+		testReclaimTransactionalAdd(false);
+	}
+	
+	//TODO commit and rollback, also transactional deletes
+	
+	private void testReclaimTransactionalAdd(boolean commit) throws Exception
+	{
+		setup(10, 10 * 1024, true);
+		createJournal();
+		startJournal();
+		load();
+		
+		List<String> files1 = fileFactory.listFiles(fileExtension);
+		
+		assertEquals(10, files1.size());
+		
+		assertEquals(0, journal.getDataFilesCount());
+		assertEquals(9, journal.getFreeFilesCount());
+		assertEquals(0, journal.getIDMapSize());
+				
+		for (int i = 0; i < 100; i++)
+		{
+			addTx(1, i);
+		}
+		
+		assertEquals(11, journal.getDataFilesCount());
+		assertEquals(0, journal.getFreeFilesCount());
+		assertEquals(0, journal.getIDMapSize());
+		
+		List<String> files2 = fileFactory.listFiles(fileExtension);
+		
+		assertEquals(12, files2.size());
+				
+		for (String file: files1)
+		{
+			assertTrue(files2.contains(file));
+		}
+		
+		journal.checkAndReclaimFiles();
+		
+		//Make sure nothing reclaimed
+		
+		assertEquals(11, journal.getDataFilesCount());
+		assertEquals(0, journal.getFreeFilesCount());
+		assertEquals(0, journal.getIDMapSize());
+		
+		List<String> files3 = fileFactory.listFiles(fileExtension);
+		
+		assertEquals(12, files3.size());
+				
+		for (String file: files1)
+		{
+			assertTrue(files3.contains(file));
+		}
+		
+		//Add a load more updates
+		
+		for (int i = 100; i < 200; i++)
+		{
+			updateTx(1, i);
+		}
+		
+		assertEquals(22, journal.getDataFilesCount());
+		assertEquals(0, journal.getFreeFilesCount());
+		assertEquals(0, journal.getIDMapSize());
+		
+		List<String> files4 = fileFactory.listFiles(fileExtension);
+		
+		assertEquals(23, files4.size());
+				
+		for (String file: files1)
+		{
+			assertTrue(files4.contains(file));
+		}
+		
+		journal.checkAndReclaimFiles();
+		
+		//Make sure nothing reclaimed
+		
+		assertEquals(22, journal.getDataFilesCount());
+		assertEquals(0, journal.getFreeFilesCount());
+		assertEquals(0, journal.getIDMapSize());
+		
+		List<String> files5 = fileFactory.listFiles(fileExtension);
+		
+		assertEquals(23, files5.size());
+				
+		for (String file: files1)
+		{
+			assertTrue(files5.contains(file));
+		}
+				
+		//Now delete them
+											
+		for (int i = 0; i < 200; i++)
+		{
+			deleteTx(1, i);
+		}
+		
+		assertEquals(22, journal.getDataFilesCount());
+		assertEquals(0, journal.getFreeFilesCount());
+		assertEquals(0, journal.getIDMapSize());
+		
+		List<String> files7 = fileFactory.listFiles(fileExtension);
+		
+		assertEquals(23, files7.size());
+				
+		for (String file: files1)
+		{
+			assertTrue(files7.contains(file));
+		}
+		
+		journal.checkAndReclaimFiles();
+		
+		assertEquals(22, journal.getDataFilesCount());
+		assertEquals(0, journal.getFreeFilesCount());
+		assertEquals(0, journal.getIDMapSize());
+		
+		List<String> files8 = fileFactory.listFiles(fileExtension);
+		
+		assertEquals(23, files8.size());
+				
+		for (String file: files1)
+		{
+			assertTrue(files8.contains(file));
+		}
+		
+		//Commit
+		
+		if (commit)
+		{
+			commit(1);
+		}
+		else
+		{
+			rollback(1);
+		}
+		
+		//Add more records to make sure we get to the next file
+		
+		for (int i = 200; i < 210; i++)
+		{
+			add(i);
+		}
+		
+		assertEquals(23, journal.getDataFilesCount());
+		assertEquals(0, journal.getFreeFilesCount());
+		assertEquals(10, journal.getIDMapSize());
+		
+		List<String> files9 = fileFactory.listFiles(fileExtension);
+		
+		assertEquals(24, files9.size());
+				
+		for (String file: files1)
+		{
+			assertTrue(files9.contains(file));
+		}
+		
+		journal.checkAndReclaimFiles();
+		
+		//Most Should now be reclaimed - leaving 10 left in total
+
+		assertEquals(1, journal.getDataFilesCount());
+		assertEquals(8, journal.getFreeFilesCount());
+		assertEquals(10, journal.getIDMapSize());
+		
+		List<String> files10 = fileFactory.listFiles(fileExtension);
+		
+		assertEquals(10, files10.size());	
+	}
+	
+	public void testReclaimTransactionalSimple() throws Exception
+	{
+		setup(2, 1054, true);
+		createJournal();
+		startJournal();
+		load();
+		
+		List<String> files1 = fileFactory.listFiles(fileExtension);
+		
+		assertEquals(2, files1.size());
+		
+		assertEquals(0, journal.getDataFilesCount());
+		assertEquals(1, journal.getFreeFilesCount());
+		assertEquals(0, journal.getIDMapSize());
+					
+		addTx(1, 1);           // in file 0
+		
+		deleteTx(1, 1);        // in file 1
+		
+		List<String> files2 = fileFactory.listFiles(fileExtension);
+		
+		assertEquals(2, files2.size());
+		
+		assertEquals(1, journal.getDataFilesCount());
+		assertEquals(0, journal.getFreeFilesCount());
+		assertEquals(0, journal.getIDMapSize());
+		
+		//Make sure we move on to the next file
+		
+		add(2);                // in file 2
+		
+		List<String> files3 = fileFactory.listFiles(fileExtension);
+		
+		assertEquals(3, files3.size());
+		
+		assertEquals(2, journal.getDataFilesCount());
+		assertEquals(0, journal.getFreeFilesCount());
+		assertEquals(1, journal.getIDMapSize());
+		
+		commit(1);            // in file 3
+		
+		List<String> files4 = fileFactory.listFiles(fileExtension);
+		
+		assertEquals(4, files4.size());
+		
+		assertEquals(3, journal.getDataFilesCount());
+		assertEquals(0, journal.getFreeFilesCount());
+		assertEquals(1, journal.getIDMapSize());
+		
+		//Make sure we move on to the next file
+		
+		add(3);                // in file 4
+		
+		List<String> files5 = fileFactory.listFiles(fileExtension);
+		
+		assertEquals(5, files5.size());
+		
+		assertEquals(4, journal.getDataFilesCount());
+		assertEquals(0, journal.getFreeFilesCount());
+		assertEquals(2, journal.getIDMapSize());
+		
+		journal.checkAndReclaimFiles();
+		
+		List<String> files6 = fileFactory.listFiles(fileExtension);
+		
+		//Three should get deleted (files 0, 1, 3)
+		
+		assertEquals(2, files6.size());
+		
+		assertEquals(1, journal.getDataFilesCount());
+		assertEquals(0, journal.getFreeFilesCount());
+		assertEquals(2, journal.getIDMapSize());		
+		
+		//Now restart
+		
+		stopJournal();
+		createJournal();
+		startJournal();
+		loadAndCheck();
+		
+		assertEquals(2, files6.size());
+		
+		assertEquals(1, journal.getDataFilesCount());
+		assertEquals(0, journal.getFreeFilesCount());
+		assertEquals(2, journal.getIDMapSize());		
+	}
+	
+	public void testAddDeleteCommitTXIDMap1() throws Exception
+	{
+		setup(10, 10 * 1024, true);
+		createJournal();
+		startJournal();
+		load();
+		
+		List<String> files1 = fileFactory.listFiles(fileExtension);
+		
+		assertEquals(10, files1.size());
+		
+		assertEquals(0, journal.getDataFilesCount());
+		assertEquals(9, journal.getFreeFilesCount());
+		assertEquals(0, journal.getIDMapSize());
+		
+		addTx(1, 1);
+		
+		assertEquals(0, journal.getDataFilesCount());
+		assertEquals(9, journal.getFreeFilesCount());
+		assertEquals(0, journal.getIDMapSize());
+		
+		deleteTx(1, 1);
+		
+		assertEquals(0, journal.getDataFilesCount());
+		assertEquals(9, journal.getFreeFilesCount());
+		assertEquals(0, journal.getIDMapSize());
+		
+		commit(1);
+		
+		assertEquals(0, journal.getDataFilesCount());
+		assertEquals(9, journal.getFreeFilesCount());
+		assertEquals(0, journal.getIDMapSize());				
+	}
+	
+	public void testAddCommitTXIDMap1() throws Exception
+	{
+		setup(10, 10 * 1024, true);
+		createJournal();
+		startJournal();
+		load();
+		
+		List<String> files1 = fileFactory.listFiles(fileExtension);
+		
+		assertEquals(10, files1.size());
+		
+		assertEquals(0, journal.getDataFilesCount());
+		assertEquals(9, journal.getFreeFilesCount());
+		assertEquals(0, journal.getIDMapSize());
+		
+		addTx(1, 1);
+		
+		assertEquals(0, journal.getDataFilesCount());
+		assertEquals(9, journal.getFreeFilesCount());
+		assertEquals(0, journal.getIDMapSize());
+						
+		commit(1);
+		
+		assertEquals(0, journal.getDataFilesCount());
+		assertEquals(9, journal.getFreeFilesCount());
+		assertEquals(1, journal.getIDMapSize());				
+	}
+	
+	public void testAddDeleteCommitTXIDMap2() throws Exception
+	{
+		setup(10, 10 * 1024, true);
+		createJournal();
+		startJournal();
+		load();
+		
+		List<String> files1 = fileFactory.listFiles(fileExtension);
+		
+		assertEquals(10, files1.size());
+		
+		assertEquals(0, journal.getDataFilesCount());
+		assertEquals(9, journal.getFreeFilesCount());
+		assertEquals(0, journal.getIDMapSize());
+		
+		add(1, 1);
+		
+		assertEquals(0, journal.getDataFilesCount());
+		assertEquals(9, journal.getFreeFilesCount());
+		assertEquals(1, journal.getIDMapSize());
+		
+		deleteTx(1, 1);
+		
+		assertEquals(0, journal.getDataFilesCount());
+		assertEquals(9, journal.getFreeFilesCount());
+		assertEquals(1, journal.getIDMapSize());
+		
+		commit(1);
+		
+		assertEquals(0, journal.getDataFilesCount());
+		assertEquals(9, journal.getFreeFilesCount());
+		assertEquals(0, journal.getIDMapSize());				
+	}
+	
+				
+	public void testAddDeleteRollbackTXIDMap1() throws Exception
+	{
+		setup(10, 10 * 1024, true);
+		createJournal();
+		startJournal();
+		load();
+		
+		List<String> files1 = fileFactory.listFiles(fileExtension);
+		
+		assertEquals(10, files1.size());
+		
+		assertEquals(0, journal.getDataFilesCount());
+		assertEquals(9, journal.getFreeFilesCount());
+		assertEquals(0, journal.getIDMapSize());
+		
+		addTx(1, 1);
+		
+		assertEquals(0, journal.getDataFilesCount());
+		assertEquals(9, journal.getFreeFilesCount());
+		assertEquals(0, journal.getIDMapSize());
+		
+		deleteTx(1, 1);
+		
+		assertEquals(0, journal.getDataFilesCount());
+		assertEquals(9, journal.getFreeFilesCount());
+		assertEquals(0, journal.getIDMapSize());
+		
+		rollback(1);
+		
+		assertEquals(0, journal.getDataFilesCount());
+		assertEquals(9, journal.getFreeFilesCount());
+		assertEquals(0, journal.getIDMapSize());				
+	}
+	
+	public void testAddRollbackTXIDMap1() throws Exception
+	{
+		setup(10, 10 * 1024, true);
+		createJournal();
+		startJournal();
+		load();
+		
+		List<String> files1 = fileFactory.listFiles(fileExtension);
+		
+		assertEquals(10, files1.size());
+		
+		assertEquals(0, journal.getDataFilesCount());
+		assertEquals(9, journal.getFreeFilesCount());
+		assertEquals(0, journal.getIDMapSize());
+		
+		addTx(1, 1);
+		
+		assertEquals(0, journal.getDataFilesCount());
+		assertEquals(9, journal.getFreeFilesCount());
+		assertEquals(0, journal.getIDMapSize());
+					
+		rollback(1);
+		
+		assertEquals(0, journal.getDataFilesCount());
+		assertEquals(9, journal.getFreeFilesCount());
+		assertEquals(0, journal.getIDMapSize());				
+	}
+	
+	public void testAddDeleteRollbackTXIDMap2() throws Exception
+	{
+		setup(10, 10 * 1024, true);
+		createJournal();
+		startJournal();
+		load();
+		
+		List<String> files1 = fileFactory.listFiles(fileExtension);
+		
+		assertEquals(10, files1.size());
+		
+		assertEquals(0, journal.getDataFilesCount());
+		assertEquals(9, journal.getFreeFilesCount());
+		assertEquals(0, journal.getIDMapSize());
+		
+		add(1, 1);
+		
+		assertEquals(0, journal.getDataFilesCount());
+		assertEquals(9, journal.getFreeFilesCount());
+		assertEquals(1, journal.getIDMapSize());
+		
+		deleteTx(1, 1);
+		
+		assertEquals(0, journal.getDataFilesCount());
+		assertEquals(9, journal.getFreeFilesCount());
+		assertEquals(1, journal.getIDMapSize());
+		
+		rollback(1);
+		
+		assertEquals(0, journal.getDataFilesCount());
+		assertEquals(9, journal.getFreeFilesCount());
+		assertEquals(1, journal.getIDMapSize());				
+	}
+	
+	public void testAddDeleteIDMap() throws Exception
+	{
+		setup(10, 10 * 1024, true);
+		createJournal();
+		startJournal();
+		load();
+		
+		List<String> files1 = fileFactory.listFiles(fileExtension);
+		
+		assertEquals(10, files1.size());
+		
+		assertEquals(0, journal.getDataFilesCount());
+		assertEquals(9, journal.getFreeFilesCount());
+		assertEquals(0, journal.getIDMapSize());
+		
+		add(1);
+		
+		assertEquals(0, journal.getDataFilesCount());
+		assertEquals(9, journal.getFreeFilesCount());
+		assertEquals(1, journal.getIDMapSize());
+		
+		delete(1);
+		
+		assertEquals(0, journal.getDataFilesCount());
+		assertEquals(9, journal.getFreeFilesCount());
+		assertEquals(0, journal.getIDMapSize());
+		
+	}
+	
+	public void testCommitRecordsInFileReclaim() throws Exception
+	{
+		setup(2, 1054, true);
+		createJournal();
+		startJournal();
+		load();
+		
+		List<String> files1 = fileFactory.listFiles(fileExtension);
+		
+		assertEquals(2, files1.size());
+		
+		assertEquals(0, journal.getDataFilesCount());
+		assertEquals(1, journal.getFreeFilesCount());
+		assertEquals(0, journal.getIDMapSize());
+					
+		addTx(1, 1);
+		
+		List<String> files2 = fileFactory.listFiles(fileExtension);
+		
+		assertEquals(2, files2.size());
+		
+		assertEquals(0, journal.getDataFilesCount());
+		assertEquals(1, journal.getFreeFilesCount());
+		assertEquals(0, journal.getIDMapSize());
+						
+		//Make sure we move on to the next file
+		
+		commit(1);
+		
+		List<String> files3 = fileFactory.listFiles(fileExtension);
+		
+		assertEquals(2, files3.size());
+		
+		assertEquals(1, journal.getDataFilesCount());
+		assertEquals(0, journal.getFreeFilesCount());
+		assertEquals(1, journal.getIDMapSize());
+		
+		add(2);
+		
+		//Move on to another file
+		
+		List<String> files4 = fileFactory.listFiles(fileExtension);
+		
+		assertEquals(3, files4.size());
+		
+		assertEquals(2, journal.getDataFilesCount());
+		assertEquals(0, journal.getFreeFilesCount());
+		assertEquals(2, journal.getIDMapSize());
+		
+		journal.checkAndReclaimFiles();
+		
+		//Nothing should be reclaimed
+		
+		List<String> files5 = fileFactory.listFiles(fileExtension);
+		
+		assertEquals(3, files5.size());
+		
+		assertEquals(2, journal.getDataFilesCount());
+		assertEquals(0, journal.getFreeFilesCount());
+		assertEquals(2, journal.getIDMapSize());		
+	}
+	
+	
+	// file 1: add 1 tx,
+	// file 2: commit 1, add 2, delete 2
+	// file 3: add 3
+		
+	public void testCommitRecordsInFileNoReclaim() throws Exception
+	{
+		setup(2, 1300, true);
+		createJournal();
+		startJournal();
+		load();
+		
+		List<String> files1 = fileFactory.listFiles(fileExtension);
+		
+		assertEquals(2, files1.size());
+		
+		assertEquals(0, journal.getDataFilesCount());
+		assertEquals(1, journal.getFreeFilesCount());
+		assertEquals(0, journal.getIDMapSize());
+					
+		addTx(1, 1);           // in file 0
+						
+		//Make sure we move on to the next file
+		
+		add(2);               // in file 1
+		
+    	List<String> files2 = fileFactory.listFiles(fileExtension);
+		
+		assertEquals(2, files2.size());
+		
+		assertEquals(1, journal.getDataFilesCount());
+		assertEquals(0, journal.getFreeFilesCount());
+		assertEquals(1, journal.getIDMapSize());
+		
+		commit(1);            // in file 1
+		
+		List<String> files3 = fileFactory.listFiles(fileExtension);
+		
+		assertEquals(2, files3.size());
+		
+		assertEquals(1, journal.getDataFilesCount());
+		assertEquals(0, journal.getFreeFilesCount());
+		assertEquals(2, journal.getIDMapSize());
+		
+		delete(2);            // in file 1
+		
+		List<String> files4 = fileFactory.listFiles(fileExtension);
+		
+		assertEquals(2, files4.size());
+		
+		assertEquals(1, journal.getDataFilesCount());
+		assertEquals(0, journal.getFreeFilesCount());
+		assertEquals(1, journal.getIDMapSize());
+		
+		//Move on to another file
+		
+		add(3);               // in file 2
+		
+		List<String> files5 = fileFactory.listFiles(fileExtension);
+		
+		assertEquals(3, files5.size());
+		
+		assertEquals(2, journal.getDataFilesCount());
+		assertEquals(0, journal.getFreeFilesCount());
+		assertEquals(2, journal.getIDMapSize());
+				
+		journal.checkAndReclaimFiles();
+		
+		List<String> files6 = fileFactory.listFiles(fileExtension);
+		
+		assertEquals(3, files6.size());
+		
+		assertEquals(2, journal.getDataFilesCount());
+		assertEquals(0, journal.getFreeFilesCount());
+		assertEquals(2, journal.getIDMapSize());	
+		
+		//Restart
+		
+		stopJournal();
+		createJournal();
+		startJournal();
+		loadAndCheck();
+		
+		List<String> files7 = fileFactory.listFiles(fileExtension);
+		
+		assertEquals(3, files7.size());
+		
+		assertEquals(2, journal.getDataFilesCount());
+		assertEquals(0, journal.getFreeFilesCount());
+		assertEquals(2, journal.getIDMapSize());
+	}
+	
+	public void testRollbackRecordsInFileNoReclaim() throws Exception
+	{
+		setup(2, 1300, true);
+		createJournal();
+		startJournal();
+		load();
+		
+		List<String> files1 = fileFactory.listFiles(fileExtension);
+		
+		assertEquals(2, files1.size());
+		
+		assertEquals(0, journal.getDataFilesCount());
+		assertEquals(1, journal.getFreeFilesCount());
+		assertEquals(0, journal.getIDMapSize());
+					
+		addTx(1, 1);          // in file 0
+						
+		//Make sure we move on to the next file
+		
+		add(2);               // in file 1
+		
+    	List<String> files2 = fileFactory.listFiles(fileExtension);
+		
+		assertEquals(2, files2.size());
+		
+		assertEquals(1, journal.getDataFilesCount());
+		assertEquals(0, journal.getFreeFilesCount());
+		assertEquals(1, journal.getIDMapSize());
+		
+		rollback(1);          // in file 1
+		
+		List<String> files3 = fileFactory.listFiles(fileExtension);
+		
+		assertEquals(2, files3.size());
+		
+		assertEquals(1, journal.getDataFilesCount());
+		assertEquals(0, journal.getFreeFilesCount());
+		assertEquals(1, journal.getIDMapSize());
+		
+		delete(2);            // in file 1
+		
+		List<String> files4 = fileFactory.listFiles(fileExtension);
+		
+		assertEquals(2, files4.size());
+		
+		assertEquals(1, journal.getDataFilesCount());
+		assertEquals(0, journal.getFreeFilesCount());
+		assertEquals(0, journal.getIDMapSize());
+		
+		//Move on to another file
+		
+		add(3);                // in file 2 (current file)
+		
+		List<String> files5 = fileFactory.listFiles(fileExtension);
+		
+		assertEquals(3, files5.size());
+		
+		assertEquals(2, journal.getDataFilesCount());
+		assertEquals(0, journal.getFreeFilesCount());
+		assertEquals(1, journal.getIDMapSize());
+				
+		journal.checkAndReclaimFiles();
+		
+		List<String> files6 = fileFactory.listFiles(fileExtension);
+		
+		// files 0 and 1 should be deleted
+		
+		assertEquals(2, files6.size());
+		
+		assertEquals(0, journal.getDataFilesCount());
+		assertEquals(1, journal.getFreeFilesCount());
+		assertEquals(1, journal.getIDMapSize());	
+		
+		//Restart
+		
+		stopJournal();
+		createJournal();
+		startJournal();
+		loadAndCheck();
+		
+		List<String> files7 = fileFactory.listFiles(fileExtension);
+		
+		assertEquals(2, files7.size());
+		
+		assertEquals(0, journal.getDataFilesCount());
+		assertEquals(1, journal.getFreeFilesCount());
+		assertEquals(1, journal.getIDMapSize());
+	}
+	
+	
+	
+	// Non transactional tests
+	// =======================
+	
+	public void testSimpleAdd() throws Exception
+	{
+		setup(10, 10 * 1024, true);
+		createJournal();
+		startJournal();
+		load();
+		add(1);	
+		stopJournal();
+		createJournal();
+		startJournal();
+		loadAndCheck();
+	}
+	
+	public void testMultipleAdd() throws Exception
+	{
+		setup(10, 10 * 1024, true);
+		createJournal();
+		startJournal();
+		load();
+		add(1,2,3,4,5,6,7,8,9,10);		
+		stopJournal();
+		createJournal();
+		startJournal();
+		loadAndCheck();
+	}
+	
+	public void testMultipleAddNonContiguous() throws Exception
+	{
+		setup(10, 10 * 1024, true);
+		createJournal();
+		startJournal();
+		load();
+		add(1,3,5,7,10,13,56,100,102,200,201,202,203);		
+		stopJournal();
+		createJournal();
+		startJournal();
+		loadAndCheck();
+	}
+	
+	public void testSimpleAddUpdate() throws Exception
+	{
+		setup(10, 10 * 1024, true);
+		createJournal();
+		startJournal();
+		load();
+		add(1);		
+		update(1);
+		stopJournal();
+		createJournal();
+		startJournal();
+		loadAndCheck();
+	}
+	
+	public void testMultipleAddUpdate() throws Exception
+	{
+		setup(10, 10 * 1024, true);
+		createJournal();
+		startJournal();
+		load();
+		add(1,2,3,4,5,6,7,8,9,10);		
+		update(1,2,4,7,9,10);
+		stopJournal();
+		createJournal();
+		startJournal();
+		loadAndCheck();
+	}
+	
+	public void testMultipleAddUpdateAll() throws Exception
+	{
+		setup(10, 10 * 1024, true);
+		createJournal();
+		startJournal();
+		load();
+		add(1,2,3,4,5,6,7,8,9,10);		
+		update(1,2,3,4,5,6,7,8,9,10);
+		stopJournal();
+		createJournal();
+		startJournal();
+		loadAndCheck();
+	}
+	
+	public void testMultipleAddUpdateNonContiguous() throws Exception
+	{
+		setup(10, 10 * 1024, true);
+		createJournal();
+		startJournal();
+		load();
+		add(1,3,5,7,10,13,56,100,102,200,201,202,203);	
+		add(3,7,10,13,56,100,200,202,203);	
+		stopJournal();
+		createJournal();
+		startJournal();
+		loadAndCheck();
+	}
+	
+	public void testMultipleAddUpdateAllNonContiguous() throws Exception
+	{
+		setup(10, 10 * 1024, true);
+		createJournal();
+		startJournal();
+		load();
+		add(1,3,5,7,10,13,56,100,102,200,201,202,203);
+		update(1,3,5,7,10,13,56,100,102,200,201,202,203);
+		stopJournal();
+		createJournal();
+		startJournal();
+		loadAndCheck();
+	}
+		
+	public void testSimpleAddUpdateDelete() throws Exception
+	{
+		setup(10, 10 * 1024, true);
+		createJournal();
+		startJournal();
+		load();
+		add(1);		
+		update(1);
+		delete(1);
+		stopJournal();
+		createJournal();
+		startJournal();
+		loadAndCheck();
+	}
+	
+	public void testMultipleAddUpdateDelete() throws Exception
+	{
+		setup(10, 10 * 1024, true);
+		createJournal();
+		startJournal();
+		load();
+		add(1,2,3,4,5,6,7,8,9,10);		
+		update(1,2,4,7,9,10);
+		delete(1,4,7,9,10);
+		stopJournal();
+		createJournal();
+		startJournal();
+		loadAndCheck();
+	}
+	
+	public void testMultipleAddUpdateDeleteAll() throws Exception
+	{
+		setup(10, 10 * 1024, true);
+		createJournal();
+		startJournal();
+		load();
+		add(1,2,3,4,5,6,7,8,9,10);		
+		update(1,2,3,4,5,6,7,8,9,10);
+		update(1,2,3,4,5,6,7,8,9,10);
+		stopJournal();
+		createJournal();
+		startJournal();
+		loadAndCheck();
+	}
+	
+	public void testMultipleAddUpdateDeleteNonContiguous() throws Exception
+	{
+		setup(10, 10 * 1024, true);
+		createJournal();
+		startJournal();
+		load();
+		add(1,3,5,7,10,13,56,100,102,200,201,202,203);	
+		add(3,7,10,13,56,100,200,202,203);	
+		delete(3,10,56,100,200,203);	
+		stopJournal();
+		createJournal();
+		startJournal();
+		loadAndCheck();
+	}
+	
+	public void testMultipleAddUpdateDeleteAllNonContiguous() throws Exception
+	{
+		setup(10, 10 * 1024, true);
+		createJournal();
+		startJournal();
+		load();
+		add(1,3,5,7,10,13,56,100,102,200,201,202,203);
+		update(1,3,5,7,10,13,56,100,102,200,201,202,203);
+		delete(1,3,5,7,10,13,56,100,102,200,201,202,203);
+		stopJournal();
+		createJournal();
+		startJournal();
+		loadAndCheck();		
+	}
+	
+	public void testMultipleAddUpdateDeleteDifferentOrder() throws Exception
+	{
+		setup(10, 10 * 1024, true);
+		createJournal();
+		startJournal();
+		load();
+		add(1,3,5,7,10,13,56,100,102,200,201,202,203);
+		update(203, 202, 201, 200, 102, 100, 1, 3, 5, 7, 10, 13, 56);
+		delete(56, 13, 10, 7, 5, 3, 1, 203, 202, 201, 200, 102, 100);
+		stopJournal();
+		createJournal();
+		startJournal();
+		loadAndCheck();		
+	}
+		
+	public void testMultipleAddUpdateDeleteDifferentRecordLengths() throws Exception
+	{
+		setup(10, 2048, true);
+		createJournal();
+		startJournal();
+		load();
+		
+		for (int i = 0; i < 1000; i++)
+		{
+			byte[] record = generateRecord(10 + (int)(1500 * Math.random()));
+			
+			journal.appendAddRecord(i, record);
+			
+			records.add(new RecordInfo(i, record, false));
+		}
+		
+		for (int i = 0; i < 1000; i++)
+		{
+			byte[] record = generateRecord(10 + (int)(1024 * Math.random()));
+			
+			journal.appendUpdateRecord(i, record);
+			
+			records.add(new RecordInfo(i, record, true));
+		}
+		
+		for (int i = 0; i < 1000; i++)
+		{
+			journal.appendDeleteRecord(i);
+			
+			removeRecordsForID(i);
+		}
+		
+		stopJournal();
+		createJournal();
+		startJournal();
+		loadAndCheck();
+		stopJournal();			
+	}
+	
+	
+	public void testAddUpdateDeleteRestartAndContinue() throws Exception
+	{
+		setup(10, 10 * 1024, true);
+		createJournal();
+		startJournal();
+		load();
+		add(1, 2, 3);
+		update(1, 2);
+		stopJournal();
+		createJournal();
+		startJournal();
+		loadAndCheck();
+		add(4, 5, 6);
+		update(5);
+		delete(3);
+		stopJournal();
+		createJournal();
+		startJournal();
+		loadAndCheck();
+		add(7, 8);
+		delete(1, 2);
+		delete(4, 5, 6);
+		stopJournal();
+		createJournal();
+		startJournal();
+		loadAndCheck();
+	}
+	
+	public void testAddUpdateDeleteTransactionalRestartAndContinue() throws Exception
+	{
+		setup(10, 10 * 1024, true);
+		createJournal();
+		startJournal();
+		load();
+		add(1, 2, 3);
+		updateTx(1, 1, 2);
+		commit(1);
+		stopJournal();
+		createJournal();
+		startJournal();
+		loadAndCheck();
+		addTx(2, 4, 5, 6);
+		update(2, 2);
+		delete(2, 3);
+		commit(2);
+		stopJournal();
+		createJournal();
+		startJournal();
+		loadAndCheck();
+		addTx(3, 7, 8);
+		deleteTx(3, 1);
+		deleteTx(3, 4, 5, 6);
+		commit(3);
+		stopJournal();
+		createJournal();
+		startJournal();
+		loadAndCheck();
+	}
+	
+	public void testFillFileExactly() throws Exception
+	{		
+		this.recordLength = 500;
+		
+		int numRecords = 2;
+		
+		//The real appended record size in the journal file = SIZE_BYTE + SIZE_LONG + SIZE_INT + recordLength + SIZE_BYTE
+		
+		int realLength = 1 + 8 + 4 + this.recordLength + 1;
+		
+		int fileSize = numRecords * realLength + 8; //8 for timestamp
+						
+		setup(10, fileSize, true);
+		
+		createJournal();
+		startJournal();
+		load();
+		
+		add(1, 2);
+		
+		stopJournal();
+		createJournal();
+		startJournal();
+		loadAndCheck();
+		
+		add(3, 4);
+		
+		stopJournal();
+		createJournal();
+		startJournal();
+		loadAndCheck();
+		
+		add(4, 5, 6, 7, 8, 9, 10);
+		
+		stopJournal();
+		createJournal();
+		startJournal();
+		loadAndCheck();		
+	}
+	
+	// Transactional tests
+	// ===================
+	
+	public void testSimpleTransaction() throws Exception
+	{
+		setup(10, 10 * 1024, true);
+		createJournal();
+		startJournal();
+		load();
+		addTx(1, 1);
+		updateTx(1, 1);		
+		deleteTx(1, 1);	
+		commit(1);
+		stopJournal();
+		createJournal();
+		startJournal();
+		loadAndCheck();		
+	}
+	
+	public void testTransactionDontDeleteAll() throws Exception
+	{
+		setup(10, 10 * 1024, true);
+		createJournal();
+		startJournal();
+		load();
+		addTx(1, 1, 2, 3);
+		updateTx(1, 1, 2);		
+		deleteTx(1, 1);	
+		commit(1);
+		stopJournal();
+		createJournal();
+		startJournal();
+		loadAndCheck();		
+	}
+	
+	public void testTransactionDeleteAll() throws Exception
+	{
+		setup(10, 10 * 1024, true);
+		createJournal();
+		startJournal();
+		load();
+		addTx(1, 1, 2, 3);
+		updateTx(1, 1, 2);		
+		deleteTx(1, 1, 2, 3);
+		commit(1);
+		stopJournal();
+		createJournal();
+		startJournal();
+		loadAndCheck();		
+	}
+	
+	public void testTransactionUpdateFromBeforeTx() throws Exception
+	{
+		setup(10, 10 * 1024, true);
+		createJournal();
+		startJournal();
+		load();
+		add(1, 2, 3);
+		addTx(1, 4, 5, 6);
+		updateTx(1, 1, 5);
+		commit(1);
+		stopJournal();
+		createJournal();
+		startJournal();
+		loadAndCheck();		
+	}
+	
+	public void testTransactionDeleteFromBeforeTx() throws Exception
+	{
+		setup(10, 10 * 1024, true);
+		createJournal();
+		startJournal();
+		load();
+		add(1, 2, 3);
+		addTx(1, 4, 5, 6);
+		deleteTx(1, 1, 2, 3, 4, 5, 6);
+		commit(1);
+		stopJournal();
+		createJournal();
+		startJournal();
+		loadAndCheck();		
+	}
+	
+	public void testTransactionChangesNotVisibleOutsideTX() throws Exception
+	{
+		setup(10, 10 * 1024, true);
+		createJournal();
+		startJournal();
+		load();
+		add(1, 2, 3);
+		addTx(1, 4, 5, 6);
+		updateTx(1, 1, 2, 4, 5);
+		deleteTx(1, 1, 2, 3, 4, 5, 6);
+		stopJournal();
+		createJournal();
+		startJournal();
+		loadAndCheck();		
+	}
+	
+	public void testMultipleTransactionsDifferentIDs() throws Exception
+	{
+		setup(10, 10 * 1024, true);
+		createJournal();
+		startJournal();
+		load();
+		
+		addTx(1, 1, 2, 3, 4, 5, 6);
+		updateTx(1, 1, 3, 5);
+		deleteTx(1, 1, 2, 3, 4, 5, 6);
+		commit(1);
+		
+		addTx(2, 11, 12, 13, 14, 15, 16);
+		updateTx(2, 11, 13, 15);
+		deleteTx(2, 11, 12, 13, 14, 15, 16);
+		commit(2);
+		
+		addTx(3, 21, 22, 23, 24, 25, 26);
+		updateTx(3, 21, 23, 25);
+		deleteTx(3, 21, 22, 23, 24, 25, 26);
+		commit(3);
+		
+		stopJournal();
+		createJournal();
+		startJournal();
+		loadAndCheck();
+	}
+	
+	public void testMultipleInterleavedTransactionsDifferentIDs() throws Exception
+	{
+		setup(10, 10 * 1024, true);
+		createJournal();
+		startJournal();
+		load();
+		
+		addTx(1, 1, 2, 3, 4, 5, 6);		
+		addTx(3, 21, 22, 23, 24, 25, 26);				
+		updateTx(1, 1, 3, 5);		
+		addTx(2, 11, 12, 13, 14, 15, 16);				
+		deleteTx(1, 1, 2, 3, 4, 5, 6);						
+		updateTx(2, 11, 13, 15);		
+		updateTx(3, 21, 23, 25);			
+		deleteTx(2, 11, 12, 13, 14, 15, 16);		
+		deleteTx(3, 21, 22, 23, 24, 25, 26);
+		
+		commit(1);
+		commit(2);
+		commit(3);
+		
+		stopJournal();
+		createJournal();
+		startJournal();
+		loadAndCheck();
+	}
+	
+	public void testMultipleInterleavedTransactionsSameIDs() throws Exception
+	{
+		setup(10, 10 * 1024, true);
+		createJournal();
+		startJournal();
+		load();
+				
+		add(1, 2, 3, 4, 5, 6, 7, 8);		
+		addTx(1, 9, 10, 11, 12);		
+		addTx(2, 13, 14, 15, 16, 17);		
+		addTx(3, 18, 19, 20, 21, 22);		
+		updateTx(1, 1, 2, 3);		
+		updateTx(2, 4, 5, 6);		
+		commit(2);		
+		updateTx(3, 7, 8);		
+		deleteTx(1, 1, 2);		
+		commit(1);		
+		deleteTx(3, 7, 8);		
+		commit(3);
+		
+		stopJournal();
+		createJournal();
+		startJournal();
+		loadAndCheck();
+	}
+	
+	public void testTransactionMixed() throws Exception
+	{
+		setup(10, 10 * 1024, true);
+		createJournal();
+		startJournal();
+		load();
+		add(1,3,5,7,10,13,56,100,102,200,201,202,203);		
+		addTx(1, 675, 676, 677, 700, 703);
+		update(1,3,5,7,10,13,56,100,102,200,201,202,203);		
+		updateTx(1, 677, 700);		
+		delete(1,3,5,7,10,13,56,100,102,200,201,202,203);		
+		deleteTx(1, 703, 675);		
+		commit(1);
+		stopJournal();
+		createJournal();
+		startJournal();
+		loadAndCheck();		
+	}
+	
+	public void testTransactionAddDeleteDifferentOrder() throws Exception
+	{
+		setup(10, 10 * 1024, true);
+		createJournal();
+		startJournal();
+		load();		
+		addTx(1, 1, 2, 3, 4, 5, 6, 7, 8, 9);					
+		deleteTx(1, 9, 8, 5, 3, 7, 6, 2, 1, 4);	
+		commit(1);
+		stopJournal();
+		createJournal();
+		startJournal();
+		loadAndCheck();		
+	}
+	
+	public void testAddOutsideTXThenUpdateInsideTX() throws Exception
+	{
+		setup(10, 10 * 1024, true);
+		createJournal();
+		startJournal();
+		load();		
+		add(1, 2, 3);
+		updateTx(1, 1, 2, 3);
+		commit(1);
+		stopJournal();
+		createJournal();
+		startJournal();
+		loadAndCheck();
+	}
+	
+	public void testAddOutsideTXThenDeleteInsideTX() throws Exception
+	{
+		setup(10, 10 * 1024, true);
+		createJournal();
+		startJournal();
+		load();		
+		add(1, 2, 3);
+		deleteTx(1, 1, 2, 3);
+		commit(1);
+		stopJournal();
+		createJournal();
+		startJournal();
+		loadAndCheck();
+	}
+	
+	public void testRollback() throws Exception
+	{
+		setup(10, 10 * 1024, true);
+		createJournal();
+		startJournal();
+		load();		
+		add(1, 2, 3);
+		deleteTx(1, 1, 2, 3);
+		rollback(1);
+		stopJournal();
+		createJournal();
+		startJournal();
+		loadAndCheck();
+	}
+	
+	public void testRollbackMultiple() throws Exception
+	{
+		setup(10, 10 * 1024, true);
+		createJournal();
+		startJournal();
+		load();		
+		add(1, 2, 3);
+		deleteTx(1, 1, 2, 3);
+		addTx(2, 4, 5, 6);
+		rollback(1);
+		rollback(2);
+		stopJournal();
+		createJournal();
+		startJournal();
+		loadAndCheck();
+	}
+	
+	public void testIsolation1() throws Exception
+	{
+		setup(10, 10 * 1024, true);
+		createJournal();
+		startJournal();
+		load();			
+		addTx(1, 1, 2, 3);		
+		deleteTx(1, 1, 2, 3);		
+		commit(1);
+		stopJournal();
+		createJournal();
+		startJournal();
+		loadAndCheck();
+	}
+	
+	public void testIsolation2() throws Exception
+	{
+		setup(10, 10 * 1024, true);
+		createJournal();
+		startJournal();
+		load();			
+		addTx(1, 1, 2, 3);		
+		try
+		{
+			update(1);
+			fail("Should throw exception");
+		}
+		catch (IllegalStateException e)
+		{
+			//Ok
+		}
+		
+		stopJournal();
+		createJournal();
+		startJournal();
+		loadAndCheck();
+	}
+	
+	public void testIsolation3() throws Exception
+	{
+		setup(10, 10 * 1024, true);
+		createJournal();
+		startJournal();
+		load();			
+		addTx(1, 1, 2, 3);		
+		try
+		{
+			delete(1);
+			fail("Should throw exception");
+		}
+		catch (IllegalStateException e)
+		{
+			//Ok
+		}
+		
+		stopJournal();
+		createJournal();
+		startJournal();
+		loadAndCheck();
+	}
+	
+	
+	// XA tests
+	// ========
+	
+	public void testXASimpleNotPrepared() throws Exception
+	{
+		setup(10, 10 * 1024, true);
+		createJournal();
+		startJournal();
+		load();		
+		addTx(1, 1, 2, 3, 4, 5, 6, 7, 8, 9);					
+		updateTx(1, 1, 2, 3, 4, 7, 8);
+		deleteTx(1, 1, 2, 3, 4, 5);		
+		stopJournal();
+		createJournal();
+		startJournal();
+		loadAndCheck();		
+	}
+	
+	public void testXASimplePrepared() throws Exception
+	{
+		setup(10, 10 * 1024, true);
+		createJournal();
+		startJournal();
+		load();		
+		addTx(1, 1, 2, 3, 4, 5, 6, 7, 8, 9);					
+		updateTx(1, 1, 2, 3, 4, 7, 8);
+		deleteTx(1, 1, 2, 3, 4, 5);	
+		prepare(1);
+		stopJournal();
+		createJournal();
+		startJournal();
+		loadAndCheck();		
+	}
+	
+	public void testXASimpleCommit() throws Exception
+	{
+		setup(10, 10 * 1024, true);
+		createJournal();
+		startJournal();
+		load();		
+		addTx(1, 1, 2, 3, 4, 5, 6, 7, 8, 9);					
+		updateTx(1, 1, 2,3, 4, 7, 8);
+		deleteTx(1, 1, 2, 3, 4, 5);	
+		prepare(1);
+		commit(1);
+		stopJournal();
+		createJournal();
+		startJournal();
+		loadAndCheck();		
+	}
+	
+	public void testXASimpleRollback() throws Exception
+	{
+		setup(10, 10 * 1024, true);
+		createJournal();
+		startJournal();
+		load();		
+		addTx(1, 1, 2, 3, 4, 5, 6, 7, 8, 9);					
+		updateTx(1, 1, 2,3, 4, 7, 8);
+		deleteTx(1, 1, 2, 3, 4, 5);	
+		prepare(1);
+		rollback(1);
+		stopJournal();
+		createJournal();
+		startJournal();
+		loadAndCheck();		
+	}
+	
+	public void testXAChangesNotVisibleNotPrepared() throws Exception
+	{
+		setup(10, 10 * 1024, true);
+		createJournal();
+		startJournal();
+		load();		
+		add(1, 2, 3, 4, 5, 6);
+		addTx(1, 7, 8, 9, 10);					
+		updateTx(1, 1, 2, 3, 7, 8, 9);
+		deleteTx(1, 1, 2, 3, 4, 5);	
+		stopJournal();
+		createJournal();
+		startJournal();
+		loadAndCheck();		
+	}
+	
+	public void testXAChangesNotVisiblePrepared() throws Exception
+	{
+		setup(10, 10 * 1024, true);
+		createJournal();
+		startJournal();
+		load();		
+		add(1, 2, 3, 4, 5, 6);
+		addTx(1, 7, 8, 9, 10);					
+		updateTx(1, 1, 2, 3, 7, 8, 9);
+		deleteTx(1, 1, 2, 3, 4, 5);	
+		prepare(1);
+		stopJournal();
+		createJournal();
+		startJournal();
+		loadAndCheck();		
+	}
+	
+	public void testXAChangesNotVisibleRollback() throws Exception
+	{
+		setup(10, 10 * 1024, true);
+		createJournal();
+		startJournal();
+		load();		
+		add(1, 2, 3, 4, 5, 6);
+		addTx(1, 7, 8, 9, 10);					
+		updateTx(1, 1, 2, 3, 7, 8, 9);
+		deleteTx(1, 1, 2, 3, 4, 5);	
+		prepare(1);
+		rollback(1);
+		stopJournal();
+		createJournal();
+		startJournal();
+		loadAndCheck();		
+	}
+	
+	public void testXAChangesisibleCommit() throws Exception
+	{
+		setup(10, 10 * 1024, true);
+		createJournal();
+		startJournal();
+		load();		
+		add(1, 2, 3, 4, 5, 6);
+		addTx(1, 7, 8, 9, 10);					
+		updateTx(1, 1, 2, 3, 7, 8, 9);
+		deleteTx(1, 1, 2, 3, 4, 5);	
+		prepare(1);
+		commit(1);
+		stopJournal();
+		createJournal();
+		startJournal();
+		loadAndCheck();		
+	}
+	
+	public void testXAMultiple() throws Exception
+	{
+		setup(10, 10 * 1024, true);
+		createJournal();
+		startJournal();
+		load();		
+		add(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
+		addTx(1, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20);
+		addTx(2, 21, 22, 23, 24, 25, 26, 27);
+		updateTx(1, 1, 3, 6, 11, 14, 17);
+		addTx(3, 28, 29, 30, 31, 32, 33, 34, 35);
+		updateTx(3, 7, 8, 9, 10);
+		deleteTx(2, 4, 5, 6, 23, 25, 27);
+		prepare(2);
+		deleteTx(1, 1, 2, 11, 14, 15);
+		prepare(1);
+		deleteTx(3, 28, 31, 32, 9);
+		prepare(3);
+		
+		commit(1);
+		rollback(2);
+		commit(3);
+	}
+
+}

Modified: trunk/tests/src/org/jboss/messaging/core/journal/impl/test/unit/RealJournalImplTest.java
===================================================================
--- trunk/tests/src/org/jboss/messaging/core/journal/impl/test/unit/RealJournalImplTest.java	2008-04-04 11:01:48 UTC (rev 4007)
+++ trunk/tests/src/org/jboss/messaging/core/journal/impl/test/unit/RealJournalImplTest.java	2008-04-04 14:25:20 UTC (rev 4008)
@@ -38,14 +38,14 @@
  * @author <a href="mailto:tim.fox at jboss.com">Tim Fox</a>
  *
  */
-public class RealJournalImplTest extends JournalImplTestBase
+public class RealJournalImplTest extends JournalImplTestUnit
 {
 	private static final Logger log = Logger.getLogger(RealJournalImplTest.class);
 	
 	protected String journalDir = System.getProperty("user.home") + "/journal-test";
 		
-	protected void prepareDirectory() throws Exception
-	{				
+	protected SequentialFileFactory getFileFactory() throws Exception
+	{
 		File file = new File(journalDir);
 		
 		log.info("deleting directory " + journalDir);
@@ -53,40 +53,8 @@
 		deleteDirectory(file);
 		
 		file.mkdir();		
-	}
-	
-	protected SequentialFileFactory getFileFactory() throws Exception
-	{
+		
 		return new NIOSequentialFileFactory(journalDir);
-	}
+	}	
 	
-	public void testSpeed() throws Exception
-	{
-		Journal journal =
-			new JournalImpl(10 * 1024 * 1024, 10, 10, true, new NIOSequentialFileFactory(journalDir),
-					5000, "jbm-data", "jbm");
-		
-		journal.start();
-		
-		journal.load(new ArrayList<RecordInfo>(), null);
-		
-		final int numMessages = 10000;
-		
-		byte[] data = new byte[1024];
-		
-		long start = System.currentTimeMillis();
-		
-		for (int i = 0; i < numMessages; i++)
-		{
-			journal.appendAddRecord(i, data);
-		}
-		
-		long end = System.currentTimeMillis();
-		
-		double rate = 1000 * (double)numMessages / (end - start);
-		
-		log.info("Rate " + rate + " records/sec");
-
-	}
-	
 }

Added: trunk/tests/src/org/jboss/messaging/core/journal/impl/test/unit/ReclaimerTest.java
===================================================================
--- trunk/tests/src/org/jboss/messaging/core/journal/impl/test/unit/ReclaimerTest.java	                        (rev 0)
+++ trunk/tests/src/org/jboss/messaging/core/journal/impl/test/unit/ReclaimerTest.java	2008-04-04 14:25:20 UTC (rev 4008)
@@ -0,0 +1,872 @@
+/*
+  * JBoss, Home of Professional Open Source
+  * Copyright 2005, JBoss Inc., and individual contributors as indicated
+  * by the @authors tag. See the copyright.txt in the distribution for a
+  * full listing of individual contributors.
+  *
+  * This is free software; you can redistribute it and/or modify it
+  * under the terms of the GNU Lesser General Public License as
+  * published by the Free Software Foundation; either version 2.1 of
+  * the License, or (at your option) any later version.
+  *
+  * This software is distributed in the hope that it will be useful,
+  * but WITHOUT ANY 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 along with this software; if not, write to the Free
+  * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+  * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+  */
+package org.jboss.messaging.core.journal.impl.test.unit;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+import org.jboss.messaging.core.journal.SequentialFile;
+import org.jboss.messaging.core.journal.impl.JournalFile;
+import org.jboss.messaging.core.journal.impl.Reclaimer;
+import org.jboss.messaging.core.logging.Logger;
+import org.jboss.messaging.test.unit.UnitTestCase;
+
+/**
+ * 
+ * A ReclaimerTest
+ * 
+ * @author <a href="mailto:tim.fox at jboss.com">Tim Fox</a>
+ *
+ */
+public class ReclaimerTest extends UnitTestCase
+{
+	private static final Logger log = Logger.getLogger(ReclaimerTest.class);	
+	
+	private JournalFile[] files;
+	
+	private Reclaimer reclaimer;
+	
+	protected void setUp() throws Exception
+	{
+		super.setUp();
+		
+		reclaimer = new Reclaimer();
+	}
+		
+	public void testOneFilePosNegAll() throws Exception
+	{
+		setup(1);
+		
+		setupPosNeg(0, 10, 10);
+		
+		reclaimer.scan(files);
+		
+		assertCanDelete(0);		
+	}
+	
+	public void testOneFilePosNegNotAll() throws Exception
+	{
+		setup(1);
+		
+		setupPosNeg(0, 10, 7);
+		
+		reclaimer.scan(files);
+		
+		assertCantDelete(0);		
+	}
+	
+	public void testOneFilePosOnly() throws Exception
+	{
+		setup(1);
+		
+		setupPosNeg(0, 10);
+		
+		reclaimer.scan(files);
+		
+		assertCantDelete(0);		
+	}
+	
+	public void testOneFileNegOnly() throws Exception
+	{
+		setup(1);
+		
+		setupPosNeg(0, 0, 10);
+		
+		reclaimer.scan(files);
+		
+		assertCanDelete(0);		
+	}
+	
+	
+	public void testTwoFilesPosNegAllDifferentFiles() throws Exception
+	{
+		setup(2);
+		
+		setupPosNeg(0, 10);
+		setupPosNeg(1, 0, 10);
+	
+		reclaimer.scan(files);
+		
+		assertCanDelete(0);
+		assertCanDelete(1);
+		
+	}
+	
+	public void testTwoFilesPosNegAllSameFiles() throws Exception
+	{
+		setup(2);
+		
+		setupPosNeg(0, 10, 10);
+		setupPosNeg(1, 10, 0, 10);
+	
+		reclaimer.scan(files);
+		
+		assertCanDelete(0);
+		assertCanDelete(1);
+		
+	}
+	
+	public void testTwoFilesPosNegMixedFiles() throws Exception
+	{
+		setup(2);
+		
+		setupPosNeg(0, 10, 7);
+		setupPosNeg(1, 10, 3, 10);
+	
+		reclaimer.scan(files);
+		
+		assertCanDelete(0);
+		assertCanDelete(1);		
+	}
+	
+	public void testTwoFilesPosNegAllFirstFile() throws Exception
+	{
+		setup(2);
+		
+		setupPosNeg(0, 10, 10);
+		setupPosNeg(1, 10);
+	
+		reclaimer.scan(files);
+		
+		assertCanDelete(0);
+		assertCantDelete(1);		
+	}
+	
+	public void testTwoFilesPosNegAllSecondFile() throws Exception
+	{
+		setup(2);
+		
+		setupPosNeg(0, 10);
+		setupPosNeg(1, 10, 0, 10);
+	
+		reclaimer.scan(files);
+		
+		assertCantDelete(0);
+		assertCanDelete(1);		
+	}
+	
+	public void testTwoFilesPosOnly() throws Exception
+	{
+		setup(2);
+		
+		setupPosNeg(0, 10);
+		setupPosNeg(1, 10);
+	
+		reclaimer.scan(files);
+		
+		assertCantDelete(0);
+		assertCantDelete(1);		
+	}
+	
+	public void testTwoFilesxyz() throws Exception
+	{
+		setup(2);
+		
+		setupPosNeg(0, 10);
+		setupPosNeg(1, 10, 10);
+	
+		reclaimer.scan(files);
+		
+		assertCanDelete(0);
+		assertCantDelete(1);		
+	}
+	
+	//Can-can-can
+	
+	public void testThreeFiles1() throws Exception
+	{
+		setup(3);
+		
+		setupPosNeg(0, 10, 10, 0, 0);
+		setupPosNeg(1, 10, 0, 10, 0);
+		setupPosNeg(2, 10, 0, 0, 10);
+		
+		reclaimer.scan(files);
+		
+		assertCanDelete(0);
+		assertCanDelete(1);
+		assertCanDelete(2);
+	}
+	
+	public void testThreeFiles2() throws Exception
+	{
+		setup(3);
+		
+		setupPosNeg(0, 10, 7, 0, 0);
+		setupPosNeg(1, 10, 3, 5, 0);
+		setupPosNeg(2, 10, 0, 5, 10);
+		
+		reclaimer.scan(files);
+		
+		assertCanDelete(0);
+		assertCanDelete(1);
+		assertCanDelete(2);
+	}
+	
+	public void testThreeFiles3() throws Exception
+	{
+		setup(3);
+		
+		setupPosNeg(0, 10, 1, 0, 0);
+		setupPosNeg(1, 10, 6, 5, 0);
+		setupPosNeg(2, 10, 3, 5, 10);
+		
+		reclaimer.scan(files);
+		
+		assertCanDelete(0);
+		assertCanDelete(1);
+		assertCanDelete(2);
+	}
+	
+	public void testThreeFiles3_1() throws Exception
+	{
+		setup(3);
+		
+		setupPosNeg(0, 10, 1, 0, 0);
+		setupPosNeg(1, 10, 6, 5, 0);
+		setupPosNeg(2, 0, 3, 5, 0);
+		
+		reclaimer.scan(files);
+		
+		assertCanDelete(0);
+		assertCanDelete(1);
+		assertCanDelete(2);
+	}
+	
+	public void testThreeFiles3_2() throws Exception
+	{
+		setup(3);
+		
+		setupPosNeg(0, 10, 1, 0, 0);
+		setupPosNeg(1, 0, 6, 0, 0);
+		setupPosNeg(2, 0, 3, 0, 0);
+		
+		reclaimer.scan(files);
+		
+		assertCanDelete(0);
+		assertCanDelete(1);
+		assertCanDelete(2);
+	}
+	
+	
+	
+	
+	//Cant-can-can
+	
+	
+	public void testThreeFiles4() throws Exception
+	{
+		setup(3);
+		
+		setupPosNeg(0, 10, 3, 0, 0);
+		setupPosNeg(1, 10, 0, 5, 0);
+		setupPosNeg(2, 10, 0, 5, 10);
+		
+		reclaimer.scan(files);
+		
+		assertCantDelete(0);
+		assertCanDelete(1);
+		assertCanDelete(2);
+	}
+	
+	public void testThreeFiles5() throws Exception
+	{
+		setup(3);
+		
+		setupPosNeg(0, 10, 3, 0, 0);
+		setupPosNeg(1, 10, 0, 5, 0);
+		setupPosNeg(2, 0, 0, 5, 0);
+		
+		reclaimer.scan(files);
+		
+		assertCantDelete(0);
+		assertCanDelete(1);
+		assertCanDelete(2);
+	}
+	
+	public void testThreeFiles6() throws Exception
+	{
+		setup(3);
+		
+		setupPosNeg(0, 10, 0, 0, 0);
+		setupPosNeg(1, 10, 0, 5, 0);
+		setupPosNeg(2, 0, 0, 5, 10);
+		
+		reclaimer.scan(files);
+		
+		assertCantDelete(0);
+		assertCanDelete(1);
+		assertCanDelete(2);
+	}
+	
+	public void testThreeFiles7() throws Exception
+	{
+		setup(3);
+		
+		setupPosNeg(0, 10, 0, 0, 0);
+		setupPosNeg(1, 10, 0, 5, 0);
+		setupPosNeg(2, 0, 0, 5, 0);
+		
+		reclaimer.scan(files);
+		
+		assertCantDelete(0);
+		assertCanDelete(1);
+		assertCanDelete(2);
+	}
+	
+	
+	//Cant can cant
+	
+	public void testThreeFiles8() throws Exception
+	{
+		setup(3);
+		
+		setupPosNeg(0, 10, 3, 0, 0);
+		setupPosNeg(1, 10, 0, 10, 0);
+		setupPosNeg(2, 10, 0, 0, 2);
+		
+		reclaimer.scan(files);
+		
+		assertCantDelete(0);
+		assertCanDelete(1);
+		assertCantDelete(2);
+	}
+	
+	public void testThreeFiles9() throws Exception
+	{
+		setup(3);
+		
+		setupPosNeg(0, 10, 3, 0, 0);
+		setupPosNeg(1, 10, 0, 10, 0);
+		setupPosNeg(2, 10, 1, 0, 2);
+		
+		reclaimer.scan(files);
+		
+		assertCantDelete(0);
+		assertCanDelete(1);
+		assertCantDelete(2);
+	}
+	
+	public void testThreeFiles10() throws Exception
+	{
+		setup(3);
+		
+		setupPosNeg(0, 10, 3, 0, 0);
+		setupPosNeg(1, 10, 0, 10, 0);
+		setupPosNeg(2, 10, 1, 0, 0);
+		
+		reclaimer.scan(files);
+		
+		assertCantDelete(0);
+		assertCanDelete(1);
+		assertCantDelete(2);
+	}
+	
+	public void testThreeFiles11() throws Exception
+	{
+		setup(3);
+		
+		setupPosNeg(0, 10, 0, 0, 0);
+		setupPosNeg(1, 10, 0, 10, 0);
+		setupPosNeg(2, 10, 0, 0, 0);
+		
+		reclaimer.scan(files);
+		
+		assertCantDelete(0);
+		assertCanDelete(1);
+		assertCantDelete(2);
+	}
+	
+	public void testThreeFiles12() throws Exception
+	{
+		setup(3);
+		
+		setupPosNeg(0, 10, 0, 0, 0);
+		setupPosNeg(1, 10, 0, 10, 0);
+		setupPosNeg(2, 0, 3, 0, 0);
+		
+		reclaimer.scan(files);
+		
+		assertCantDelete(0);
+		assertCanDelete(1);
+		assertCantDelete(2);
+	}
+	
+	//Cant-cant-cant
+	
+	public void testThreeFiles13() throws Exception
+	{
+		setup(3);
+		
+		setupPosNeg(0, 10, 3, 0, 0);
+		setupPosNeg(1, 10, 2, 3, 0);
+		setupPosNeg(2, 10, 1, 5, 7);
+		
+		reclaimer.scan(files);
+		
+		assertCantDelete(0);
+		assertCantDelete(1);
+		assertCantDelete(2);
+	}
+	
+	public void testThreeFiles14() throws Exception
+	{
+		setup(3);
+		
+		setupPosNeg(0, 10, 3, 0, 0);
+		setupPosNeg(1, 0, 2, 0, 0);
+		setupPosNeg(2, 10, 1, 0, 7);
+		
+		reclaimer.scan(files);
+		
+		assertCantDelete(0);
+		assertCantDelete(1);
+		assertCantDelete(2);
+	}
+	
+	public void testThreeFiles15() throws Exception
+	{
+		setup(3);
+		
+		setupPosNeg(0, 10, 3, 0, 0);
+		setupPosNeg(1, 10, 2, 3, 0);
+		setupPosNeg(2, 0, 1, 5, 0);
+		
+		reclaimer.scan(files);
+		
+		assertCantDelete(0);
+		assertCantDelete(1);
+		assertCantDelete(2);
+	}
+	
+	public void testThreeFiles16() throws Exception
+	{
+		setup(3);
+		
+		setupPosNeg(0, 10, 3, 0, 0);
+		setupPosNeg(1, 0, 2, 0, 0);
+		setupPosNeg(2, 0, 1, 0, 0);
+		
+		reclaimer.scan(files);
+		
+		assertCantDelete(0);
+		assertCantDelete(1);
+		assertCantDelete(2);
+	}
+	
+	public void testThreeFiles17() throws Exception
+	{
+		setup(3);
+		
+		setupPosNeg(0, 10, 3, 0, 0);
+		setupPosNeg(1, 10, 0, 3, 0);
+		setupPosNeg(2, 10, 1, 5, 7);
+		
+		reclaimer.scan(files);
+		
+		assertCantDelete(0);
+		assertCantDelete(1);
+		assertCantDelete(2);
+	}
+	
+	public void testThreeFiles18() throws Exception
+	{
+		setup(3);
+		
+		setupPosNeg(0, 10, 3, 0, 0);
+		setupPosNeg(1, 10, 0, 3, 0);
+		setupPosNeg(2, 10, 1, 0, 7);
+		
+		reclaimer.scan(files);
+		
+		assertCantDelete(0);
+		assertCantDelete(1);
+		assertCantDelete(2);
+	}
+	
+	public void testThreeFiles19() throws Exception
+	{
+		setup(3);
+		
+		setupPosNeg(0, 10, 3, 0, 0);
+		setupPosNeg(1, 10, 0, 3, 0);
+		setupPosNeg(2, 10, 1, 0, 0);
+		
+		reclaimer.scan(files);
+		
+		assertCantDelete(0);
+		assertCantDelete(1);
+		assertCantDelete(2);
+	}
+	
+	public void testThreeFiles20() throws Exception
+	{
+		setup(3);
+		
+		setupPosNeg(0, 10, 3, 0, 0);
+		setupPosNeg(1, 10, 0, 0, 0);
+		setupPosNeg(2, 10, 1, 0, 0);
+		
+		reclaimer.scan(files);
+		
+		assertCantDelete(0);
+		assertCantDelete(1);
+		assertCantDelete(2);
+	}
+	
+	public void testThreeFiles21() throws Exception
+	{
+		setup(3);
+		
+		setupPosNeg(0, 10, 0, 0, 0);
+		setupPosNeg(1, 10, 0, 0, 0);
+		setupPosNeg(2, 10, 0, 0, 0);
+		
+		reclaimer.scan(files);
+		
+		assertCantDelete(0);
+		assertCantDelete(1);
+		assertCantDelete(2);
+	}
+	
+	// Can-can-cant
+	
+	public void testThreeFiles22() throws Exception
+	{
+		setup(3);
+		
+		setupPosNeg(0, 10, 10, 0, 0);
+		setupPosNeg(1, 10, 0, 10, 0);
+		setupPosNeg(2, 10, 0, 0, 0);
+		
+		reclaimer.scan(files);
+		
+		assertCanDelete(0);
+		assertCanDelete(1);
+		assertCantDelete(2);
+	}
+	
+	public void testThreeFiles23() throws Exception
+	{
+		setup(3);
+		
+		setupPosNeg(0, 10, 10, 0, 0);
+		setupPosNeg(1, 10, 0, 10, 0);
+		setupPosNeg(2, 10, 3, 0, 0);
+		
+		reclaimer.scan(files);
+		
+		assertCanDelete(0);
+		assertCanDelete(1);
+		assertCantDelete(2);
+	}
+	
+	public void testThreeFiles24() throws Exception
+	{
+		setup(3);
+		
+		setupPosNeg(0, 10, 7, 0, 0);
+		setupPosNeg(1, 10, 3, 10, 0);
+		setupPosNeg(2, 10, 3, 0, 0);
+		
+		reclaimer.scan(files);
+		
+		assertCanDelete(0);
+		assertCanDelete(1);
+		assertCantDelete(2);
+	}
+	
+	public void testThreeFiles25() throws Exception
+	{
+		setup(3);
+		
+		setupPosNeg(0, 10, 7, 0, 0);
+		setupPosNeg(1, 0, 3, 10, 0);
+		setupPosNeg(2, 10, 3, 0, 0);
+		
+		reclaimer.scan(files);
+		
+		assertCanDelete(0);
+		assertCanDelete(1);
+		assertCantDelete(2);
+	}
+	
+	public void testThreeFiles26() throws Exception
+	{
+		setup(3);
+		
+		setupPosNeg(0, 10, 7, 0, 0);
+		setupPosNeg(1, 0, 3, 10, 0);
+		setupPosNeg(2, 10, 0, 0, 0);
+		
+		reclaimer.scan(files);
+		
+		assertCanDelete(0);
+		assertCanDelete(1);
+		assertCantDelete(2);
+	}
+	
+
+	//Can-cant-cant
+	
+	public void testThreeFiles27() throws Exception
+	{
+		setup(3);
+		
+		setupPosNeg(0, 10, 10, 0, 0);
+		setupPosNeg(1, 10, 0, 0, 0);
+		setupPosNeg(2, 10, 0, 0, 0);
+		
+		reclaimer.scan(files);
+		
+		assertCanDelete(0);
+		assertCantDelete(1);
+		assertCantDelete(2);
+	}
+	
+	public void testThreeFiles28() throws Exception
+	{
+		setup(3);
+		
+		setupPosNeg(0, 10, 10, 0, 0);
+		setupPosNeg(1, 10, 0, 3, 0);
+		setupPosNeg(2, 10, 0, 0, 5);
+		
+		reclaimer.scan(files);
+		
+		assertCanDelete(0);
+		assertCantDelete(1);
+		assertCantDelete(2);
+	}
+	
+	public void testThreeFiles29() throws Exception
+	{
+		setup(3);
+		
+		setupPosNeg(0, 10, 10, 0, 0);
+		setupPosNeg(1, 10, 0, 3, 0);
+		setupPosNeg(2, 10, 0, 6, 5);
+		
+		reclaimer.scan(files);
+		
+		assertCanDelete(0);
+		assertCantDelete(1);
+		assertCantDelete(2);
+	}
+	
+	public void testThreeFiles30() throws Exception
+	{
+		setup(3);
+		
+		setupPosNeg(0, 10, 10, 0, 0);
+		setupPosNeg(1, 10, 0, 3, 0);
+		setupPosNeg(2, 0, 0, 6, 0);
+		
+		reclaimer.scan(files);
+		
+		assertCanDelete(0);
+		assertCantDelete(1);
+		assertCantDelete(2);
+	}
+	
+	
+	// Private ------------------------------------------------------------------------
+		
+	private void setup(int numFiles)
+	{
+		files = new JournalFile[numFiles];
+		
+		for (int i = 0; i < numFiles; i++)
+		{
+			files[i] = new MockJournalFile();
+		}		                       
+	}
+		
+	private void setupPosNeg(int fileNumber, int pos, int... neg)
+	{
+		JournalFile file = files[fileNumber];
+		
+		for (int i = 0; i < pos; i++)
+		{
+			file.incPosCount();
+		}
+		
+		for (int i = 0; i < neg.length; i++)
+		{
+			JournalFile reclaimable2 = files[i];
+			
+			for (int j = 0; j < neg[i]; j++)
+			{
+				file.incNegCount(reclaimable2);
+			}
+		}
+	}
+	
+	private void assertCanDelete(int... fileNumber)
+	{
+		for (int num: fileNumber)
+		{
+			assertTrue(files[num].isCanReclaim());
+		}
+	}
+	
+	private void assertCantDelete(int... fileNumber)
+	{
+		for (int num: fileNumber)
+		{
+			assertFalse(files[num].isCanReclaim());
+		}
+	}
+	
+	class MockJournalFile implements JournalFile
+	{
+	   private Set<Long> transactionIDs = new HashSet<Long>();
+		
+		private Set<Long> transactionTerminationIDs = new HashSet<Long>();
+		
+		private Set<Long> transactionPrepareIDs = new HashSet<Long>();
+
+		private Map<JournalFile, Integer> negCounts = new HashMap<JournalFile, Integer>();
+		
+		private int posCount;
+		
+		private boolean canDelete;
+		
+		public void extendOffset(int delta)
+		{
+		}
+
+		public SequentialFile getFile()
+		{
+			return null;
+		}
+
+		public int getOffset()
+		{
+			return 0;
+		}
+
+		public long getOrderingID()
+		{
+			return 0;
+		}
+
+		public void setOffset(int offset)
+		{
+		}
+		
+		public int getNegCount(final JournalFile file)
+		{
+			Integer count = negCounts.get(file);
+			
+			if (count != null)
+			{
+				return count.intValue();
+			}
+			else
+			{
+				return 0;
+			}
+		}
+		
+		public void incNegCount(final JournalFile file)
+		{
+			Integer count = negCounts.get(file);
+			
+			int c = count == null ? 1 : count.intValue() + 1;
+			
+			negCounts.put(file, c);
+		}
+		
+		public int getPosCount()
+		{
+			return posCount;
+		}
+		
+		public void incPosCount()
+		{
+			this.posCount++;
+		}
+		
+		public void decPosCount()
+		{
+			this.posCount--;
+		}
+
+		public boolean isCanReclaim()
+		{
+			return canDelete;
+		}
+
+		public void setCanReclaim(boolean canDelete)
+		{
+			this.canDelete = canDelete;
+		}		
+		
+		public void addTransactionID(long id)
+		{
+			transactionIDs.add(id);
+		}
+
+		public void addTransactionPrepareID(long id)
+		{
+			transactionPrepareIDs.add(id);
+		}
+
+		public void addTransactionTerminationID(long id)
+		{
+			transactionTerminationIDs.add(id);
+		}
+
+		public boolean containsTransactionID(long id)
+		{
+			return transactionIDs.contains(id);
+		}
+
+		public boolean containsTransactionPrepareID(long id)
+		{
+			return transactionPrepareIDs.contains(id);
+		}
+
+		public boolean containsTransactionTerminationID(long id)
+		{
+			return transactionTerminationIDs.contains(id);
+		}
+		
+		public Set<Long> getTranactionTerminationIDs()
+		{
+			return transactionTerminationIDs;
+		}
+
+		public Set<Long> getTransactionPrepareIDs()
+		{
+			return transactionPrepareIDs;
+		}
+
+		public Set<Long> getTransactionsIDs()
+		{
+			return transactionIDs;
+		}
+	}
+}




More information about the jboss-cvs-commits mailing list