directory-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From s...@apache.org
Subject svn commit: r1181167 [1/2] - in /directory/apacheds/branches/apacheds-txns: core-api/src/main/java/org/apache/directory/server/core/log/ core-api/src/main/java/org/apache/directory/server/core/txn/ core-api/src/main/java/org/apache/directory/server/cor...
Date Mon, 10 Oct 2011 19:39:37 GMT
Author: saya
Date: Mon Oct 10 19:39:36 2011
New Revision: 1181167

URL: http://svn.apache.org/viewvc?rev=1181167&view=rev
Log:
initial changes for the txn manager. Txn manager handles, begin, commit and abort of txns. It makes use of txn log manager to log begin, abort, commit log records and order of commit records in the wal log defines a serializable order for txns. TXN management is optimized for read only txns. Read only txns do not have to insert any log record into the wal.

TxnLogManager helps txn manager to log txn marker records and also exposes an interface for txns to log changes. 

Also moved log manager packages to core-api and core and replaced assert statements with illegalstate exceptions.


Added:
    directory/apacheds/branches/apacheds-txns/core-api/src/main/java/org/apache/directory/server/core/log/
    directory/apacheds/branches/apacheds-txns/core-api/src/main/java/org/apache/directory/server/core/log/InvalidLogException.java
    directory/apacheds/branches/apacheds-txns/core-api/src/main/java/org/apache/directory/server/core/log/Log.java
    directory/apacheds/branches/apacheds-txns/core-api/src/main/java/org/apache/directory/server/core/log/LogAnchor.java
    directory/apacheds/branches/apacheds-txns/core-api/src/main/java/org/apache/directory/server/core/log/LogAnchorComparator.java
    directory/apacheds/branches/apacheds-txns/core-api/src/main/java/org/apache/directory/server/core/log/LogScanner.java
    directory/apacheds/branches/apacheds-txns/core-api/src/main/java/org/apache/directory/server/core/log/UserLogRecord.java
    directory/apacheds/branches/apacheds-txns/core-api/src/main/java/org/apache/directory/server/core/txn/
    directory/apacheds/branches/apacheds-txns/core-api/src/main/java/org/apache/directory/server/core/txn/TxnLogManager.java
    directory/apacheds/branches/apacheds-txns/core-api/src/main/java/org/apache/directory/server/core/txn/TxnManager.java
    directory/apacheds/branches/apacheds-txns/core-api/src/main/java/org/apache/directory/server/core/txn/logedit/
    directory/apacheds/branches/apacheds-txns/core-api/src/main/java/org/apache/directory/server/core/txn/logedit/AbstractLogEdit.java
    directory/apacheds/branches/apacheds-txns/core-api/src/main/java/org/apache/directory/server/core/txn/logedit/LogEdit.java
    directory/apacheds/branches/apacheds-txns/core-api/src/main/java/org/apache/directory/server/core/txn/logedit/TxnStateChange.java
    directory/apacheds/branches/apacheds-txns/core/src/main/java/org/apache/directory/server/core/log/
    directory/apacheds/branches/apacheds-txns/core/src/main/java/org/apache/directory/server/core/log/DefaultLog.java
    directory/apacheds/branches/apacheds-txns/core/src/main/java/org/apache/directory/server/core/log/DefaultLogFileManager.java
    directory/apacheds/branches/apacheds-txns/core/src/main/java/org/apache/directory/server/core/log/DefaultLogScanner.java
    directory/apacheds/branches/apacheds-txns/core/src/main/java/org/apache/directory/server/core/log/LogFileManager.java
    directory/apacheds/branches/apacheds-txns/core/src/main/java/org/apache/directory/server/core/log/LogFileRecords.java
    directory/apacheds/branches/apacheds-txns/core/src/main/java/org/apache/directory/server/core/log/LogFlushManager.java
    directory/apacheds/branches/apacheds-txns/core/src/main/java/org/apache/directory/server/core/log/LogManager.java
    directory/apacheds/branches/apacheds-txns/core/src/main/java/org/apache/directory/server/core/log/LogScannerInternal.java
    directory/apacheds/branches/apacheds-txns/core/src/main/java/org/apache/directory/server/core/txn/
    directory/apacheds/branches/apacheds-txns/core/src/main/java/org/apache/directory/server/core/txn/AbstractTransaction.java
    directory/apacheds/branches/apacheds-txns/core/src/main/java/org/apache/directory/server/core/txn/DefaultTxnLogManager.java
    directory/apacheds/branches/apacheds-txns/core/src/main/java/org/apache/directory/server/core/txn/DefaultTxnManager.java
    directory/apacheds/branches/apacheds-txns/core/src/main/java/org/apache/directory/server/core/txn/ReadOnlyTxn.java
    directory/apacheds/branches/apacheds-txns/core/src/main/java/org/apache/directory/server/core/txn/ReadWriteTxn.java
    directory/apacheds/branches/apacheds-txns/core/src/main/java/org/apache/directory/server/core/txn/Transaction.java
    directory/apacheds/branches/apacheds-txns/core/src/main/java/org/apache/directory/server/core/txn/TxnManagerInternal.java
    directory/apacheds/branches/apacheds-txns/core/src/test/java/org/apache/directory/server/core/log/
    directory/apacheds/branches/apacheds-txns/core/src/test/java/org/apache/directory/server/core/log/LogFlushScanTest.java
Removed:
    directory/apacheds/branches/apacheds-txns/xdbm-partition/src/main/java/org/apache/directory/server/log/

Added: directory/apacheds/branches/apacheds-txns/core-api/src/main/java/org/apache/directory/server/core/log/InvalidLogException.java
URL: http://svn.apache.org/viewvc/directory/apacheds/branches/apacheds-txns/core-api/src/main/java/org/apache/directory/server/core/log/InvalidLogException.java?rev=1181167&view=auto
==============================================================================
--- directory/apacheds/branches/apacheds-txns/core-api/src/main/java/org/apache/directory/server/core/log/InvalidLogException.java (added)
+++ directory/apacheds/branches/apacheds-txns/core-api/src/main/java/org/apache/directory/server/core/log/InvalidLogException.java Mon Oct 10 19:39:36 2011
@@ -0,0 +1,28 @@
+
+package org.apache.directory.server.core.log;
+
+/** 
+ * An exception used when the log content could be invalid.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ */
+public class InvalidLogException extends Exception
+{
+    public InvalidLogException() {}
+
+    public InvalidLogException(String s) 
+    {
+        super(s);
+    }
+
+    public InvalidLogException(Throwable cause) 
+    {
+        super(cause);
+    }
+
+    public InvalidLogException(String s, Throwable cause) 
+    {
+        super(s, cause);
+    }
+
+}

Added: directory/apacheds/branches/apacheds-txns/core-api/src/main/java/org/apache/directory/server/core/log/Log.java
URL: http://svn.apache.org/viewvc/directory/apacheds/branches/apacheds-txns/core-api/src/main/java/org/apache/directory/server/core/log/Log.java?rev=1181167&view=auto
==============================================================================
--- directory/apacheds/branches/apacheds-txns/core-api/src/main/java/org/apache/directory/server/core/log/Log.java (added)
+++ directory/apacheds/branches/apacheds-txns/core-api/src/main/java/org/apache/directory/server/core/log/Log.java Mon Oct 10 19:39:36 2011
@@ -0,0 +1,51 @@
+
+package org.apache.directory.server.core.log;
+
+import java.io.IOException;
+
+public interface Log
+{
+    
+    /**
+     * Initializes the logging subsystem
+     *
+     * @param logFilepath log file path
+     * @param suffix suffix for log file.
+     * @param logBufferSize size of buffer that will hold unflushed log changes. Specifigy zero if no buffering is desired
+     * @param logFileSize A soft limit on the log file size
+     * @throws IOException
+     * @throws InvalidLogException
+     */
+   public void init( String logFilepath, String suffix, int logBufferSize, long logFileSize ) throws IOException, InvalidLogException;
+    
+    /**
+     * Logs the given user record to the log. Position in the log files where the record is logged is returned as part of
+     * userRecord.
+     *
+     * @param userLogRecord provides the user data to be logged
+     * @param sync if true, this calls returns after making sure that the appended data is reflected to the underlying media
+     * @throws IOException
+     * @throws InvalidLogException
+     */
+    public void log( UserLogRecord userRecord, boolean sync ) throws IOException, InvalidLogException;
+    
+    
+    /**
+     * Starts a san in the logs starting from the given log position
+     *
+     * @param startPoint starting position of the scan.
+     * @return
+     */
+    public LogScanner beginScan( LogAnchor startPoint );
+    
+    
+    /**
+     * Advances the min needed position in the logs. Logging subsystem uses this
+     * information to get rid of unneeded
+     *
+     * @param newAnchor
+     */
+    public void advanceMinNeededLogPosition( LogAnchor newAnchor );
+
+    
+}

Added: directory/apacheds/branches/apacheds-txns/core-api/src/main/java/org/apache/directory/server/core/log/LogAnchor.java
URL: http://svn.apache.org/viewvc/directory/apacheds/branches/apacheds-txns/core-api/src/main/java/org/apache/directory/server/core/log/LogAnchor.java?rev=1181167&view=auto
==============================================================================
--- directory/apacheds/branches/apacheds-txns/core-api/src/main/java/org/apache/directory/server/core/log/LogAnchor.java (added)
+++ directory/apacheds/branches/apacheds-txns/core-api/src/main/java/org/apache/directory/server/core/log/LogAnchor.java Mon Oct 10 19:39:36 2011
@@ -0,0 +1,83 @@
+
+package org.apache.directory.server.core.log;
+
+import org.apache.directory.server.i18n.I18n;
+
+/**
+ * Implements a pointer in to the log files
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ */
+public class LogAnchor
+{
+    // TODO move this to logger 
+    /** Invalid/unknown lsn. Log LSN starts at UNKNOWN_LSN + 1 and is ever increasing */
+    public final static long UNKNOWN_LSN = Long.MIN_VALUE;
+    
+    /** Min log file number */
+    public final static long MIN_LOG_NUMBER = 0;
+    
+    /** Min log file offset */
+    public final static long MIN_LOG_OFFSET = 0;
+    
+    
+    /** log file identifier of the anchor */
+    private long logFileNumber = 0 ;
+    
+    /** Offset into the log file identified by logfilenumber */
+    private long logFileOffset = 0;
+    
+    /** LSN corresponding to the logFileNumber and fileOffset */
+    private long logLSN = UNKNOWN_LSN; 
+    
+    public LogAnchor()
+    {
+        
+    }
+    
+    public LogAnchor( long logFileNumber, long logFileOffset, long logLSN )
+    {
+        this.resetLogAnchor( logFileNumber, logFileOffset, logLSN );
+    }
+    
+    
+    public void resetLogAnchor( long logFileNumber, long logFileOffset, long logLSN )
+    {
+        if ( logFileNumber < 0 )
+        {
+            throw new IllegalArgumentException( I18n.err( I18n.ERR_746, logFileNumber ) );
+        }
+        
+        if ( logFileOffset < 0 )
+        {
+            throw new IllegalArgumentException( I18n.err( I18n.ERR_747, logFileOffset ) );
+        }
+        
+        
+        this.logFileNumber = logFileNumber;
+        this.logFileOffset = logFileOffset;
+        this.logLSN = logLSN;
+    }
+    
+    public void resetLogAnchor( LogAnchor logAnchor )
+    {
+        this.resetLogAnchor( logAnchor.getLogFileNumber(), logAnchor.getLogFileOffset(), logAnchor.getLogLSN() );
+    }
+    
+     
+    public long getLogFileNumber()
+    {
+        return this.logFileNumber;
+    }
+    
+    
+    public long getLogFileOffset()
+    {
+        return this.logFileOffset;
+    }
+    
+    
+    public long getLogLSN()
+    {
+        return this.logLSN;
+    }  
+}

Added: directory/apacheds/branches/apacheds-txns/core-api/src/main/java/org/apache/directory/server/core/log/LogAnchorComparator.java
URL: http://svn.apache.org/viewvc/directory/apacheds/branches/apacheds-txns/core-api/src/main/java/org/apache/directory/server/core/log/LogAnchorComparator.java?rev=1181167&view=auto
==============================================================================
--- directory/apacheds/branches/apacheds-txns/core-api/src/main/java/org/apache/directory/server/core/log/LogAnchorComparator.java (added)
+++ directory/apacheds/branches/apacheds-txns/core-api/src/main/java/org/apache/directory/server/core/log/LogAnchorComparator.java Mon Oct 10 19:39:36 2011
@@ -0,0 +1,57 @@
+
+package org.apache.directory.server.core.log;
+
+import java.util.Comparator;
+
+import org.apache.directory.server.i18n.I18n;
+
+public class LogAnchorComparator implements Comparator<LogAnchor>
+{
+    /**
+     * Compare two log anchors.
+     *
+     * @param obj1 First object
+     * @param obj2 Second object
+     * @return a positive integer if obj1 > obj2, 0 if obj1 == obj2,
+     *         and a negative integer if obj1 < obj2
+     */
+     public int compare( LogAnchor obj1, LogAnchor obj2 )
+     {
+        if ( obj1 == null ) {
+            throw new IllegalArgumentException( I18n.err( I18n.ERR_525 ) );
+        }
+
+        if ( obj2 == null ) {
+            throw new IllegalArgumentException( I18n.err( I18n.ERR_526 ) );
+        }
+
+        long logFileNumber1 = obj1.getLogFileNumber();
+        long logFileOffset1 = obj1.getLogFileOffset();
+        long logFileNumber2 = obj2.getLogFileNumber();
+        long logFileOffset2 = obj2.getLogFileOffset();
+        
+        if ( logFileNumber1 > logFileNumber2 )
+        {
+            return 1;
+        }
+        else if ( logFileNumber1 == logFileNumber2 )
+        {
+            if ( logFileOffset1 > logFileOffset2 )
+            {
+                return 1;
+            }
+            else if ( logFileOffset1 == logFileOffset2 )
+            {
+                return 0;
+            }
+            else
+            {
+                return -1;
+            }
+        }
+        else
+        {
+            return -1;
+        }     
+     }
+}

Added: directory/apacheds/branches/apacheds-txns/core-api/src/main/java/org/apache/directory/server/core/log/LogScanner.java
URL: http://svn.apache.org/viewvc/directory/apacheds/branches/apacheds-txns/core-api/src/main/java/org/apache/directory/server/core/log/LogScanner.java?rev=1181167&view=auto
==============================================================================
--- directory/apacheds/branches/apacheds-txns/core-api/src/main/java/org/apache/directory/server/core/log/LogScanner.java (added)
+++ directory/apacheds/branches/apacheds-txns/core-api/src/main/java/org/apache/directory/server/core/log/LogScanner.java Mon Oct 10 19:39:36 2011
@@ -0,0 +1,42 @@
+
+package org.apache.directory.server.core.log;
+
+
+import java.io.IOException;
+
+public interface LogScanner
+{
+    /**
+     * Reads and returns the next user record from the log into a backing byte array 
+     * and returns a reference to it. Returned array can be overwritten 
+     * after the next call to getNextRecord()
+     *
+     * @param  log record to be filled in by
+     * @return true if there is a next record
+     * throws IOException
+     * throws InvalidLogException thrown if the log content is invalid 
+     */
+    public boolean getNextRecord(UserLogRecord logRecord) throws IOException, InvalidLogException;
+    
+    
+    /**
+     * Returns the last successfully read log file number
+     *
+     * @return last successfully read log file number
+     */
+    public long getLastGoodFileNumber();
+    
+    /**
+     * Returns the last successfully read log file number
+     *
+     * @return last successfully read log file number
+     */
+    public long getLastGoodOffset();
+    
+    /**
+     * Closes the scanner and releases any
+     * resources. 
+     *
+     */
+    public void close();
+}

Added: directory/apacheds/branches/apacheds-txns/core-api/src/main/java/org/apache/directory/server/core/log/UserLogRecord.java
URL: http://svn.apache.org/viewvc/directory/apacheds/branches/apacheds-txns/core-api/src/main/java/org/apache/directory/server/core/log/UserLogRecord.java?rev=1181167&view=auto
==============================================================================
--- directory/apacheds/branches/apacheds-txns/core-api/src/main/java/org/apache/directory/server/core/log/UserLogRecord.java (added)
+++ directory/apacheds/branches/apacheds-txns/core-api/src/main/java/org/apache/directory/server/core/log/UserLogRecord.java Mon Oct 10 19:39:36 2011
@@ -0,0 +1,48 @@
+
+package org.apache.directory.server.core.log;
+
+/** 
+ * A user log record that can be used to pass user record between the clients and the logger
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ */
+public class UserLogRecord
+{
+    private final static int INITIAL_SIZE =  1024;
+    
+    /** array used to hold user log records */
+    private byte[] recordHolder;
+    
+    /** offset int the  byte array where user record starts */
+    int offset;
+    
+    /** length of the user record in the byte array */
+    int length;
+    
+    /** Position of the log record in the log */
+    private LogAnchor logAnchor = new LogAnchor();
+    
+    public void setData( byte[] data, int length )
+    {
+        this.recordHolder = data;
+        this.length = length;
+    }
+    
+    public byte[] getDataBuffer()
+    {
+        return recordHolder;
+    }
+    
+   
+    public int getDataLength()
+    {
+        return length;
+    }
+    
+    
+    public LogAnchor getLogAnchor()
+    {
+        return logAnchor;
+    }
+    
+}

Added: directory/apacheds/branches/apacheds-txns/core-api/src/main/java/org/apache/directory/server/core/txn/TxnLogManager.java
URL: http://svn.apache.org/viewvc/directory/apacheds/branches/apacheds-txns/core-api/src/main/java/org/apache/directory/server/core/txn/TxnLogManager.java?rev=1181167&view=auto
==============================================================================
--- directory/apacheds/branches/apacheds-txns/core-api/src/main/java/org/apache/directory/server/core/txn/TxnLogManager.java (added)
+++ directory/apacheds/branches/apacheds-txns/core-api/src/main/java/org/apache/directory/server/core/txn/TxnLogManager.java Mon Oct 10 19:39:36 2011
@@ -0,0 +1,15 @@
+
+package org.apache.directory.server.core.txn;
+
+import org.apache.directory.server.core.txn.logedit.LogEdit;
+
+import org.apache.directory.server.core.log.UserLogRecord;
+
+import java.io.IOException;
+
+public interface TxnLogManager
+{
+    public void log( LogEdit logEdit, boolean sync ) throws IOException;
+    
+    public void log( UserLogRecord logRecord, boolean sync ) throws IOException;
+}

Added: directory/apacheds/branches/apacheds-txns/core-api/src/main/java/org/apache/directory/server/core/txn/TxnManager.java
URL: http://svn.apache.org/viewvc/directory/apacheds/branches/apacheds-txns/core-api/src/main/java/org/apache/directory/server/core/txn/TxnManager.java?rev=1181167&view=auto
==============================================================================
--- directory/apacheds/branches/apacheds-txns/core-api/src/main/java/org/apache/directory/server/core/txn/TxnManager.java (added)
+++ directory/apacheds/branches/apacheds-txns/core-api/src/main/java/org/apache/directory/server/core/txn/TxnManager.java Mon Oct 10 19:39:36 2011
@@ -0,0 +1,13 @@
+
+package org.apache.directory.server.core.txn;
+
+import java.io.IOException;
+
+public interface TxnManager
+{
+    public void beginTransaction( boolean readOnly ) throws IOException;
+   
+    public void commitTransaction() throws IOException;
+    
+    public void abortTransaction() throws IOException;
+}

Added: directory/apacheds/branches/apacheds-txns/core-api/src/main/java/org/apache/directory/server/core/txn/logedit/AbstractLogEdit.java
URL: http://svn.apache.org/viewvc/directory/apacheds/branches/apacheds-txns/core-api/src/main/java/org/apache/directory/server/core/txn/logedit/AbstractLogEdit.java?rev=1181167&view=auto
==============================================================================
--- directory/apacheds/branches/apacheds-txns/core-api/src/main/java/org/apache/directory/server/core/txn/logedit/AbstractLogEdit.java (added)
+++ directory/apacheds/branches/apacheds-txns/core-api/src/main/java/org/apache/directory/server/core/txn/logedit/AbstractLogEdit.java Mon Oct 10 19:39:36 2011
@@ -0,0 +1,18 @@
+
+package org.apache.directory.server.core.txn.logedit;
+
+import org.apache.directory.server.core.log.LogAnchor;
+
+public abstract class AbstractLogEdit implements LogEdit
+{
+    /** position in the wal */
+    private transient LogAnchor logAnchor = new LogAnchor();
+    
+    /**
+     * {@inheritDoc}
+     */
+    public LogAnchor getLogAnchor()
+    {
+        return logAnchor;
+    }
+}

Added: directory/apacheds/branches/apacheds-txns/core-api/src/main/java/org/apache/directory/server/core/txn/logedit/LogEdit.java
URL: http://svn.apache.org/viewvc/directory/apacheds/branches/apacheds-txns/core-api/src/main/java/org/apache/directory/server/core/txn/logedit/LogEdit.java?rev=1181167&view=auto
==============================================================================
--- directory/apacheds/branches/apacheds-txns/core-api/src/main/java/org/apache/directory/server/core/txn/logedit/LogEdit.java (added)
+++ directory/apacheds/branches/apacheds-txns/core-api/src/main/java/org/apache/directory/server/core/txn/logedit/LogEdit.java Mon Oct 10 19:39:36 2011
@@ -0,0 +1,18 @@
+
+package org.apache.directory.server.core.txn.logedit;
+
+import org.apache.directory.server.core.log.LogAnchor;
+
+import java.io.Externalizable;
+
+public interface LogEdit extends Externalizable 
+{
+    /**
+     * Returns the position the edit is inserted in the wal.
+     * Log anchor is initialized is set after the edit is serialized and inserted into
+     * the wal so it should be transient.
+     *
+     * @return position of the log edit in the wal
+     */
+    public LogAnchor getLogAnchor();
+}

Added: directory/apacheds/branches/apacheds-txns/core-api/src/main/java/org/apache/directory/server/core/txn/logedit/TxnStateChange.java
URL: http://svn.apache.org/viewvc/directory/apacheds/branches/apacheds-txns/core-api/src/main/java/org/apache/directory/server/core/txn/logedit/TxnStateChange.java?rev=1181167&view=auto
==============================================================================
--- directory/apacheds/branches/apacheds-txns/core-api/src/main/java/org/apache/directory/server/core/txn/logedit/TxnStateChange.java (added)
+++ directory/apacheds/branches/apacheds-txns/core-api/src/main/java/org/apache/directory/server/core/txn/logedit/TxnStateChange.java Mon Oct 10 19:39:36 2011
@@ -0,0 +1,51 @@
+
+package org.apache.directory.server.core.txn.logedit;
+
+import java.io.IOException;
+import java.io.ObjectInput;
+import java.io.ObjectOutput;
+
+public class TxnStateChange extends AbstractLogEdit
+{
+    long txnID;
+    State txnState;
+    
+    public TxnStateChange( long txnID, State txnState )
+    {
+        this.txnID = txnID;
+        this.txnState = txnState;
+    }
+    
+    public long getTxnID()
+    {
+        return this.txnID;
+    }
+    
+    public State getTxnState()
+    {
+        return this.txnState;
+    }
+    
+    @Override
+    public void readExternal( ObjectInput in ) throws IOException, ClassNotFoundException
+    {
+        txnID = in.readLong();
+        txnState = State.values()[in.readInt()];
+    }
+
+
+    @Override
+    public void writeExternal( ObjectOutput out ) throws IOException
+    {
+        out.writeLong( txnID );
+        out.writeInt( txnState.ordinal() );
+    }
+    
+    public enum State
+    {
+        TXN_BEGIN,
+        TXN_COMMIT,
+        TXN_ABORT
+    }
+
+}

Added: directory/apacheds/branches/apacheds-txns/core/src/main/java/org/apache/directory/server/core/log/DefaultLog.java
URL: http://svn.apache.org/viewvc/directory/apacheds/branches/apacheds-txns/core/src/main/java/org/apache/directory/server/core/log/DefaultLog.java?rev=1181167&view=auto
==============================================================================
--- directory/apacheds/branches/apacheds-txns/core/src/main/java/org/apache/directory/server/core/log/DefaultLog.java (added)
+++ directory/apacheds/branches/apacheds-txns/core/src/main/java/org/apache/directory/server/core/log/DefaultLog.java Mon Oct 10 19:39:36 2011
@@ -0,0 +1,59 @@
+
+package org.apache.directory.server.core.log;
+
+import java.io.IOException;
+
+
+public class DefaultLog implements Log
+{
+    /** Log manager */
+    LogManager logManager;
+    
+    /** Log File Manager */
+    LogFileManager logFileManager;
+    
+    
+    /** LogFlushManager */
+    LogFlushManager logFlushManager;
+    
+    /**
+     * {@inheritDoc}
+     */
+   public void init( String logFilepath, String suffix, int logBufferSize, long logFileSize ) throws IOException, InvalidLogException
+   {
+       logFileManager = new DefaultLogFileManager();
+       logFileManager.init( logFilepath, suffix );
+       
+       logManager = new LogManager( logFileManager );
+       logManager.initLogManager();
+       
+       logFlushManager = new LogFlushManager( logManager, logBufferSize, logFileSize );
+   }
+    
+   /**
+    * {@inheritDoc}
+    */
+    public void log( UserLogRecord userRecord, boolean sync ) throws IOException, InvalidLogException
+    {
+        logFlushManager.append( userRecord, sync );
+    }
+    
+    
+    /**
+     * {@inheritDoc}
+     */
+    public LogScanner beginScan( LogAnchor startPoint )
+    {
+        LogScannerInternal logScanner = new DefaultLogScanner();
+        logScanner.init( startPoint, logFileManager );
+        return logScanner;
+    }
+    
+    /**
+     * {@inheritDoc}
+     */
+    public void advanceMinNeededLogPosition( LogAnchor newAnchor )
+    {
+       logManager.advanceMinLogAnchor( newAnchor ); 
+    }
+}

Added: directory/apacheds/branches/apacheds-txns/core/src/main/java/org/apache/directory/server/core/log/DefaultLogFileManager.java
URL: http://svn.apache.org/viewvc/directory/apacheds/branches/apacheds-txns/core/src/main/java/org/apache/directory/server/core/log/DefaultLogFileManager.java?rev=1181167&view=auto
==============================================================================
--- directory/apacheds/branches/apacheds-txns/core/src/main/java/org/apache/directory/server/core/log/DefaultLogFileManager.java (added)
+++ directory/apacheds/branches/apacheds-txns/core/src/main/java/org/apache/directory/server/core/log/DefaultLogFileManager.java Mon Oct 10 19:39:36 2011
@@ -0,0 +1,249 @@
+
+package org.apache.directory.server.core.log;
+
+import java.io.EOFException;
+import java.io.File;
+import java.io.IOException;
+import java.io.FileNotFoundException;
+
+import java.io.RandomAccessFile;
+
+import org.apache.directory.server.i18n.I18n;
+
+class DefaultLogFileManager implements LogFileManager 
+{
+    private String logFilePath;
+    private String suffix;
+    
+    /**
+     * Inits the log file manager to use the given logfile path and the suffix. Each log file
+     * has name logFileName_<logFileNumber>.suffix 
+     *
+     * @param logFilepath log file path
+     * @param suffix suffix for log file.
+     */
+    public void init( String logFilePath, String suffix )
+    {
+        this.logFilePath = logFilePath;
+        this.suffix = suffix;
+    }
+    
+    
+    /**
+     * {@inheritDoc}
+     */
+    public LogFileReader getReaderForLogFile( long logFileNumber ) throws IOException, FileNotFoundException
+    {      
+        File logFile = this.makeLogFileName( logFileNumber );
+        
+        // This will throw a file not found exception if file does not exist
+        RandomAccessFile raf = new RandomAccessFile( logFile, "r" );
+        
+        return new LogFileReader( raf, logFileNumber );
+    }
+    
+    /**
+     * {@inheritDoc}
+     */
+    public LogFileWriter getWriterForLogFile( long logFileNumber ) throws IOException, FileNotFoundException
+    {
+        File logFile = this.makeLogFileName( logFileNumber );
+        
+        // This will throw a file not found exception if file does not exist
+        RandomAccessFile raf = new RandomAccessFile( logFile, "rw" );
+        
+        return new LogFileWriter( raf, logFileNumber );
+    }
+    
+    
+    /**
+     * {@inheritDoc}
+     */
+    public boolean createLogFile( long logFileNumber ) throws IOException
+    {
+        File logFile = this.makeLogFileName( logFileNumber );
+        
+        boolean fileAlreadyExists = !logFile.createNewFile();
+        
+        return fileAlreadyExists;
+    }
+    
+       
+    /**
+     * {@inheritDoc}
+     */
+    public void truncateLogFile( long logFileNumber, long size ) throws IOException, FileNotFoundException
+    {
+        if ( size < 0 )
+        {
+            throw new IllegalArgumentException( "Invalid file size is specified for the log file: " + logFileNumber + " " + size );
+        }
+        
+        File logFile = this.makeLogFileName( logFileNumber );
+        
+        // This will throw a file not found exception if file does not exist
+        RandomAccessFile raf = new RandomAccessFile( logFile, "rw" );
+        
+        raf.setLength( size );
+        raf.getFD().sync();
+    }
+    
+   
+    /**
+     * {@inheritDoc}
+     */
+    public void deleteLogFile( long logFileNumber )
+    {
+        File logFile = this.makeLogFileName( logFileNumber );
+        
+        logFile.delete();
+    }
+    
+   
+    /**
+     * {@inheritDoc}
+     */
+    public boolean rename(long originalLogFileNumber, long newLongFileNumber)
+    {
+        File oldLogFile = this.makeLogFileName( originalLogFileNumber );  
+        boolean result = oldLogFile.renameTo( this.makeLogFileName( newLongFileNumber ) );
+        return result;
+    }
+    
+    
+    private File makeLogFileName( long logFileNumber )
+    {
+        
+        return new File( logFilePath + "/" + LogFileManager.LOG_NAME_PREFIX + logFileNumber + "." + suffix );
+    }
+    
+    static class LogFileReader implements LogFileManager.LogFileReader
+    {
+        /** Underlying log file */
+        RandomAccessFile raf;
+        
+        /** Log file identifier */
+        long logFileNumber;
+        
+  
+        public LogFileReader( RandomAccessFile raf, long logFileNumber )
+        {
+            this.raf = raf;
+            this.logFileNumber = logFileNumber;
+        }
+        
+        /**
+         * {@inheritDoc}
+         */
+        public int read( byte[] buffer, int offset, int length ) throws IOException, EOFException
+        {
+            raf.readFully( buffer, offset, length );
+            return length;
+        }
+        
+        /**
+         * {@inheritDoc}
+         */
+        public void seek( long position ) throws IOException
+        {
+            raf.seek( position );
+        }
+        
+        /**
+         * {@inheritDoc}
+         */
+        public void close() throws IOException
+        {
+            raf.close();
+        }
+        
+        
+        /**
+         * {@inheritDoc}
+         */
+        public long logFileNumber()
+        {
+            return logFileNumber;
+        }
+        
+        /**
+         * {@inheritDoc}
+         */
+        public long getLength() throws IOException
+        {
+            return raf.length();
+        }
+        
+        /**
+         * {@inheritDoc}
+         */
+        public long getOffset() throws IOException
+        {
+            return raf.getFilePointer();
+        }
+    }
+    
+    
+    static class LogFileWriter implements LogFileManager.LogFileWriter
+    {
+        /** Underlying log file */
+        RandomAccessFile raf;
+        
+        /** Log file identifier */
+        long logFileNumber;
+        
+  
+        public LogFileWriter( RandomAccessFile raf, long logFileNumber )
+        {
+            this.raf = raf;
+            this.logFileNumber = logFileNumber;
+        }
+        /**
+         * {@inheritDoc}
+         */
+        public void append( byte[] buffer, int offset, int length ) throws IOException
+        {
+            raf.write( buffer, offset, length );
+        }
+        
+        /**
+         * {@inheritDoc}
+         */
+        public void sync() throws IOException
+        {
+             raf.getFD().sync();
+        }
+        
+        /**
+         * {@inheritDoc}
+         */
+        public void close() throws IOException
+        {
+            raf.close();
+        }
+        
+         /**
+          * {@inheritDoc}
+          */
+        public long logFileNumber()
+        {
+            return logFileNumber;
+        }
+        
+        /**
+         * {@inheritDoc}
+         */
+        public long getLength() throws IOException
+        {
+            return raf.length();
+        }
+        
+        /**
+         * {@inheritDoc}
+         */
+        public void seek( long position ) throws IOException
+        {
+            raf.seek( position );
+        }
+    }
+}

Added: directory/apacheds/branches/apacheds-txns/core/src/main/java/org/apache/directory/server/core/log/DefaultLogScanner.java
URL: http://svn.apache.org/viewvc/directory/apacheds/branches/apacheds-txns/core/src/main/java/org/apache/directory/server/core/log/DefaultLogScanner.java?rev=1181167&view=auto
==============================================================================
--- directory/apacheds/branches/apacheds-txns/core/src/main/java/org/apache/directory/server/core/log/DefaultLogScanner.java (added)
+++ directory/apacheds/branches/apacheds-txns/core/src/main/java/org/apache/directory/server/core/log/DefaultLogScanner.java Mon Oct 10 19:39:36 2011
@@ -0,0 +1,364 @@
+    
+package org.apache.directory.server.core.log;
+
+import java.io.IOException;
+import java.io.EOFException;
+import java.io.FileNotFoundException;
+import java.nio.ByteBuffer;
+
+import org.apache.directory.server.i18n.I18n;
+
+public class DefaultLogScanner implements LogScannerInternal
+{
+    /** LSN of the last successfully read log record */
+    private long prevLSN = LogAnchor.UNKNOWN_LSN;
+    
+    /** File number of the last successfully read position's file number */
+    private long prevLogFileNumber = -1;
+    
+    /** File number of the last known good offset */
+    private long prevLogFileOffset = -1;
+    
+    /** Position to read the next record from */
+    private LogAnchor startingLogAnchor = new LogAnchor();
+    
+    /** Last Read Lsn */
+    private long lastReadLSN = LogAnchor.UNKNOWN_LSN;
+    
+    /** Current log file pointer to read from */
+    LogFileManager.LogFileReader currentLogFile;
+    
+    /** True if scanner is closed */
+    boolean closed = false;
+    
+    /** True if scanner hit invalid content. No more reads will be done after invalid log content is hit */
+    boolean invalidLog = false;
+    
+    /** log file manager used to open files for reading */
+    LogFileManager logFileManager;
+    
+    /** Buffer used to read log file markers */
+    byte markerBuffer[] = new byte[LogFileRecords.MAX_MARKER_SIZE];
+    
+    /** ByteBuffer wrapper for the marker buffer */
+    ByteBuffer markerHead = ByteBuffer.wrap( markerBuffer );
+    
+    /**
+     * {@inheritDoc}
+     */
+    public void init( LogAnchor startingLogAnchor, LogFileManager logFileManager )
+    {
+        this.startingLogAnchor.resetLogAnchor( startingLogAnchor );
+        this.logFileManager = logFileManager;
+    }
+    
+    /**
+     * {@inheritDoc}
+     */
+    public boolean getNextRecord(UserLogRecord logRecord) throws IOException, InvalidLogException
+    {
+        boolean startingRead = false;
+        
+        checkIfClosed();
+        
+        if ( invalidLog )
+        {
+            throw new InvalidLogException( I18n.err( I18n.ERR_750 ) );
+        }
+        
+        long fileLength;
+        long fileOffset;
+        
+        
+        try
+        {
+            if ( currentLogFile == null )
+            {
+                
+                long startingOffset = startingLogAnchor.getLogFileOffset();
+                
+                // Read and verify header
+                currentLogFile = this.readFileHeader( startingLogAnchor.getLogFileNumber() );
+                
+                if ( currentLogFile == null )
+                {
+                    return false; // Nothing to read
+                }
+    
+                if ( startingOffset > 0 )
+                {
+                    if ( startingOffset < LogFileRecords.LOG_FILE_HEADER_SIZE )
+                    {
+                        // Offset should be at log file marker boundary
+                        this.markScanInvalid( null );
+                    }
+                    
+                    prevLogFileOffset = Math.max( startingOffset, currentLogFile.getLength() );
+                    currentLogFile.seek( startingOffset );
+                }
+                startingRead = true;
+            }     
+            
+            while ( true )
+            {
+                fileLength = currentLogFile.getLength();
+                fileOffset = currentLogFile.getOffset(); 
+                
+                if ( fileOffset > fileLength )
+                {
+                    this.markScanInvalid( null );
+                }
+                else if ( fileOffset == fileLength )
+                {
+                    // Switch to next file.. This reads and verifies the header of the new file
+                    long nextLogFileNumber = currentLogFile.logFileNumber() + 1;
+                    currentLogFile.close();
+                    currentLogFile = this.readFileHeader( nextLogFileNumber );
+                    
+                    if ( currentLogFile == null )
+                        return false; // Done.. End of log stream
+                    
+                   
+                }
+                else
+                {
+                    break;  // break to read the user record
+                }
+            }
+            
+            // Read and verify record header
+            int recordLength = this.readRecordHeader();
+            
+            // If starting read, then check if we have the expected lsn in case
+            // expected lsn is known
+            if ( startingRead )
+            {
+                long startingLSN = startingLogAnchor.getLogLSN();
+                
+                if ( ( startingLSN != LogAnchor.UNKNOWN_LSN ) && ( startingLSN != lastReadLSN ) )
+                {
+                    this.markScanInvalid( null );
+                }
+            }
+            
+            // Read and verify user block
+            this.readLogRecord( logRecord, recordLength - ( LogFileRecords.RECORD_HEADER_SIZE + LogFileRecords.RECORD_FOOTER_SIZE ));
+            
+            // Read and verify footer
+            this.readRecordFooter();
+            
+            
+            // If we are here, then we successfully read the log record. 
+            // Set the read record's position, uptate last read good location
+            // and then return
+            fileOffset = currentLogFile.getOffset();
+            
+            LogAnchor userLogAnchor = logRecord.getLogAnchor();
+            userLogAnchor.resetLogAnchor( currentLogFile.logFileNumber(), fileOffset - recordLength, lastReadLSN );
+            
+            prevLogFileOffset = fileOffset;
+            prevLogFileNumber = currentLogFile.logFileNumber();
+            prevLSN = lastReadLSN;
+        }
+        catch( EOFException e)
+        {
+            // This means either the log record or the log file header was
+            // partially written. Treat this as invalid log content
+            this.markScanInvalid( e );
+        }
+        catch( IOException e)
+        {
+            this.close();
+            throw e;
+        }
+        catch( InvalidLogException e)
+        {
+            this.close();
+            throw e;
+        }
+        
+        
+        return true;
+        
+    }
+    
+    /**
+     * {@inheritDoc}
+     */
+    public long getLastGoodFileNumber()
+    {
+        return this.prevLogFileNumber;
+    }
+    
+    
+    /**
+     * {@inheritDoc}
+     */
+    public long getLastGoodOffset()
+    {
+        return this.prevLogFileOffset;
+    }
+
+    
+    
+    /**
+     * {@inheritDoc}
+     */
+    public void close()
+    {
+        if ( closed == false )
+        {
+            closed = true;
+            if (currentLogFile != null)
+            {
+                try
+                {
+                    currentLogFile.close();
+                    currentLogFile = null;
+                }
+                catch( IOException e )
+                {
+                    // Ignore
+                }
+            }
+        }
+        
+    }
+    
+    
+    private int readRecordHeader() throws IOException, InvalidLogException, EOFException
+    {
+        boolean invalid = false; 
+        
+        markerHead.rewind();
+        currentLogFile.read( markerBuffer, 0, LogFileRecords.RECORD_HEADER_SIZE );
+        int magicNumber = markerHead.getInt();
+        int length = markerHead.getInt();
+        long lsn = markerHead.getLong();
+        long checksum = markerHead.getLong();
+        
+        if ( magicNumber != LogFileRecords.RECORD_HEADER_MAGIC_NUMBER )
+        {
+            invalid = true;
+        }
+        
+        if ( length <= ( LogFileRecords.RECORD_HEADER_SIZE + LogFileRecords.RECORD_FOOTER_SIZE ) )
+        {
+            invalid = true;
+        }
+        
+        if ( lsn < prevLSN )
+        {
+            invalid = true;
+        }
+        
+        if ( checksum != ( lsn ^ length ) )
+        {
+            invalid = true;
+        }
+        
+        if ( invalid == true )
+        {
+            this.markScanInvalid( null );
+        }
+        
+        // Everything went fine
+        lastReadLSN = lsn;
+        return length;
+    }
+    
+    
+    private void readRecordFooter() throws IOException, InvalidLogException, EOFException 
+    {
+        boolean invalid = false; 
+        
+        markerHead.rewind();
+        currentLogFile.read( markerBuffer, 0, LogFileRecords.RECORD_FOOTER_SIZE );
+        int checksum = markerHead.getInt();
+        int magicNumber = markerHead.getInt();
+      
+        if ( magicNumber != LogFileRecords.RECORD_FOOTER_MAGIC_NUMBER )
+        {
+            invalid = true;
+        }
+        
+        // TODO compute checksum
+        
+        if ( invalid == true )
+        {
+            this.markScanInvalid( null );
+        }
+    }
+    
+    private void readLogRecord( UserLogRecord userRecord, int length ) throws IOException, EOFException
+    {
+        byte dataBuffer[] = userRecord.getDataBuffer();
+        
+        if ( dataBuffer == null || dataBuffer.length < length )
+        {
+            // Allocate a larger buffer
+            dataBuffer = new byte[length];
+        }
+        
+        currentLogFile.read( dataBuffer, 0, length );
+        userRecord.setData( dataBuffer, length );
+    }
+    
+    private LogFileManager.LogFileReader readFileHeader( long logFileNumber ) throws IOException, InvalidLogException, EOFException
+    {
+        boolean invalid = false;
+        LogFileManager.LogFileReader logFile;      
+          
+        try
+        {
+            logFile = logFileManager.getReaderForLogFile( logFileNumber );
+        }
+        catch ( FileNotFoundException e )
+        {
+            return null; // end of log scan
+        }
+        
+        // File exists
+        this.prevLogFileNumber = logFileNumber;
+        this.prevLogFileOffset = 0;
+        
+        markerHead.rewind();
+        logFile.read( markerBuffer, 0, LogFileRecords.LOG_FILE_HEADER_SIZE );
+        long persistedLogFileNumber = markerHead.getLong();
+        int magicNumber = markerHead.getInt();
+      
+        if ( persistedLogFileNumber != logFileNumber )
+        {
+            invalid = true;
+        }
+        
+        if ( magicNumber != LogFileRecords.LOG_FILE_HEADER_MAGIC_NUMBER )
+        {
+            invalid = true;
+        }
+       
+        
+        if ( invalid == true )
+        {
+            this.markScanInvalid( null );
+        }
+        
+        // Everything is fine, advance good file offset and return
+        this.prevLogFileOffset = LogFileRecords.LOG_FILE_HEADER_SIZE;
+        return logFile;
+    }
+    
+    private void checkIfClosed()
+    {
+        if ( closed == true )
+        {
+            throw new IllegalStateException( I18n.err( I18n.ERR_749 ) );
+        }
+    }
+    
+    private void markScanInvalid( Exception cause ) throws InvalidLogException
+    {
+        invalidLog = true;
+        throw new InvalidLogException( I18n.err( I18n.ERR_750 ), cause );
+    }
+}

Added: directory/apacheds/branches/apacheds-txns/core/src/main/java/org/apache/directory/server/core/log/LogFileManager.java
URL: http://svn.apache.org/viewvc/directory/apacheds/branches/apacheds-txns/core/src/main/java/org/apache/directory/server/core/log/LogFileManager.java?rev=1181167&view=auto
==============================================================================
--- directory/apacheds/branches/apacheds-txns/core/src/main/java/org/apache/directory/server/core/log/LogFileManager.java (added)
+++ directory/apacheds/branches/apacheds-txns/core/src/main/java/org/apache/directory/server/core/log/LogFileManager.java Mon Oct 10 19:39:36 2011
@@ -0,0 +1,182 @@
+package org.apache.directory.server.core.log;
+
+import java.io.IOException;
+import java.io.EOFException;
+import java.io.FileNotFoundException;
+
+
+/**
+ * Defines an interface that log manager can use to manage log files.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ */
+interface LogFileManager
+{
+    final static String LOG_NAME_PREFIX = "log_"; 
+  
+    /**
+     * Inits the log file manager to use the given logfile path and the suffix. Each log file
+     * has name logFileName_<logFileNumber>.suffix 
+     *
+     * @param logFilepath log file path
+     * @param suffix suffix for log file.
+     */
+    public void init( String logFilepath, String suffix );
+    
+    
+    /**
+     * Returns a reader for the given log file number
+     *
+     * @param logFileNumber identifier of the log file to read
+     * @return reader for the given logfile
+     * @throws IOException
+     * @throws FileNotFoundException
+     */
+    public LogFileReader getReaderForLogFile( long logFileNumber ) throws IOException, FileNotFoundException;
+    
+  
+    /**
+     * Returns a writer for the given log file number
+     *
+     * @param logFileNumber identifier of the log file to read
+     * @return writer for the given logfile
+     * @throws IOException
+     * @throws FileNotFoundException
+     */
+    public LogFileWriter getWriterForLogFile( long logFileNumber ) throws IOException, FileNotFoundException;
+    
+    
+    /**
+     * Create a log file with the given identifier
+     * 
+     * @param logFileNumber identifier of the log file to write to.
+     * @return true if file already existed
+     * @throws IOException
+     */
+    public boolean createLogFile( long logFileNumber ) throws IOException;
+    
+       
+    /**
+     * Truncates the file to the given size. Mostly used for throwing away
+     * junk at the end of log file after a log replay after a crash.
+     *
+     * @param logFileNumber identifier of the log file
+     * @param size new size of the file
+     * @throws IOException
+     */
+    public void truncateLogFile( long logFileNumber, long size ) throws IOException;
+    
+    /**
+     * Deletes the underlying log file.
+     *
+     * @param logFileNumber identifier of the log file
+     */
+    public void deleteLogFile( long logFileNumber ); 
+    
+    
+    /**
+     * Moves the old log file to a new name
+     *
+     * @param orignalLogFileNumber identifier of the old file
+     * @param newLongFileNumber identifier of the new file
+     * @return true if the rename succeeded
+     */
+    public boolean rename(long orignalLogFileNumber, long newLongFileNumber);
+    
+    
+    interface LogFileReader
+    {
+        /**
+         *     
+         * Reads from the file at the current position 
+         *
+         * @param buffer data destination
+         * @param offset destination offset
+         * @param length size of read
+         * @return number of bytes actually read.
+         * @throws IOException
+         */
+        public int read( byte[] buffer, int offset, int length ) throws IOException, EOFException;
+        
+
+        /**
+         * Repositions the reader at the given offset
+         *
+         * @param position
+         */
+        public void seek( long position ) throws IOException;
+        
+        /**
+         * Close the log file reader and releases the resources 
+         *
+         */
+        public void close() throws IOException;
+        
+        
+        /**
+         * Each log file is assigned a sequence number. This method
+         * returns that number
+         *
+         * @return number assigned to this log file
+         */
+        public long logFileNumber();
+        
+        /**
+         * returns the length of the file
+         */
+        public long getLength() throws IOException;
+        
+        /**
+         * returns the offset of the next read
+         */
+        public long getOffset() throws IOException;
+    }
+    
+    interface LogFileWriter
+    {
+        /**
+         * Append the given data to the log file 
+         *
+         * @param buffer source of data
+         * @param offset offset into buffer
+         * @param length number of bytes to be appended
+         */
+        public void append( byte[] buffer, int offset, int length ) throws IOException;
+        
+        
+        
+        /**
+         * Sync the file contents to media  
+         *
+         */
+        public void sync() throws IOException;
+        
+        /**
+         * Close the log file reader and releases the resources 
+         *
+         */
+        public void close() throws IOException;
+        
+        /**
+         * Each log file is assigned a sequence number. This method
+         * returns that number
+         *
+         * @return number assigned to this log file
+         */
+        public long logFileNumber();
+        
+        /**
+         * returns the length of the file
+         */
+        public long getLength() throws IOException;
+        
+        /**
+         * Repositions the reader at the given offset
+         *
+         * @param position
+         */
+        public void seek( long position ) throws IOException;
+
+    }
+    
+}
\ No newline at end of file

Added: directory/apacheds/branches/apacheds-txns/core/src/main/java/org/apache/directory/server/core/log/LogFileRecords.java
URL: http://svn.apache.org/viewvc/directory/apacheds/branches/apacheds-txns/core/src/main/java/org/apache/directory/server/core/log/LogFileRecords.java?rev=1181167&view=auto
==============================================================================
--- directory/apacheds/branches/apacheds-txns/core/src/main/java/org/apache/directory/server/core/log/LogFileRecords.java (added)
+++ directory/apacheds/branches/apacheds-txns/core/src/main/java/org/apache/directory/server/core/log/LogFileRecords.java Mon Oct 10 19:39:36 2011
@@ -0,0 +1,62 @@
+
+package org.apache.directory.server.core.log;
+
+
+public class LogFileRecords
+{
+    /** 
+     * When a user appends a log record, a header and 
+     * footer that surrounds the log record is inserted
+     * into the log. Header and footer information makes
+     * it easier to scan the log, detect the end of 
+     * log during log scan and verify the integrity of log.  
+     */  
+    
+    /**
+     * Record Header marker
+     * int RECORD_HEADER_MAGIC_NUMBER
+     * int length  length of header + user log record + legnth of footer
+     * long recordLSN     lsn of the log record   
+     * long headerChecksum checksum to verify header
+     */
+    
+    /** Header magic number */
+    final static int RECORD_HEADER_MAGIC_NUMBER = 0x010F010F;
+     
+    /** Total header size */
+    final static int RECORD_HEADER_SIZE = 24;
+    
+    /**
+     * Record Footer marker 
+     * int checksum 
+     * int RECORD_FOOTER_MAGIC_NUMBER
+     */
+    
+    /** Footer magic number */
+    final static int RECORD_FOOTER_MAGIC_NUMBER = 0x0F010F01;
+   
+    /** Total header size */
+    final static int RECORD_FOOTER_SIZE = 8;
+    
+    /**
+     * LogFileHeader marker
+     * long log file number
+     * int LOG_FILE_HEADER_MAGIC_NUMBER 0xFF00FF00
+     */
+    
+    /** Log file header marker size */
+    final static int LOG_FILE_HEADER_SIZE = 12;
+    
+    /** Log file header magic number */
+    final static int LOG_FILE_HEADER_MAGIC_NUMBER = 0xFF00FF00;
+    
+    /** Maximum marker size */
+    final static int MAX_MARKER_SIZE;
+    
+    static
+    {
+        int markerSize = Math.max( RECORD_HEADER_SIZE, RECORD_FOOTER_SIZE );
+        MAX_MARKER_SIZE = Math.max( markerSize, LOG_FILE_HEADER_SIZE );
+    }
+    
+}

Added: directory/apacheds/branches/apacheds-txns/core/src/main/java/org/apache/directory/server/core/log/LogFlushManager.java
URL: http://svn.apache.org/viewvc/directory/apacheds/branches/apacheds-txns/core/src/main/java/org/apache/directory/server/core/log/LogFlushManager.java?rev=1181167&view=auto
==============================================================================
--- directory/apacheds/branches/apacheds-txns/core/src/main/java/org/apache/directory/server/core/log/LogFlushManager.java (added)
+++ directory/apacheds/branches/apacheds-txns/core/src/main/java/org/apache/directory/server/core/log/LogFlushManager.java Mon Oct 10 19:39:36 2011
@@ -0,0 +1,564 @@
+
+package org.apache.directory.server.core.log;
+
+import java.nio.ByteBuffer;
+
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
+import java.util.concurrent.locks.Condition;
+import java.util.concurrent.atomic.AtomicInteger;
+
+
+import java.io.IOException;
+
+import org.apache.directory.server.i18n.I18n;
+
+/**
+ * Manages the flushing of log to media and scanning of logs. All appends to the log file go through this class. 
+ *
+ * Internally it manages a circular  buffer where appends initially go. Appends are first 
+ * appended to this in memory circular log. As the in memory circular log fills up or as the user requests
+ *  memory buffer is flushed to the underlying media.  
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ */
+class LogFlushManager
+{
+    
+    /** Ever increasing logical log sequence number assigned to user log records. Bumped up under append lock */
+    private long logLSN = Long.MIN_VALUE + 1;
+       
+    /** Memory buffer size in bytes */
+    private final int logBufferSize;
+        
+    /** Synchronizes appends */
+    private final Lock appendLock = new ReentrantLock();
+    
+    /** Synchronizes flushes to media */
+    private final Lock flushLock = new ReentrantLock();
+    
+    /** Used to wait on ongoing flush */
+    private final Condition flushCondition = flushLock.newCondition();
+     
+    /** In memory LogBuffer */
+    private LogBuffer logBuffer;
+    
+    /** Flush status */
+    private FlushStatus flushStatus = new FlushStatus(); 
+       
+    /** Current LogFile appends go to */
+    private LogFileManager.LogFileWriter currentLogFile;
+
+    /** Log manager */
+    LogManager logManager;
+
+    /** Size of data appended to the currentLogFile so far */
+    long appendedSize;
+    
+    /** Sof limit on the log file size */
+    long targetLogFileSize;
+    
+    /** If logging cannot succeed, then loggingFailed is set to true and further logging is prevented */
+    boolean logFailed;
+    
+    public LogFlushManager(LogManager logManager, int logMemoryBufferSize, long logFileSize )
+    {
+        if ( ( logMemoryBufferSize < 0 ) || ( logFileSize < 0 ) )
+        {
+            throw new IllegalArgumentException( I18n.err( I18n.ERR_748, logMemoryBufferSize, logFileSize ) );
+        }
+        
+        logBufferSize = logMemoryBufferSize;
+        targetLogFileSize = logFileSize;
+        this.logManager = logManager;
+        
+        logBuffer = new LogBuffer( logBufferSize, currentLogFile );
+        
+    }
+    
+    /**
+     * Appends the given user record to the log. Position where the record is appended is returned as part of
+     * userRecord.
+     *
+     * @param userLogRecord provides the user data to be appended to the log
+     * @param sync if true, this calls returns after making sure that the appended data is reflected to the underlying file
+     * @throws IOException
+     * @throws InvalidLogException
+     */
+    public void append(UserLogRecord userRecord, boolean sync ) throws IOException, InvalidLogException
+    {
+        long lsn = LogAnchor.UNKNOWN_LSN;
+        boolean appendedRecord = false;
+        byte[] userBuffer = userRecord.getDataBuffer();
+        int length  = userRecord.getDataLength();
+        LogAnchor userLogAnchor = userRecord.getLogAnchor(); 
+        
+        int recordSize = LogFileRecords.RECORD_HEADER_SIZE + LogFileRecords.RECORD_FOOTER_SIZE + length;
+        
+        appendLock.lock();
+        
+        if ( logFailed )
+        {
+            appendLock.unlock();
+            throw new InvalidLogException( I18n.err( I18n.ERR_750 ) );
+        }
+        
+        try
+        {
+            lsn = logLSN++;
+            
+            if ( currentLogFile == null )
+            {
+                // We are just starting, get the current log file
+                currentLogFile = logManager.switchToNextLogFile( null );
+                appendedSize = currentLogFile.getLength();
+            }
+            
+            if ( appendedSize > this.targetLogFileSize )
+            {
+                // Make sure everything outstanding goes to the current log file
+                this.flush( lsn, null, 0, 0, true);
+                
+                currentLogFile = logManager.switchToNextLogFile( currentLogFile );
+                appendedSize = currentLogFile.getLength();
+            }
+            
+            if ( recordSize <= logBufferSize )
+            {
+                ByteBuffer writeHead = logBuffer.writeHead;
+                
+                while ( !appendedRecord )
+                {
+                    // First get the rewind count then the position to which the readhead advanced
+                    int readHeadRewindCount = logBuffer.readHeadRewindCount.get();
+                    int readHeadPosition = logBuffer.readHeadPosition;                
+                    
+                    if ( ( logBuffer.writeHeadRewindCount == readHeadRewindCount ) || 
+                        ( ( logBuffer.writeHeadRewindCount == readHeadRewindCount + 1 ) && 
+                            ( readHeadPosition < writeHead.position() ) ) )
+                    {
+                        if ( writeHead.remaining() >= recordSize )
+                        {
+                            this.writeHeader( writeHead, recordSize, lsn );
+                            writeHead.put( userBuffer, 0, length );
+                            this.writeFooter( writeHead, 0 );
+                            appendedRecord = true;
+                        }
+                        else // ( writeHead.remaining() < recordSize )
+                        {
+                            if ( writeHead.remaining() >= LogFileRecords.RECORD_HEADER_SIZE )
+                            {
+                                // Write a skip record
+                                this.writeHeader( writeHead, -1, -1 );
+                            }
+                            
+                            // rewind buffer now
+                            writeHead.rewind();
+                            logBuffer.writeHeadRewindCount++;
+                        }
+                    }
+                    else 
+                    {
+                        if ( logBuffer.writeHeadRewindCount != ( readHeadRewindCount + 1 )  )
+                        {
+                            throw new IllegalStateException( "Unexpected sequence number for read/write heads:" + logBuffer.writeHeadRewindCount +
+                                    " " + readHeadRewindCount );
+                        }
+                        
+                        if ( ( readHeadPosition - writeHead.position() ) > recordSize )
+                        {
+                            this.writeHeader( writeHead, recordSize, lsn );
+                            writeHead.put( userBuffer, 0, length );
+                            this.writeFooter( writeHead, 0 );
+                            appendedRecord = true;
+                        }
+                        else
+                        {
+                            this.flush( lsn, null, 0, 0, true);
+                        }
+                    }
+                }
+                
+            }
+            else
+            {   
+                this.flush( lsn, userBuffer, 0, length, true );
+            }
+            
+            
+            
+            userLogAnchor.resetLogAnchor( currentLogFile.logFileNumber(), appendedSize, lsn );
+            this.appendedSize += recordSize;
+        }
+        catch( IOException e )
+        {
+            e.printStackTrace();
+            logFailed = true; // Mark log subsytem failed
+        }
+        catch( InvalidLogException e )
+        {
+            e.printStackTrace();
+            logFailed = true; // Mark log subsystem failed
+        }
+        finally
+        {
+            appendLock.unlock();
+        }
+        
+        if ( sync )
+            this.flush( lsn, null, 0, 0, false );
+
+    }
+    
+    /**
+     * Flushes the changes in the log buffer upto the given point. The given point is determined as follows:
+     * appendLock is held: flushLSN is the highest lsn generated by the logging system and no more appends can
+     * proceed. In this case log is flushed until where the write head is.Log record with the flushLSN might not
+     * have been appended yet.
+     * 
+     * Otherwise: Given flushLSN is appended to the log already. Log is flushed upto max(flushLSN, current flashSatus.uptoLSN)
+     * 
+     * Also userBuffer != null => appendLockHeld == true
+     * 
+     * Only one thread can do flush. Once a thread find out that a flush is already going on, it waits for the ongoing flush
+     * and is woken up to do its flush.
+     * 
+     * flushStatus.uptoLSN represents the highest lsn that any thread wanted to sync. If a couple of threads wait on sync to
+     * complete, the thread that wakes up and does the sync will take it for the team and sync upto flushStatus.uptoLSN so 
+     * that logging is more efficient.
+     * 
+     *  
+     *
+     * @param flushLSN max LSN the calling thread wants to sync upto
+     * @param userBuffer if not null, user buffer is appended to the log without any buffering
+     * @param offset offset of data in user buffer
+     * @param length length of user data
+     * @param appendLockHeld true if append lock is held
+     * @throws IOException
+     * @throws InvalidLogException
+     */
+    private void flush( long flushLSN, byte[] userBuffer, int offset, int length, 
+                        boolean appendLockHeld ) throws IOException, InvalidLogException
+    {    
+        long uptoLSN = flushLSN;
+       
+        if ( appendLockHeld == true )
+        {
+            uptoLSN--;
+        }
+        
+        flushLock.lock();
+        
+        // Update max requested lsn if necessary
+        if ( uptoLSN > flushStatus.uptoLSN )
+        {
+            flushStatus.uptoLSN = uptoLSN;
+        }
+        
+        /*
+         * Check if we need to do flush and wait for ongoing flush if
+         * necessary
+         */
+        
+        while ( true )
+        {
+            if ( logFailed )
+            {
+                flushLock.unlock();
+                throw new InvalidLogException( I18n.err( I18n.ERR_750 ) );
+            }
+            if ( ( flushStatus.flushedLSN >= uptoLSN ) && ( appendLockHeld == false ) )
+            {
+                flushLock.unlock();
+                return;
+            }      
+            
+            if ( flushStatus.flushInProgress == false )
+            {
+                break;
+            }
+            
+            flushStatus.numWaiters++;
+            flushCondition.awaitUninterruptibly();
+            flushStatus.numWaiters--;
+        }
+        
+        // Mark flush in progress and do the flush
+        flushStatus.flushInProgress = true;
+        
+        // If not appendlock held, adjust uptoLSN with the max one requested by any thread
+        if ( appendLockHeld == false )
+        {
+            uptoLSN = flushStatus.uptoLSN;
+        }
+        else
+        {
+            uptoLSN = flushLSN;
+        }
+        
+        flushLock.unlock();
+        
+        long flushedLSN = LogAnchor.UNKNOWN_LSN;
+        
+        
+        try
+        {
+            flushedLSN = this.doFlush( uptoLSN, appendLockHeld );
+            
+            // Now if there is a user buffer, flush from that        
+            if ( userBuffer != null )
+            {
+                ByteBuffer headerFooterHead = logBuffer.headerFooterHead;
+                int recordSize = LogFileRecords.RECORD_HEADER_SIZE + LogFileRecords.RECORD_FOOTER_SIZE + length;
+                
+                headerFooterHead.rewind();
+                this.writeHeader( headerFooterHead, recordSize, flushLSN );
+                currentLogFile.append( logBuffer.headerFooterBuffer, 0, LogFileRecords.RECORD_HEADER_MAGIC_NUMBER );
+                
+                currentLogFile.append( userBuffer, offset, length );   
+                
+                headerFooterHead.rewind();
+                this.writeFooter( headerFooterHead, 0 );
+                currentLogFile.append( logBuffer.headerFooterBuffer, 0, LogFileRecords.RECORD_FOOTER_SIZE );
+    
+                flushedLSN = flushLSN;
+            }
+            
+            currentLogFile.sync();
+        }
+        catch( IOException e )
+        {
+            // Mark the logger invalid, wakeup any waiters and return
+            flushLock.lock();
+            logFailed = true;
+            flushStatus.flushInProgress = false;
+            
+            if ( flushStatus.numWaiters != 0 )
+            {
+                flushCondition.signalAll();
+            }
+            
+            flushLock.unlock();
+            
+            throw e;
+        }
+        
+        flushLock.lock();
+        
+        if ( flushedLSN != LogAnchor.UNKNOWN_LSN )
+        {
+            flushStatus.flushedLSN = flushedLSN;
+            
+            if ( flushStatus.flushedLSN > flushStatus.uptoLSN )
+            {
+                // This should only happen with append lock held
+                if ( appendLockHeld == false )
+                {
+                    throw new IllegalStateException( "FlushedLSN went ahead of uptoLSN while appendlock is not held: " + 
+                        flushStatus.flushedLSN + "  " + flushStatus.uptoLSN);
+                }
+                
+                flushStatus.uptoLSN = flushStatus.flushedLSN;
+            }
+        }
+        
+        flushStatus.flushInProgress = false;
+        
+        if ( flushStatus.numWaiters != 0 )
+        {
+            flushCondition.signalAll();
+        }
+        
+        flushLock.unlock();
+    }
+    
+    
+    /**
+     * Walks the log buffer and writes it to the underlying log file until the uptoLSN or current write head.
+     *
+     * @param uptoLSN max LSN until where log is flushed
+     * @param appendLockHeld true if appendlock held.
+     * @return lsn upto which flush is done. UNKNOWN_LSN if no flushing is done.
+     * @throws IOException
+     */
+    private long doFlush( long uptoLSN, boolean appendLockHeld  ) throws IOException
+    {
+        ByteBuffer readHead = logBuffer.readHead;
+        ByteBuffer writeHead = logBuffer.writeHead;
+        boolean done = false;
+        
+        int magicNumber;
+        int length;
+        long lsn = LogAnchor.UNKNOWN_LSN;
+        
+        while ( !done )
+        {
+            int totalLength = 0;
+            while( true )
+            {
+                /*
+                 * If append lock is held, we might hit write head. We can read
+                 * the write head here when append lock is held
+                 */
+                if ( appendLockHeld )
+                {
+                    if ( ( writeHead.position() == readHead.position() ) &&
+                            ( logBuffer.writeHeadRewindCount == logBuffer.readHeadRewindCount.get() ) )
+                    {
+                        done = true;
+                        break;
+                    }
+                }
+                
+                // If less than header length left to process, then break and flush whatever we got so far
+                if ( readHead.remaining() < LogFileRecords.RECORD_HEADER_SIZE )
+                    break;
+                
+                magicNumber = readHead.getInt();
+                
+                if ( magicNumber != LogFileRecords.RECORD_HEADER_MAGIC_NUMBER )
+                {
+                    throw new IllegalStateException( " Record header magic " +
+                        "number does not match " + magicNumber + " expected "+ 
+                        LogFileRecords.RECORD_HEADER_MAGIC_NUMBER );
+                }
+                
+                length = readHead.getInt();
+                
+                // Did we hit a skip record at the end of the buffer?
+                if ( length == LogBuffer.SKIP_RECORD_LENGTH )
+                    break;
+                
+                
+      
+                
+                // Sanitize length, it includes header and footer overhead
+                
+                if ( length <= ( LogFileRecords.RECORD_HEADER_SIZE + LogFileRecords.RECORD_FOOTER_SIZE) )
+                {
+                    throw new IllegalStateException( "Record length doesnt make sense:" + length + " expected:" +
+                        ( LogFileRecords.RECORD_HEADER_MAGIC_NUMBER + LogFileRecords.RECORD_FOOTER_MAGIC_NUMBER) );
+                }
+                
+                // Add to the total length
+                totalLength += length;
+                
+                lsn = readHead.getLong();
+                
+                // Move to the next record, we processed 16 bytes already
+                readHead.position( readHead.position() + length - 16 );
+                
+                if ( lsn >= uptoLSN )
+                {
+                    done = true;
+                    break;
+                }
+                    
+            }
+            
+            // If there is something to flush, then do it now
+            if ( totalLength > 0 )
+            {
+                int offset;                 
+                offset = logBuffer.readHeadPosition;
+                
+                currentLogFile.append( logBuffer.buffer, offset, totalLength );            
+                       
+                //move the position to the next record
+                logBuffer.readHeadPosition = readHead.position();
+            }
+            
+            if ( !done )
+            {
+                // this means we need to rewind and keep flushing
+                logBuffer.readHeadPosition = 0;
+                readHead.rewind();
+                logBuffer.readHeadRewindCount.incrementAndGet();
+            }
+        }
+        
+        return lsn;
+
+    }
+    
+    private void writeHeader ( ByteBuffer buffer, int length, long lsn )
+    {
+        buffer.putInt( LogFileRecords.RECORD_HEADER_MAGIC_NUMBER );
+        buffer.putInt( length );
+        buffer.putLong( lsn );
+        buffer.putLong( length ^ lsn ); 
+    }
+    
+    private void writeFooter ( ByteBuffer buffer, int checksum )
+    {
+        buffer.putInt( checksum );
+        buffer.putInt( LogFileRecords.RECORD_FOOTER_MAGIC_NUMBER );
+    }
+    
+ 
+    /**
+     * Used to group the memory buffer data together 
+     */
+    private static class LogBuffer
+    {        
+        /** In memory buffer */
+        byte buffer[];
+                
+        /** Used to scan the buffer while reading it to flush */
+        ByteBuffer readHead;
+        
+        /** Advanced as readHead flushes data */
+        int readHeadPosition;
+        
+        /** Rewind count of readHead..used to avoid overwriting nonflushed data */
+        AtomicInteger readHeadRewindCount;
+        
+        /** Used to scan the buffer while appending records into it */
+        ByteBuffer writeHead;
+        
+        /** Rewind count of writeHead..used to avoid overwriting nonflushed data */
+        int writeHeadRewindCount;
+        
+        /** Used to mark records that should be skipped at the end of the log buffer */
+        final static int SKIP_RECORD_LENGTH = -1;
+        
+        /** Header footer buffer used when writing user buffers directly */
+        byte headerFooterBuffer[];
+        
+        /** Used to format header footer buffer */
+        ByteBuffer headerFooterHead;
+        
+        public LogBuffer( int bufferSize, LogFileManager.LogFileWriter currentLogFile )
+        {
+            buffer = new byte[bufferSize];
+            readHead = ByteBuffer.wrap( buffer );
+            
+            readHeadRewindCount = new AtomicInteger( 0 );
+            
+            writeHead = ByteBuffer.wrap( buffer );
+            
+            headerFooterBuffer = new byte[LogFileRecords.MAX_MARKER_SIZE];
+            headerFooterHead = ByteBuffer.wrap( headerFooterBuffer );
+        }
+    }
+    
+    /**
+     * Used to group the flush related data together
+     */
+    private static class FlushStatus
+    {
+        /** whether flush is going on */
+        boolean flushInProgress;
+        
+        /** Current flush request */
+        long uptoLSN = LogAnchor.UNKNOWN_LSN;
+        
+        
+        /** Current flushed lsn */
+        long flushedLSN = LogAnchor.UNKNOWN_LSN;
+        
+        
+        /** Keeps track of the number of waiters */
+        int numWaiters;
+    }
+}



Mime
View raw message