[jboss-cvs] JBoss Messaging SVN: r4174 - in trunk: src/etc and 21 other directories.

jboss-cvs-commits at lists.jboss.org jboss-cvs-commits at lists.jboss.org
Mon May 12 17:53:05 EDT 2008


Author: clebert.suconic at jboss.com
Date: 2008-05-12 17:53:04 -0400 (Mon, 12 May 2008)
New Revision: 4174

Added:
   trunk/src/main/org/jboss/messaging/core/journal/EncodingSupport.java
   trunk/src/main/org/jboss/messaging/util/VariableLatch.java
   trunk/tests/src/org/jboss/messaging/tests/unit/core/journal/impl/fakes/FakeCallback.java
   trunk/tests/src/org/jboss/messaging/tests/unit/core/persistence/
   trunk/tests/src/org/jboss/messaging/tests/unit/core/persistence/fakes/
   trunk/tests/src/org/jboss/messaging/tests/unit/core/persistence/fakes/FakeBinding.java
   trunk/tests/src/org/jboss/messaging/tests/unit/core/persistence/fakes/FakePostOffice.java
   trunk/tests/src/org/jboss/messaging/tests/unit/core/persistence/impl/
   trunk/tests/src/org/jboss/messaging/tests/unit/core/persistence/impl/timing/
   trunk/tests/src/org/jboss/messaging/tests/unit/core/persistence/impl/timing/StorageManagerTimingTest.java
   trunk/tests/src/org/jboss/messaging/tests/unit/core/util/VariableLatchTest.java
Modified:
   trunk/native/src/org_jboss_messaging_core_asyncio_impl_AsynchronousFileImpl.h
   trunk/src/etc/jbm-configuration.xml
   trunk/src/main/org/jboss/messaging/core/asyncio/impl/AsynchronousFileImpl.java
   trunk/src/main/org/jboss/messaging/core/config/impl/ConfigurationImpl.java
   trunk/src/main/org/jboss/messaging/core/journal/Journal.java
   trunk/src/main/org/jboss/messaging/core/journal/RecordInfo.java
   trunk/src/main/org/jboss/messaging/core/journal/SequentialFile.java
   trunk/src/main/org/jboss/messaging/core/journal/SequentialFileFactory.java
   trunk/src/main/org/jboss/messaging/core/journal/impl/AIOSequentialFile.java
   trunk/src/main/org/jboss/messaging/core/journal/impl/AIOSequentialFileFactory.java
   trunk/src/main/org/jboss/messaging/core/journal/impl/JournalImpl.java
   trunk/src/main/org/jboss/messaging/core/journal/impl/NIOSequentialFile.java
   trunk/src/main/org/jboss/messaging/core/journal/impl/NIOSequentialFileFactory.java
   trunk/src/main/org/jboss/messaging/core/message/Message.java
   trunk/src/main/org/jboss/messaging/core/message/impl/MessageImpl.java
   trunk/src/main/org/jboss/messaging/core/persistence/impl/journal/JournalStorageManager.java
   trunk/src/main/org/jboss/messaging/core/remoting/impl/wireformat/ProducerSendMessage.java
   trunk/src/main/org/jboss/messaging/core/remoting/impl/wireformat/ReceiveMessage.java
   trunk/src/main/org/jboss/messaging/util/ByteBufferWrapper.java
   trunk/src/main/org/jboss/messaging/util/TypedProperties.java
   trunk/tests/jms-tests/src/
   trunk/tests/src/org/jboss/messaging/tests/integration/core/asyncio/impl/MultiThreadWriteNativeTest.java
   trunk/tests/src/org/jboss/messaging/tests/integration/core/asyncio/impl/SingleThreadWriteNativeTest.java
   trunk/tests/src/org/jboss/messaging/tests/unit/core/journal/impl/AIOSequentialFileFactoryTest.java
   trunk/tests/src/org/jboss/messaging/tests/unit/core/journal/impl/JournalImplTestBase.java
   trunk/tests/src/org/jboss/messaging/tests/unit/core/journal/impl/JournalImplTestUnit.java
   trunk/tests/src/org/jboss/messaging/tests/unit/core/journal/impl/SequentialFileFactoryTestBase.java
   trunk/tests/src/org/jboss/messaging/tests/unit/core/journal/impl/fakes/FakeSequentialFileFactory.java
   trunk/tests/src/org/jboss/messaging/tests/unit/core/journal/impl/timing/JournalImplTestUnit.java
   trunk/tests/src/org/jboss/messaging/tests/unit/core/journal/impl/timing/RealJournalImplAIOTest.java
   trunk/tests/src/org/jboss/messaging/tests/unit/core/journal/impl/timing/RealJournalImplTest.java
   trunk/tests/src/org/jboss/messaging/tests/unit/core/message/impl/MessageTest.java
Log:
JBMESSAGING-1283 - More improvements on AIO

Modified: trunk/native/src/org_jboss_messaging_core_asyncio_impl_AsynchronousFileImpl.h
===================================================================
--- trunk/native/src/org_jboss_messaging_core_asyncio_impl_AsynchronousFileImpl.h	2008-05-12 14:57:22 UTC (rev 4173)
+++ trunk/native/src/org_jboss_messaging_core_asyncio_impl_AsynchronousFileImpl.h	2008-05-12 21:53:04 UTC (rev 4174)
@@ -9,6 +9,7 @@
 #endif
 /* Inaccessible static: log */
 /* Inaccessible static: loaded */
+/* Inaccessible static: totalMaxIO */
 /*
  * Class:     org_jboss_messaging_core_asyncio_impl_AsynchronousFileImpl
  * Method:    init

Modified: trunk/src/etc/jbm-configuration.xml
===================================================================
--- trunk/src/etc/jbm-configuration.xml	2008-05-12 14:57:22 UTC (rev 4173)
+++ trunk/src/etc/jbm-configuration.xml	2008-05-12 21:53:04 UTC (rev 4174)
@@ -22,7 +22,7 @@
       <remoting-host>localhost</remoting-host>
 
       <!--  timeout in seconds -->
-      <remoting-timeout>5</remoting-timeout>
+      <remoting-timeout>5000</remoting-timeout>
       
       <!-- true to disable invm communication when the client and the server are in the same JVM.     -->
       <!-- it is not allowed to disable invm communication when the remoting-transport is set to INVM -->
@@ -67,9 +67,9 @@
       
       <journal-sync>true</journal-sync>
       
-      <journal-file-size>10485760</journal-file-size>
+      <journal-file-size>104857600</journal-file-size>
       
-      <journal-min-files>10</journal-min-files>
+      <journal-min-files>2</journal-min-files>
       
       <journal-task-period>5000</journal-task-period>
       

Modified: trunk/src/main/org/jboss/messaging/core/asyncio/impl/AsynchronousFileImpl.java
===================================================================
--- trunk/src/main/org/jboss/messaging/core/asyncio/impl/AsynchronousFileImpl.java	2008-05-12 14:57:22 UTC (rev 4173)
+++ trunk/src/main/org/jboss/messaging/core/asyncio/impl/AsynchronousFileImpl.java	2008-05-12 21:53:04 UTC (rev 4174)
@@ -9,6 +9,7 @@
 
 import java.nio.ByteBuffer;
 import java.util.concurrent.Semaphore;
+import java.util.concurrent.atomic.AtomicInteger;
 import java.util.concurrent.locks.Lock;
 import java.util.concurrent.locks.ReadWriteLock;
 import java.util.concurrent.locks.ReentrantReadWriteLock;
@@ -17,6 +18,7 @@
 import org.jboss.messaging.core.asyncio.AsynchronousFile;
 import org.jboss.messaging.core.logging.Logger;
 
+
 /**
  * 
  * @author clebert.suconic at jboss.com
@@ -32,6 +34,19 @@
 	private static boolean loaded = false;
 	private int maxIO;
 	
+	private static AtomicInteger totalMaxIO = new AtomicInteger(0);
+	
+	static void addMax(int io)
+	{
+	   totalMaxIO.addAndGet(io);
+	}
+
+	/** For test purposes */
+	public static int getTotalMaxIO()
+	{
+	   return totalMaxIO.get();
+	}
+	
 	Semaphore writeSemaphore;
 	
 	ReadWriteLock lock = new ReentrantReadWriteLock();
@@ -108,6 +123,7 @@
 			opened = true;
 			this.fileName=fileName;
 			handler = init (fileName, maxIO, log);
+			addMax(maxIO);
 			startPoller();
 		}
 		finally
@@ -148,6 +164,7 @@
 		{
 			pollerSemaphore.acquire();
 			closeInternal(handler);
+			addMax(maxIO * -1);
 			opened = false;
 			handler = 0;
 		}

Modified: trunk/src/main/org/jboss/messaging/core/config/impl/ConfigurationImpl.java
===================================================================
--- trunk/src/main/org/jboss/messaging/core/config/impl/ConfigurationImpl.java	2008-05-12 14:57:22 UTC (rev 4173)
+++ trunk/src/main/org/jboss/messaging/core/config/impl/ConfigurationImpl.java	2008-05-12 21:53:04 UTC (rev 4174)
@@ -350,6 +350,11 @@
 	{
 		return journalType;
 	}
+	
+	public void setJournalType(JournalType type)
+	{
+	   this.journalType = type;
+	}
 
 	public boolean isJournalSync()
 	{

Added: trunk/src/main/org/jboss/messaging/core/journal/EncodingSupport.java
===================================================================
--- trunk/src/main/org/jboss/messaging/core/journal/EncodingSupport.java	                        (rev 0)
+++ trunk/src/main/org/jboss/messaging/core/journal/EncodingSupport.java	2008-05-12 21:53:04 UTC (rev 4174)
@@ -0,0 +1,12 @@
+package org.jboss.messaging.core.journal;
+
+import org.jboss.messaging.util.MessagingBuffer;
+
+/** 
+ * This class provides encoding support for the Journal.
+ * */
+public interface EncodingSupport
+{
+   int encodeSize();
+   void encode(MessagingBuffer buffer);
+}

Modified: trunk/src/main/org/jboss/messaging/core/journal/Journal.java
===================================================================
--- trunk/src/main/org/jboss/messaging/core/journal/Journal.java	2008-05-12 14:57:22 UTC (rev 4173)
+++ trunk/src/main/org/jboss/messaging/core/journal/Journal.java	2008-05-12 21:53:04 UTC (rev 4174)
@@ -36,22 +36,23 @@
 {
 	// Non transactional operations
 	
-	// TODO: Implement callbacks
-	void appendAddRecord(long id, byte[] record, IOCallback callback) throws Exception;
+   void appendAddRecord(long id, byte recordType, EncodingSupport record) throws Exception;
+
+	void appendAddRecord(long id, byte recordType, byte[] record) throws Exception;
 	
-	void appendAddRecord(long id, byte[] record) throws Exception;
+	void appendUpdateRecord(long id, byte recordType, byte[] record) throws Exception;
 	
-	void appendUpdateRecord(long id, byte[] record) throws Exception;
-	
 	void appendDeleteRecord(long id) throws Exception;
 	
 	// Transactional operations
 	
 	long getTransactionID();
 	
-	void appendAddRecordTransactional(long txID, long id, byte[] record) throws Exception;
+   void appendAddRecordTransactional(long txID, byte recordType, long id, EncodingSupport record) throws Exception;
+   
+	void appendAddRecordTransactional(long txID, byte recordType, long id, byte[] record) throws Exception;
 	
-	void appendUpdateRecordTransactional(long txID, long id, byte[] record) throws Exception;
+	void appendUpdateRecordTransactional(long txID, byte recordType, long id, byte[] record) throws Exception;
 	
 	void appendDeleteRecordTransactional(long txID, long id) throws Exception;
 	

Modified: trunk/src/main/org/jboss/messaging/core/journal/RecordInfo.java
===================================================================
--- trunk/src/main/org/jboss/messaging/core/journal/RecordInfo.java	2008-05-12 14:57:22 UTC (rev 4173)
+++ trunk/src/main/org/jboss/messaging/core/journal/RecordInfo.java	2008-05-12 21:53:04 UTC (rev 4174)
@@ -10,10 +10,12 @@
  */
 public class RecordInfo
 {
-	public RecordInfo(final long id, final byte[] data, final boolean isUpdate)
+	public RecordInfo(final long id, byte userRecordType, final byte[] data, final boolean isUpdate)
 	{
 		this.id = id;
 		
+		this.userRecordType = userRecordType;
+		
 		this.data = data;
 		
 		this.isUpdate = isUpdate;
@@ -21,10 +23,17 @@
 	
 	public final long id;
 	
+	public final byte userRecordType;
+	
 	public final byte[] data;
 	
 	public boolean isUpdate;
 	
+	public byte getUserRecordType()
+	{
+	   return userRecordType;
+	}
+	
 	public int hashCode()
 	{
 		return (int)((id >>> 32) ^ id);

Modified: trunk/src/main/org/jboss/messaging/core/journal/SequentialFile.java
===================================================================
--- trunk/src/main/org/jboss/messaging/core/journal/SequentialFile.java	2008-05-12 14:57:22 UTC (rev 4173)
+++ trunk/src/main/org/jboss/messaging/core/journal/SequentialFile.java	2008-05-12 21:53:04 UTC (rev 4174)
@@ -1,24 +1,24 @@
 /*
-  * 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.
-  */
+ * 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;
 
 import java.nio.ByteBuffer;
@@ -28,38 +28,36 @@
  * A SequentialFile
  * 
  * @author <a href="mailto:tim.fox at jboss.com">Tim Fox</a>Journal
- *
+ * 
  */
 public interface SequentialFile
 {
-	/*
-	 * Creates the file if it doesn't already exist, then opens it
-	 */
-	void open() throws Exception;
-	
-	int getAlignment() throws Exception;
-	
-	int calculateBlockStart(int position) throws Exception;
-	
-	String getFileName();
-	
-	void fill(int position, int size, byte fillCharacter) throws Exception;
-	
-	void delete() throws Exception;
-	
-	int write(ByteBuffer bytes, boolean sync, IOCallback callback) throws Exception;
-	
-	int write(ByteBuffer bytes, boolean sync) throws Exception;
-	
-	int read(ByteBuffer bytes, IOCallback callback) throws Exception;
-	
-	int read(ByteBuffer bytes) throws Exception;
-	
-	void position(int pos) throws Exception;
-	
-	void close() throws Exception;
-	
-	ByteBuffer newBuffer(int size);
-	
-	ByteBuffer wrapBuffer(byte bytes[]);
+   /*
+    * Creates the file if it doesn't already exist, then opens it
+    */
+   void open() throws Exception;
+   
+   int getAlignment() throws Exception;
+   
+   int calculateBlockStart(int position) throws Exception;
+   
+   String getFileName();
+   
+   void fill(int position, int size, byte fillCharacter) throws Exception;
+   
+   void delete() throws Exception;
+   
+   int write(ByteBuffer bytes, boolean sync, IOCallback callback)
+         throws Exception;
+   
+   int write(ByteBuffer bytes, boolean sync) throws Exception;
+   
+   int read(ByteBuffer bytes, IOCallback callback) throws Exception;
+   
+   int read(ByteBuffer bytes) throws Exception;
+   
+   void position(int pos) throws Exception;
+   
+   void close() throws Exception;
+   
 }

Modified: trunk/src/main/org/jboss/messaging/core/journal/SequentialFileFactory.java
===================================================================
--- trunk/src/main/org/jboss/messaging/core/journal/SequentialFileFactory.java	2008-05-12 14:57:22 UTC (rev 4173)
+++ trunk/src/main/org/jboss/messaging/core/journal/SequentialFileFactory.java	2008-05-12 21:53:04 UTC (rev 4174)
@@ -21,6 +21,7 @@
   */
 package org.jboss.messaging.core.journal;
 
+import java.nio.ByteBuffer;
 import java.util.List;
 
 /**
@@ -32,7 +33,15 @@
  */
 public interface SequentialFileFactory
 {
-	SequentialFile createSequentialFile(String fileName, boolean sync) throws Exception;
+	SequentialFile createSequentialFile(String fileName, boolean sync, int maxIO) throws Exception;
 	
 	List<String> listFiles(String extension) throws Exception;
+	
+	boolean supportsCallbacks();
+	
+   ByteBuffer newBuffer(int size);
+
+   // Avoid using this method in production as it creates an unecessary copy 
+   ByteBuffer wrapBuffer(byte[] bytes);
+
 }

Modified: trunk/src/main/org/jboss/messaging/core/journal/impl/AIOSequentialFile.java
===================================================================
--- trunk/src/main/org/jboss/messaging/core/journal/impl/AIOSequentialFile.java	2008-05-12 14:57:22 UTC (rev 4173)
+++ trunk/src/main/org/jboss/messaging/core/journal/impl/AIOSequentialFile.java	2008-05-12 21:53:04 UTC (rev 4174)
@@ -10,28 +10,40 @@
 import java.io.File;
 import java.nio.ByteBuffer;
 import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
 import java.util.concurrent.atomic.AtomicLong;
+import java.util.concurrent.TimeUnit;
 
 import org.jboss.messaging.core.asyncio.AsynchronousFile;
 import org.jboss.messaging.core.asyncio.impl.AsynchronousFileImpl;
 import org.jboss.messaging.core.exception.MessagingException;
 import org.jboss.messaging.core.journal.IOCallback;
 import org.jboss.messaging.core.journal.SequentialFile;
+import org.jboss.messaging.core.logging.Logger;
 
 public class AIOSequentialFile implements SequentialFile
 {
-	
-	String journalDir;
+   private static final Logger log = Logger.getLogger(AIOSequentialFile.class);
+
+   String journalDir;
 	String fileName;
+	boolean opened = false;
+	int maxIO;
 	
 	AsynchronousFile aioFile;
 	
 	AtomicLong position = new AtomicLong(0);
+
+	// A context switch on AIO would make it to synchronize the disk before switching to the new thread, what would cuase
+	// serious performance problems. Because of that we make all the writes on AIO using a single thread.
+	ExecutorService executor;
 	
-	public AIOSequentialFile(String journalDir, String fileName) throws Exception
+	public AIOSequentialFile(String journalDir, String fileName, int maxIO) throws Exception
 	{
 		this.journalDir = journalDir;
 		this.fileName = fileName;
+		this.maxIO = maxIO;
 	}
 	
 	public int getAlignment() throws Exception
@@ -51,9 +63,12 @@
 	
 	
 	
-	public void close() throws Exception
+	public synchronized void close() throws Exception
 	{
 		checkOpened();
+		opened = false;
+      executor.shutdown();
+      executor.awaitTermination(120, TimeUnit.SECONDS);
 		aioFile.close();
 		aioFile = null;
 		
@@ -107,7 +122,6 @@
 		{
 			position = ((position / aioFile.getBlockSize()) + 1) * aioFile.getBlockSize();
 		}
-		//System.out.println("filling " + blocks + " blocks with blockSize=" + blockSize + " on file=" + this.getFileName());
 		aioFile.fill((long)position, blocks, blockSize, (byte)fillCharacter);
 		
 	}
@@ -117,10 +131,12 @@
 		return fileName;
 	}
 	
-	public void open() throws Exception
+	public synchronized void open() throws Exception
 	{
+	   opened = true;
+	   executor = Executors.newSingleThreadExecutor();
 		aioFile = new AsynchronousFileImpl();
-		aioFile.open(journalDir + "/" + fileName, 1000);
+		aioFile.open(journalDir + "/" + fileName, maxIO);
 		position.set(0);
 		
 	}
@@ -157,38 +173,69 @@
 		return bytesRead;
 	}
 	
-	public int write(ByteBuffer bytes, boolean sync, IOCallback callback)
+	
+	public int write(final ByteBuffer bytes, boolean sync, final IOCallback callback)
 	throws Exception
 	{
-		int bytesToWrite = bytes.limit();
-		long positionToWrite = position.getAndAdd(bytesToWrite);
+		final int bytesToWrite = bytes.limit();
+		final long positionToWrite = position.getAndAdd(bytesToWrite);
 		
-		aioFile.write(positionToWrite, bytesToWrite, bytes, callback);
+		execWrite(bytes, callback, bytesToWrite, positionToWrite);
+		
 		return bytesToWrite;
 	}
+
+   private void execWrite(final ByteBuffer bytes, final IOCallback callback,
+         final int bytesToWrite, final long positionToWrite)
+   {
+      executor.execute(new Runnable()
+		{
+		   public void run()
+		   {
+		      try
+		      {
+		         aioFile.write(positionToWrite, bytesToWrite, bytes, callback);
+		      }
+		      catch (Exception e)
+		      {
+		         log.warn (e.getMessage(), e);
+		         if (callback != null)
+		         {
+		            callback.onError(-1, e.getMessage());
+		         }
+		      }
+		   }
+		});
+      
+   }
 	
 	public int write(ByteBuffer bytes, boolean sync) throws Exception
 	{
-		WaitCompletion waitCompletion = new WaitCompletion();
-		int bytesWritten = write (bytes, sync, waitCompletion);
-		
-		waitCompletion.waitLatch();
-		
-		if (waitCompletion.errorMessage != null)
-		{
-			throw new MessagingException(waitCompletion.errorCode, waitCompletion.errorMessage);
-		}
-		
-		return bytesWritten;
+		return write (bytes, sync, DummyCallback.instance);
 	}
 	
 	private void checkOpened() throws Exception
 	{
-		if (aioFile == null)
+		if (aioFile == null || !opened)
 		{
 			throw new IllegalStateException ("File not opened");
 		}
 	}
+
+	static class DummyCallback implements IOCallback
+	{
+	   
+	   static DummyCallback instance = new DummyCallback();
+
+      public void done()
+      {
+      }
+
+      public void onError(int errorCode, String errorMessage)
+      {
+      }
+	   
+	}
 	
 	class WaitCompletion implements IOCallback
 	{
@@ -205,7 +252,6 @@
 		
 		public void onError(int errorCode, String errorMessage)
 		{
-			System.out.println("OK Error!");
 			this.errorCode = errorCode;
 			this.errorMessage = errorMessage;
 			
@@ -220,20 +266,4 @@
 		
 	}
 	
-	public ByteBuffer newBuffer(int size)
-	{
-		if (size % aioFile.getBlockSize() != 0)
-		{
-			size = ((size / aioFile.getBlockSize()) + 1) * aioFile.getBlockSize();
-		}
-		return ByteBuffer.allocateDirect(size);
-	}
-	
-	public ByteBuffer wrapBuffer(byte[] bytes)
-	{
-		ByteBuffer newbuffer = newBuffer(bytes.length);
-		newbuffer.put(bytes);
-		return newbuffer;
-	};
-	
 }

Modified: trunk/src/main/org/jboss/messaging/core/journal/impl/AIOSequentialFileFactory.java
===================================================================
--- trunk/src/main/org/jboss/messaging/core/journal/impl/AIOSequentialFileFactory.java	2008-05-12 14:57:22 UTC (rev 4173)
+++ trunk/src/main/org/jboss/messaging/core/journal/impl/AIOSequentialFileFactory.java	2008-05-12 21:53:04 UTC (rev 4174)
@@ -7,6 +7,8 @@
 
 package org.jboss.messaging.core.journal.impl;
 
+import java.nio.ByteBuffer;
+
 import org.jboss.messaging.core.journal.SequentialFile;
 import org.jboss.messaging.core.asyncio.impl.AsynchronousFileImpl;
 
@@ -18,14 +20,36 @@
 		super(journalDir);
 	}
 	
-	public SequentialFile createSequentialFile(String fileName, boolean sync) throws Exception
+	public SequentialFile createSequentialFile(String fileName, boolean sync, int maxIO) throws Exception
 	{
-		return new AIOSequentialFile(journalDir, fileName);
+		return new AIOSequentialFile(journalDir, fileName, maxIO);
 	}
 	
+   public boolean supportsCallbacks()
+   {
+      return true;
+   }
+   
 	public static boolean isSupported()
 	{
 		return AsynchronousFileImpl.isLoaded();
 	}
 	
+   public ByteBuffer newBuffer(int size)
+   {
+      if (size % 512 != 0)
+      {
+         size = (size / 512 + 1) * 512;
+      }
+      return ByteBuffer.allocateDirect(size);
+   }
+   
+   // For tests only
+   public ByteBuffer wrapBuffer(byte[] bytes)
+   {
+      ByteBuffer newbuffer = newBuffer(bytes.length);
+      newbuffer.put(bytes);
+      return newbuffer;
+   };
+   
 }

Modified: trunk/src/main/org/jboss/messaging/core/journal/impl/JournalImpl.java
===================================================================
--- trunk/src/main/org/jboss/messaging/core/journal/impl/JournalImpl.java	2008-05-12 14:57:22 UTC (rev 4173)
+++ trunk/src/main/org/jboss/messaging/core/journal/impl/JournalImpl.java	2008-05-12 21:53:04 UTC (rev 4174)
@@ -30,15 +30,20 @@
 import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.NoSuchElementException;
 import java.util.Queue;
 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.ConcurrentMap;
+import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.Semaphore;
+import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicLong;
 
+import org.jboss.messaging.core.journal.EncodingSupport;
 import org.jboss.messaging.core.journal.IOCallback;
 import org.jboss.messaging.core.journal.PreparedTransactionInfo;
 import org.jboss.messaging.core.journal.RecordInfo;
@@ -46,7 +51,9 @@
 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.ByteBufferWrapper;
 import org.jboss.messaging.util.Pair;
+import org.jboss.messaging.util.VariableLatch;
 
 /**
  * 
@@ -83,11 +90,11 @@
 	
 	public static final int SIZE_HEADER = 8;
 	
-	public static final int SIZE_ADD_RECORD = SIZE_BYTE + SIZE_LONG + SIZE_INT + SIZE_BYTE;
+	public static final int SIZE_ADD_RECORD = SIZE_BYTE + SIZE_LONG + SIZE_BYTE + SIZE_INT + SIZE_BYTE; // + record.length
 	
 	public static final byte ADD_RECORD = 11;
 	
-	public static final byte SIZE_UPDATE_RECORD = SIZE_BYTE + SIZE_LONG + SIZE_INT + SIZE_BYTE;
+	public static final byte SIZE_UPDATE_RECORD = SIZE_BYTE + SIZE_LONG + SIZE_BYTE + SIZE_INT + SIZE_BYTE; // + record.length;
 	
 	public static final byte UPDATE_RECORD = 12;
 	
@@ -97,11 +104,10 @@
 	
 	public static final byte ADD_RECORD_TX = 14;
 	
-	public static final int SIZE_ADD_RECORD_TX = SIZE_BYTE + SIZE_LONG + SIZE_LONG + SIZE_INT + SIZE_BYTE; // Add the size of Bytes on this
+	public static final int SIZE_ADD_RECORD_TX = SIZE_BYTE + SIZE_LONG + SIZE_BYTE + SIZE_LONG + SIZE_INT + SIZE_BYTE; // Add the size of Bytes on this
 	
+	public static final int  SIZE_UPDATE_RECORD_TX = SIZE_BYTE + SIZE_LONG + SIZE_BYTE + SIZE_LONG + SIZE_INT + SIZE_BYTE;  // Add the size of Bytes on this
 	
-	public static final int  SIZE_UPDATE_RECORD_TX = SIZE_BYTE + SIZE_LONG + SIZE_LONG + SIZE_INT + SIZE_BYTE;  // Add the size of Bytes on this
-	
 	public static final byte UPDATE_RECORD_TX = 15;
 	
 	public static final int  SIZE_DELETE_RECORD_TX = SIZE_BYTE + SIZE_LONG + SIZE_LONG + SIZE_BYTE;
@@ -126,6 +132,9 @@
 	public static final byte FILL_CHARACTER = 74; // Letter 'J' 
 	
 	
+	// used for Asynchronous IO only (ignored on NIO).
+	private final int maxAIO;
+	
 	private final int fileSize;
 	
 	private final int minFiles;
@@ -148,8 +157,13 @@
 	private Map<Long, PosFiles> posFilesMap = new ConcurrentHashMap<Long, PosFiles>();
 	
 	private Map<Long, TransactionNegPos> transactionInfos = new ConcurrentHashMap<Long, TransactionNegPos>();
+
+	private final ConcurrentMap<Long, TransactionCallback> transactionCallbacks = new ConcurrentHashMap<Long, TransactionCallback>();
 	
+	private boolean shouldUseCallback = false;
+   
 	
+	
 	/*
 	 * We use a semaphore rather than synchronized since it performs better when contended
 	 */
@@ -173,7 +187,7 @@
 	
 	public JournalImpl(final int fileSize, final int minFiles,
 			final boolean sync, final SequentialFileFactory fileFactory, final long taskPeriod,
-			final String filePrefix, final String fileExtension)
+			final String filePrefix, final String fileExtension, final int maxAIO)
 	{
 		if (fileSize < MIN_FILE_SIZE)
 		{
@@ -199,6 +213,10 @@
 		{
 			throw new NullPointerException("fileExtension is null");
 		}
+		if (maxAIO <= 0)
+		{
+		   throw new IllegalStateException("maxAIO should aways be a positive number");
+		}
 		
 		this.fileSize = fileSize;
 		
@@ -213,68 +231,93 @@
 		this.filePrefix = filePrefix;
 		
 		this.fileExtension = fileExtension;
+		
+		this.maxAIO = maxAIO;
+		
+		shouldUseCallback = fileFactory.supportsCallbacks() && sync;
 	}
 	
 	// Journal implementation ----------------------------------------------------------------
-	
-	/*public ByteBuffer allocateBuffer(final int size) throws Exception
+
+	public void appendAddRecord(long id, byte recordType, EncodingSupport record) throws Exception
    {
-      return ByteBuffer.allocateDirect(size);
-   }*/
+      if (state != STATE_LOADED)
+      {
+         throw new IllegalStateException("Journal must be loaded first");
+      }
+      
+      int recordLength = record.encodeSize();
+      
+      int size = SIZE_ADD_RECORD + recordLength;
+      
+      ByteBufferWrapper bb = new ByteBufferWrapper(fileFactory.newBuffer(size));
+      
+      bb.putByte(ADD_RECORD);     
+      bb.putLong(id);
+      bb.putByte(recordType);
+      bb.putInt(recordLength);
+      record.encode(bb);
+      bb.putByte(DONE);        
+      bb.rewind();
+      
+      JournalFile usedFile;
+      
+      if (shouldUseCallback)
+      {
+         SimpleCallback callback = new SimpleCallback();
+         usedFile = appendRecord(bb.getBuffer(), true, callback);
+         callback.waitCompletion();
+      }
+      else
+      {
+         usedFile = appendRecord(bb.getBuffer(), true);
+      }
+      
+      posFilesMap.put(id, new PosFiles(usedFile));
+   }
 	
-	public void appendAddRecord(long id, byte[] record, IOCallback callback) throws Exception
+	public void appendAddRecord(long id, byte recordType, byte[] record) throws Exception
 	{
 		if (state != STATE_LOADED)
 		{
 			throw new IllegalStateException("Journal must be loaded first");
 		}
 		
-		int size = SIZE_BYTE + SIZE_LONG + SIZE_INT + record.length + SIZE_BYTE;
+		int size = SIZE_ADD_RECORD + record.length;
 		
-		ByteBuffer bb = currentFile.getFile().newBuffer(size);
+		ByteBuffer bb = fileFactory.newBuffer(size);
 		
 		bb.put(ADD_RECORD);		
-		bb.putLong(id);		
+		bb.putLong(id);
+		bb.put(recordType);
 		bb.putInt(record.length);		
 		bb.put(record);		
 		bb.put(DONE);			
 		bb.rewind();
 		
-		appendRecord(bb, true, callback);
+		JournalFile usedFile;
+      if (shouldUseCallback)
+      {
+         
+         SimpleCallback callback = new SimpleCallback();
+         usedFile = appendRecord(bb, true, callback);
+         callback.waitCompletion();
+      }
+      else
+      {
+         usedFile = appendRecord(bb, true);
+      }
 		
-		posFilesMap.put(id, new PosFiles(currentFile));
+		posFilesMap.put(id, new PosFiles(usedFile));
 	}
 	
-	public void appendAddRecord(final long id, final byte[] record) throws Exception
+	public void appendUpdateRecord(final long id, final byte recordType, final byte[] record) throws Exception
 	{
 		if (state != STATE_LOADED)
 		{
 			throw new IllegalStateException("Journal must be loaded first");
 		}
 		
-		int size = SIZE_BYTE + SIZE_LONG + SIZE_INT + record.length + SIZE_BYTE;
-		
-		ByteBuffer bb = currentFile.getFile().newBuffer(size); 
-		
-		bb.put(ADD_RECORD);     
-		bb.putLong(id);      
-		bb.putInt(record.length);     
-		bb.put(record);      
-		bb.put(DONE);
-		bb.rewind();
-		
-		appendRecord(bb, true);
-		
-		posFilesMap.put(id, new PosFiles(currentFile));
-	}
-	
-	public void appendUpdateRecord(final long id, final byte[] record) throws Exception
-	{
-		if (state != STATE_LOADED)
-		{
-			throw new IllegalStateException("Journal must be loaded first");
-		}
-		
 		PosFiles posFiles = posFilesMap.get(id);
 		
 		if (posFiles == null)
@@ -284,18 +327,30 @@
 		
 		int size = SIZE_UPDATE_RECORD + record.length;
 		
-		ByteBuffer bb = currentFile.getFile().newBuffer(size); 
+		ByteBuffer bb = fileFactory.newBuffer(size); 
 		
 		bb.put(UPDATE_RECORD);     
 		bb.putLong(id);      
+		bb.put(recordType);
 		bb.putInt(record.length);     
 		bb.put(record);      
 		bb.put(DONE);     
 		bb.rewind();
 		
-		appendRecord(bb, true);    
+      JournalFile usedFile;
+      if (shouldUseCallback)
+      {
+         SimpleCallback callback = new SimpleCallback();
+         usedFile = appendRecord(bb, true, callback);
+         callback.waitCompletion();
+      }
+      else
+      {
+         usedFile = appendRecord(bb, true);
+      }
+      
 		
-		posFiles.addUpdateFile(currentFile);
+		posFiles.addUpdateFile(usedFile);
 	}
 	
 	public void appendDeleteRecord(long id) throws Exception
@@ -316,14 +371,24 @@
 		
 		int size = SIZE_DELETE_RECORD;
 		
-		ByteBuffer bb = currentFile.getFile().newBuffer(size); 
+		ByteBuffer bb = fileFactory.newBuffer(size); 
 		
 		bb.put(DELETE_RECORD);     
 		bb.putLong(id);      
 		bb.put(DONE);     
 		bb.rewind();
 		
-		appendRecord(bb, true);                   
+      if (shouldUseCallback)
+      {
+         SimpleCallback callback = new SimpleCallback();
+         appendRecord(bb, true, callback);
+         callback.waitCompletion();
+      }
+      else
+      {
+         appendRecord(bb, true);
+      }
+      
 	}     
 	
 	public long getTransactionID()
@@ -331,59 +396,109 @@
 		return transactionIDSequence.getAndIncrement();
 	}
 	
-	public void appendAddRecordTransactional(final long txID, final long id,
-			final byte[] record) throws Exception
-			{
-		if (state != STATE_LOADED)
-		{
-			throw new IllegalStateException("Journal must be loaded first");
-		}
-		
-		int size = SIZE_ADD_RECORD_TX + record.length;
-		
-		ByteBuffer bb = currentFile.getFile().newBuffer(size); 
-		
-		bb.put(ADD_RECORD_TX);
-		bb.putLong(txID);
-		bb.putLong(id);
-		bb.putInt(record.length);
-		bb.put(record);
-		bb.put(DONE);     
-		bb.rewind();
-		
-		appendRecord(bb, false);
-		
-		TransactionNegPos tx = getTransactionInfo(txID);
-		
-		tx.addPos(currentFile, id);
-			}
+   public void appendAddRecordTransactional(final long txID, final byte recordType, final long id,
+         final EncodingSupport record) throws Exception
+   {
+      if (state != STATE_LOADED)
+      {
+         throw new IllegalStateException("Journal must be loaded first");
+      }
+      
+      
+      int recordLength = record.encodeSize();
+      
+      int size = SIZE_ADD_RECORD_TX + recordLength;
+      
+      ByteBufferWrapper bb = new ByteBufferWrapper(fileFactory.newBuffer(size)); 
+      
+      bb.putByte(ADD_RECORD_TX);
+      bb.putLong(txID);
+      bb.putByte(recordType);
+      bb.putLong(id);
+      bb.putInt(recordLength);
+      record.encode(bb);
+      bb.putByte(DONE);     
+      bb.rewind();
+      
+      JournalFile usedFile;
+
+      if (shouldUseCallback)
+      {
+         TransactionCallback callback = getTransactionCallback(txID);
+         callback.countUp();
+         usedFile = appendRecord(bb.getBuffer(), false, callback);
+      }
+      else
+      {
+         usedFile = appendRecord(bb.getBuffer(), false);
+      }
+      
+      TransactionNegPos tx = getTransactionInfo(txID);
+      
+      tx.addPos(usedFile, id);
+   }
+   
+	public void appendAddRecordTransactional(final long txID, final byte recordType, final long id,
+	      final byte[] record) throws Exception
+   {
+	   if (state != STATE_LOADED)
+	   {
+	      throw new IllegalStateException("Journal must be loaded first");
+	   }
+	   
+      TransactionCallback callback = getTransactionCallback(txID);
+      callback.countUp();
+      
+	   int size = SIZE_ADD_RECORD_TX + record.length;
+	   
+	   ByteBuffer bb = fileFactory.newBuffer(size); 
+	   
+	   bb.put(ADD_RECORD_TX);
+	   bb.putLong(txID);
+      bb.put(recordType);
+	   bb.putLong(id);
+	   bb.putInt(record.length);
+	   bb.put(record);
+	   bb.put(DONE);     
+	   bb.rewind();
+	   
+	   JournalFile usedFile = appendRecord(bb, false, callback);
+	   
+	   TransactionNegPos tx = getTransactionInfo(txID);
+	   
+	   tx.addPos(usedFile, id);
+   }
 	
-	public void appendUpdateRecordTransactional(final long txID, final long id,
+	public void appendUpdateRecordTransactional(final long txID, byte recordType, final long id,
 			final byte[] record) throws Exception
-			{
+	{
 		if (state != STATE_LOADED)
 		{
 			throw new IllegalStateException("Journal must be loaded first");
 		}
 		
+      TransactionCallback callback = getTransactionCallback(txID);
+      callback.countUp();
+      
 		int size = SIZE_UPDATE_RECORD_TX + record.length; 
 		
-		ByteBuffer bb = currentFile.getFile().newBuffer(size); 
+		ByteBuffer bb = fileFactory.newBuffer(size); 
 		
 		bb.put(UPDATE_RECORD_TX);     
-		bb.putLong(txID);    
+		bb.putLong(txID);
+		bb.put(recordType);
 		bb.putLong(id);      
 		bb.putInt(record.length);     
 		bb.put(record);
 		bb.put(DONE);     
 		bb.rewind();
 		
-		appendRecord(bb, false);
+		JournalFile usedFile = appendRecord(bb, false, callback);
 		
 		TransactionNegPos tx = getTransactionInfo(txID);
 		
-		tx.addPos(currentFile, id);
-			}
+		tx.addPos(usedFile, id);
+	}
 	
 	public void appendDeleteRecordTransactional(final long txID, final long id) throws Exception
 	{
@@ -392,9 +507,12 @@
 			throw new IllegalStateException("Journal must be loaded first");
 		}
 		
+      TransactionCallback callback = getTransactionCallback(txID);
+      callback.countUp();
+      
 		int size = SIZE_DELETE_RECORD_TX;
 		
-		ByteBuffer bb = currentFile.getFile().newBuffer(size); 
+		ByteBuffer bb = fileFactory.newBuffer(size); 
 		
 		bb.put(DELETE_RECORD_TX);     
 		bb.putLong(txID);    
@@ -402,11 +520,11 @@
 		bb.put(DONE);        
 		bb.rewind();
 		
-		appendRecord(bb, false);      
+		JournalFile usedFile = appendRecord(bb, false, callback);      
 		
 		TransactionNegPos tx = getTransactionInfo(txID);
 		
-		tx.addNeg(currentFile, id);      
+		tx.addNeg(usedFile, id);      
 	}  
 	
 	public void appendPrepareRecord(final long txID) throws Exception
@@ -416,6 +534,9 @@
 			throw new IllegalStateException("Journal must be loaded first");
 		}
 		
+      TransactionCallback callback = getTransactionCallback(txID);
+      callback.countUp();
+      
 		TransactionNegPos tx = transactionInfos.get(txID);
 		
 		if (tx == null)
@@ -425,16 +546,16 @@
 		
 		int size = SIZE_PREPARE_RECORD;
 		
-		ByteBuffer bb = currentFile.getFile().newBuffer(size); 
+		ByteBuffer bb = fileFactory.newBuffer(size); 
 		
 		bb.put(PREPARE_RECORD);    
 		bb.putLong(txID);
 		bb.put(DONE);           
 		bb.rewind();
 		
-		appendRecord(bb, true);    
+		JournalFile usedFile = appendRecord(bb, true, callback);    
 		
-		tx.prepare(currentFile);
+		tx.prepare(usedFile);
 	}
 	
 	public void appendCommitRecord(final long txID) throws Exception
@@ -444,6 +565,9 @@
 			throw new IllegalStateException("Journal must be loaded first");
 		}
 		
+      TransactionCallback callback = getTransactionCallback(txID);
+      callback.countUp();
+      
 		TransactionNegPos tx = transactionInfos.remove(txID);
 		
 		if (tx == null)
@@ -453,16 +577,20 @@
 		
 		int size = SIZE_COMMIT_RECORD;
 		
-		ByteBuffer bb = currentFile.getFile().newBuffer(size); 
+		ByteBuffer bb = fileFactory.newBuffer(size); 
 		
 		bb.put(COMMIT_RECORD);     
 		bb.putLong(txID);    
 		bb.put(DONE);           
 		bb.rewind();
 		
-		appendRecord(bb, true); 
+		JournalFile usedFile = appendRecord(bb, true, callback); 
 		
-		tx.commit(currentFile);          
+		callback.waitCompletion();
+		transactionCallbacks.remove(txID);
+		
+		tx.commit(usedFile);
+		
 	}
 	
 	public void appendRollbackRecord(final long txID) throws Exception
@@ -481,549 +609,563 @@
 		
 		int size = SIZE_ROLLBACK_RECORD;
 		
-		ByteBuffer bb = currentFile.getFile().newBuffer(size); 
+		ByteBuffer bb = fileFactory.newBuffer(size); 
 		
 		bb.put(ROLLBACK_RECORD);      
 		bb.putLong(txID);
 		bb.put(DONE);        
 		bb.rewind();
 		
-		appendRecord(bb, true);       
+		SimpleCallback callback = new SimpleCallback();
 		
-		tx.rollback(currentFile);
+		JournalFile usedFile = appendRecord(bb, true, callback);       
+		
+		callback.waitCompletion();
+		
+		tx.rollback(usedFile);
 	}
 	
-	public synchronized long load(final List<RecordInfo> committedRecords,
-			final List<PreparedTransactionInfo> preparedTransactions) throws Exception
-	{
-		if (state != STATE_STARTED)
-		{
-			throw new IllegalStateException("Journal must be in started state");
-		}
-		
-		Set<Long> recordsToDelete = new HashSet<Long>();
-		
-		Map<Long, TransactionHolder> transactions = new LinkedHashMap<Long, TransactionHolder>();
-		
-		List<RecordInfo> records = new ArrayList<RecordInfo>();
-		
-		List<String> fileNames = fileFactory.listFiles(fileExtension);
-		
-		List<JournalFile> orderedFiles = new ArrayList<JournalFile>(fileNames.size());
-		
-		for (String fileName: fileNames)
-		{
-			SequentialFile file = fileFactory.createSequentialFile(fileName, sync);
-			
-			file.open();
-			
-			ByteBuffer bb = file.newBuffer(SIZE_LONG);
-			
-			file.read(bb);
-			
-			long orderingID = bb.getLong();
-			
-			orderedFiles.add(new JournalFileImpl(file, orderingID));
-			
-			file.close();
-		}
-		
-		//Now order them by ordering id - we can't use the file name for ordering since we can re-use dataFiles
-		
-		class JournalFileComparator implements Comparator<JournalFile>
-		{
-			public int compare(JournalFile f1, JournalFile f2)
-			{
-				long id1 = f1.getOrderingID();
-				long id2 = f2.getOrderingID();
-				
-				return (id1 < id2 ? -1 : (id1 == id2 ? 0 : 1));
-			}
-		}
-		
-		Collections.sort(orderedFiles, new JournalFileComparator());
-		
-		int lastDataPos = -1;
-		
-		long maxTransactionID = -1;
-		
-		long maxMessageID = -1;
-		
-		for (JournalFile file: orderedFiles)
-		{  
-			file.getFile().open();
-				
-			ByteBuffer bb = file.getFile().newBuffer(fileSize);
-			
-			int bytesRead = file.getFile().read(bb);
-			
-			if (bytesRead != fileSize)
-			{
-				//deal with this better
-				
-				throw new IllegalStateException("File is wrong size " + bytesRead +
-						" expected " + fileSize + " : " + file.getFile().getFileName());
-			}
-			
-			//First long is the ordering timestamp, we just jump its position
-			bb.position(file.getFile().calculateBlockStart(SIZE_LONG));
-			
-			boolean hasData = false;
-			
-			while (bb.hasRemaining())
-			{
-				int pos = bb.position();
-				
-				byte recordType = bb.get();
-								
-				switch(recordType)
-				{
-					case ADD_RECORD:
-					{                          
-						long id = bb.getLong();  
-						
-						maxMessageID = Math.max(maxMessageID, id);
-						
-						int size = bb.getInt();                
-						byte[] record = new byte[size];                 
-						bb.get(record);
-						byte end = bb.get();
-						
-						if (end != DONE)
-						{
-							repairFrom(pos, file);
-						}
-						else
-						{                                                           
-							records.add(new RecordInfo(id, record, false));
-							hasData = true;                  
-							
-							posFilesMap.put(id, new PosFiles(file));
-						}
-						
-						break;
-					}                             
-					case UPDATE_RECORD:                 
-					{
-						long id = bb.getLong();    
+   public synchronized long load(final List<RecordInfo> committedRecords,
+         final List<PreparedTransactionInfo> preparedTransactions) throws Exception
+   {
+      if (state != STATE_STARTED)
+      {
+         throw new IllegalStateException("Journal must be in started state");
+      }
+      
+      Set<Long> recordsToDelete = new HashSet<Long>();
+      
+      Map<Long, TransactionHolder> transactions = new LinkedHashMap<Long, TransactionHolder>();
+      
+      List<RecordInfo> records = new ArrayList<RecordInfo>();
+      
+      List<String> fileNames = fileFactory.listFiles(fileExtension);
+      
+      List<JournalFile> orderedFiles = new ArrayList<JournalFile>(fileNames.size());
+      
+      for (String fileName: fileNames)
+      {
+         SequentialFile file = fileFactory.createSequentialFile(fileName, sync, maxAIO);
+         
+         file.open();
+         
+         ByteBuffer bb = fileFactory.newBuffer(SIZE_LONG);
+         
+         file.read(bb);
+         
+         long orderingID = bb.getLong();
+         
+         orderedFiles.add(new JournalFileImpl(file, orderingID));
+         
+         file.close();
+      }
+      
+      //Now order them by ordering id - we can't use the file name for ordering since we can re-use dataFiles
+      
+      class JournalFileComparator implements Comparator<JournalFile>
+      {
+         public int compare(JournalFile f1, JournalFile f2)
+         {
+            long id1 = f1.getOrderingID();
+            long id2 = f2.getOrderingID();
+            
+            return (id1 < id2 ? -1 : (id1 == id2 ? 0 : 1));
+         }
+      }
+      
+      Collections.sort(orderedFiles, new JournalFileComparator());
+      
+      int lastDataPos = -1;
+      
+      long maxTransactionID = -1;
+      
+      long maxMessageID = -1;
+      
+      for (JournalFile file: orderedFiles)
+      {  
+         file.getFile().open();
+            
+         ByteBuffer bb = fileFactory.newBuffer(fileSize);
+         
+         int bytesRead = file.getFile().read(bb);
+         
+         if (bytesRead != fileSize)
+         {
+            //deal with this better
+            
+            throw new IllegalStateException("File is wrong size " + bytesRead +
+                  " expected " + fileSize + " : " + file.getFile().getFileName());
+         }
+         
+         //First long is the ordering timestamp, we just jump its position
+         bb.position(file.getFile().calculateBlockStart(SIZE_LONG));
+         
+         boolean hasData = false;
+         
+         while (bb.hasRemaining())
+         {
+            int pos = bb.position();
+            
+            byte recordType = bb.get();
+                        
+            switch(recordType)
+            {
+               case ADD_RECORD:
+               {                          
+                  long id = bb.getLong();  
+                  
+                  maxMessageID = Math.max(maxMessageID, id);
+                  
+                  byte userRecordType = bb.get();
+                  
+                  int size = bb.getInt();                
+                  byte[] record = new byte[size];                 
+                  bb.get(record);
+                  byte end = bb.get();
+                  
+                  if (end != DONE)
+                  {
+                     repairFrom(pos, file);
+                  }
+                  else
+                  {                                                           
+                     records.add(new RecordInfo(id, userRecordType, record, false));
+                     hasData = true;                  
+                     
+                     posFilesMap.put(id, new PosFiles(file));
+                  }
+                  
+                  break;
+               }                             
+               case UPDATE_RECORD:                 
+               {
+                  long id = bb.getLong();    
 
-						maxMessageID = Math.max(maxMessageID, id);
-						
-						int size = bb.getInt();                
-						byte[] record = new byte[size];                 
-						bb.get(record);                  
-						byte end = bb.get();
-						
-						if (end != DONE)
-						{
-							repairFrom(pos, file);
-						}
-						else
-						{              
-							records.add(new RecordInfo(id, record, 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;
-					}              
-					case DELETE_RECORD:                 
-					{
-						long id = bb.getLong(); 
-						
-						maxMessageID = Math.max(maxMessageID, id);
-						
-						byte end = bb.get();
-						
-						if (end != DONE)
-						{
-							repairFrom(pos, file);
-						}
-						else
-						{                 
-							recordsToDelete.add(id);                     
-							hasData = true;
-							
-							PosFiles posFiles = posFilesMap.remove(id);
-							
-							if (posFiles != null)
-							{
-								posFiles.addDelete(file);
-							}                    
-						}
-						
-						break;
-					}              
-					case ADD_RECORD_TX:
-					{              
-						long txID = bb.getLong();                    
-						maxTransactionID = Math.max(maxTransactionID, txID);                 
-						long id = bb.getLong();          
-						maxMessageID = Math.max(maxMessageID, id);
-						
-						int size = bb.getInt();                
-						byte[] record = new byte[size];                 
-						bb.get(record);                  
-						byte end = bb.get();
-						
-						if (end != DONE)
-						{
-							repairFrom(pos, file);
-						}
-						else
-						{                 
-							TransactionHolder tx = transactions.get(txID);
-							
-							if (tx == null)
-							{
-								tx = new TransactionHolder(txID);                        
-								transactions.put(txID, tx);
-							}
-							
-							tx.recordInfos.add(new RecordInfo(id, record, false));                     
-							
-							TransactionNegPos tnp = transactionInfos.get(txID);
-							
-							if (tnp == null)
-							{
-								tnp = new TransactionNegPos();
-								
-								transactionInfos.put(txID, tnp);
-							}
-							
-							tnp.addPos(file, id);
-							
-							hasData = true;                                          
-						}
-						
-						break;
-					}     
-					case UPDATE_RECORD_TX:
-					{              
-						long txID = bb.getLong();  
-						maxTransactionID = Math.max(maxTransactionID, txID);                 
-						long id = bb.getLong();
-						maxMessageID = Math.max(maxMessageID, id);
-						
-						int size = bb.getInt();                
-						byte[] record = new byte[size];                 
-						bb.get(record);                  
-						byte end = bb.get();
-						
-						if (end != DONE)
-						{
-							repairFrom(pos, file);
-						}
-						else
-						{              
-							TransactionHolder tx = transactions.get(txID);
-							
-							if (tx == null)
-							{
-								tx = new TransactionHolder(txID);                        
-								transactions.put(txID, tx);
-							}
-							
-							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;                     
-						}
-						
-						break;
-					}  
-					case DELETE_RECORD_TX:
-					{              
-						long txID = bb.getLong();  
-						maxTransactionID = Math.max(maxTransactionID, txID);                 
-						long id = bb.getLong(); 
-						maxMessageID = Math.max(maxMessageID, id);
+                  maxMessageID = Math.max(maxMessageID, id);
+                  
+                  byte userRecordType = bb.get();
+                 
+                  int size = bb.getInt();                
+                  byte[] record = new byte[size];                 
+                  bb.get(record);                  
+                  byte end = bb.get();
+                  
+                  if (end != DONE)
+                  {
+                     repairFrom(pos, file);
+                  }
+                  else
+                  {              
+                     records.add(new RecordInfo(id, userRecordType, record, 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;
+               }              
+               case DELETE_RECORD:                 
+               {
+                  long id = bb.getLong(); 
+                  
+                  maxMessageID = Math.max(maxMessageID, id);
+                  
+                  byte end = bb.get();
+                  
+                  if (end != DONE)
+                  {
+                     repairFrom(pos, file);
+                  }
+                  else
+                  {                 
+                     recordsToDelete.add(id);                     
+                     hasData = true;
+                     
+                     PosFiles posFiles = posFilesMap.remove(id);
+                     
+                     if (posFiles != null)
+                     {
+                        posFiles.addDelete(file);
+                     }                    
+                  }
+                  
+                  break;
+               }              
+               case ADD_RECORD_TX:
+               {              
+                  long txID = bb.getLong();                    
+                  maxTransactionID = Math.max(maxTransactionID, txID); 
+                  
+                  byte userRecordType = bb.get();
+                  
+                  long id = bb.getLong();          
+                  maxMessageID = Math.max(maxMessageID, id);
+                  
+                  int size = bb.getInt();                
+                  byte[] record = new byte[size];                 
+                  bb.get(record);                  
+                  byte end = bb.get();
+                  
+                  if (end != DONE)
+                  {
+                     repairFrom(pos, file);
+                  }
+                  else
+                  {                 
+                     TransactionHolder tx = transactions.get(txID);
+                     
+                     if (tx == null)
+                     {
+                        tx = new TransactionHolder(txID);                        
+                        transactions.put(txID, tx);
+                     }
+                     
+                     tx.recordInfos.add(new RecordInfo(id, userRecordType, record, false));                     
+                     
+                     TransactionNegPos tnp = transactionInfos.get(txID);
+                     
+                     if (tnp == null)
+                     {
+                        tnp = new TransactionNegPos();
+                        
+                        transactionInfos.put(txID, tnp);
+                     }
+                     
+                     tnp.addPos(file, id);
+                     
+                     hasData = true;                                          
+                  }
+                  
+                  break;
+               }     
+               case UPDATE_RECORD_TX:
+               {              
+                  long txID = bb.getLong();  
+                  maxTransactionID = Math.max(maxTransactionID, txID);
+                  
+                  byte userRecordType = bb.get();
+                  
+                  long id = bb.getLong();
+                  maxMessageID = Math.max(maxMessageID, id);
+                  
+                  int size = bb.getInt();                
+                  byte[] record = new byte[size];                 
+                  bb.get(record);                  
+                  byte end = bb.get();
+                  
+                  if (end != DONE)
+                  {
+                     repairFrom(pos, file);
+                  }
+                  else
+                  {              
+                     TransactionHolder tx = transactions.get(txID);
+                     
+                     if (tx == null)
+                     {
+                        tx = new TransactionHolder(txID);                        
+                        transactions.put(txID, tx);
+                     }
+                     
+                     tx.recordInfos.add(new RecordInfo(id, userRecordType, record, true));
+                     
+                     TransactionNegPos tnp = transactionInfos.get(txID);
+                     
+                     if (tnp == null)
+                     {
+                        tnp = new TransactionNegPos();
+                        
+                        transactionInfos.put(txID, tnp);
+                     }
+                     
+                     tnp.addPos(file, id);
+                     
+                     hasData = true;                     
+                  }
+                  
+                  break;
+               }  
+               case DELETE_RECORD_TX:
+               {              
+                  long txID = bb.getLong();  
+                  maxTransactionID = Math.max(maxTransactionID, txID);                 
+                  long id = bb.getLong(); 
+                  maxMessageID = Math.max(maxMessageID, id);
 
-						byte end = bb.get();
-						
-						if (end != DONE)
-						{
-							repairFrom(pos, file);
-						}
-						else
-						{              
-							TransactionHolder tx = transactions.get(txID);
-							
-							if (tx == null)
-							{
-								tx = new TransactionHolder(txID);                        
-								transactions.put(txID, tx);
-							}
-							
-							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;                     
-						}
-						
-						break;
-					}  
-					case PREPARE_RECORD:
-					{
-						long txID = bb.getLong();           
+                  byte end = bb.get();
+                  
+                  if (end != DONE)
+                  {
+                     repairFrom(pos, file);
+                  }
+                  else
+                  {              
+                     TransactionHolder tx = transactions.get(txID);
+                     
+                     if (tx == null)
+                     {
+                        tx = new TransactionHolder(txID);                        
+                        transactions.put(txID, tx);
+                     }
+                     
+                     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;                     
+                  }
+                  
+                  break;
+               }  
+               case PREPARE_RECORD:
+               {
+                  long txID = bb.getLong();           
 
-						maxTransactionID = Math.max(maxTransactionID, txID);                 
-						byte end = bb.get();
-						
-						if (end != DONE)
-						{
-							repairFrom(pos, file);
-						}
-						else
-						{
-							TransactionHolder tx = transactions.get(txID);
-							
-							if (tx == null)
-							{
-								throw new IllegalStateException("Cannot find tx with id " + txID);
-							}
-							
-							tx.prepared = true;
-							
-							TransactionNegPos tnp = transactionInfos.get(txID);
-							
-							if (tnp == null)
-							{
-								throw new IllegalStateException("Cannot find tx " + txID);
-							}
-							
-							tnp.prepare(file);   
-							
-							hasData = true;         
-						}
-						
-						break;
-					}
-					case COMMIT_RECORD:
-					{
-						long txID = bb.getLong();  
-						
-						maxTransactionID = Math.max(maxTransactionID, txID);
-						byte end = bb.get();
-						
-						if (end != DONE)
-						{
-							repairFrom(pos, file);
-						}
-						else
-						{
-							TransactionHolder tx = transactions.remove(txID);
-							
-							if (tx != null)
-							{
-								records.addAll(tx.recordInfos);                    
-								recordsToDelete.addAll(tx.recordsToDelete);  
-								
-								TransactionNegPos tnp = transactionInfos.remove(txID);
-								
-								if (tnp == null)
-								{
-									throw new IllegalStateException("Cannot find tx " + txID);
-								}
-								
-								tnp.commit(file);       
-								
-								hasData = true;         
-							}
-						}
-						
-						break;
-					}
-					case ROLLBACK_RECORD:
-					{
-						long txID = bb.getLong();     
+                  maxTransactionID = Math.max(maxTransactionID, txID);                 
+                  byte end = bb.get();
+                  
+                  if (end != DONE)
+                  {
+                     repairFrom(pos, file);
+                  }
+                  else
+                  {
+                     TransactionHolder tx = transactions.get(txID);
+                     
+                     if (tx == null)
+                     {
+                        throw new IllegalStateException("Cannot find tx with id " + txID);
+                     }
+                     
+                     tx.prepared = true;
+                     
+                     TransactionNegPos tnp = transactionInfos.get(txID);
+                     
+                     if (tnp == null)
+                     {
+                        throw new IllegalStateException("Cannot find tx " + txID);
+                     }
+                     
+                     tnp.prepare(file);   
+                     
+                     hasData = true;         
+                  }
+                  
+                  break;
+               }
+               case COMMIT_RECORD:
+               {
+                  long txID = bb.getLong();  
+                  
+                  maxTransactionID = Math.max(maxTransactionID, txID);
+                  byte end = bb.get();
+                  
+                  if (end != DONE)
+                  {
+                     repairFrom(pos, file);
+                  }
+                  else
+                  {
+                     TransactionHolder tx = transactions.remove(txID);
+                     
+                     if (tx != null)
+                     {
+                        records.addAll(tx.recordInfos);                    
+                        recordsToDelete.addAll(tx.recordsToDelete);  
+                        
+                        TransactionNegPos tnp = transactionInfos.remove(txID);
+                        
+                        if (tnp == null)
+                        {
+                           throw new IllegalStateException("Cannot find tx " + txID);
+                        }
+                        
+                        tnp.commit(file);       
+                        
+                        hasData = true;         
+                     }
+                  }
+                  
+                  break;
+               }
+               case ROLLBACK_RECORD:
+               {
+                  long txID = bb.getLong();     
+   
+                  maxTransactionID = Math.max(maxTransactionID, txID);                 
+                  byte end = bb.get();
+                  
+                  if (end != DONE)
+                  {
+                     repairFrom(pos, file);
+                  }
+                  else
+                  {
+                     TransactionHolder tx = transactions.remove(txID);
+                     
+                     if (tx != null)
+                     {                       
+                        TransactionNegPos tnp = transactionInfos.remove(txID);
+                        
+                        if (tnp == null)
+                        {
+                           throw new IllegalStateException("Cannot find tx " + txID);
+                        }
+                        
+                        tnp.rollback(file);  
+                        
+                        hasData = true;         
+                     }
+                  }
+                  
+                  break;
+               }
+               case FILL_CHARACTER:                
+               {  
+                  //End of records in file - we check the file only contains fill characters from this point
+                  while (bb.hasRemaining())
+                  {
+                     byte b = bb.get();
+                     
+                     if (b != FILL_CHARACTER)
+                     {
+                        throw new IllegalStateException("Corrupt file " + file.getFile().getFileName() +
+                              " contains non fill character at position " + pos);
+                     }
+                  }
+                  
+                  break;                  
+               }              
+               default:                
+               {
+                  throw new IllegalStateException("Journal " + file.getFile().getFileName() +
+                        " is corrupt, invalid record type " + recordType);
+               }
+            }
+            
+            bb.position(file.getFile().calculateBlockStart(bb.position()));
+            
+            if (recordType != FILL_CHARACTER)
+            {
+               lastDataPos = bb.position();
+            }
+         }
+         
+         if (hasData)
+         {        
+            dataFiles.add(file);
+            
+            file.getFile().close();          
+         }
+         else
+         {           
+            //Empty dataFiles with no data
+            freeFiles.add(file);
+            
+            //Position it ready for writing
+            file.getFile().position(file.getFile().calculateBlockStart(SIZE_LONG));
+         }                       
+      }        
+      
+      transactionIDSequence.set(maxTransactionID + 1);
+      
+      //Create any more files we need
+      
+      //FIXME - size() involves a scan
+      int filesToCreate = minFiles - (dataFiles.size() + freeFiles.size());
+      
+      for (int i = 0; i < filesToCreate; i++)
+      {
+         // Keeping all files opened can be very costly (mainly on AIO)
+         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();
+         
+         currentFile.getFile().position(lastDataPos);
+         
+         currentFile.setOffset(lastDataPos);
+      }
+      else
+      {
+         currentFile = freeFiles.remove();
+      }           
+      
+      for (RecordInfo record: records)
+      {
+         if (!recordsToDelete.contains(record.id))
+         {
+            committedRecords.add(record);
+         }
+      }
+      
+      for (TransactionHolder transaction: transactions.values())
+      {
+         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
+         {
+            PreparedTransactionInfo info = new PreparedTransactionInfo(transaction.transactionID);
+            
+            info.records.addAll(transaction.recordInfos);
+            
+            info.recordsToDelete.addAll(transaction.recordsToDelete);
+            
+            preparedTransactions.add(info);
+         }
+      }
+      
+      state = STATE_LOADED;
+      
+      return maxMessageID;
+   }
 	
-						maxTransactionID = Math.max(maxTransactionID, txID);                 
-						byte end = bb.get();
-						
-						if (end != DONE)
-						{
-							repairFrom(pos, file);
-						}
-						else
-						{
-							TransactionHolder tx = transactions.remove(txID);
-							
-							if (tx != null)
-							{                       
-								TransactionNegPos tnp = transactionInfos.remove(txID);
-								
-								if (tnp == null)
-								{
-									throw new IllegalStateException("Cannot find tx " + txID);
-								}
-								
-								tnp.rollback(file);  
-								
-								hasData = true;         
-							}
-						}
-						
-						break;
-					}
-					case FILL_CHARACTER:                
-					{  
-						//End of records in file - we check the file only contains fill characters from this point
-						while (bb.hasRemaining())
-						{
-							byte b = bb.get();
-							
-							if (b != FILL_CHARACTER)
-							{
-								throw new IllegalStateException("Corrupt file " + file.getFile().getFileName() +
-										" contains non fill character at position " + pos);
-							}
-						}
-						
-						break;                  
-					}              
-					default:                
-					{
-						throw new IllegalStateException("Journal " + file.getFile().getFileName() +
-								" is corrupt, invalid record type " + recordType);
-					}
-				}
-				
-				bb.position(file.getFile().calculateBlockStart(bb.position()));
-				
-				if (recordType != FILL_CHARACTER)
-				{
-					lastDataPos = bb.position();
-				}
-			}
-			
-			if (hasData)
-			{        
-				dataFiles.add(file);
-				
-				file.getFile().close();          
-			}
-			else
-			{           
-				//Empty dataFiles with no data
-				freeFiles.add(file);
-				
-				//Position it ready for writing
-				file.getFile().position(file.getFile().calculateBlockStart(SIZE_LONG));
-			}                       
-		}        
-		
-		transactionIDSequence.set(maxTransactionID + 1);
-		
-		//Create any more files we need
-		
-		//FIXME - size() involves a scan
-		int filesToCreate = minFiles - (dataFiles.size() + freeFiles.size());
-		
-		for (int i = 0; i < filesToCreate; i++)
-		{
-			// Keeping all files opened can be very costly (mainly on AIO)
-			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();
-			
-			currentFile.getFile().position(lastDataPos);
-			
-			currentFile.setOffset(lastDataPos);
-		}
-		else
-		{
-			currentFile = freeFiles.remove();
-		}           
-		
-		for (RecordInfo record: records)
-		{
-			if (!recordsToDelete.contains(record.id))
-			{
-				committedRecords.add(record);
-			}
-		}
-		
-		for (TransactionHolder transaction: transactions.values())
-		{
-			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
-			{
-				PreparedTransactionInfo info = new PreparedTransactionInfo(transaction.transactionID);
-				
-				info.records.addAll(transaction.recordInfos);
-				
-				info.recordsToDelete.addAll(transaction.recordsToDelete);
-				
-				preparedTransactions.add(info);
-			}
-		}
-		
-		state = STATE_LOADED;
-		
-		return maxMessageID;
-	}
-	
 	public int getAlignment() throws Exception
 	{
 		return this.currentFile.getFile().getAlignment();
@@ -1037,7 +1179,30 @@
 		
 	}
 	
-	// TestableJournal implementation --------------------------------------------------------------
+   public String debug() throws Exception
+   {
+      this.checkReclaimStatus();
+      
+      StringBuilder builder = new StringBuilder();
+      
+      for (JournalFile file: dataFiles)
+      {
+         builder.append("DataFile:" + file + " posCounter = " + file.getPosCount() + " reclaimStatus = " +  file.isCanReclaim() + "\n");
+         if (file instanceof JournalFileImpl)
+         {
+            builder.append(((JournalFileImpl)file).debug());
+            
+         }
+      }
+      
+      builder.append("CurrentFile:" + currentFile+ " posCounter = " + currentFile.getPosCount() + "\n");
+      builder.append(((JournalFileImpl)currentFile).debug());
+      
+      
+      return builder.toString();
+   }
+
+   // TestableJournal implementation --------------------------------------------------------------
 	
 	public synchronized void checkAndReclaimFiles() throws Exception
 	{
@@ -1064,7 +1229,7 @@
 					
 					sf.open();
 					
-					ByteBuffer bb = sf.newBuffer(SIZE_LONG); 
+					ByteBuffer bb = fileFactory.newBuffer(SIZE_LONG); 
 					
 					bb.putLong(newOrderingID);
 					
@@ -1178,7 +1343,7 @@
 	
 	// Private -----------------------------------------------------------------------------
 	
-	private void appendRecord(ByteBuffer bb, boolean sync) throws Exception
+	private JournalFile appendRecord(ByteBuffer bb, boolean sync) throws Exception
 	{
 		lock.acquire();
 		
@@ -1189,6 +1354,7 @@
 			checkFile(size);
 			currentFile.getFile().write(bb, sync);       
 			currentFile.extendOffset(size);
+			return currentFile;
 		}
 		finally
 		{
@@ -1196,7 +1362,7 @@
 		}
 	}
 	
-	private void appendRecord(ByteBuffer bb, boolean sync, IOCallback callback) throws Exception
+	private JournalFile appendRecord(ByteBuffer bb, boolean sync, IOCallback callback) throws Exception
 	{
 		lock.acquire();
 		
@@ -1207,6 +1373,7 @@
 			checkFile(size);
 			currentFile.getFile().write(bb, sync, callback);       
 			currentFile.extendOffset(size);
+			return currentFile;
 		}
 		finally
 		{
@@ -1233,13 +1400,13 @@
 		
 		if (trace) log.trace("Creating file " + fileName);
 		
-		SequentialFile sequentialFile = fileFactory.createSequentialFile(fileName, sync);
+		SequentialFile sequentialFile = fileFactory.createSequentialFile(fileName, sync, maxAIO);
 		
 		sequentialFile.open();
 		
 		sequentialFile.fill(0, fileSize, FILL_CHARACTER);
 		
-		ByteBuffer bb = sequentialFile.newBuffer(SIZE_LONG); 
+		ByteBuffer bb = fileFactory.newBuffer(SIZE_LONG); 
 		
 		bb.putLong(orderingID);
 		
@@ -1295,15 +1462,15 @@
 			
 			dataFiles.add(currentFile);
 			
-			//FIXME - isEmpty() involves a scan!!
-			if (!freeFiles.isEmpty())
+			try
 			{
-				currentFile = freeFiles.remove();
+			   currentFile = freeFiles.remove();
 			}
-			else
+			catch (NoSuchElementException e)
 			{
-				currentFile = createFile();
+            currentFile = createFile();
 			}
+
 		}     
 	}
 	
@@ -1321,7 +1488,99 @@
 		return tx;
 	}
 	
+   private TransactionCallback getTransactionCallback(long transactionId)
+   {
+      TransactionCallback callback = this.transactionCallbacks.get(transactionId);
+      
+      if (callback == null)
+      {
+         callback = new TransactionCallback();
+         transactionCallbacks.put(transactionId, callback);
+      }
+      
+      return callback;
+   }
+   
+   private void removeTransactionCallback(long transactionId)
+   {
+      transactionCallbacks.remove(transactionId);
+   }
+   
+	
+	
 	// Inner classes ---------------------------------------------------------------------------
+
+   class SimpleCallback implements IOCallback
+   {
+      
+      String errorMessage;
+      int errorCode;
+      CountDownLatch latch = new CountDownLatch(1);
+
+      public void done()
+      {
+         latch.countDown();
+      }
+
+      public void onError(int errorCode, String errorMessage)
+      {
+         this.errorMessage = errorMessage;
+         this.errorCode = errorCode;
+         latch.countDown();
+         
+      }
+      
+      public void waitCompletion() throws InterruptedException 
+      {
+         // TODO: Variable Timeout?
+         if (!latch.await(30, TimeUnit.SECONDS))
+         {
+            throw new IllegalStateException("Timeout!");
+         }
+         if (errorMessage != null)
+         {
+            throw new IllegalStateException("Error on Transaction: " + errorCode + " - " + errorMessage);
+         }
+     }
+      
+   }
+   
+   class TransactionCallback implements IOCallback
+   {
+      
+      VariableLatch countLatch = new VariableLatch();
+      
+      String errorMessage = null;
+      int errorCode = 0;
+      
+      public void countUp()
+      {
+         countLatch.up();
+      }
+
+      public void done()
+      {
+         countLatch.down();
+      }
+      
+      public void waitCompletion() throws InterruptedException
+      {
+         countLatch.waitCompletion(90);
+         
+         if (errorMessage != null)
+         {
+            throw new IllegalStateException("Error on Transaction: " + errorCode + " - " + errorMessage);
+         }
+      }
+
+      public void onError(int errorCode, String errorMessage)
+      {
+         this.errorMessage = errorMessage;
+         this.errorCode = errorCode;
+         countLatch.down();
+      }
+      
+   }
 	
 	private class ReclaimerTask extends TimerTask
 	{
@@ -1510,28 +1769,5 @@
 			}
 		}
 	}
-	
-	public String debug() throws Exception
-	{
-		this.checkReclaimStatus();
-		
-		StringBuilder builder = new StringBuilder();
-		
-		for (JournalFile file: dataFiles)
-		{
-			builder.append("DataFile:" + file + " posCounter = " + file.getPosCount() + " reclaimStatus = " +  file.isCanReclaim() + "\n");
-			if (file instanceof JournalFileImpl)
-			{
-				builder.append(((JournalFileImpl)file).debug());
-				
-			}
-		}
-		
-		builder.append("CurrentFile:" + currentFile+ " posCounter = " + currentFile.getPosCount() + "\n");
-		builder.append(((JournalFileImpl)currentFile).debug());
-		
-		
-		return builder.toString();
-	}
-	
+
 }

Modified: trunk/src/main/org/jboss/messaging/core/journal/impl/NIOSequentialFile.java
===================================================================
--- trunk/src/main/org/jboss/messaging/core/journal/impl/NIOSequentialFile.java	2008-05-12 14:57:22 UTC (rev 4173)
+++ trunk/src/main/org/jboss/messaging/core/journal/impl/NIOSequentialFile.java	2008-05-12 21:53:04 UTC (rev 4174)
@@ -183,13 +183,4 @@
 		channel.position(pos);
 	}
 	
-	public ByteBuffer newBuffer(int size)
-	{
-		return ByteBuffer.allocate(size);
-	}
-	
-	public ByteBuffer wrapBuffer(byte[] bytes)
-	{
-		return ByteBuffer.wrap(bytes);
-	}
 }

Modified: trunk/src/main/org/jboss/messaging/core/journal/impl/NIOSequentialFileFactory.java
===================================================================
--- trunk/src/main/org/jboss/messaging/core/journal/impl/NIOSequentialFileFactory.java	2008-05-12 14:57:22 UTC (rev 4173)
+++ trunk/src/main/org/jboss/messaging/core/journal/impl/NIOSequentialFileFactory.java	2008-05-12 21:53:04 UTC (rev 4174)
@@ -21,6 +21,8 @@
   */
 package org.jboss.messaging.core.journal.impl;
 
+import java.nio.ByteBuffer;
+
 import org.jboss.messaging.core.journal.SequentialFile;
 import org.jboss.messaging.core.journal.SequentialFileFactory;
 
@@ -38,9 +40,25 @@
 		super(journalDir);
 	}	
 	
-	public SequentialFile createSequentialFile(final String fileName, final boolean sync)
+	public SequentialFile createSequentialFile(final String fileName, final boolean sync, int maxIO)
 	{
 		return new NIOSequentialFile(journalDir, fileName, sync);
 	}
 
+   public boolean supportsCallbacks()
+   {
+      return false;
+   }
+   
+   public ByteBuffer newBuffer(int size)
+   {
+      return ByteBuffer.allocate(size);
+   }
+   
+   public ByteBuffer wrapBuffer(byte[] bytes)
+   {
+      return ByteBuffer.wrap(bytes);
+   }
+   
+	
 }

Modified: trunk/src/main/org/jboss/messaging/core/message/Message.java
===================================================================
--- trunk/src/main/org/jboss/messaging/core/message/Message.java	2008-05-12 14:57:22 UTC (rev 4173)
+++ trunk/src/main/org/jboss/messaging/core/message/Message.java	2008-05-12 21:53:04 UTC (rev 4174)
@@ -23,6 +23,7 @@
 
 import java.util.Set;
 
+import org.jboss.messaging.core.journal.EncodingSupport;
 import org.jboss.messaging.util.MessagingBuffer;
 import org.jboss.messaging.util.SimpleString;
 
@@ -37,7 +38,7 @@
  *
  * $Id: Message.java 3341 2007-11-19 14:34:57Z timfox $
  */
-public interface Message
+public interface Message extends EncodingSupport
 {
    public static final SimpleString HDR_ACTUAL_EXPIRY_TIME = new SimpleString("JBMActualExpiryTime");
    
@@ -65,8 +66,9 @@
    
    void setPriority(byte priority);
    
-      
-   MessagingBuffer encode();
+   int encodeSize();
+
+   void encode(MessagingBuffer buffer);
    
    void decode(MessagingBuffer buffer);
    

Modified: trunk/src/main/org/jboss/messaging/core/message/impl/MessageImpl.java
===================================================================
--- trunk/src/main/org/jboss/messaging/core/message/impl/MessageImpl.java	2008-05-12 14:57:22 UTC (rev 4173)
+++ trunk/src/main/org/jboss/messaging/core/message/impl/MessageImpl.java	2008-05-12 21:53:04 UTC (rev 4174)
@@ -21,6 +21,27 @@
  */
 package org.jboss.messaging.core.message.impl;
 
+import static org.jboss.messaging.util.DataConstants.BOOLEAN;
+import static org.jboss.messaging.util.DataConstants.BYTE;
+import static org.jboss.messaging.util.DataConstants.BYTES;
+import static org.jboss.messaging.util.DataConstants.CHAR;
+import static org.jboss.messaging.util.DataConstants.DOUBLE;
+import static org.jboss.messaging.util.DataConstants.FLOAT;
+import static org.jboss.messaging.util.DataConstants.INT;
+import static org.jboss.messaging.util.DataConstants.LONG;
+import static org.jboss.messaging.util.DataConstants.NOT_NULL;
+import static org.jboss.messaging.util.DataConstants.NULL;
+import static org.jboss.messaging.util.DataConstants.SHORT;
+import static org.jboss.messaging.util.DataConstants.SIZE_BOOLEAN;
+import static org.jboss.messaging.util.DataConstants.SIZE_BYTE;
+import static org.jboss.messaging.util.DataConstants.SIZE_CHAR;
+import static org.jboss.messaging.util.DataConstants.SIZE_DOUBLE;
+import static org.jboss.messaging.util.DataConstants.SIZE_FLOAT;
+import static org.jboss.messaging.util.DataConstants.SIZE_INT;
+import static org.jboss.messaging.util.DataConstants.SIZE_LONG;
+import static org.jboss.messaging.util.DataConstants.SIZE_SHORT;
+import static org.jboss.messaging.util.DataConstants.STRING;
+
 import java.util.Set;
 
 import org.jboss.messaging.core.logging.Logger;
@@ -105,25 +126,50 @@
    
    // Message implementation ----------------------------------------
 
-   public MessagingBuffer encode()
+   public void encode(MessagingBuffer buff)
    {
-      MessagingBuffer buff = new BufferWrapper(1024);
-      
+//      buff.putSimpleString(destination);
+//      buff.putInt(type);
+//      buff.putBoolean(durable);
+//      buff.putLong(expiration);
+//      buff.putLong(timestamp);
+//      buff.putByte(priority);
+//      properties.encode(buff);
+//      buff.putInt(body.limit());
+//      buff.putBytes(body.array(), 0, body.limit());
+
+   
       buff.putSimpleString(destination);
       buff.putInt(type);
       buff.putBoolean(durable);
       buff.putLong(expiration);
       buff.putLong(timestamp);
       buff.putByte(priority);
-      
       properties.encode(buff);
-                       
       buff.putInt(body.limit());
-      
-      //TODO this can be optimisied
       buff.putBytes(body.array(), 0, body.limit());
+   
+   }
+   
+   public int encodeSize()
+   {
+//      return /* Destination */ SimpleString.sizeofString(destination) + 
+//             /* Type */ SIZE_INT + 
+//             /* Durable */ SIZE_BOOLEAN + 
+//             /* Expiration */ SIZE_LONG + 
+//             /* Timestamp */ SIZE_LONG +
+//             /* Priority */  SIZE_BYTE + 
+//             /* PropertySize and Properties */ properties.encodeSize() + 
+//             /* BodySize and Body */ SIZE_INT + body.limit();
+      return /* Destination */ SimpleString.sizeofString(destination) + 
+      /* Type */ SIZE_INT + 
+      /* Durable */ SIZE_BOOLEAN + 
+      /* Expiration */ SIZE_LONG + 
+      /* Timestamp */ SIZE_LONG + 
+      /* Priority */ SIZE_BYTE + 
+      /* PropertySize and Properties */ properties.encodeSize() + 
+      /* BodySize and Body */ SIZE_INT + body.limit();
       
-      return buff;
    }
    
    public void decode(final MessagingBuffer buffer)

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-05-12 14:57:22 UTC (rev 4173)
+++ trunk/src/main/org/jboss/messaging/core/persistence/impl/journal/JournalStorageManager.java	2008-05-12 21:53:04 UTC (rev 4174)
@@ -5,17 +5,20 @@
 import java.io.DataInputStream;
 import java.io.DataOutputStream;
 import java.io.File;
+import java.io.IOException;
 import java.nio.ByteBuffer;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Map;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.atomic.AtomicLong;
 
 import org.jboss.messaging.core.config.Configuration;
 import org.jboss.messaging.core.filter.Filter;
 import org.jboss.messaging.core.filter.impl.FilterImpl;
+import org.jboss.messaging.core.journal.IOCallback;
 import org.jboss.messaging.core.journal.Journal;
 import org.jboss.messaging.core.journal.PreparedTransactionInfo;
 import org.jboss.messaging.core.journal.RecordInfo;
@@ -28,7 +31,6 @@
 import org.jboss.messaging.core.postoffice.Binding;
 import org.jboss.messaging.core.postoffice.PostOffice;
 import org.jboss.messaging.core.postoffice.impl.BindingImpl;
-import org.jboss.messaging.core.remoting.impl.mina.BufferWrapper;
 import org.jboss.messaging.core.server.JournalType;
 import org.jboss.messaging.core.server.MessageReference;
 import org.jboss.messaging.core.server.Queue;
@@ -38,6 +40,7 @@
 import org.jboss.messaging.util.ByteBufferWrapper;
 import org.jboss.messaging.util.MessagingBuffer;
 import org.jboss.messaging.util.SimpleString;
+import org.jboss.messaging.util.VariableLatch;
 
 /**
  * 
@@ -84,7 +87,7 @@
 	private final Journal bindingsJournal;
 	
 	private final ConcurrentMap<SimpleString, Long> destinationIDMap = new ConcurrentHashMap<SimpleString, Long>();
-   
+	
 	private volatile boolean started;
 	
 	public JournalStorageManager(Configuration config)
@@ -105,7 +108,7 @@
 			
 	   SequentialFileFactory bindingsFF = new NIOSequentialFileFactory(bindingsDir);
       
-	   bindingsJournal = new JournalImpl(1024 * 1024, 2, true, bindingsFF, 10000, "jbm-bindings", "bindings");
+	   bindingsJournal = new JournalImpl(1024 * 1024, 2, true, bindingsFF, 10000, "jbm-bindings", "bindings", 1);
 	      
 	   String journalDir = config.getJournalDirectory();
 	   
@@ -123,11 +126,11 @@
          if (!AIOSequentialFileFactory.isSupported())
          {
             log.warn("AIO wasn't located on this platform, using just standard Java NIO. If you are on Linux, install LibAIO and the required wrapper and you will get a lot of performance benefit");
-            journalFF = new NIOSequentialFileFactory(bindingsDir);
+            journalFF = new NIOSequentialFileFactory(journalDir);
          }
          else
          {
-            journalFF = new AIOSequentialFileFactory(bindingsDir);
+            journalFF = new AIOSequentialFileFactory(journalDir);
             log.info("AIO loaded successfully");
          }
       }
@@ -144,7 +147,7 @@
 	      
 	   messageJournal = new JournalImpl(config.getJournalFileSize(), 
 	   		config.getJournalMinFiles(), config.isJournalSync(), journalFF,
-	   		config.getJournalTaskPeriod(), "jbm-data", "jbm");
+	   		config.getJournalTaskPeriod(), "jbm-data", "jbm", 10000);
 	}
 	
 	public long generateMessageID()
@@ -161,22 +164,14 @@
 	
 	public void storeMessage(final ServerMessage message) throws Exception
 	{		
-		//TODO too much copying is occurring here
-	   
-		MessagingBuffer buffer = new BufferWrapper(1024);
-		
-		buffer.putByte(ADD_MESSAGE);
-		
-		buffer.putBytes(message.encode().array());
-		
-      messageJournal.appendAddRecord(message.getMessageID(), buffer.array());      
+      messageJournal.appendAddRecord(message.getMessageID(), ADD_MESSAGE, message);      
 	}
 
 	public void storeAcknowledge(final long queueID, final long messageID) throws Exception
 	{		
 		byte[] record = ackBytes(queueID, messageID);
 		
-		messageJournal.appendUpdateRecord(messageID, record);					
+		messageJournal.appendUpdateRecord(messageID, ACKNOWLEDGE_REF, record);					
 	}
 	
 	public void storeDelete(final long messageID) throws Exception
@@ -188,22 +183,14 @@
 	
    public void storeMessageTransactional(long txID, ServerMessage message) throws Exception
    {
-      //TODO too much copying is occurring here
-      
-      MessagingBuffer buffer = new BufferWrapper(1024);
-      
-      buffer.putByte(ADD_MESSAGE);
-      
-      buffer.putBytes(message.encode().array());
-      
-      messageJournal.appendAddRecordTransactional(txID, message.getMessageID(), buffer.array());
+      messageJournal.appendAddRecordTransactional(txID, ADD_MESSAGE, message.getMessageID(), message);
    }
    
    public void storeAcknowledgeTransactional(long txID, long queueID, long messageID) throws Exception
    {
    	byte[] record = ackBytes(queueID, messageID);
 		
-		messageJournal.appendUpdateRecordTransactional(txID, messageID, record);	
+		messageJournal.appendUpdateRecordTransactional(txID, ACKNOWLEDGE_REF, messageID, record);	
    }
    
    public void storeDeleteTransactional(long txID, long messageID) throws Exception
@@ -223,7 +210,7 @@
    
    public void rollback(long txID) throws Exception
    {
-   	messageJournal.appendRollbackRecord(txID);
+      messageJournal.appendRollbackRecord(txID);
    }
    
    // Other operations
@@ -234,15 +221,13 @@
 		
 		ByteBuffer bb = ByteBuffer.wrap(bytes);
 		
-		bb.put(UPDATE_DELIVERY_COUNT);
-		
 		bb.putLong(ref.getQueue().getPersistenceID());
 		
 		bb.putLong(ref.getMessage().getMessageID());
 		
 		bb.putInt(ref.getDeliveryCount());
 		
-		messageJournal.appendUpdateRecord(ref.getMessage().getMessageID(), bytes);
+		messageJournal.appendUpdateRecord(ref.getMessage().getMessageID(), UPDATE_DELIVERY_COUNT, bytes);
 	}
 
 	public void loadMessages(final PostOffice postOffice, final Map<Long, Queue> queues) throws Exception
@@ -261,7 +246,7 @@
 			
 			ByteBuffer bb = ByteBuffer.wrap(data);
 			
-			byte recordType = bb.get();
+			byte recordType = record.getUserRecordType();
 			
 			switch (recordType)
 			{
@@ -368,8 +353,6 @@
 
 		 queue.setPersistenceID(queueID);
 
-		 daos.writeByte(BINDING_RECORD);
-		 
 		 byte[] nameBytes = queue.getName().getData();
 		 
 		 daos.writeInt(nameBytes.length);
@@ -399,7 +382,7 @@
 
 		 byte[] data = baos.toByteArray();
 		 
-		 bindingsJournal.appendAddRecord(queueID, data);
+		 bindingsJournal.appendAddRecord(queueID, BINDING_RECORD, data);
 	}
 
 	public void deleteBinding(Binding binding) throws Exception
@@ -429,8 +412,6 @@
 	      
 			DataOutputStream daos = new DataOutputStream(baos);
 			
-			daos.writeByte(DESTINATION_RECORD);
-			
 			byte[] destBytes = destination.getData();
 			
 			daos.writeInt(destBytes.length);
@@ -441,7 +422,7 @@
 			
 			byte[] data = baos.toByteArray();
 			
-			bindingsJournal.appendAddRecord(destinationID, data);
+			bindingsJournal.appendAddRecord(destinationID, DESTINATION_RECORD, data);
 			
 			return true;
 		}		
@@ -484,7 +465,7 @@
 
 			DataInputStream dais = new DataInputStream(bais);
 			
-			byte rec = dais.readByte();
+			byte rec = record.getUserRecordType();
 			
 			if (rec == BINDING_RECORD)
 			{
@@ -575,15 +556,13 @@
 	}
 	
 	// Private ----------------------------------------------------------------------------------
-			
+	
 	private byte[] ackBytes(final long queueID, final long messageID)
    {
-      byte[] record = new byte[SIZE_BYTE + SIZE_LONG + SIZE_LONG];
+      byte[] record = new byte[SIZE_LONG + SIZE_LONG];
       
       ByteBuffer bb = ByteBuffer.wrap(record);
       
-      bb.put(ACKNOWLEDGE_REF);
-      
       bb.putLong(queueID);
       
       bb.putLong(messageID);

Modified: trunk/src/main/org/jboss/messaging/core/remoting/impl/wireformat/ProducerSendMessage.java
===================================================================
--- trunk/src/main/org/jboss/messaging/core/remoting/impl/wireformat/ProducerSendMessage.java	2008-05-12 14:57:22 UTC (rev 4173)
+++ trunk/src/main/org/jboss/messaging/core/remoting/impl/wireformat/ProducerSendMessage.java	2008-05-12 21:53:04 UTC (rev 4174)
@@ -64,15 +64,7 @@
    
    public void encodeBody(final MessagingBuffer buffer)
    {
-      MessagingBuffer buf = clientMessage.encode();
-      
-      buf.flip();
-      
-      //TODO - can be optimised
-      
-      byte[] data = buf.array();
-       
-      buffer.putBytes(data, 0, buf.limit());
+      clientMessage.encode(buffer);
    }
    
    public void decodeBody(final MessagingBuffer buffer)

Modified: trunk/src/main/org/jboss/messaging/core/remoting/impl/wireformat/ReceiveMessage.java
===================================================================
--- trunk/src/main/org/jboss/messaging/core/remoting/impl/wireformat/ReceiveMessage.java	2008-05-12 14:57:22 UTC (rev 4173)
+++ trunk/src/main/org/jboss/messaging/core/remoting/impl/wireformat/ReceiveMessage.java	2008-05-12 21:53:04 UTC (rev 4174)
@@ -91,18 +91,9 @@
    
    public void encodeBody(final MessagingBuffer buffer)
    {
-      MessagingBuffer buf = serverMessage.encode();
-      
-      buf.flip();
-      
-      //TODO - can be optimised
-      
-      byte[] data = buf.array();
-      
       buffer.putInt(deliveryCount);
       buffer.putLong(deliveryID);
-      
-      buffer.putBytes(data, 0, buf.limit());
+      serverMessage.encode(buffer);
    }
    
    public void decodeBody(final MessagingBuffer buffer)

Modified: trunk/src/main/org/jboss/messaging/util/ByteBufferWrapper.java
===================================================================
--- trunk/src/main/org/jboss/messaging/util/ByteBufferWrapper.java	2008-05-12 14:57:22 UTC (rev 4173)
+++ trunk/src/main/org/jboss/messaging/util/ByteBufferWrapper.java	2008-05-12 21:53:04 UTC (rev 4174)
@@ -33,6 +33,11 @@
 		this.buffer = buffer;
 	}
 	
+	public ByteBuffer getBuffer()
+	{
+	   return buffer;
+	}
+	
 	public byte[] array()
    {
    	return buffer.array();

Modified: trunk/src/main/org/jboss/messaging/util/TypedProperties.java
===================================================================
--- trunk/src/main/org/jboss/messaging/util/TypedProperties.java	2008-05-12 14:57:22 UTC (rev 4173)
+++ trunk/src/main/org/jboss/messaging/util/TypedProperties.java	2008-05-12 21:53:04 UTC (rev 4174)
@@ -46,6 +46,7 @@
 import java.util.Map;
 import java.util.Set;
 
+import org.jboss.messaging.core.journal.EncodingSupport;
 import org.jboss.messaging.core.logging.Logger;
 
 /**
@@ -57,7 +58,7 @@
  * @author <a href="mailto:tim.fox at jboss.com">Tim Fox</a>
  *
  */
-public class TypedProperties
+public class TypedProperties implements EncodingSupport
 {  
 	private static final Logger log = Logger.getLogger(TypedProperties.class);
 
@@ -269,7 +270,7 @@
       	
       	for (Map.Entry<SimpleString, PropertyValue> entry: properties.entrySet())
    		{
-      		SimpleString s = entry.getKey();;
+      		SimpleString s = entry.getKey();
       		byte[] data = s.getData();
       		buffer.putInt(data.length);
       		buffer.putBytes(data);
@@ -279,6 +280,23 @@
 		}
 	}
 	
+	public int encodeSize()
+	{
+	   if (properties == null)
+	   {
+	      return SIZE_BYTE;
+	   }
+	   else
+	   {
+	      int size = SIZE_BYTE + SIZE_INT;
+         for (Map.Entry<SimpleString, PropertyValue> entry: properties.entrySet())
+         {
+            size += SimpleString.sizeofString(entry.getKey()) + entry.getValue().encodeSize(); 
+         }
+         return size;
+	   }
+	}
+	
 	public void clear()
 	{
 		if (properties != null)
@@ -343,6 +361,8 @@
 		
 		void write(MessagingBuffer buffer);
 		
+		int encodeSize();
+		
 		byte getType();
 	}
    
@@ -366,6 +386,12 @@
       {
          return NULL;
       }
+
+      public int encodeSize()
+      {
+         return SIZE_BYTE;
+      }
+      
    }
    
    private static final class BooleanValue implements PropertyValue
@@ -397,6 +423,12 @@
 		{
 			return BOOLEAN;
 		}
+
+      public int encodeSize()
+      {
+         return SIZE_BYTE + SIZE_BOOLEAN;
+      }
+		
 	}
 	
    private static final class ByteValue implements PropertyValue
@@ -428,6 +460,11 @@
 		{
 			return BYTE;
 		}
+		
+		public int encodeSize()
+		{
+		   return SIZE_BYTE + SIZE_BYTE;
+		}
 	}
    
    private static final class BytesValue implements PropertyValue
@@ -462,6 +499,12 @@
 		{
 			return BYTES;
 		}
+		
+      public int encodeSize()
+      {
+         return SIZE_BYTE + SIZE_INT + val.length;
+      }
+		
 	}
 	
    private static final class ShortValue implements PropertyValue
@@ -493,6 +536,11 @@
 		{
 			return SHORT;
 		}
+		
+		public int encodeSize()
+		{
+		   return SIZE_BYTE + SIZE_SHORT;
+		}
 	}
 	
    private static final class IntValue implements PropertyValue
@@ -524,6 +572,11 @@
 		{
 			return INT;
 		}
+
+		public int encodeSize()
+      {
+         return SIZE_BYTE + SIZE_INT;
+      }
 	}
 	
    private static final class LongValue implements PropertyValue
@@ -555,6 +608,11 @@
 		{
 			return LONG;
 		}
+
+		public int encodeSize()
+      {
+         return SIZE_BYTE + SIZE_LONG;
+      }
 	}
 	
    private static final class FloatValue implements PropertyValue
@@ -586,6 +644,12 @@
 		{
 			return FLOAT;
 		}
+		
+      public int encodeSize()
+      {
+         return SIZE_BYTE + SIZE_FLOAT;
+      }
+		
 	}
 	
    private static final class DoubleValue implements PropertyValue
@@ -617,6 +681,11 @@
 		{
 			return DOUBLE;
 		}
+
+		public int encodeSize()
+      {
+         return SIZE_BYTE + SIZE_DOUBLE;
+      }
 	}
    
    private static final class CharValue implements PropertyValue
@@ -653,6 +722,11 @@
 		{
 			return SIZE_CHAR;
 		}
+
+		public int encodeSize()
+      {
+         return SIZE_BYTE + SIZE_CHAR;
+      }
 	}
 	
    private static final class StringValue implements PropertyValue
@@ -689,5 +763,10 @@
 		{
 			return SimpleString.sizeofString(val);
 		}
+		
+		public int encodeSize()
+		{
+		   return SIZE_BYTE + SimpleString.sizeofString(val);
+		}
 	}
 }

Added: trunk/src/main/org/jboss/messaging/util/VariableLatch.java
===================================================================
--- trunk/src/main/org/jboss/messaging/util/VariableLatch.java	                        (rev 0)
+++ trunk/src/main/org/jboss/messaging/util/VariableLatch.java	2008-05-12 21:53:04 UTC (rev 4174)
@@ -0,0 +1,126 @@
+/*
+  * 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.util;
+
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.locks.AbstractQueuedSynchronizer;
+
+
+/** This class will use the framework provided to by AbstractQueuedSynchronizer.
+ * AbstractQueuedSynchronizer is the framework for any sort of concurrent synchronization, such as Semaphores, events, etc, based on AtomicIntegers.
+ * 
+ * The idea is, instead of providing each user specific Latch/Synchronization, java.util.concurrent provides the framework for reuses, based on an AtomicInteger (getState())
+ * 
+ * On JBossMessaging we have the requirement of increment and decrement a counter until the user fires a ready event (commit). At that point we just act as a regular countDown
+ * 
+ * @author Clebert Suconic
+ * */
+public class VariableLatch
+{
+   /** 
+    * Look at the doc and examples provided by AbstractQueuedSynchronizer for more information 
+    * @see AbstractQueuedSynchronizer*/
+   @SuppressWarnings("serial")
+   private static class CountSync extends AbstractQueuedSynchronizer
+   {
+      
+      public CountSync ()
+      {
+         setState(0);
+      }
+      
+      
+      public int getCount()
+      {
+         return getState();
+      }
+      
+      public int tryAcquireShared(int numberOfAqcquires)
+      {
+         return getState()==0 ? 1 : -1;
+      }
+      
+      public void add()
+      {
+         for (;;)
+         {
+            int actualState = getState();
+            int newState = actualState + 1;
+            if (compareAndSetState(actualState, newState))
+            {
+               return;
+            }
+         }
+      }
+      
+      
+      public boolean tryReleaseShared(int numberOfReleases)
+      {
+         for (;;)
+         {
+            int actualState = getState();
+            if (actualState == 0)
+            {
+               return true;
+            }
+            
+            int newState = getState() - numberOfReleases;
+            
+            if (compareAndSetState(actualState, newState))
+            {
+               return newState == 0; 
+            }
+         }
+      }
+   }
+   
+   CountSync control = new CountSync();
+   
+   
+   public int getCount()
+   {
+      return control.getCount();
+   }
+   
+   public void up()
+   {
+      control.add();
+   }
+   
+   public void down()
+   {
+      control.releaseShared(1);
+   }
+   
+   public void waitCompletion() throws InterruptedException
+   {
+      control.acquireSharedInterruptibly(1);
+   }
+   
+   public void waitCompletion(int seconds) throws InterruptedException
+   {
+      if (!control.tryAcquireSharedNanos(1, TimeUnit.SECONDS.toNanos(seconds)))
+      {
+         throw new IllegalStateException("Timeout!");
+      }
+   }
+}


Property changes on: trunk/tests/jms-tests/src
___________________________________________________________________
Name: svn:ignore
   + local


Modified: trunk/tests/src/org/jboss/messaging/tests/integration/core/asyncio/impl/MultiThreadWriteNativeTest.java
===================================================================
--- trunk/tests/src/org/jboss/messaging/tests/integration/core/asyncio/impl/MultiThreadWriteNativeTest.java	2008-05-12 14:57:22 UTC (rev 4173)
+++ trunk/tests/src/org/jboss/messaging/tests/integration/core/asyncio/impl/MultiThreadWriteNativeTest.java	2008-05-12 21:53:04 UTC (rev 4174)
@@ -119,6 +119,13 @@
        position.set(0);
    }
    
+   protected void tearDown() throws Exception
+   {
+      super.tearDown();
+      
+      assertEquals(0, AsynchronousFileImpl.getTotalMaxIO());
+   }
+   
    public void testMultipleASynchronousWrites() throws Throwable
    {
        executeTest(false);
@@ -134,43 +141,48 @@
        log.info(sync?"Sync test:":"Async test");
        AsynchronousFileImpl jlibAIO = new AsynchronousFileImpl();
        jlibAIO.open(FILE_NAME, 21000);
-       log.debug("Preallocating file");
-      
-       jlibAIO.fill(0l, NUMBER_OF_THREADS,  SIZE * NUMBER_OF_LINES, (byte)0);
-       log.debug("Done Preallocating file");
-       
-       CountDownLatch latchStart = new CountDownLatch (NUMBER_OF_THREADS + 1);
-       
-       ArrayList<ThreadProducer> list = new ArrayList<ThreadProducer>(NUMBER_OF_THREADS);
-       for(int i=0;i<NUMBER_OF_THREADS;i++)
+       try
        {
-           ThreadProducer producer = new ThreadProducer("Thread " + i, latchStart, jlibAIO, sync);
-           list.add(producer);
-           producer.start();
+          log.debug("Preallocating file");
+         
+          jlibAIO.fill(0l, NUMBER_OF_THREADS,  SIZE * NUMBER_OF_LINES, (byte)0);
+          log.debug("Done Preallocating file");
+          
+          CountDownLatch latchStart = new CountDownLatch (NUMBER_OF_THREADS + 1);
+          
+          ArrayList<ThreadProducer> list = new ArrayList<ThreadProducer>(NUMBER_OF_THREADS);
+          for(int i=0;i<NUMBER_OF_THREADS;i++)
+          {
+              ThreadProducer producer = new ThreadProducer("Thread " + i, latchStart, jlibAIO, sync);
+              list.add(producer);
+              producer.start();
+          }
+          
+          latchStart.countDown();
+          latchStart.await();
+          
+          
+          long startTime = System.currentTimeMillis();
+          
+   
+          
+          for (ThreadProducer producer: list)
+          {
+              producer.join();
+              if (producer.failed != null)
+              {
+                  throw producer.failed;
+              }
+          }
+          long endTime = System.currentTimeMillis();
+          
+          log.debug((sync?"Sync result:":"Async result:") + " Records/Second = " + (NUMBER_OF_THREADS * NUMBER_OF_LINES * 1000 / (endTime - startTime)) + " total time = " + (endTime - startTime) + " total number of records = " + (NUMBER_OF_THREADS * NUMBER_OF_LINES));
        }
-       
-       latchStart.countDown();
-       latchStart.await();
-       
-       
-       long startTime = System.currentTimeMillis();
-       
-
-       
-       for (ThreadProducer producer: list)
+       finally
        {
-           producer.join();
-           if (producer.failed != null)
-           {
-               throw producer.failed;
-           }
+          jlibAIO.close();
        }
-       long endTime = System.currentTimeMillis();
        
-       log.debug((sync?"Sync result:":"Async result:") + " Records/Second = " + (NUMBER_OF_THREADS * NUMBER_OF_LINES * 1000 / (endTime - startTime)) + " total time = " + (endTime - startTime) + " total number of records = " + (NUMBER_OF_THREADS * NUMBER_OF_LINES));
-
-       jlibAIO.close();
-       
    }
    
    

Modified: trunk/tests/src/org/jboss/messaging/tests/integration/core/asyncio/impl/SingleThreadWriteNativeTest.java
===================================================================
--- trunk/tests/src/org/jboss/messaging/tests/integration/core/asyncio/impl/SingleThreadWriteNativeTest.java	2008-05-12 14:57:22 UTC (rev 4173)
+++ trunk/tests/src/org/jboss/messaging/tests/integration/core/asyncio/impl/SingleThreadWriteNativeTest.java	2008-05-12 21:53:04 UTC (rev 4174)
@@ -50,6 +50,12 @@
        File file = new File(FILE_NAME);
        file.delete();
    }
+   
+   protected void tearDown() throws Exception
+   {
+      super.tearDown();
+      assertEquals(0, AsynchronousFileImpl.getTotalMaxIO());
+   }
 
    private void encodeBufer(ByteBuffer buffer)
    {
@@ -147,7 +153,8 @@
        }
        finally
        {
-           try {controller.close();} catch (Exception ignored){}
+          try {controller.close();} catch (Exception ignored){}
+          try {controller2.close();} catch (Exception ignored){}
        }
        
        
@@ -499,49 +506,53 @@
    private void validateData(int numberOfLines, int size, int aioLimit) throws Exception
    {
        final AsynchronousFileImpl controller = new AsynchronousFileImpl();
-       controller.open(FILE_NAME, aioLimit);
-       
-       ByteBuffer compareBlock = ByteBuffer.allocateDirect(size);
-       encodeBufer(compareBlock);
-       
-       ByteBuffer readBuffer = controller.newBuffer(size);
-       
-       
-       boolean firstInvalid = false;
-       for (int i=0;i<numberOfLines;i++)
+       try
        {
-          if (i % 1000 == 0)
-          {
-             log.info("line = " + i);
-          }
-          CountDownLatch latch = new CountDownLatch(1);
-          LocalAIO callback = new LocalAIO(latch);
-          controller.read(i * size, size, readBuffer, callback);
-
-          latch.await();
+          controller.open(FILE_NAME, aioLimit);
           
-          if (!compareBuffers(compareBlock, readBuffer))
+          ByteBuffer compareBlock = ByteBuffer.allocateDirect(size);
+          encodeBufer(compareBlock);
+          
+          ByteBuffer readBuffer = controller.newBuffer(size);
+          
+          
+          boolean firstInvalid = false;
+          for (int i=0;i<numberOfLines;i++)
           {
-             //log.info("Invalid line at " + i);
-             firstInvalid=true;
-          }
-          else
-          {
-             if (firstInvalid)
+             if (i % 1000 == 0)
              {
-                for (int line=0;line<10;line++) log.info("*********************************************");
-                log.warn("Valid line after an invalid line!!!");
+                log.info("line = " + i);
              }
+             CountDownLatch latch = new CountDownLatch(1);
+             LocalAIO callback = new LocalAIO(latch);
+             controller.read(i * size, size, readBuffer, callback);
+   
+             latch.await();
+             
+             if (!compareBuffers(compareBlock, readBuffer))
+             {
+                //log.info("Invalid line at " + i);
+                firstInvalid=true;
+             }
+             else
+             {
+                if (firstInvalid)
+                {
+                   for (int line=0;line<10;line++) log.info("*********************************************");
+                   log.warn("Valid line after an invalid line!!!");
+                }
+             }
+             
+             readBuffer.position(100);
+             ByteBuffer buf1 = readBuffer.slice();
+             
+             //System.out.println("buf1=" + buf1);
           }
-          
-          readBuffer.position(100);
-          ByteBuffer buf1 = readBuffer.slice();
-          
-          //System.out.println("buf1=" + buf1);
-          
-          
-          
        }
+       finally
+       {
+          controller.close();
+       }
    }
    
    

Modified: trunk/tests/src/org/jboss/messaging/tests/unit/core/journal/impl/AIOSequentialFileFactoryTest.java
===================================================================
--- trunk/tests/src/org/jboss/messaging/tests/unit/core/journal/impl/AIOSequentialFileFactoryTest.java	2008-05-12 14:57:22 UTC (rev 4173)
+++ trunk/tests/src/org/jboss/messaging/tests/unit/core/journal/impl/AIOSequentialFileFactoryTest.java	2008-05-12 21:53:04 UTC (rev 4174)
@@ -24,10 +24,18 @@
 
 import java.io.File;
 import java.nio.ByteBuffer;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
 
 import org.jboss.messaging.core.journal.SequentialFile;
 import org.jboss.messaging.core.journal.SequentialFileFactory;
 import org.jboss.messaging.core.journal.impl.AIOSequentialFileFactory;
+import org.jboss.messaging.core.journal.IOCallback;
 
 public class AIOSequentialFileFactoryTest extends SequentialFileFactoryTestBase
 {
@@ -52,12 +60,122 @@
    
    public void testBuffer() throws Exception
    {
-      SequentialFile file = factory.createSequentialFile("filtetmp.log", true);
+      SequentialFile file = factory.createSequentialFile("filtetmp.log", true, 10);
       file.open();
-      ByteBuffer buff = file.newBuffer(10);
+      ByteBuffer buff = factory.newBuffer(10);
       assertEquals(512, buff.limit());
-      //ByteBuffer buffer = 
+      file.close();
    }
    
+   public void testBlockCallback() throws Exception
+   {
+      class BlockCallback implements IOCallback
+      {
+         
+         int countDone = 0;
+         int countError = 0;
+         CountDownLatch blockLatch;
 
+         BlockCallback()
+         {
+            this.blockLatch = new CountDownLatch(1);
+         }
+         
+         public void release()
+         {
+            blockLatch.countDown();
+         }
+         
+         public void done()
+         {
+            
+           try
+            {
+               blockLatch.await();
+            } catch (InterruptedException e)
+            {
+               e.printStackTrace();
+            }
+            
+            countDone ++;
+            
+            
+            
+         }
+
+         public void onError(int errorCode, String errorMessage)
+         {
+            try
+            {
+               blockLatch.await();
+            } catch (InterruptedException e)
+            {
+               e.printStackTrace();
+            }
+            
+            countError ++;
+         }
+      }
+      
+      BlockCallback callback = new BlockCallback();
+      
+      final int NUMBER_OF_RECORDS = 10000;
+      
+      SequentialFile file = factory.createSequentialFile("callbackBlock.log", true, 1000);
+      file.open();
+      file.fill(0, 512 * NUMBER_OF_RECORDS, (byte)'a');
+
+      
+      for (int i=0; i<NUMBER_OF_RECORDS; i++)
+      {
+         ByteBuffer buffer = factory.newBuffer(512);
+         
+         buffer.putInt(i + 10);
+         
+         for (int j=buffer.position(); j<buffer.limit(); j++)
+         {
+            buffer.put((byte)'b');
+         }
+         
+         file.write(buffer, true, callback);
+      }
+      
+      
+      callback.release();
+      file.close();
+      assertEquals(NUMBER_OF_RECORDS, callback.countDone);
+      assertEquals(0, callback.countError);
+      
+      
+      
+      file.open();
+      
+      ByteBuffer buffer = factory.newBuffer(512);
+
+      for (int i=0; i<NUMBER_OF_RECORDS; i++)
+      {
+         
+         file.read(buffer);
+         buffer.rewind();
+         
+         int recordRead = buffer.getInt();
+         
+         assertEquals(i + 10, recordRead);
+         
+         for (int j=buffer.position(); j<buffer.limit(); j++)
+         {
+            assertEquals((byte)'b', buffer.get());
+         }
+         
+       }
+      
+      
+      file.close();
+      
+      
+      
+      
+   }
+   
+
 }

Modified: trunk/tests/src/org/jboss/messaging/tests/unit/core/journal/impl/JournalImplTestBase.java
===================================================================
--- trunk/tests/src/org/jboss/messaging/tests/unit/core/journal/impl/JournalImplTestBase.java	2008-05-12 14:57:22 UTC (rev 4173)
+++ trunk/tests/src/org/jboss/messaging/tests/unit/core/journal/impl/JournalImplTestBase.java	2008-05-12 21:53:04 UTC (rev 4174)
@@ -29,12 +29,14 @@
 import java.util.ListIterator;
 import java.util.Map;
 
+import org.jboss.messaging.core.asyncio.impl.AsynchronousFileImpl;
 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.tests.unit.core.journal.impl.fakes.FakeCallback;
 import org.jboss.messaging.tests.util.RandomUtil;
 import org.jboss.messaging.tests.util.UnitTestCase;
 
@@ -57,6 +59,8 @@
 	
 	protected Map<Long, TransactionHolder> transactions = new LinkedHashMap<Long, TransactionHolder>();
 	
+	protected int maxAIO;
+	
 	protected int minFiles;
 	
 	protected int fileSize;
@@ -97,7 +101,9 @@
 		
 		fileFactory = null;
 		
-		journal = null;;
+		journal = null;
+		
+		assertEquals(0, AsynchronousFileImpl.getTotalMaxIO());
 	}
 	
 	protected void resetFileFactory() throws Exception
@@ -109,17 +115,26 @@
 	
 	// Private ---------------------------------------------------------------------------------
 	
-	protected void setup(int minFreeFiles, int fileSize, boolean sync)
-	{     
-		this.minFiles = minFreeFiles;
-		this.fileSize = fileSize;
-		this.sync = sync;
-	}
-	
+   protected void setup(int minFreeFiles, int fileSize, boolean sync, int maxAIO)
+   {     
+      this.minFiles = minFreeFiles;
+      this.fileSize = fileSize;
+      this.sync = sync;
+      this.maxAIO = maxAIO;
+   }
+   
+   protected void setup(int minFreeFiles, int fileSize, boolean sync)
+   {     
+      this.minFiles = minFreeFiles;
+      this.fileSize = fileSize;
+      this.sync = sync;
+      this.maxAIO = 1000;
+   }
+   
 	public void createJournal() throws Exception
 	{     
 		journal =
-			new JournalImpl(fileSize, minFiles, sync, fileFactory, 1000, filePrefix, fileExtension);
+			new JournalImpl(fileSize, minFiles, sync, fileFactory, 1000, filePrefix, fileExtension, maxAIO);
 	}
 	
 	protected void startJournal() throws Exception
@@ -190,9 +205,9 @@
 		{     
 			byte[] record = generateRecord(size);
 			
-			journal.appendAddRecord(arguments[i], record);
+			journal.appendAddRecord(arguments[i], (byte)0, record);
 			
-			records.add(new RecordInfo(arguments[i], record, false));         
+			records.add(new RecordInfo(arguments[i], (byte)0, record, false));         
 		}
 	}
 	
@@ -202,9 +217,9 @@
 		{     
 			byte[] updateRecord = generateRecord(recordLength);
 			
-			journal.appendUpdateRecord(arguments[i], updateRecord);
+			journal.appendUpdateRecord(arguments[i], (byte)0, updateRecord);
 			
-			records.add(new RecordInfo(arguments[i], updateRecord, true)); 
+			records.add(new RecordInfo(arguments[i], (byte)0, updateRecord, true)); 
 		}
 	}
 	
@@ -227,9 +242,9 @@
 			// SIZE_BYTE + SIZE_LONG + SIZE_LONG + SIZE_INT + record.length + SIZE_BYTE
 			byte[] record = generateRecord(recordLength - JournalImpl.SIZE_ADD_RECORD_TX );
 			
-			journal.appendAddRecordTransactional(txID, arguments[i], record);
+			journal.appendAddRecordTransactional(txID, (byte)0, arguments[i], record);
 			
-			tx.records.add(new RecordInfo(arguments[i], record, false));
+			tx.records.add(new RecordInfo(arguments[i], (byte)0, record, false));
 			
 		}     
 	}
@@ -242,9 +257,9 @@
 		{     
 			byte[] updateRecord = generateRecord(recordLength - JournalImpl.SIZE_UPDATE_RECORD_TX );
 			
-			journal.appendUpdateRecordTransactional(txID, arguments[i], updateRecord);
+			journal.appendUpdateRecordTransactional(txID, (byte)0, arguments[i], updateRecord);
 			
-			tx.records.add(new RecordInfo(arguments[i], updateRecord, true));
+			tx.records.add(new RecordInfo(arguments[i], (byte)0, updateRecord, true));
 		}     
 	}
 	

Modified: trunk/tests/src/org/jboss/messaging/tests/unit/core/journal/impl/JournalImplTestUnit.java
===================================================================
--- trunk/tests/src/org/jboss/messaging/tests/unit/core/journal/impl/JournalImplTestUnit.java	2008-05-12 14:57:22 UTC (rev 4173)
+++ trunk/tests/src/org/jboss/messaging/tests/unit/core/journal/impl/JournalImplTestUnit.java	2008-05-12 21:53:04 UTC (rev 4174)
@@ -102,7 +102,7 @@
 	{
 		try
 		{
-			new JournalImpl(JournalImpl.MIN_FILE_SIZE - 1, 10, true, fileFactory, 5000, filePrefix, fileExtension);
+			new JournalImpl(JournalImpl.MIN_FILE_SIZE - 1, 10, true, fileFactory, 5000, filePrefix, fileExtension, 1);
 			
 			fail("Should throw exception");
 		}
@@ -113,7 +113,7 @@
 		
 		try
 		{
-			new JournalImpl(10 * 1024, 1, true, fileFactory, 5000, filePrefix, fileExtension);
+			new JournalImpl(10 * 1024, 1, true, fileFactory, 5000, filePrefix, fileExtension, 1);
 			
 			fail("Should throw exception");
 		}
@@ -124,7 +124,7 @@
 		
 		try
 		{
-			new JournalImpl(10 * 1024, 10, true, null, 5000, filePrefix, fileExtension);
+			new JournalImpl(10 * 1024, 10, true, null, 5000, filePrefix, fileExtension, 1);
 			
 			fail("Should throw exception");
 		}
@@ -135,7 +135,7 @@
 		
 		try
 		{
-			new JournalImpl(10 * 1024, 10, true, fileFactory, JournalImpl.MIN_TASK_PERIOD - 1, filePrefix, fileExtension);
+			new JournalImpl(10 * 1024, 10, true, fileFactory, JournalImpl.MIN_TASK_PERIOD - 1, filePrefix, fileExtension, 1);
 			
 			fail("Should throw exception");
 		}
@@ -146,7 +146,7 @@
 		
 		try
 		{
-			new JournalImpl(10 * 1024, 10, true, fileFactory, 5000, null, fileExtension);
+			new JournalImpl(10 * 1024, 10, true, fileFactory, 5000, null, fileExtension, 1);
 			
 			fail("Should throw exception");
 		}
@@ -155,17 +155,28 @@
 			//Ok
 		}
 		
-		try
-		{
-			new JournalImpl(10 * 1024, 10, true, fileFactory, 5000, filePrefix, null);
-			
-			fail("Should throw exception");
-		}
-		catch (NullPointerException e)
-		{
-			//Ok
-		}
-		
+      try
+      {
+         new JournalImpl(10 * 1024, 10, true, fileFactory, 5000, filePrefix, null, 1);
+         
+         fail("Should throw exception");
+      }
+      catch (NullPointerException e)
+      {
+         //Ok
+      }
+      
+      try
+      {
+         new JournalImpl(10 * 1024, 10, true, fileFactory, 5000, filePrefix, null, 0);
+         
+         fail("Should throw exception");
+      }
+      catch (NullPointerException e)
+      {
+         //Ok
+      }
+      
 	}
 	
 	public void testFilesImmediatelyAfterload() throws Exception
@@ -1999,18 +2010,18 @@
 		{
 			byte[] record = generateRecord(10 + (int)(1500 * Math.random()));
 			
-			journal.appendAddRecord(i, record);
+			journal.appendAddRecord(i, (byte)0, record);
 			
-			records.add(new RecordInfo(i, record, false));
+			records.add(new RecordInfo(i, (byte)0, record, false));
 		}
 		
 		for (int i = 0; i < 1000; i++)
 		{
 			byte[] record = generateRecord(10 + (int)(1024 * Math.random()));
 			
-			journal.appendUpdateRecord(i, record);
+			journal.appendUpdateRecord(i, (byte)0, record);
 			
-			records.add(new RecordInfo(i, record, true));
+			records.add(new RecordInfo(i, (byte)0, record, true));
 		}
 		
 		for (int i = 0; i < 1000; i++)

Modified: trunk/tests/src/org/jboss/messaging/tests/unit/core/journal/impl/SequentialFileFactoryTestBase.java
===================================================================
--- trunk/tests/src/org/jboss/messaging/tests/unit/core/journal/impl/SequentialFileFactoryTestBase.java	2008-05-12 14:57:22 UTC (rev 4173)
+++ trunk/tests/src/org/jboss/messaging/tests/unit/core/journal/impl/SequentialFileFactoryTestBase.java	2008-05-12 21:53:04 UTC (rev 4174)
@@ -26,9 +26,11 @@
 import java.util.List;
 import java.util.UUID;
 
+import org.jboss.messaging.core.asyncio.impl.AsynchronousFileImpl;
 import org.jboss.messaging.core.journal.SequentialFile;
 import org.jboss.messaging.core.journal.SequentialFileFactory;
 import org.jboss.messaging.core.logging.Logger;
+import org.jboss.messaging.tests.unit.core.journal.impl.fakes.FakeCallback;
 import org.jboss.messaging.tests.util.UnitTestCase;
 
 /**
@@ -40,7 +42,7 @@
  */
 public abstract class SequentialFileFactoryTestBase extends UnitTestCase
 {
-   private static final Logger log = Logger.getLogger(SequentialFileFactoryTestBase.class);
+   protected final Logger log = Logger.getLogger(this.getClass());
    
    
    protected void setUp() throws Exception
@@ -50,6 +52,12 @@
       factory = createFactory();
    }
    
+   protected void tearDown() throws Exception
+   {
+      super.tearDown();
+      assertEquals(0, AsynchronousFileImpl.getTotalMaxIO());
+   }
+   
    protected abstract SequentialFileFactory createFactory();
    
    protected SequentialFileFactory factory;
@@ -66,19 +74,21 @@
          
          expectedFiles.add(fileName);
          
-         SequentialFile sf = factory.createSequentialFile(fileName, false);
+         SequentialFile sf = factory.createSequentialFile(fileName, false, 1);
          
          sf.open();
          
          assertEquals(fileName, sf.getFileName());
+         
+         sf.close();
       }           
       
       //Create a couple with a different extension - they shouldn't be picked up
       
-      SequentialFile sf1 = factory.createSequentialFile("different.file", false);
+      SequentialFile sf1 = factory.createSequentialFile("different.file", false, 1);
       sf1.open();
       
-      SequentialFile sf2 = factory.createSequentialFile("different.cheese", false);
+      SequentialFile sf2 = factory.createSequentialFile("different.cheese", false, 1);
       sf2.open();
                   
       List<String> fileNames = factory.listFiles("jbm");
@@ -101,33 +111,44 @@
       assertEquals(1, fileNames.size());
       
       assertTrue(fileNames.contains("different.cheese"));   
+      
+      sf1.close();
+      sf2.close();
    }
    
    
    public void testFill() throws Exception
    {
-      SequentialFile sf = factory.createSequentialFile("fill.jbm", true);
+      SequentialFile sf = factory.createSequentialFile("fill.jbm", true, 1);
       
       sf.open();
       
-      checkFill(sf, 0, 2048, (byte)'X');
+      try
+      {
       
-      checkFill(sf, 512, 512, (byte)'Y');
-      
-      checkFill(sf, 0, 1, (byte)'Z');
-      
-      checkFill(sf, 512, 1, (byte)'A');
-      
-      checkFill(sf, 1024, 512*4, (byte)'B');
+         checkFill(sf, 0, 2048, (byte)'X');
+         
+         checkFill(sf, 512, 512, (byte)'Y');
+         
+         checkFill(sf, 0, 1, (byte)'Z');
+         
+         checkFill(sf, 512, 1, (byte)'A');
+         
+         checkFill(sf, 1024, 512*4, (byte)'B');
+      }
+      finally
+      {
+         sf.close();
+      }
    }
    
    public void testDelete() throws Exception
    {
-      SequentialFile sf = factory.createSequentialFile("delete-me.jbm", true);
+      SequentialFile sf = factory.createSequentialFile("delete-me.jbm", true, 1);
       
       sf.open();
       
-      SequentialFile sf2 = factory.createSequentialFile("delete-me2.jbm", true);
+      SequentialFile sf2 = factory.createSequentialFile("delete-me2.jbm", true, 1);
       
       sf2.open();
       
@@ -147,45 +168,55 @@
       
       assertTrue(fileNames.contains("delete-me2.jbm"));
       
+      sf2.close();
+      
    }
    
    public void testWriteandRead() throws Exception
    {
-      SequentialFile sf = factory.createSequentialFile("write.jbm", true);
+      SequentialFile sf = factory.createSequentialFile("write.jbm", true, 1);
       
       sf.open();
       
       String s1 = "aardvark";
       byte[] bytes1 = s1.getBytes("UTF-8");
-      ByteBuffer bb1 = sf.wrapBuffer(bytes1);
+      ByteBuffer bb1 = factory.wrapBuffer(bytes1);
       
       String s2 = "hippopotamus";
       byte[] bytes2 = s2.getBytes("UTF-8");
-      ByteBuffer bb2 = sf.wrapBuffer(bytes2);
+      ByteBuffer bb2 = factory.wrapBuffer(bytes2);
       
       String s3 = "echidna";
       byte[] bytes3 = s3.getBytes("UTF-8");
-      ByteBuffer bb3 = sf.wrapBuffer(bytes3);
+      ByteBuffer bb3 = factory.wrapBuffer(bytes3);
       
-      int bytesWritten = sf.write(bb1, true);
+      FakeCallback callback = new FakeCallback();
+      int bytesWritten = sf.write(bb1, true, callback);
+      callback.waitComplete();
       
       assertEquals(calculateRecordSize(bytes1.length, sf.getAlignment()), bytesWritten);
       
-      bytesWritten = sf.write(bb2, true);
+      callback = new FakeCallback();
+      bytesWritten = sf.write(bb2, true, callback);
+      callback.waitComplete();
       
       assertEquals(calculateRecordSize(bytes2.length, sf.getAlignment()), bytesWritten);
       
-      bytesWritten = sf.write(bb3, true);
+      callback = new FakeCallback();
+      bytesWritten = sf.write(bb3, true, callback);
+      callback.waitComplete();
       
       assertEquals(calculateRecordSize(bytes3.length, sf.getAlignment()), bytesWritten);
       
       sf.position(0);
       
-      ByteBuffer rb1 = sf.newBuffer(bytes1.length);
-      ByteBuffer rb2 = sf.newBuffer(bytes2.length);
-      ByteBuffer rb3 = sf.newBuffer(bytes3.length);
+      ByteBuffer rb1 = factory.newBuffer(bytes1.length);
+      ByteBuffer rb2 = factory.newBuffer(bytes2.length);
+      ByteBuffer rb3 = factory.newBuffer(bytes3.length);
 
-      int bytesRead = sf.read(rb1);
+      callback = new FakeCallback();
+      int bytesRead = sf.read(rb1, callback);
+      callback.waitComplete();
       assertEquals(calculateRecordSize(bytes1.length, sf.getAlignment()), bytesRead);     
 
       for (int i=0; i<bytes1.length; i++)
@@ -193,7 +224,9 @@
       	assertEquals(bytes1[i], rb1.get(i));
       }
       
-      bytesRead = sf.read(rb2);
+      callback = new FakeCallback();
+      bytesRead = sf.read(rb2, callback);
+      callback.waitComplete();
       assertEquals(calculateRecordSize(bytes2.length, sf.getAlignment()), bytesRead);     
       for (int i=0; i<bytes2.length; i++)
       {
@@ -201,42 +234,52 @@
       }
       
       
-      bytesRead = sf.read(rb3);
+      callback = new FakeCallback();
+      bytesRead = sf.read(rb3, callback);
+      callback.waitComplete();
       assertEquals(calculateRecordSize(bytes3.length, sf.getAlignment()), bytesRead);     
       for (int i=0; i<bytes3.length; i++)
       {
       	assertEquals(bytes3[i], rb3.get(i));
       }
       
+      sf.close();
+      
    }
    
    public void testPosition() throws Exception
    {
-      SequentialFile sf = factory.createSequentialFile("position.jbm", true);
+      SequentialFile sf = factory.createSequentialFile("position.jbm", true, 1);
       
       sf.open();
       
       String s1 = "orange";
       byte[] bytes1 = s1.getBytes("UTF-8");
-      ByteBuffer bb1 = sf.wrapBuffer(bytes1); 
+      ByteBuffer bb1 = factory.wrapBuffer(bytes1); 
       
       String s2 = "grapefruit";
-      byte[] bytes2 = s2.getBytes("UTF-8");
-      ByteBuffer bb2 = sf.wrapBuffer(bytes2);
+      byte[] bytes2 = s1.getBytes("UTF-8");
+      ByteBuffer bb2 = factory.wrapBuffer(bytes2);
       
       String s3 = "lemon";
       byte[] bytes3 = s3.getBytes("UTF-8");
-      ByteBuffer bb3 = sf.wrapBuffer(bytes3);
+      ByteBuffer bb3 = factory.wrapBuffer(bytes3);
       
-      int bytesWritten = sf.write(bb1, true);
+      FakeCallback callback = new FakeCallback();
+      int bytesWritten = sf.write(bb1, true, callback);
+      callback.waitComplete();
       
       assertEquals(bb1.limit(), bytesWritten);
       
-      bytesWritten = sf.write(bb2, true);
+      callback = new FakeCallback();
+      bytesWritten = sf.write(bb2, true, callback);
+      callback.waitComplete();
       
       assertEquals(bb2.limit(), bytesWritten);
       
-      bytesWritten = sf.write(bb3, true);
+      callback = new FakeCallback();
+      bytesWritten = sf.write(bb3, true, callback);
+      callback.waitComplete();
       
       assertEquals(bb3.limit(), bytesWritten);
       
@@ -246,9 +289,9 @@
       
       byte[] rbytes3 = new byte[bytes3.length];
       
-      ByteBuffer rb1 = sf.newBuffer(rbytes1.length);
-      ByteBuffer rb2 = sf.newBuffer(rbytes2.length);
-      ByteBuffer rb3 = sf.newBuffer(rbytes3.length);
+      ByteBuffer rb1 = factory.newBuffer(rbytes1.length);
+      ByteBuffer rb2 = factory.newBuffer(rbytes2.length);
+      ByteBuffer rb3 = factory.newBuffer(rbytes3.length);
       
       sf.position(bb1.limit() + bb2.limit());
       
@@ -271,20 +314,24 @@
       assertEquals(rb1.limit(), bytesRead);
       rb1.get(rbytes1);
       
-      assertByteArraysEquivalent(bytes1, rbytes1);    
+      assertByteArraysEquivalent(bytes1, rbytes1);
+      
+      sf.close();
    }
     
    public void testOpenClose() throws Exception
    {
-      SequentialFile sf = factory.createSequentialFile("openclose.jbm", true);
+      SequentialFile sf = factory.createSequentialFile("openclose.jbm", true, 1);
       
       sf.open();
       
       String s1 = "cheesecake";
       byte[] bytes1 = s1.getBytes("UTF-8");
-      ByteBuffer bb1 = sf.wrapBuffer(bytes1);
+      ByteBuffer bb1 = factory.wrapBuffer(bytes1);
       
-      int bytesWritten = sf.write(bb1, true);
+      FakeCallback callback = new FakeCallback();
+      int bytesWritten = sf.write(bb1, true, callback);
+      callback.waitComplete();
       
       assertEquals(bb1.limit(), bytesWritten);
       
@@ -304,6 +351,8 @@
       sf.open();
       
       sf.write(bb1, true);
+      
+      sf.close();
    }
    
    // Private ---------------------------------
@@ -318,7 +367,7 @@
       
       file.position(pos);
       
-      ByteBuffer bb = file.newBuffer(size);
+      ByteBuffer bb = factory.newBuffer(size);
       
       int bytesRead = file.read(bb);
       

Added: trunk/tests/src/org/jboss/messaging/tests/unit/core/journal/impl/fakes/FakeCallback.java
===================================================================
--- trunk/tests/src/org/jboss/messaging/tests/unit/core/journal/impl/fakes/FakeCallback.java	                        (rev 0)
+++ trunk/tests/src/org/jboss/messaging/tests/unit/core/journal/impl/fakes/FakeCallback.java	2008-05-12 21:53:04 UTC (rev 4174)
@@ -0,0 +1,40 @@
+package org.jboss.messaging.tests.unit.core.journal.impl.fakes;
+
+import java.util.concurrent.CountDownLatch;
+
+import org.jboss.messaging.core.journal.IOCallback;
+
+public class FakeCallback implements IOCallback
+{
+
+   String msg;
+   CountDownLatch latch;
+   
+   public FakeCallback(CountDownLatch latch)
+   {
+      this.latch = latch;
+   }
+   
+   public FakeCallback()
+   {
+      this.latch = new CountDownLatch(1);
+   }
+   
+   public void done()
+   {
+      latch.countDown();
+   }
+
+   public void onError(int errorCode, String errorMessage)
+   {
+      latch.countDown();
+      this.msg = errorMessage;
+   }
+   
+   public void waitComplete() throws Exception
+   {
+      latch.await();
+   }
+   
+}
+

Modified: trunk/tests/src/org/jboss/messaging/tests/unit/core/journal/impl/fakes/FakeSequentialFileFactory.java
===================================================================
--- trunk/tests/src/org/jboss/messaging/tests/unit/core/journal/impl/fakes/FakeSequentialFileFactory.java	2008-05-12 14:57:22 UTC (rev 4173)
+++ trunk/tests/src/org/jboss/messaging/tests/unit/core/journal/impl/fakes/FakeSequentialFileFactory.java	2008-05-12 21:53:04 UTC (rev 4174)
@@ -45,7 +45,7 @@
       
    private Map<String, FakeSequentialFile> fileMap = new ConcurrentHashMap<String, FakeSequentialFile>();
    
-   public SequentialFile createSequentialFile(final String fileName, final boolean sync) throws Exception
+   public SequentialFile createSequentialFile(final String fileName, final boolean sync, final int maxAIO) throws Exception
    {
       FakeSequentialFile sf = fileMap.get(fileName);
       
@@ -90,6 +90,21 @@
       fileMap.clear();
    }
    
+   public boolean supportsCallbacks()
+   {
+      return false;
+   }
+   
+   public ByteBuffer newBuffer(int size)
+   {
+      return ByteBuffer.allocate(size);
+   }
+
+   public ByteBuffer wrapBuffer(byte[] bytes)
+   {
+      return ByteBuffer.wrap(bytes);
+   }
+
    public class FakeSequentialFile implements SequentialFile
    {
       private volatile boolean open;
@@ -259,16 +274,6 @@
          }
       }
 
-      public ByteBuffer newBuffer(int size)
-      {
-         return ByteBuffer.allocate(size);
-      }
-
-      public ByteBuffer wrapBuffer(byte[] bytes)
-      {
-         return ByteBuffer.wrap(bytes);
-      }
-
       public int getAlignment() throws Exception
       {
          return 1;

Modified: trunk/tests/src/org/jboss/messaging/tests/unit/core/journal/impl/timing/JournalImplTestUnit.java
===================================================================
--- trunk/tests/src/org/jboss/messaging/tests/unit/core/journal/impl/timing/JournalImplTestUnit.java	2008-05-12 14:57:22 UTC (rev 4173)
+++ trunk/tests/src/org/jboss/messaging/tests/unit/core/journal/impl/timing/JournalImplTestUnit.java	2008-05-12 21:53:04 UTC (rev 4174)
@@ -22,10 +22,17 @@
 package org.jboss.messaging.tests.unit.core.journal.impl.timing;
 
 import java.util.ArrayList;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
 
 import org.jboss.messaging.tests.unit.core.journal.impl.JournalImplTestBase;
+import org.jboss.messaging.tests.unit.core.journal.impl.fakes.FakeCallback;
+import org.jboss.messaging.core.asyncio.impl.AsynchronousFileImpl;
+import org.jboss.messaging.core.journal.IOCallback;
+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.impl.JournalImpl;
 import org.jboss.messaging.core.logging.Logger;
 
 /**
@@ -39,8 +46,13 @@
 {
    private static final Logger log = Logger.getLogger(JournalImplTestUnit.class);
    
+   protected void tearDown() throws Exception
+   {
+      super.tearDown();
+      
+      assertEquals(0, AsynchronousFileImpl.getTotalMaxIO());
+   }
    
-   
    public void testAddUpdateDeleteManyLargeFileSize() throws Exception
    {
       final int numberAdds = 10000;
@@ -70,7 +82,8 @@
          deletes[i] = i;
       }
       
-      setup(10, 10 * 1024 * 1024, true);
+      // This would take a long time with sync=true, and still validates the file. 
+      setup(10, 10 * 1024 * 1024, false);
       createJournal();
       startJournal();
       load();
@@ -113,14 +126,16 @@
          deletes[i] = i;
       }
       
-      setup(10, 10 * 1024, true);
+      setup(10, 10 * 1024, false);
       createJournal();
       startJournal();
       load();
       add(adds);
       update(updates);
       delete(deletes);
-      stopJournal();
+
+      log.info("Debug journal:" + debugJournal());
+      stopJournal(false);
       createJournal();
       startJournal();
       loadAndCheck();
@@ -140,17 +155,19 @@
       
                   
       byte[] record = generateRecord(recordLength);
+      
+      int NUMBER_OF_RECORDS = 1000;
 
-      for (int count = 0; count < 100000; count++)
+      for (int count = 0; count < NUMBER_OF_RECORDS; count++)
       {
-         journal.appendAddRecord(count, record);
+         journal.appendAddRecord(count, (byte)0, record);
          
-         if (count >= 5000)
+         if (count >= NUMBER_OF_RECORDS / 2)
          {
-            journal.appendDeleteRecord(count - 5000);
+            journal.appendDeleteRecord(count - NUMBER_OF_RECORDS / 2);
          }
          
-         if (count % 10000 == 0)
+         if (count % 100 == 0)
          {
             log.info("Done: " + count);
          }
@@ -158,7 +175,7 @@
       
       long end = System.currentTimeMillis();
       
-      double rate = 1000 * ((double)100000) / (end - start);
+      double rate = 1000 * ((double)NUMBER_OF_RECORDS) / (end - start);
       
       log.info("Rate of " + rate + " adds/removes per sec");
       
@@ -169,11 +186,173 @@
       startJournal();
       journal.load(new ArrayList<RecordInfo>(), new ArrayList<PreparedTransactionInfo>());
       
-      assertEquals(5000, journal.getIDMapSize());
+      assertEquals(NUMBER_OF_RECORDS / 2, journal.getIDMapSize());
       
       stopJournal();
    }
    
+   public void testSpeedNonTransactional() throws Exception
+   {
+      for (int i=0;i<1;i++)
+      {
+         this.setUp();
+         System.gc(); Thread.sleep(500);
+         internaltestSpeedNonTransactional();
+         this.tearDown();
+      }
+   }
+   
+   public void internaltestSpeedNonTransactional() throws Exception
+   {
+      
+      final long numMessages = 10000;
+      
+      int numFiles =  (int)(((numMessages * 1024 + 512) / (10 * 1024 * 1024)) * 1.3);
+      
+      if (numFiles<2) numFiles = 2;
+      
+      log.info("num Files=" + numFiles);
+
+      Journal journal =
+         new JournalImpl(10 * 1024 * 1024,  numFiles, true, getFileFactory(),
+               5000, "jbm-data", "jbm", 5000);
+      
+      journal.start();
+      
+      journal.load(new ArrayList<RecordInfo>(), null);
+      
+
+      final CountDownLatch latch = new CountDownLatch((int)numMessages);
+      
+      
+      class LocalCallback implements IOCallback
+      {
+
+         int i=0;
+         String message = null;
+         boolean done = false;
+         CountDownLatch latch;
+         
+         public LocalCallback(int i, CountDownLatch latch)
+         {
+            this.i = i;
+            this.latch = latch;
+         }
+         public void done()
+         {
+            synchronized (this)
+            {
+               if (done)
+               {
+                  message = "done received in duplicate";
+               }
+               done = true;
+               this.latch.countDown();
+            }
+         }
+
+         public void onError(int errorCode, String errorMessage)
+         {
+            synchronized (this)
+            {
+               System.out.println("********************** Error = " + (i++));
+               message = errorMessage;
+               latch.countDown();
+            }
+         }
+         
+      }
+      
+      
+      log.info("Adding data");
+      byte[] data = new byte[700];
+      
+      long start = System.currentTimeMillis();
+      
+      for (int i = 0; i < numMessages; i++)
+      {
+         journal.appendAddRecord(i, (byte)0, data);
+      }
+      
+      long end = System.currentTimeMillis();
+      
+      double rate = 1000 * (double)numMessages / (end - start);
+      
+      boolean failed = false;
+      
+      // If this fails it is probably because JournalImpl it is closing the files without waiting all the completes to arrive first
+      assertFalse(failed);
+      
+      
+      log.info("Rate " + rate + " records/sec");
+
+      journal.stop();
+      
+      journal =
+         new JournalImpl(10 * 1024 * 1024,  numFiles, true, getFileFactory(),
+               5000, "jbm-data", "jbm", 5000);
+      
+      journal.start();
+      journal.load(new ArrayList<RecordInfo>(), null);
+      journal.stop();
+      
+   }
+   
+   public void testSpeedTransactional() throws Exception
+   {
+      Journal journal =
+         new JournalImpl(10 * 1024 * 1024, 10, true, getFileFactory(),
+               5000, "jbm-data", "jbm", 5000);
+      
+      journal.start();
+      
+      journal.load(new ArrayList<RecordInfo>(), null);
+      
+      try
+      {
+         final int numMessages = 50050;
+         
+         byte[] data = new byte[1024];
+         
+         long start = System.currentTimeMillis();
+         
+         int count = 0;
+         double rates[] = new double[50];
+         for (int i = 0; i < 50; i++)
+         {
+            long startTrans = System.currentTimeMillis();
+            for (int j=0; j<1000; j++)
+            {
+               journal.appendAddRecordTransactional(i, (byte)0, count++, data);
+            }
+            
+            journal.appendCommitRecord(i);
+            
+            long endTrans = System.currentTimeMillis();
+   
+            rates[i] = 1000 * (double)1000 / (endTrans - startTrans);
+         }
+         
+         long end = System.currentTimeMillis();
+         
+         for (double rate: rates)
+         {
+            log.info("Transaction Rate = " + rate + " records/sec");
+            
+         }
+         
+         double rate = 1000 * (double)numMessages / (end - start);
+         
+         log.info("Rate " + rate + " records/sec");
+      }
+      finally
+      {
+         journal.stop();
+      }
+
+   }
+   
+   
 }
 
 

Modified: trunk/tests/src/org/jboss/messaging/tests/unit/core/journal/impl/timing/RealJournalImplAIOTest.java
===================================================================
--- trunk/tests/src/org/jboss/messaging/tests/unit/core/journal/impl/timing/RealJournalImplAIOTest.java	2008-05-12 14:57:22 UTC (rev 4173)
+++ trunk/tests/src/org/jboss/messaging/tests/unit/core/journal/impl/timing/RealJournalImplAIOTest.java	2008-05-12 21:53:04 UTC (rev 4174)
@@ -35,6 +35,7 @@
 import org.jboss.messaging.core.journal.impl.JournalImpl;
 import org.jboss.messaging.core.journal.impl.NIOSequentialFileFactory;
 import org.jboss.messaging.core.logging.Logger;
+import org.jboss.messaging.tests.unit.core.journal.impl.fakes.FakeCallback;
 
 /**
  * 
@@ -59,152 +60,8 @@
       
       file.mkdir();     
       
-      return new NIOSequentialFileFactory(journalDir);
+      return new AIOSequentialFileFactory(journalDir);
    }
    
-   public void testSpeedNonTransactional() throws Exception
-   {
-      for (int i=0;i<1;i++)
-      {
-         this.setUp();
-         System.gc(); Thread.sleep(500);
-         internaltestSpeedNonTransactional();
-         this.tearDown();
-      }
-   }
-   
-   public void internaltestSpeedNonTransactional() throws Exception
-   {
-      
-      final long numMessages = 100000;
-      
-      int numFiles =  (int)(((numMessages * 1024 + 512) / (10 * 1024 * 1024)) * 1.3);
-      
-      if (numFiles<2) numFiles = 2;
-      
-      log.info("num Files=" + numFiles);
-
-      Journal journal =
-         new JournalImpl(10 * 1024 * 1024,  numFiles, true, new AIOSequentialFileFactory(journalDir),
-               5000, "jbm-data", "jbm");
-      
-      journal.start();
-      
-      journal.load(new ArrayList<RecordInfo>(), null);
-      
-
-      final CountDownLatch latch = new CountDownLatch((int)numMessages);
-      
-      
-      class LocalCallback implements IOCallback
-      {
-
-         int i=0;
-         String message = null;
-         boolean done = false;
-         CountDownLatch latch;
-         
-         public LocalCallback(int i, CountDownLatch latch)
-         {
-            this.i = i;
-            this.latch = latch;
-         }
-         public void done()
-         {
-            synchronized (this)
-            {
-               if (done)
-               {
-                  message = "done received in duplicate";
-               }
-               done = true;
-               this.latch.countDown();
-            }
-         }
-
-         public void onError(int errorCode, String errorMessage)
-         {
-            synchronized (this)
-            {
-               System.out.println("********************** Error = " + (i++));
-               message = errorMessage;
-               latch.countDown();
-            }
-         }
-         
-      }
-      
-      
-      log.info("Adding data");
-      byte[] data = new byte[700];
-      
-      long start = System.currentTimeMillis();
-      
-      LocalCallback callback = new LocalCallback(1, latch);
-      for (int i = 0; i < numMessages; i++)
-      {
-         journal.appendAddRecord(i, data, callback);
-      }
-      
-      latch.await(10, TimeUnit.SECONDS);
-      
-      // Validates if the test has completed 
-      assertEquals(0, latch.getCount());
-      
-      long end = System.currentTimeMillis();
-      
-      double rate = 1000 * (double)numMessages / (end - start);
-      
-      boolean failed = false;
-      
-      // If this fails it is probably because JournalImpl it is closing the files without waiting all the completes to arrive first
-      assertFalse(failed);
-      
-      
-      log.info("Rate " + rate + " records/sec");
-
-      journal.stop();
-      
-      journal =
-         new JournalImpl(10 * 1024 * 1024,  numFiles, true, new AIOSequentialFileFactory(journalDir),
-               5000, "jbm-data", "jbm");
-      
-      journal.start();
-      journal.load(new ArrayList<RecordInfo>(), null);
-      journal.stop();
-      
-   }
-   
-   public void testSpeedTransactional() throws Exception
-   {
-      Journal journal =
-         new JournalImpl(10 * 1024 * 1024, 10, true, new AIOSequentialFileFactory(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");
-
-   }
 }
 

Modified: trunk/tests/src/org/jboss/messaging/tests/unit/core/journal/impl/timing/RealJournalImplTest.java
===================================================================
--- trunk/tests/src/org/jboss/messaging/tests/unit/core/journal/impl/timing/RealJournalImplTest.java	2008-05-12 14:57:22 UTC (rev 4173)
+++ trunk/tests/src/org/jboss/messaging/tests/unit/core/journal/impl/timing/RealJournalImplTest.java	2008-05-12 21:53:04 UTC (rev 4174)
@@ -59,75 +59,5 @@
 	}
 	
 	
-	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");
-		
-      journal.stop();
-      
-      journal =
-         new JournalImpl(10 * 1024 * 1024,  10, true, new NIOSequentialFileFactory(journalDir),
-               5000, "jbm-data", "jbm");
-      
-      journal.start();
-      journal.load(new ArrayList<RecordInfo>(), null);
-		
-
-	}
-	
-	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");
-
-	}
 }
 

Modified: trunk/tests/src/org/jboss/messaging/tests/unit/core/message/impl/MessageTest.java
===================================================================
--- trunk/tests/src/org/jboss/messaging/tests/unit/core/message/impl/MessageTest.java	2008-05-12 14:57:22 UTC (rev 4173)
+++ trunk/tests/src/org/jboss/messaging/tests/unit/core/message/impl/MessageTest.java	2008-05-12 21:53:04 UTC (rev 4174)
@@ -21,10 +21,13 @@
  */
 package org.jboss.messaging.tests.unit.core.message.impl;
 
+import java.nio.ByteBuffer;
+
 import org.jboss.messaging.core.client.ClientMessage;
 import org.jboss.messaging.core.client.impl.ClientMessageImpl;
-import org.jboss.messaging.core.message.Message;
-import org.jboss.messaging.core.message.impl.MessageImpl;
+import org.jboss.messaging.core.journal.EncodingSupport;
+import org.jboss.messaging.core.logging.Logger;
+import org.jboss.messaging.core.remoting.impl.mina.BufferWrapper;
 import org.jboss.messaging.core.server.MessageReference;
 import org.jboss.messaging.core.server.Queue;
 import org.jboss.messaging.core.server.QueueFactory;
@@ -32,7 +35,9 @@
 import org.jboss.messaging.core.server.impl.ServerMessageImpl;
 import org.jboss.messaging.tests.unit.core.server.impl.fakes.FakeQueueFactory;
 import org.jboss.messaging.tests.util.UnitTestCase;
+import org.jboss.messaging.util.ByteBufferWrapper;
 import org.jboss.messaging.util.SimpleString;
+import org.jboss.messaging.util.TypedProperties;
 
 /**
  * 
@@ -45,7 +50,9 @@
  */
 public class MessageTest extends UnitTestCase
 {
-	private QueueFactory queueFactory = new FakeQueueFactory();
+   private static final Logger log = Logger.getLogger(MessageTest.class);
+
+   private QueueFactory queueFactory = new FakeQueueFactory();
    
    public void testCreateMessageBeforeSending()
    {
@@ -245,7 +252,59 @@
       
       assertEquals(1, messageDurable.getDurableRefCount());                 
    }
+
+   public void testEncodingMessageProperties()
+   {
+
+      TypedProperties properties = new TypedProperties();
+      properties.putStringProperty(new SimpleString("str"), new SimpleString("Str2"));
+      properties.putStringProperty(new SimpleString("str2"), new SimpleString("Str2"));
+      properties.putBooleanProperty(new SimpleString("str3"), true );
+      properties.putByteProperty(new SimpleString("str4"), (byte)1);
+      properties.putBytesProperty(new SimpleString("str5"), new byte[]{1,2,3,4,5});
+      properties.putShortProperty(new SimpleString("str6"),(short)1);
+      properties.putIntProperty(new SimpleString("str7"), (int)1);
+      properties.putLongProperty(new SimpleString("str8"), (long)1);
+      properties.putFloatProperty(new SimpleString("str9"),(float) 1);
+      properties.putDoubleProperty(new SimpleString("str10"), (double) 1);
+      properties.putCharProperty(new SimpleString("str11"), 'a');
+      
+      checkSizes(properties);
+      
+   }
+
+   public void testEncodingMessage() throws Exception
+   {
+      byte[] bytes = new byte[]{(byte)1, (byte)2, (byte)3};
+      final BufferWrapper bufferBody = new BufferWrapper(bytes.length);
+      bufferBody.putBytes(bytes);
+      
+      
+      SimpleString address = new SimpleString("Simple Destination ");
+      
+      ServerMessageImpl implMsg = new ServerMessageImpl(/* type */ 1, /* durable */ true, /* expiration */ 0,
+            /* timestamp */ 0, /* priority */(byte)0);
+      
+      implMsg.setDestination(address);
+      implMsg.setBody(bufferBody);
+      implMsg.putStringProperty(new SimpleString("Key"), new SimpleString("This String is worthless!"));
+
+      checkSizes(implMsg);
+
+      implMsg.removeProperty(new SimpleString("Key"));
+      
+      checkSizes(implMsg);
+
+   }
    
+   private void checkSizes(EncodingSupport obj)
+   {
+      ByteBuffer bf = ByteBuffer.allocateDirect(1024);
+      ByteBufferWrapper buffer = new ByteBufferWrapper(bf);
+      obj.encode(buffer);
+      assertEquals (buffer.position(), obj.encodeSize());
+   }
+   
 
    
 }

Added: trunk/tests/src/org/jboss/messaging/tests/unit/core/persistence/fakes/FakeBinding.java
===================================================================
--- trunk/tests/src/org/jboss/messaging/tests/unit/core/persistence/fakes/FakeBinding.java	                        (rev 0)
+++ trunk/tests/src/org/jboss/messaging/tests/unit/core/persistence/fakes/FakeBinding.java	2008-05-12 21:53:04 UTC (rev 4174)
@@ -0,0 +1,28 @@
+package org.jboss.messaging.tests.unit.core.persistence.fakes;
+
+import org.jboss.messaging.core.postoffice.Binding;
+import org.jboss.messaging.core.server.Queue;
+import org.jboss.messaging.util.SimpleString;
+
+public class FakeBinding implements Binding
+{
+   SimpleString address;
+   Queue queue;
+   
+   public FakeBinding(SimpleString address, Queue queue)
+   {
+      this.address = address;
+      this.queue = queue;
+   }
+
+   public SimpleString getAddress()
+   {
+      return address;
+   }
+
+   public Queue getQueue()
+   {
+      return queue;
+   }
+   
+}

Added: trunk/tests/src/org/jboss/messaging/tests/unit/core/persistence/fakes/FakePostOffice.java
===================================================================
--- trunk/tests/src/org/jboss/messaging/tests/unit/core/persistence/fakes/FakePostOffice.java	                        (rev 0)
+++ trunk/tests/src/org/jboss/messaging/tests/unit/core/persistence/fakes/FakePostOffice.java	2008-05-12 21:53:04 UTC (rev 4174)
@@ -0,0 +1,109 @@
+package org.jboss.messaging.tests.unit.core.persistence.fakes;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.jboss.messaging.core.filter.Filter;
+import org.jboss.messaging.core.postoffice.Binding;
+import org.jboss.messaging.core.postoffice.FlowController;
+import org.jboss.messaging.core.postoffice.PostOffice;
+import org.jboss.messaging.core.server.Queue;
+import org.jboss.messaging.core.server.QueueFactory;
+import org.jboss.messaging.core.server.ServerMessage;
+import org.jboss.messaging.tests.unit.core.server.impl.fakes.FakeQueueFactory;
+import org.jboss.messaging.util.ConcurrentHashSet;
+import org.jboss.messaging.util.SimpleString;
+
+
+
+/** Maybe this Fake should be moved to postoffice.fakes, but since this 
+ *  Fake only has the basic needed for StorageManagerTest, I have left it here for now */
+public class FakePostOffice implements PostOffice
+{
+
+   ConcurrentHashMap<SimpleString, Binding> bindings = new ConcurrentHashMap<SimpleString, Binding>();
+   
+   QueueFactory queueFactory = new FakeQueueFactory();
+   
+   ConcurrentHashSet<SimpleString> addresses = new ConcurrentHashSet<SimpleString>();
+   
+   public Binding addBinding(SimpleString address, SimpleString queueName,
+         Filter filter, boolean durable, boolean temporary) throws Exception
+   {
+      Queue queue = queueFactory.createQueue(-1, queueName, filter, durable, temporary); 
+      Binding binding = new FakeBinding(address, queue);
+      bindings.put(address, binding);
+      return binding;
+   }
+
+   public boolean addDestination(SimpleString address, boolean temporary)
+         throws Exception
+   {
+      return addresses.addIfAbsent(address);
+   }
+
+   public boolean containsDestination(SimpleString address)
+   {
+      return addresses.contains(address);
+   }
+
+   public Binding getBinding(SimpleString queueName) throws Exception
+   {
+      return bindings.get(queueName);
+   }
+
+   public List<Binding> getBindingsForAddress(SimpleString address)
+         throws Exception
+   {
+      return null;
+   }
+
+   public FlowController getFlowController(SimpleString address)
+   {
+      return null;
+   }
+
+   public Map<SimpleString, List<Binding>> getMappings()
+   {
+      return null;
+   }
+
+   public Set<SimpleString> listAllDestinations()
+   {
+      return null;
+   }
+
+   public Binding removeBinding(SimpleString queueName) throws Exception
+   {
+      return null;
+   }
+
+   public boolean removeDestination(SimpleString address, boolean temporary)
+         throws Exception
+   {
+      // TODO Auto-generated method stub
+      return false;
+   }
+
+   public void start() throws Exception
+   {
+      // TODO Auto-generated method stub
+      
+   }
+
+   public void stop() throws Exception
+   {
+      // TODO Auto-generated method stub
+      
+   }
+
+   public List<org.jboss.messaging.core.server.MessageReference> route(
+         ServerMessage message) throws Exception
+   {
+      // TODO Auto-generated method stub
+      return null;
+   }
+   
+}

Added: trunk/tests/src/org/jboss/messaging/tests/unit/core/persistence/impl/timing/StorageManagerTimingTest.java
===================================================================
--- trunk/tests/src/org/jboss/messaging/tests/unit/core/persistence/impl/timing/StorageManagerTimingTest.java	                        (rev 0)
+++ trunk/tests/src/org/jboss/messaging/tests/unit/core/persistence/impl/timing/StorageManagerTimingTest.java	2008-05-12 21:53:04 UTC (rev 4174)
@@ -0,0 +1,285 @@
+/*
+ * 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.tests.unit.core.persistence.impl.timing;
+
+import java.io.File;
+import java.util.HashMap;
+import java.util.concurrent.atomic.AtomicLong;
+
+import org.jboss.messaging.core.asyncio.impl.AsynchronousFileImpl;
+import org.jboss.messaging.core.config.impl.FileConfiguration;
+import org.jboss.messaging.core.logging.Logger;
+import org.jboss.messaging.core.message.impl.MessageImpl;
+import org.jboss.messaging.core.persistence.impl.journal.JournalStorageManager;
+import org.jboss.messaging.core.remoting.impl.mina.BufferWrapper;
+import org.jboss.messaging.core.server.JournalType;
+import org.jboss.messaging.core.server.Queue;
+import org.jboss.messaging.core.server.impl.ServerMessageImpl;
+import org.jboss.messaging.tests.unit.core.persistence.fakes.FakePostOffice;
+import org.jboss.messaging.tests.util.UnitTestCase;
+import org.jboss.messaging.util.SimpleString;
+
+public class StorageManagerTimingTest extends UnitTestCase
+{
+
+   private static final Logger log = Logger.getLogger(StorageManagerTimingTest.class);
+   
+   protected void tearDown() throws Exception
+   {
+      super.tearDown();
+      assertEquals(0, AsynchronousFileImpl.getTotalMaxIO());
+   }
+
+
+   public void testAIO() throws Exception
+   {
+      // just to do some initial loading.. ignore this rate
+      internalTestStorage(JournalType.ASYNCIO, 1000, 1, 1);
+
+      double rate = internalTestStorage(JournalType.ASYNCIO, 60000, 1, 1)[0];
+      printRates("Rate of AIO, 60000 inserts / commits on every insert", rate);
+      
+      rate = internalTestStorage(JournalType.ASYNCIO, 30000, -1, 1)[0];
+      printRates("Rate of AIO, 30000 inserts / single commit at the end", rate);
+
+      rate = internalTestStorage(JournalType.ASYNCIO, 30000, 5, 1)[0];
+      printRates("Rate of AIO, 30000 inserts / commit every 5 recodds", rate);
+
+      rate = internalTestStorage(JournalType.ASYNCIO, 30000, -1, 1)[0];
+      printRates("Rate of AIO, 30000 inserts / single commit at the end (again)", rate);
+      
+   }
+   
+   public void testAIOMultiThread() throws Exception
+   {
+      double[] rates = internalTestStorage(JournalType.ASYNCIO, 10000, -1, 1);
+      rates = internalTestStorage(JournalType.ASYNCIO, 30000, -1, 5);
+
+      printRates("Rate of AIO, 30000 inserts / single commit at the end", rates);
+
+   
+      rates = internalTestStorage(JournalType.ASYNCIO, 5000, 1, 5);
+
+      printRates("Rate of AIO, 30000 inserts / commit on every insert", rates);
+   }
+   
+   public void testNIO() throws Exception
+   {
+      // just to do some initial loading.. ignore this rate
+      internalTestStorage(JournalType.NIO, 1000, 1, 1);
+      double rate = internalTestStorage(JournalType.NIO, 1000, 1, 1)[0];
+      printRates("Rate of NIO, 1000 inserts, 1000 commits", rate);
+
+      rate = internalTestStorage(JournalType.NIO, 30000, -1, 1)[0];
+      printRates("Rate of NIO, 30000 inserts / single commit at the end", rate);
+
+      rate = internalTestStorage(JournalType.NIO, 30000, 5, 1)[0];
+      printRates("Rate of NIO, 30000 inserts / commit every 5 records", rate);
+   }
+   
+   public void testNIOMultiThread() throws Exception
+   {
+
+      double[] rates = internalTestStorage(JournalType.NIO, 5000, -1, 5);
+      
+      printRates("Rate of NIO, 5000 inserts / single commit at the end", rates);
+      
+      rates = internalTestStorage(JournalType.NIO, 5000, 1, 5);
+
+      printRates("Rate of NIO, 5000 inserts / commit on every insert", rates);
+      
+
+   }
+
+   public double[] internalTestStorage(final JournalType journalType, 
+                                                final long numberOfMessages, 
+                                                final int transInterval,
+                                                final int numberOfThreads) throws Exception
+   {
+      FileConfiguration configuration = new FileConfiguration();
+
+      configuration.start();
+      
+      deleteDirectory(new File(configuration.getBindingsDirectory()));
+      deleteDirectory(new File(configuration.getJournalDirectory()));
+      
+      configuration.setJournalType(journalType);
+      
+      final JournalStorageManager journal = new JournalStorageManager(configuration);
+      journal.start();
+      
+      FakePostOffice office = new FakePostOffice();
+      
+      HashMap<Long, Queue> queues = new HashMap<Long, Queue>();
+      
+      journal.loadMessages(office, queues);
+      
+      final byte[] bytes = new byte[900];
+      
+      for (int i=0;i<bytes.length;i++)
+      {
+         bytes[i] = (byte)('a' + (i%20));
+      }
+      
+      
+      final BufferWrapper buffer = new BufferWrapper(1024);
+      buffer.putBytes(bytes);
+
+      final AtomicLong transactionGenerator = new AtomicLong(1);
+      
+      class LocalThread extends Thread
+      {
+         int id;
+         int commits = 1;
+         Exception e;
+         long totalTime = 0;
+         public LocalThread(int id)
+         {
+            super("LocalThread:" + id);
+            this.id = id;
+         }
+
+         public void run()
+         {
+            try
+            {
+               long start = System.currentTimeMillis();
+               
+               long trans = transactionGenerator.incrementAndGet();
+               boolean commitPending=false;
+               for (long i=1;i<=numberOfMessages;i++)
+               {
+                  
+                  final SimpleString address = new SimpleString("Destination " + i);
+
+                  
+                  ServerMessageImpl implMsg = new ServerMessageImpl(/* type */ 1, /* durable */ true, /* expiration */ 0,
+                        /* timestamp */ 0, /* priority */(byte)0);
+                  
+                  implMsg.putStringProperty(new SimpleString("Key"), new SimpleString("This String is worthless!"));
+
+                  implMsg.setMessageID(i);
+                  implMsg.setBody(buffer);
+                  
+                  implMsg.setDestination(address);
+
+                  
+                  
+                  journal.storeMessageTransactional(trans, implMsg);
+                  
+                  commitPending = true;
+                  
+                  if (transInterval>0 && i%transInterval == 0)
+                  {
+                     journal.commit(trans);
+                     commits ++;
+                     trans = transactionGenerator.incrementAndGet();
+                     commitPending = false;
+                  }
+               }
+               
+               if (commitPending) journal.commit(trans);
+               
+               
+               long end = System.currentTimeMillis();
+               
+               totalTime = end - start;
+            }
+            catch (Exception e)
+            {
+               log.error(e.getMessage(), e);
+               this.e = e;
+            }
+         }
+      }
+
+      try
+      {
+         LocalThread[] threads = new LocalThread[numberOfThreads];
+         
+   
+         for (int i = 0; i < numberOfThreads; i++)
+         {
+            threads[i] = new LocalThread(i);
+         }
+         
+         for (int i = 0; i < numberOfThreads; i++)
+         {
+            threads[i].start();
+         }
+         
+         for (int i = 0; i < numberOfThreads; i++)
+         {
+            threads[i].join();
+         }
+   
+         for (int i = 0; i < numberOfThreads; i++)
+         {
+            assertNull(threads[i].e);
+         }
+         
+         double rates[] = new double[numberOfThreads];
+         
+         for (int i=0; i<numberOfThreads; i++)
+         {
+            rates[i] = (numberOfMessages + threads[i].commits) * 1000 / threads[i].totalTime;
+         }
+
+         return rates;
+      }
+      finally
+      {
+         journal.stop();
+      }
+      
+   }
+
+   
+   private void printRates(String msg, double rate)
+   {
+      printRates(msg, new double[] { rate });
+   }
+   private void printRates(String msg, double[] rates)
+   {
+      double rate = 0;
+      
+      log.info("*************************************************************************");
+      log.info(" " + msg + " ");
+      
+      double totalRate = 0;
+      for (int i=0; i<rates.length; i++)
+      {
+         rate = rates[i];
+         totalRate += rate;
+         if (rates.length>1)
+         {
+            log.info( " Thread " + i + ": = " + rate + " inserts/sec (including commits)");
+         }
+      }
+      
+      log.info( " Total rate     : = " + totalRate + " inserts/sec (including commits)");
+      log.info("*************************************************************************");
+   }
+   
+   
+}

Added: trunk/tests/src/org/jboss/messaging/tests/unit/core/util/VariableLatchTest.java
===================================================================
--- trunk/tests/src/org/jboss/messaging/tests/unit/core/util/VariableLatchTest.java	                        (rev 0)
+++ trunk/tests/src/org/jboss/messaging/tests/unit/core/util/VariableLatchTest.java	2008-05-12 21:53:04 UTC (rev 4174)
@@ -0,0 +1,236 @@
+/*
+  * 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.tests.unit.core.util;
+
+import java.util.concurrent.CountDownLatch;
+
+import junit.framework.TestCase;
+
+import org.jboss.messaging.core.logging.Logger;
+import org.jboss.messaging.util.VariableLatch;
+
+
+/**
+ * 
+ * @author clebert
+ *
+ */
+public class VariableLatchTest extends TestCase
+{
+   private static final Logger log = Logger.getLogger(VariableLatchTest.class);
+   
+   
+   public void testLatchOnSingleThread() throws Exception
+   {
+      VariableLatch latch = new VariableLatch();
+      
+      for (int i=1;i<=10000;i++)
+      {
+         latch.up();
+         assertEquals(i, latch.getCount());
+      }
+      
+      for (int i=10000; i>0; i--)
+      {
+         assertEquals(i, latch.getCount());
+         latch.down();
+         assertEquals(i-1, latch.getCount());
+      }
+      
+      latch.waitCompletion();
+   }
+
+   /** 
+    * 
+    * This test will open numberOfThreads threads, and add numberOfAdds on the VariableLatch
+    * After those addthreads are finished, the latch count should be numberOfThreads * numberOfAdds
+    * Then it will open numberOfThreads threads again releasing numberOfAdds on the VariableLatch
+    * After those releaseThreads are finished, the latch count should be 0
+    * And all the waiting threads should be finished also
+    * 
+    * @throws Exception
+    */
+   public void testLatchOnMultiThread() throws Exception
+   {
+      final VariableLatch latch = new VariableLatch();
+      
+      latch.up(); // We hold at least one, so ThreadWaits won't go away  
+      
+      final int numberOfThreads = 100;
+      final int numberOfAdds = 1000;
+      
+      class ThreadWait extends Thread
+      {
+         boolean waiting = true;
+         public void run()
+         {
+            try
+            {
+               latch.waitCompletion(5);
+            }
+            catch (Exception e)
+            {
+               log.error(e);
+            }
+            waiting = false;
+         }
+      }
+      
+      class ThreadAdd extends Thread
+      {
+         CountDownLatch latchReady; 
+         CountDownLatch latchStart;
+         
+         ThreadAdd(CountDownLatch latchReady, CountDownLatch latchStart)
+         {
+            this.latchReady = latchReady;
+            this.latchStart = latchStart;
+         }
+         
+         public void run()
+         {
+            try
+            {
+               latchReady.countDown();
+               // Everybody should start at the same time, to worse concurrency effects
+               latchStart.await();
+               for (int i=0; i< numberOfAdds; i++)
+               {
+                  latch.up();
+               }
+            }
+            catch (Exception e)
+            {
+               log.error(e.getMessage(), e);
+            }
+         }
+      }
+      
+      CountDownLatch latchReady = new CountDownLatch(numberOfThreads);
+      CountDownLatch latchStart = new CountDownLatch(1);
+      
+      ThreadAdd[] threadAdds = new ThreadAdd[numberOfThreads];
+      ThreadWait waits[] = new ThreadWait[numberOfThreads];
+      
+      
+      for (int i=0; i< numberOfThreads; i++)
+      {
+         threadAdds[i] = new ThreadAdd(latchReady, latchStart);
+         threadAdds[i].start();
+         waits[i] = new ThreadWait();
+         waits[i].start();
+      }
+      
+      latchReady.await();
+      latchStart.countDown();
+      
+      
+      for (int i=0; i< numberOfThreads; i++)
+      {
+         threadAdds[i].join();
+      }
+      
+      for (int i=0; i< numberOfThreads; i++)
+      {
+         assertTrue(waits[i].waiting);
+      }
+      
+      assertEquals(numberOfThreads * numberOfAdds + 1, latch.getCount());
+      
+      class ThreadDown extends Thread
+      {
+         CountDownLatch latchReady; 
+         CountDownLatch latchStart;
+         
+         ThreadDown(CountDownLatch latchReady, CountDownLatch latchStart)
+         {
+            this.latchReady = latchReady;
+            this.latchStart = latchStart;
+         }
+         
+         public void run()
+         {
+            try
+            {
+               latchReady.countDown();
+               // Everybody should start at the same time, to worse concurrency effects
+               latchStart.await();
+               for (int i=0; i< numberOfAdds; i++)
+               {
+                  latch.down();
+               }
+            }
+            catch (Exception e)
+            {
+               log.error(e.getMessage(), e);
+            }
+         }
+      }
+      
+      latchReady = new CountDownLatch(numberOfThreads);
+      latchStart = new CountDownLatch(1);
+      
+      ThreadDown down[] = new ThreadDown[numberOfThreads];
+      
+      for (int i=0; i< numberOfThreads; i++)
+      {
+         down[i] = new ThreadDown(latchReady, latchStart);
+         down[i].start();
+      }
+      
+      latchReady.await();
+      latchStart.countDown();
+      
+      for (int i=0; i< numberOfThreads; i++)
+      {
+         down[i].join();
+      }
+      
+      assertEquals(1, latch.getCount());
+
+      for (int i=0; i< numberOfThreads; i++)
+      {
+         assertTrue(waits[i].waiting);
+      }
+
+      latch.down();
+      
+      for (int i=0; i< numberOfThreads; i++)
+      {
+         waits[i].join();
+      }
+      
+      assertEquals(0, latch.getCount());
+      
+      for (int i=0; i< numberOfThreads; i++)
+      {
+         assertFalse (waits[i].waiting);
+      }
+      
+      
+      
+      
+   }
+   
+
+}




More information about the jboss-cvs-commits mailing list