FileDocCategorySizeDatePackage
LogHandle.javaAPI DocGlassfish v2 API74322Wed Jun 13 23:03:46 BST 2007com.sun.jts.CosTransactions

LogHandle

public class LogHandle extends Object
A class containing attributes of an open log file.
version
0.01
author
Simon Holdsworth, IBM Corporation
see

Fields Summary
static final int
BUFFER
Buffer the data and return (minimal overhead);
static final int
FORCE
Flush and force the data to permanent storage before returning (high overhead) - ie to physically write the record.
static final int
TAIL_NOT_INCLUSIVE
Don't include tail LSN
static final int
TAIL_INCLUSIVE
Include tail LSN
static final int
START_CKPT
Start-of-checkpoint record - internally generated by &damjo.
static final int
INDV_CKPT
Checkpoint record from an individual registered module
static final int
END_CKPT
End-of-checkpoint record - internally generated by &damjo
static final int
NEW_JRNL
Record of a newly opened journal
static final int
RECORD_TYPE_MAX
Upper limit for user specified record type value.
static final int
MARKER
Record type written to local system logs to indicate the position corresponding to the start of the last successful checkpoint.
static final int
LINK
The record type written to at the end of an extent to signify that the log record is a link record (a dummy log record).
static final int
MAX_NUMBER_EXTENTS
The maximum number of extents which can be created for a log file.
static final int
CONTROL_FORCE_INTERVAL
Number of log write operations which will be performed before forcing the control data to permanent storage
static final int
MAX_EXTENT_SIZE
This determines the size of the largest log record which can be written.
static final int
CUSHION_SIZE
This is the size of the cushion file used to find if the log is short on space.
static final int
NAME_LENGTH
The length of the name assigned to a logfile. This is restricted to 8 to support the FAT file system.
static final int
MAX_NAMES
The maximum number of names available to be assigned for logfiles. The name is made up of LOG_FILENAME_PREFIX which is 5 characters followed by a 3 digit hex extension.
static final int
FILENAME_PREFIX_LEN
The length of the fixed filename prefix used when allocating new log file names.
static final int
EXTENT_TABLE_SIZE
The number of entries in each log file descriptor. It is used for performance reason, so we can get to the extent descriptor quickly.
static final int
CALLBACK_REASON_SOS
This is the reason why we are calling the calling back function.
static final int
RESTART_OFFSET_1
The offset in the control file for the first restart data record.
static final int
RESTART_OFFSET_2
The offset in the control file for the second restart data record.
static final int
MAX_RECORD_SIZE
This is the maximum size of a log record.
static final int
MAX_RESTART_SIZE
The maximum size of a restart record
static final int
CONTROL_FILE_SIZE
The size of a control file which is allocated at open time.
static final int
ALLOCATE_SIZE
The size of a chunk to allocate from the disk space
LogHandle
blockValid
int
restartDataLength
int
recordsWritten
int
chunkRemaining
int
activeRestartVersion
LogUpcallTarget
upcallTarget
ArrayList
cursors
boolean
cushionExists
boolean
upcallInProgress
Hashtable
extentTable
String
logFileName
LogFileHandle
logFileHandle
LogControlDescriptor
logControlDescriptor
LogControl
logControl
Constructors Summary
LogHandle(LogControl control, String logName, LogFileHandle controlFH, LogUpcallTarget upcall)
Creates a LogHandle object for the given log instance.

param
control The log instance.
param
logName The name of the log.
param
controlFH The handle of the control file.
param
upcall The log upcall.
return
exception
LogException The creation failed.
see



                                                            
           
                         
                  
                 
          

        // Initialise instance members.

        logFileName = new String(logName);
        upcallTarget = upcall;
        logControl = control;
        logFileHandle = controlFH;
        blockValid = this;
        logControlDescriptor = new LogControlDescriptor();
        cursors = new ArrayList();
        extentTable = new Hashtable(EXTENT_TABLE_SIZE);

    
Methods Summary
static final intalternateRestart(int restart)
Returns the alternate restart number.

param
restart The current restart number.
return
The new restart number.
see

        return (restart == 1) ? 2 : 1;
    
synchronized voidcheckLSN(LogLSN chkLSN)
Ensures that the log is written up to the given point.

param
chkLSN The last LSN which must be written.
return
exception
LogException The operation failed.
see


        // Check BlockValid field in Log_FileDescriptor block pointed to
        // by logHandle parameter, and ensure it is valid
        // IF not valid Log_FileDescriptor
        //   Return LOG_INVALID_FILE_DESCRIPTOR

        if( blockValid != this )
            throw new LogException(null,LogException.LOG_INVALID_FILE_DESCRIPTOR,1);

        // IF not LogInitialised
        //   Return LOG_NOT_INITIALISED

        if( !logControl.logInitialised )
            throw new LogException(null,LogException.LOG_NOT_INITIALISED,2);

        // IF ReadOnly log
        //   Return LOG_READ_ONLY_ACCESS

        if( logControl.logReadOnly )
            throw new LogException(null,LogException.LOG_READ_ONLY_ACCESS,3);

        // IF the lsn parameter is equal to LOG_HEAD_LSN
        //   Copy head LSN from Log_FileDescriptor into lsn
        // ELSE IF the lsn parameter is equal to LOG_TAIL_LSN
        //   Copy tail LSN from Log_FileDescriptor into lsn
        // ELSE Copy lsn parameter into lsn

        LogLSN lsn;

        if( chkLSN.equals(LogLSN.HEAD_LSN) )
            lsn = new LogLSN(logControlDescriptor.headLSN);
        else if( chkLSN.equals(LogLSN.TAIL_LSN) )
            lsn = new LogLSN(logControlDescriptor.tailLSN);
        else
            lsn = new LogLSN(chkLSN);

        // IF lsn value is less than log tail LSN
        //   Return LOG_SUCCESS

        if( lsn.lessThan(logControlDescriptor.tailLSN) ) {
            return;
        }

        // IF log file is empty (log head = LOG_NULL_LSN)
        //   Unlock the Log_FileDescriptor latch
        //   Return LOG_SUCCESS

        if( logControlDescriptor.headLSN.isNULL() ) {
            return;
        }

        // IF lsn value is greater than log head LSN
        //   Copy head LSN from Log_FileDescriptor into lsn parameter

        if( lsn.greaterThan(logControlDescriptor.headLSN) )
            lsn.copy(logControlDescriptor.headLSN);

        // Determine the extent which contains the record to be forced (this is
        // derived from the 'extent' part of the lsn parameter) - remember this
        // as LAST_EXTENT

        int lastExtent = lsn.extent;

        // Determine the extent which contains the log tail LSN, remember this
        // as FIRST_EXTENT

        int firstExtent = logControlDescriptor.tailLSN.extent;

        // Now force each of the extent files (FIRST_EXTENT to LAST_EXTENT
        // inclusive)

        for( int extent = firstExtent; extent <= lastExtent; extent++ ) {

            // IF extent is currently open (Log_ExtentDescriptor block exists)
            //   IF the Written flag in the Log_ExtentDescriptor is TRUE
            //     Issue FSYNC for extent file
            //     IF not successful allow the error to pass to the caller.
            //     ELSE
            //       Set 'extent written' flag to FALSE

            LogExtent logEDP = (LogExtent)extentTable.get(new Integer(extent));
            if( logEDP != null &&
                logEDP.writtenSinceLastForce ) {
                logEDP.fileHandle.fileSync();
                logEDP.writtenSinceLastForce = false;
            }
        }

        // IF 'extent' part of head LSN is same as LAST_EXTENT
        //   Force the Log_ControlDescriptor structure to the control file
        //   by issuing WRITE (implied sync)
        //   IF not successful allow the error to pass to the caller.
        // ELSE
        //   Don't force control data, since we do not want control data to
        //   be 'ahead' of extent data
        // The following block of code will no longer be executed as a result
        // of a performance suggestion.  This reduces the number of occasions
        // when the control data is forced to disk

        /*
          if( logControlDescriptor.headLSN.extent == lastExtent )
          writeControlFile();
        */

    
static voidcheckRestart(LogFileHandle fileHandle, int restartNumber, int[] restartInfo)
Checks restart record information. This internal method does not need to be synchronized.

param
fileHandle The handle of the file.
param
restartNumber The restart number.
param
restartInfo An array which will contain the length and timestamp.
return
exception
LogException The operation failed.
see


        // Initialise callers output parameters

        restartInfo[0] = 0;                     // length
        restartInfo[1] = 0;                     // time stamp

        // Calculate the offsets within control file for both restart records
        // Use LSEEK to move to the first record and issue a READ for its
        // Log_RestartDescriptor block

        byte[] restartBytes = new byte[LogRestartDescriptor.SIZEOF];

        int offset = restartPosition(restartNumber);
        fileHandle.fileSeek(offset,LogFileHandle.SEEK_ABSOLUTE);
        int bytesRead = fileHandle.fileRead(restartBytes);
        LogRestartDescriptor logRD = new LogRestartDescriptor(restartBytes,0);

        // IF the READ is successful and it return the restart data

        if( bytesRead > 0 ) {

            // Check that the RestartValid value in the
            // Log_RestartDescriptor block matches the record offset
            // IF it matches
            //   Use LSEEK to move to the end of the restart data and read
            //   in the matching Log_RestartDescriptor block

            if( logRD.restartValid == restartPosition(restartNumber) ) {
                fileHandle.fileSeek(logRD.restartDataLength,
                                    LogFileHandle.SEEK_RELATIVE);
                bytesRead = fileHandle.fileRead(restartBytes);
                LogRestartDescriptor logRDEnd = new LogRestartDescriptor(restartBytes,0);


                // Check that the two Log_RestartDescriptor blocks are the same
                // IF they are identical
                //   Assume the first version of restart data is valid
                //   Remember the timestamp contained in the block

                if( logRD.equals(logRDEnd) ) {
                    restartInfo[0] = logRD.restartDataLength;
                    restartInfo[1] = logRD.timeStamp;
                }
                else
                    throw new LogException(null,LogException.LOG_CORRUPTED,1);
            }
        }

    
voidcleanUpExtents()
Removes all extent information from the log file. This internal method does not need to be synchronized.

param
return
see


        Enumeration extents = extentTable.elements();
        while( extents.hasMoreElements() ) {
            LogExtent logEDP = (LogExtent)extents.nextElement();
            extentTable.remove(new Integer(logEDP.extentNumber));
            logEDP.doFinalize();
        }
        extentTable = null;

    
synchronized voidcloseCursor(LogCursor cursor)
Closes the cursor.

param
LogCursor The cursor to be closed.
return
exception
LogException The cursor could not be closed.
see


        // Check BlockValid field in Log_CursorDescriptor block and
        // ensure it is valid
        // IF not valid Log_CursorDescriptor
        //   Return LOG_INVALID_CURSOR

        if( cursor == null || cursor.blockValid != cursor )
            throw new LogException(null,LogException.LOG_INVALID_CURSOR,1);

        // Check BlockValid field in Log_FileDescriptor block pointed to
        // by field in Log_CursorDescriptor block and ensure it is valid

        if( blockValid != this )
            throw new LogException(null,LogException.LOG_INVALID_CURSOR,2);

        // IF not LogInitialised
        //   Return LOG_NOT_INITIALISED

        if( !logControl.logInitialised )
            throw new LogException(null,LogException.LOG_NOT_INITIALISED,3);

        // Now we know the blocks are valid.
        // Remove the Log_CursorDescriptor block from the chain hung off
        // the Log_FileDescriptor (CursorDescriptorHead)

        cursors.remove(cursor);

        // Deallocate the Log_CursorDescriptor block

       //  cursor.finalize();

    
synchronized voidcloseFile(boolean deleteFile)
Closes (and optionally deletes) the log file.

param
deleteFile Indicates whether file should be deleted.
return
exception
LogException The close failed.
see

        // Check BlockValid field in Log_FileDescriptor block and
        // ensure it is valid
        // IF not valid Log_FileDescriptor
        //   Return LOG_INVALID_FILE_DESCRIPTOR

        if( blockValid != this )
            throw new LogException(null,LogException.LOG_INVALID_FILE_DESCRIPTOR,1);

        // IF not LogInitialised
        //   Return LOG_NOT_INITIALISED

        if( !logControl.logInitialised )
            throw new LogException(null,LogException.LOG_NOT_INITIALISED,2);

        // Set the block valid to NULL

        blockValid = null;

        // LOOP for each of the 16 elements in the log file's extent hash table

        boolean forced = false;
        Enumeration extents = extentTable.elements();
        while( extents.hasMoreElements() ) {
            LogExtent logEDP = (LogExtent)extents.nextElement();


            // IF extent has been written since last force
            //   Issue FSYNC for the extent's file descriptor
            //   IF not successful
            //     Return LOG_WRITE_FAILURE

            if( logEDP.writtenSinceLastForce ) {
                logEDP.fileHandle.fileSync();
                logEDP.writtenSinceLastForce = false;
                forced = true;
            }

            // Issue a close for the extent file.
            // Allow any error to pass to the caller.

            logEDP.fileHandle.fileClose();

            // If deletion of the logfile was requested, delete it.
            //Start IASRI 4720539
            if( deleteFile ){
                //if( !logEDP.file.delete() )
                final LogExtent tmplogEDP =  logEDP;
                Boolean isdeleted = (Boolean) java.security.AccessController.doPrivileged(
                    new java.security.PrivilegedAction() {
                        public Object run(){
                            return new Boolean(tmplogEDP.file.delete());
                        }
                    }
                );
                if(!isdeleted.booleanValue())
                    throw new LogException(null,LogException.LOG_CLOSE_FAILURE,6);

            }
            //End IASRI 4720539
            // Address next block in chain
            // Clear the signature in the Log_ExtentDescriptor block
            // Deallocate the Log_ExtentDescriptor block

            extentTable.remove(new Integer(logEDP.extentNumber));
            logEDP.doFinalize();
        }

        // IF any log extents were forced (FSYNC'ed)
        //   WRITE the Log_ControlDescriptor block to the control file (with
        //   implied sync)
        //   IF not successful allow the error to pass to the caller.
        //     Return LOG_WRITE_FAILURE

        if( forced && !logControl.logReadOnly )
            writeControlFile();

        // Issue CLOSE for the control file
        // IF not successful allow the error to pass to the caller.

        logFileHandle.fileClose();
        // logFileHandle.finalize();

        // If deletion of the logfile was requested, delete it's
        // control File and the cushion file.

        if( deleteFile ) {

            // Delete the control file.
            // Start IASRI 4720539
            //if( !logControl.controlFile.delete() )
            Boolean isdeleted = (Boolean) java.security.AccessController.doPrivileged(
                new java.security.PrivilegedAction() {
                    public Object run(){
                        return new Boolean(logControl.controlFile.delete());
                    }
                }
            );
            if( !isdeleted.booleanValue() )
                throw new LogException(null,LogException.LOG_CLOSE_FAILURE,7);
            // End  IASRI 4720539
            freeCushion();

            // Finally remove the directory.
            // Start IASRI 4720539
            //LogControl.directory(logFileName,logControl.directoryPath).delete();
            java.security.AccessController.doPrivileged(
                new java.security.PrivilegedAction() {
                    public Object run(){
                        LogControl.directory(logFileName,logControl.directoryPath).delete();
                        return null;
                    }
                }
            );
            // End IASRI 4720539
        }

        // Unchain the Log_FileDescriptor block from the RCA chain
        // the latch will be unset and terminated by Log_RemoveFileDescriptor

        logControl.removeFile(this);

    
voiddump()
Dumps the state of the object.

param
return
exception
LogException The operation failed.
see

        LogExtent logEDP;                       // Extent file descriptor
        LogCursor logCuDP;                      // ptr to cursor descriptor

        // Check that the LogHandle passed points to a genuine File
        // Descriptor block using the BlockValid field.
        // IF the block is not genuine
        //   Return LOG_INVALID_FILE_DESCRIPTOR

        if( blockValid != this )
            throw new LogException(null,LogException.LOG_INVALID_FILE_DESCRIPTOR,1);

        // LOOP for each of the elements in the log file's extent hash table

        Enumeration extents = extentTable.elements();
        while( extents.hasMoreElements() ) {
            logEDP = (LogExtent)extents.nextElement();
        }

        java.util.Iterator curs = cursors.iterator();
        while( curs.hasNext() ) {
            logCuDP = (LogCursor)curs.next();
        }

    
private voidfreeCushion()
Frees the cushion file.

This internal method does not need to be synchronized.

param
return
see

        // If the cushion file exists, remove it.

        if( cushionExists ) {

            // Delete the cushion file.
            // Start IASRI 4720539
            //logControl.cushionFile.delete();
            java.security.AccessController.doPrivileged(
                new java.security.PrivilegedAction() {
                    public Object run(){
                        logControl.cushionFile.delete();
                        return null;
                    }
                }
            );
            // End IASRI 4720539
            cushionExists = false;
        }

    
voidfreeFileStorage(LogLSN tailLSN)
Frees file storage for the file. This internal method does not need to be synchronized.

param
tailLSN The point from which storage is not required.
return
exception
LogException The operation failed.
see


        // Using the extent containing the tail LSN, calculate the number of
        // bytes up to but not including the log tail record
        // Zero this space by issuing an FCLEAR request for the extent file
        // IF not successful
        //   Unlock the Log_FileDescriptor mutex
        //   Return LOG_WRITE_FAILURE

        int bytesToClear = tailLSN.offset;
        if( bytesToClear == 0 ) {
            return;
        }

        // Build LSN which will cause Log_PositionFilePointer to
        // position the file pointer at the start of the extent file

        LogLSN startOfExtent = new LogLSN(tailLSN.extent,0);
        LogExtent logEDP = positionFilePointer(startOfExtent,0,LogExtent.ACCESSTYPE_UNKNOWN);

        // Write the change to permanent storage via an FSYNC request

        logEDP.fileHandle.fileSync();

    
final java.lang.StringlogFileName()
Returns the log file name.

param
return
The log file name.
see

        return logFileName;
    
synchronized LogCursoropenCursor(LogLSN startLSN, LogLSN endLSN)
Opens a cursor on the log file.

param
startLSN The start of the browse.
param
endLSN The end of the browse.
return
The new cursor.
exception
LogException The browse was not possible.
see


        // Check BlockValid field in Log_FileDescriptor block and
        // ensure it is valid
        // IF not valid Log_FileDescriptor
        //   Return LOG_INVALID_FILE_DESCRIPTOR

        if( blockValid != this )
            throw new LogException(null,LogException.LOG_INVALID_FILE_DESCRIPTOR,1);

        // IF not LogInitialised
        //   Return LOG_NOT_INITIALISED

        if( !logControl.logInitialised )
            throw new LogException(null,LogException.LOG_NOT_INITIALISED,2);

        // Allocate a Log_CursorDescriptor block
        // IF allocate fails
        //   Return LOG_INSUFFICIENT_MEMORY

        LogCursor cursor = new LogCursor(logControl,this,startLSN,endLSN);
        if( cursor == null ) {
            throw new LogException(null,LogException.LOG_INSUFFICIENT_MEMORY,4);
        } 
        

        // Add the Log_CursorDescriptor block to the chain of similar blocks
        // hung off the LogFileDescriptor (anchor is CursorDescriptorHead)

        cursors.add(cursor);

        return cursor;
    
LogExtentopenExtent(int extent)
Opens the given extent.

This internal method does not need to be synchronized.

param
extent The extent to open.
return
The extent opened.
exception
LogException The open failed.
see


        // Build the extent file name from the CurrentLSN parameter

        File extentFile = logControl.extentFile(logFileName,LogExtent.modExtent(extent));

        // Issue an OPEN request for the file
        // IF not successful (rc == -1 and not EOF)
        //   Return LOG_OPEN_FAILURE

        int openOptions = LogFileHandle.OPEN_RDWR | LogFileHandle.OPEN_CREAT;
        if( logControl.logReadOnly )
            openOptions = LogFileHandle.OPEN_RDONLY;

        LogFileHandle extentFH = new LogFileHandle(extentFile,openOptions);

        // Allocate a Log_ExtentDescriptor block and initialise it

        LogExtent logEDP = new LogExtent(extent,extentFH,extentFile);
        if( logEDP == null ) {
            extentFH.finalize();
            throw new LogException(null,LogException.LOG_INSUFFICIENT_MEMORY,2);
        }

        // Use the already hashed extent number to find the position in the
        // hash table and add it to the chain

        extentTable.put(new Integer(extent),logEDP);
        logEDP.blockValid = logEDP;

        return logEDP;
    
LogExtentpositionFilePointer(LogLSN currentLSN, int extra, int accessType)
Positions the file pointer to the given position in the log. This internal method does not need to be synchronized.

param
lsn The base position sought.
param
extra An extra offset for the position.
param
accessType The type of access.
return
The extent containing the given position.
exception
LogException The operation failed.
see


        boolean extentJustOpened = false;       // Remember open operation

        // Run the extent chain to see if extent file is already open

        LogExtent extent = (LogExtent)extentTable.get(new Integer(currentLSN.extent));

        // Open the extent file if it was not found in the extent chain

        if( extent == null ) {

            // Open the new extent file

            extent = openExtent(currentLSN.extent);
            extentJustOpened = true;
        }

        // If the current cursor position for the extent is not in the
        // required position, seek to the correct position

        if( extent.cursorPosition != currentLSN.offset + extra ||
            extent.lastAccess != accessType ) {

            // lseek to the correct offset in the open extent file
            // if the last cursor position is unknown or the distance
            // of the seek from the start of the file is closer to the
            // required position than the current position do a seek
            // from the start of the file, otherwise do a seek from the
            // current position.

            int seekDist =  ((currentLSN.offset + extra) > extent.cursorPosition) ?
                (currentLSN.offset + extra - extent.cursorPosition)  :
                (extent.cursorPosition - currentLSN.offset - extra);

            try {
                if( extent.lastAccess == LogExtent.ACCESSTYPE_UNKNOWN ||
                    currentLSN.offset + extra < seekDist )
                    extent.fileHandle.fileSeek(currentLSN.offset+extra,LogFileHandle.SEEK_ABSOLUTE);
                else
                    extent.fileHandle.fileSeek(currentLSN.offset+extra-extent.cursorPosition,LogFileHandle.SEEK_RELATIVE);
            } catch( LogException le ) {
                if( extentJustOpened ) {
                    extentTable.remove(new Integer(currentLSN.extent));
                    extent.doFinalize();
                }

                throw new LogException(null,LogException.LOG_READ_FAILURE,3);
            }

            extent.cursorPosition = currentLSN.offset + extra;
            extent.lastAccess = accessType;
        }

        // Return the file descriptor for the extent file


        return extent;
    
synchronized byte[]readRecord(LogLSN readLSN, int[] type)
Reads a record from the log.

param
readLSN The LSN of the record to be read.
param
type An array with a single element which will be set to the type of the record read.
return
The record read in.
exception
LogException The read failed.
see


        // Check BlockValid field in Log_FileDescriptor block pointed to
        // by logHandle parameter, and ensure it is valid
        // IF not valid Log_FileDescriptor
        //   Return LOG_INVALID_FILE_DESCRIPTOR

        if( blockValid != this )
            throw new LogException(null,LogException.LOG_INVALID_FILE_DESCRIPTOR,1);

        // IF not LogInitialised
        //   Return LOG_NOT_INITIALISED

        if( !logControl.logInitialised )
            throw new LogException(null,LogException.LOG_NOT_INITIALISED,2);

        // IF the log file is empty (head LSN equal to LOG_NULL_LSN)
        //   Unlock the log file latch
        //   Return LOG_INVALID_LSN

        if( logControlDescriptor.headLSN.isNULL() )
            throw new LogException(null,LogException.LOG_INVALID_LSN,3);

        // IF the lsn specified is LOG_HEAD_LSN or LOG_TAIL_LSN
        //   substitute the current head or tail LSN from the
        //   Log_ControlDescriptor structure
        // ELSE
        //   Ensure that the lsn specified is <= current head LSN and
        //                                    >= current tail LSN
        //   IF lsn does not pass these checks
        //     Unlock the log file latch
        //     Return LOG_INVALID_LSN

        LogLSN lsn;

        if( readLSN.equals(LogLSN.HEAD_LSN) )
            lsn = logControlDescriptor.headLSN;
        else if( readLSN.equals(LogLSN.TAIL_LSN) )
            lsn = logControlDescriptor.tailLSN;
        else if( readLSN.lessThan(logControlDescriptor.tailLSN) ||
                 readLSN.greaterThan(logControlDescriptor.headLSN) )
            throw new LogException(null,LogException.LOG_INVALID_LSN,4);
        else
            lsn = readLSN;

        // Position the file pointer to the LSN specified
        // IF not successful allow the error to pass to the caller.

        LogExtent logEDP = positionFilePointer(lsn,0,LogExtent.ACCESSTYPE_READ);

        // Issue a READ for the log header record
        // IF the READ was not successful
        //   Unlock the log file latch
        //   Return LOG_READ_FAILURE

        byte[] headerBytes = new byte[LogRecordHeader.SIZEOF];
        int bytesRead = 0;
        try {
            bytesRead = logEDP.fileHandle.fileRead(headerBytes);
        } catch( LogException le ) {
            logEDP.lastAccess = LogExtent.ACCESSTYPE_UNKNOWN;
            throw new LogException(null,le.errorCode,6);
        }

        LogRecordHeader logRH = new LogRecordHeader(headerBytes,0);

        logEDP.cursorPosition += bytesRead;

        // Check the record type is not a LOG_LINK_RECORD_TYPE &&
        // the LSN in the header record is same as lsn parameter
        // IF either test fails
        //   Unlock the log file latch
        //   Return LOG_INVALID_LSN

        if( logRH.recordType == LINK ||
            !logRH.currentLSN.equals(lsn)  )
            throw new LogException(null,LogException.LOG_INVALID_LSN,7);

        // Set up a 2-element iovec array to enable the log record data and record
        // ending to be read into a separate buffers
        // Issue a READV request for the extent file, passing the iovec array as
        // an input parameter
        // IF the READV was not successful
        //   Unlock the log file latch
        //   Return LOG_READ_FAILURE

        byte[][] readVect = new byte[2][];
        readVect[0] = new byte[logRH.recordLength];
        readVect[1] = new byte[LogRecordEnding.SIZEOF];

        try {
            bytesRead = logEDP.fileHandle.readVector(readVect);
        } catch( LogException le ) {
            logEDP.lastAccess = LogExtent.ACCESSTYPE_UNKNOWN;
            throw new LogException(null,le.errorCode,9);
        }

        LogRecordEnding logRE = new LogRecordEnding(readVect[1],0);
        logEDP.cursorPosition += bytesRead;

        // IF the LSN contained in the record ending != lsn parameter
        //   Unlock the log file latch
        //   Return LOG_CORRUPTED

        if( !logRE.currentLSN.equals(lsn) )
            throw new LogException(null,LogException.LOG_CORRUPTED,10);

        // Copy the returned number of bytes into the recordLengthP parameter and
        // the record type value into the recordTypeP parameter.

        type[0] = logRH.recordType;

        return readVect[0];
    
synchronized byte[]readRestart()
Reads the restart record.

param
return
The record read in.
exception
LogException The read failed.
see

        // Check BlockValid field in Log_FileDescriptor block pointed to
        // by logHandle parameter, and ensure it is valid
        // IF not valid Log_FileDescriptor
        //   Return LOG_INVALID_FILE_DESCRIPTOR

        if( blockValid != this )
            throw new LogException(null,LogException.LOG_INVALID_FILE_DESCRIPTOR,1);

        // IF not LogInitialised
        //   Return LOG_NOT_INITIALISED

        if( !logControl.logInitialised )
            throw new LogException(null,LogException.LOG_NOT_INITIALISED,2);

        // Check BlockValid field in Log_FileDescriptor block pointed to
        // by logHandle parameter, and ensure it is still valid

        if( blockValid != this )
            throw new LogException(null,LogException.LOG_INVALID_FILE_DESCRIPTOR,3);

        // IF there is no restart data (restart length in Log_FileDescriptor
        //   block is zero)
        //   Return LOG_NO_RESTART_RECORD

        if( restartDataLength == 0 )
            return new byte[0];

        // Use the ActiveRestartVersion field in the Log_FileDescriptor block
        // to find out which restart record is currently the active one and
        // determine its offset within the control file
        // Use LSEEK to move the file pointer to the start of the restart record
        // Allow any error to pass to the caller.

        int restartOffset = restartPosition(activeRestartVersion);
        logFileHandle.fileSeek(restartOffset,LogFileHandle.SEEK_ABSOLUTE);

        // Initialise an iovec array with the first element containing details of
        // a Log_RestartDescriptor block, the second containing details of
        // the callers buffer (bufferP and restart data length) and the third also
        // pointing to a Log_RestartDescriptor block

        byte[][] readVect = new byte[3][];

        readVect[0] = new byte[LogRestartDescriptor.SIZEOF];
        readVect[1] = new byte[restartDataLength];
        readVect[2] = new byte[LogRestartDescriptor.SIZEOF];

        // Issue a READV for the restart data
        // IF not successful let the error pass to the caller.

        int bytesRead = logFileHandle.readVector(readVect);

        LogRestartDescriptor logRD    = new LogRestartDescriptor(readVect[0],0);
        LogRestartDescriptor logRDEnd = new LogRestartDescriptor(readVect[2],0);

        // IF the offset value stored in the returned Log_RestartDescriptor
        // block is not equal to the offset of the record just read OR
        // the length held in the Log_RestartDescriptor block is not equal to
        // the restart data length held in the Log_FileDescriptor block OR
        // the first Log_RestartDescriptor block is not equal to the second
        //   Return LOG_CORRUPTED

        if( logRD.restartValid != restartOffset ||
            logRD.restartDataLength != restartDataLength ||
            !logRD.equals(logRDEnd) )
            throw new LogException(null,LogException.LOG_CORRUPTED,7);

        // Copy the restart data length from Log_RestartDescriptor block into
        // the callers recordLengthP parameter
        // Return LOG_SUCCESS

        return readVect[1];
    
static final intrestartPosition(int restart)
Returns the offset of the specified restart number.

param
restart The restart number.
return
The restart position.
see

        return (restart == 1) ? RESTART_OFFSET_1 : RESTART_OFFSET_2;
    
voidrestoreCushion(boolean callUpcall)
Restores the cushion file

This internal method does not need to be synchronized.

param
callUpcall Indicates whether the upcall should be called.
return
exception
LogException The operation failed.
see


        // IF the LOG_CUSHION_SIZE > 0

        if( CUSHION_SIZE > 0 ) {

            // if the cushion file already exists, set the flag in the
            // File Descriptor block to say so.

            if( !logControl.cushionFile.exists() ) {
                LogFileHandle cushionFH;              // Cushion file descriptor
                int openOptions = LogFileHandle.OPEN_RDWR  |
                    LogFileHandle.OPEN_CREAT |
                    LogFileHandle.OPEN_SYNC;

                // Create an empty log file as a storage cushion
                // Issue OPEN request for $REGIONDIR/log/cushion
                // IF OPEN fails
                //   Call function whose address is stored in UpcallFunction,
                //   passing a reason value of LOG_CALLBACK_REASON_SOS
                //   Unlock the Log_ProcessSharedLock
                //   Return

                try {
                    cushionFH = new LogFileHandle(logControl.cushionFile,openOptions); 
                } catch( LogException le ) {
                    if( callUpcall && !upcallInProgress ) {
                        upcallInProgress = true;
                        upcallTarget.upcall(CALLBACK_REASON_SOS);
                        upcallInProgress = false;
                    }
                    throw new LogException(null,LogException.LOG_OPEN_FAILURE,3);
                }

                // Use Log_AllocFileStorage to create a file the
                // size LOG_CUSHION_SIZE
                // IF Log_AllocFileStorage fails
                //   Call function whose address is stored in UpcallFunction,
                //   passing a reason value of LOG_CALLBACK_REASON_SOS
                //   CLOSE & Unlink the cushion file
                //   Unlock the Log_ProcessSharedLock
                //   Return

                try {
                    cushionFH.allocFileStorage(CUSHION_SIZE);
                } catch( LogException le ) {
                    cushionFH.finalize();
                    // Start IASRI 4720539
                    //logControl.cushionFile.delete();
                    java.security.AccessController.doPrivileged(
                        new java.security.PrivilegedAction() {
                            public Object run(){
                                logControl.cushionFile.delete();
                                return null;
                            }
                        }
                    );
                    // End IASRI 4720539

                    if( callUpcall && !upcallInProgress ) {
                        upcallInProgress = true;
                        upcallTarget.upcall(CALLBACK_REASON_SOS);
                        upcallInProgress = false;
                    }
                    cushionExists = false;
                    throw new LogException(null,LogException.LOG_OPEN_FAILURE,4);
                }

                // CLOSE the cushion file

                cushionFH.finalize();
            }

            cushionExists = true;
        }

    
synchronized voidtruncate(LogLSN truncLSN, int inclusive)
Truncates the log at the given point.

param
truncLSN The LSN of the truncation point.
param
inclusive Indicates whether truncation includes the LSN.
return
exception
LogException The operation failed.
see


        // Check BlockValid field in Log_FileDescriptor block and
        // ensure it is valid
        // IF not valid Log_FileDescriptor
        //   Return LOG_INVALID_FILE_DESCRIPTOR

        if( blockValid != this )
            throw new LogException(null,LogException.LOG_INVALID_FILE_DESCRIPTOR,1);

        // IF not LogInitialised
        //   Return LOG_NOT_INITIALISED

        if( !logControl.logInitialised )
            throw new LogException(null,LogException.LOG_NOT_INITIALISED,2);

        // IF ReadOnly log
        //   Return LOG_READ_ONLY_ACCESS

        if( logControl.logReadOnly )
            throw new LogException(null,LogException.LOG_READ_ONLY_ACCESS,3);

        // IF the log file is empty (head LSN = LOG_NULL_LSN) &&
        // the lsn value specified is not equal to LOG_HEAD_LSN
        //   Unlock the Log_FileDescriptor latch
        //   Return LOG_NEW_TAIL_TOO_HIGH

        if( logControlDescriptor.headLSN.isNULL() ) {
            if( truncLSN.equals(LogLSN.HEAD_LSN) ) {
                return;
            } else
                throw new LogException(null,LogException.LOG_NEW_TAIL_TOO_HIGH,6);
        }

        // IF the lsn parameter is equal to the symbolic LOG_HEAD_LSN or
        // the lsn parameter is equal to the actual log head LSN
        //   Copy head LSN from Log_FileDescriptor into lsn
        //   Remember that head of log is being truncated
        // ELSE IF the lsn parameter is equal to LOG_TAIL_LSN
        //   Copy tail LSN from Log_FileDescriptor into lsn
        // ELSE Copy lsn parameter into lsn

        LogLSN lsn;
        boolean truncateHead = false;

        if( truncLSN.equals(LogLSN.HEAD_LSN) ||
            truncLSN.equals(logControlDescriptor.headLSN) ) {
            lsn = new LogLSN(logControlDescriptor.headLSN);
            truncateHead = true;
        } else if( truncLSN.equals(LogLSN.TAIL_LSN) )
            lsn = new LogLSN(logControlDescriptor.tailLSN);
        else
            lsn = new LogLSN(truncLSN);

        // Check the lsn parameter to ensure it is within the range of log records
        // IF lsn < log tail LSN (in Log_FileDescriptor)
        //   Unlock the Log_FileDescriptor latch
        //   Return LOG_NEW_TAIL_TOO_LOW
        // ELSE
        //   IF lsn > log head LSN
        //     Unlock the Log_FileDescriptor latch
        //     Return LOG_NEW_TAIL_TOO_HIGH

        if( lsn.lessThan(logControlDescriptor.tailLSN) )
            throw new LogException(null,LogException.LOG_NEW_TAIL_TOO_LOW,7);
        else if( lsn.greaterThan(logControlDescriptor.headLSN) )
            throw new LogException(null,LogException.LOG_NEW_TAIL_TOO_HIGH,8);

        // IF log head is being truncated &&
        // inclusive parameter = LOG_TAIL_NOT_INCLUSIVE
        //   Set Truncation record to the lsn specified (head LSN)
        //   and the New Tail LSN to the next lsn;
        // ELSE
        //   set truncation record and new log tail LSN depending
        //   on whether or not LOG_TAIL_INCLUSIVE was set. Either way the
        //   record pointed to by the current lsn must be read first

        LogLSN truncationRecord;
        LogLSN newTailRecord;
        boolean truncLastExtent = false;

        if( truncateHead &&
            inclusive == TAIL_NOT_INCLUSIVE ) {
            truncationRecord = new LogLSN(lsn);
            newTailRecord = new LogLSN(logControlDescriptor.nextLSN);
        } else {

            // IF inclusive parameter = LOG_TAIL_INCLUSIVE and
            // lsn parameter = log tail LSN (in Log_FileDescriptor)
            // (then there is nothing to truncate)
            //   Unlock the Log_FileDescriptor latch
            //   Return LOG_SUCCESS

            if( inclusive == TAIL_INCLUSIVE &&
                lsn.equals(logControlDescriptor.tailLSN) ) {
                return;
            }

            // Call Log_PositionFilePointer to position file pointer at the
            // start of the record specified by the lsn parameter
            // Allow any error to pass to the caller.

            LogExtent logEDP = positionFilePointer(lsn,0,LogExtent.ACCESSTYPE_READ);

            // Issue READ for the log record header
            // IF not successful return LOG_READ_ERROR

            byte[] headerBytes = new byte[LogRecordHeader.SIZEOF];
            int bytesRead = 0;

            try {
                bytesRead = logEDP.fileHandle.fileRead(headerBytes);
            } catch( LogException le ) {
                logEDP.lastAccess = LogExtent.ACCESSTYPE_UNKNOWN;
                throw new LogException(null,LogException.LOG_READ_FAILURE,11);
            }

            logEDP.cursorPosition += bytesRead;
            LogRecordHeader recordHeader = new LogRecordHeader(headerBytes,0);

            // Check that retrieved record is not an extent link record
            // IF it is
            //   Unlock the Log_FileDescriptor latch
            //   Return LOG_INVALID_TAIL

            if( recordHeader.recordType == LINK )
                throw new LogException(null,LogException.LOG_INVALID_TAIL,12);

            // Now set truncation record, and new tail LSN according to whether
            // or not LOG_TAIL_INCLUSIVE was specified

            if( inclusive == TAIL_INCLUSIVE ) {
                // The specified LSN is to be retained in the logfile so
                // set the truncation record to the previous LSN and the
                // new tail to the specified LSN

                truncationRecord = new LogLSN(recordHeader.previousLSN);
                newTailRecord = new LogLSN(lsn);

                // IF the current LSN is the first record in an extent file
                // Remember that previous extent file is to be truncated

                if( lsn.offset == 0 )
                    truncLastExtent = true;
            } else {

                // The specified LSN is to be truncated from the logfile so
                // set the truncation record to the specified LSN and the
                // new tail to the next LSN

                truncationRecord = new LogLSN(lsn);
                newTailRecord = new LogLSN(recordHeader.nextLSN);
            }
        }

        // Now that the true truncation point in the log file is known, work out
        // how many extent files (if any) can be unlinked
        // - Set first_extent to extent number from log tail LSN
        // - Set last_extent to extent number from truncation point LSN

        int firstExtent = logControlDescriptor.tailLSN.extent;
        int lastExtent = truncationRecord.extent;

        // IF log head is being truncated &&
        // inclusive parameter = LOG_TAIL_NOT_INCLUSIVE
        //   Set log tail LSN to current log head LSN
        //   Set log head LSN in Log_ControlDescriptor structure to LOG_NULL_LSN
        // ELSE
        //   Set log tail LSN in Log_ControlDescriptor structure
        //   to truncation point LSN

        if( truncateHead &&
            inclusive == TAIL_NOT_INCLUSIVE ) {
            logControlDescriptor.tailLSN.copy(newTailRecord);
            logControlDescriptor.headLSN.copy(LogLSN.NULL_LSN);
        } else
            logControlDescriptor.tailLSN.copy(newTailRecord);

        // Write (and implicitly sync) the Log_ControlDescriptor structure
        // to the control file.  Allow any error to pass to the caller.

        writeControlFile();

        // Now unlink any extent files no longer required
        // This involves processing each of the extent files in the range
        // FirstExtent to LastExtent-1.
        // Note: If the TruncationRecord is a link record (last in the extent
        // file), then the LastExtent must also be processed.

        if( truncLastExtent )
            lastExtent++;

        for( int extent = firstExtent; extent <= lastExtent-1; extent++ ) {

            // IF extent is currently open
            // Issue CLOSE for extent file
            // IF not successful allow the error to pass to the caller.

            LogExtent logEDP = (LogExtent)extentTable.get(new Integer(extent));
            if( logEDP != null )
                logEDP.fileHandle.fileClose();

            // Issue UNLINK for extent file
            // IF not successful
            //   Return LOG_CLOSE_FAILURE

            //Start IASRI 4720539
            //if( !logEDP.file.delete() )
            final LogExtent tmplogEDP = logEDP;
            Boolean isdeleted = (Boolean) java.security.AccessController.doPrivileged(
		new java.security.PrivilegedAction() {
		    public Object run(){
                        return new Boolean(tmplogEDP.file.delete());
                    }
                }
            );
            if(!isdeleted.booleanValue())
                throw new LogException(null,LogException.LOG_CLOSE_FAILURE,15);
            //End IASRI 4720539
            // Unchain the Log_ExtentDescriptor block, set its BlockValid
            // field to binary zeroes and deallocate it.

            extentTable.remove(new Integer(extent));
            logEDP.doFinalize();
        }

        // If the cushion file does not exist and at least one extents has
        // just been removed, now is a good time to try and restore the
        // cushion file.
        // Call RestoreCushion but specify that the upcall should not
        // be called if the restore fails, as it should already have
        // been called when the cushion was first freed.

        if( !cushionExists &&
            firstExtent <= lastExtent - 1 )
            restoreCushion(false);

        // Call the platform specific SUPOS_LOG_FREE_FILE_STORAGE macro to
        // release any unwanted areas of the extent file containing the TAIL LSN
        // Allow any error to pass to the caller.

        if( logControlDescriptor.tailLSN.offset > 0 )
            freeFileStorage(logControlDescriptor.tailLSN);

        // If the log head has been set to 00000000.00000000, then ensure that
        // the next record which is written to the log causes the log control
        // data to be forced.  Otherwise, should a crash occur AFTER writing the
        // record and BEFORE writing the control file, when the log is re-opened
        // during restart, it will be assumed that the log is empty and no
        // scanning for records 'beyond the end of the log' will take place.

        if( logControlDescriptor.headLSN.isNULL() )
            recordsWritten = CONTROL_FORCE_INTERVAL;

    
voidwriteControlFile()
Writes the control file. This internal method does not need to be synchronized.

param
return
exception
LogException The write failed.
see


        // BUGFIX (Ram J) This fixes the log corruption problem.
        // The log extents have to be forced every time control
        // information is written. If not, there is a chance that
        // the control information (particularly headLSN) will go
        // inconsistent. The extent log that the headLSN in controlFile
        // points to, may not exist in the extent log, if it is
        // not forced. So, if JTS crashes, during recovery/reconstruction
        // there will be no log in the extents corresponding to the
        // headLSN stored in the control file. The fix is to force
        // all the dirty extents, everytime the control information
        // is updated.
        Enumeration extents = extentTable.elements();
        while (extents.hasMoreElements()) {
            LogExtent nextEDP = (LogExtent) extents.nextElement();
            if (nextEDP.writtenSinceLastForce) {
                try {
                    nextEDP.fileHandle.fileSync();
                    nextEDP.writtenSinceLastForce = false;
                } catch (LogException le) {
                    throw new LogException(null,
                                           LogException.LOG_ERROR_FORCING_LOG,
                                           14);
                }
            }
        }

        // Move the file pointer to the beginning of the control file

        logFileHandle.fileSeek(0,LogFileHandle.SEEK_ABSOLUTE);

        // Write out the control data to the control file

        byte[] controlBytes = new byte[LogControlDescriptor.SIZEOF];
        logControlDescriptor.toBytes(controlBytes,0);
        int bytesWritten = logFileHandle.fileWrite(controlBytes);

    
synchronized LogLSNwriteRecord(byte[] record, int recordType, int writeMode)
Writes a record to the log.

param
record The log record.
param
recordType The log record type.
param
writeMode The write mode.
return
The LSN of the written record
exception
LogException The write failed.
see


        // Check BlockValid field in Log_FileDescriptor block pointed to
        // by logHandle parameter, and ensure it is valid
        // IF not valid Log_FileDescriptor
        //   Return LOG_INVALID_FILE_DESCRIPTOR

        if( blockValid != this )
            throw new LogException(null,LogException.LOG_INVALID_FILE_DESCRIPTOR,1);

        // IF not LogInitialised
        //   Return LOG_NOT_INITIALISED

        if( !logControl.logInitialised )
            throw new LogException(null,LogException.LOG_NOT_INITIALISED,2);

        // IF ReadOnly log
        //   Return LOG_READ_ONLY_ACCESS

        if( logControl.logReadOnly )
            throw new LogException(null,LogException.LOG_READ_ONLY_ACCESS,3);

        // Sanity check the recordType and writeMode parameters

        if( recordType > RECORD_TYPE_MAX )
            throw new LogException(null,LogException.LOG_INVALID_RECORDTYPE,5);

        if( writeMode != FORCE && writeMode != BUFFER )
            throw new LogException(null,LogException.LOG_INVALID_WRITEMODE,6);

        // Calculate the total size of the log record by totalling size of all
        // input buffers together with the record header and record ending

        int recordSize = record.length + LogRecordHeader.SIZEOF + LogRecordEnding.SIZEOF;


        // IF the log record data is greater than LOG_MAX_LOG_RECORD_SIZE
        //   Unlock the log file latch
        //   Return LOG_RECORD_TOO_LARGE

        if( recordSize > MAX_RECORD_SIZE )
            throw new LogException(null,LogException.LOG_RECORD_TOO_LARGE,7);

        // Calculate the remaining space in the current extent by subtracting
        // (log head LSN's offset + 2*LOG_HEADER_SIZE + LOG_ENDING_SIZE) from
        // LOG_MAX_EXTENT_SIZE

        int remainingSpace = MAX_EXTENT_SIZE - ( logControlDescriptor.nextLSN.offset
                                                 + 2*LogRecordHeader.SIZEOF
                                                 + LogRecordEnding.SIZEOF );


        // Position the file pointer to the next free location
        // NOTE: either the record or a link record will be wrote here
        // Set the WORKING extent descriptor to the returned value
        //
        // IF an error occurs let it go to the caller.

        LogExtent logEDP = positionFilePointer(logControlDescriptor.nextLSN,0,LogExtent.ACCESSTYPE_WRITE);

        // IF not enough space in current extent

        if( remainingSpace < recordSize ) {
            LogRecordHeader link = new LogRecordHeader();

            // Calculate the number of the next (new) extent
            // Calculate LSN of first record in the new extent.
            // Test that the new extent number has not wrapped to become negative;
            //   if it has, throw an exception.

            int nextExtent = logControlDescriptor.headLSN.extent+1;
            if( nextExtent < 0 )
                throw new LogException(null,LogException.LOG_WRITE_FAILURE,8);

            // If the new extent file is already open, there is nothing we can do but
            // fail.  We cannot run the short-on-storage upcall to try to free the
            // extent as the upcall needs to write information to the offending extent.

            if( extentTable.containsKey(new Integer(LogExtent.modExtent(nextExtent))) )
                throw new LogException(null,LogException.LOG_WRITE_FAILURE,9);

            // Create link record containing
            // - the LSN of the link record (i.e. its own LSN)
            // - the LSN of the previous log record (log head LSN from
            //   Log_FileDescriptor block)
            // - the LSN of the next log record (this is the LSN of the
            //   first record in new extent file

            link.recordType = LINK;
            link.previousLSN = new LogLSN(logControlDescriptor.headLSN);
            link.currentLSN  = new LogLSN(logControlDescriptor.nextLSN);
            link.nextLSN     = new LogLSN(nextExtent,0);

            // Move a file pointer to the next record position

            LogExtent nextEDP = positionFilePointer(link.nextLSN,0,LogExtent.ACCESSTYPE_WRITE);

            // Issue WRITE to add link record to the 'full' extent file
            // IF the WRITE fails
            //   Close the new extent file
            //   Unchain its extent descriptor block from the hash table
            //   Deallocate the extent descriptor block
            //   Unlock the log file latch
            //   Return LOG_WRITE_FAILURE

            byte[] linkBytes = new byte[link.SIZEOF];
            link.toBytes(linkBytes,0);
            int bytesWritten = 0;
            try {
                bytesWritten = logEDP.fileHandle.fileWrite(linkBytes); 
            } catch( LogException le ) {
                extentTable.remove(new Integer(logControlDescriptor.headLSN.extent));
                nextEDP.doFinalize();
                throw new LogException(null,LogException.LOG_WRITE_FAILURE,10);
            }

            // Set its 'extent written' flag to TRUE

            logEDP.writtenSinceLastForce = true;
            logEDP.cursorPosition += bytesWritten;

            // Update the head LSN value in the Log_FileDescriptor block
            // with the LSN of the link record
            // Update the next LSN value in the Log_FileDescriptor block
            // with the LSN of the first block in the new extent

            logControlDescriptor.headLSN.copy(link.currentLSN);
            logControlDescriptor.nextLSN.copy(link.nextLSN);

            // Set the WORKING extent descriptor to the new/next extent

            logEDP = nextEDP;

            // Set the ChunkRemaining to Zero

            chunkRemaining = 0;
        }

        // Use the offset value from the next LSN to calculate the next free offset
        // in the extent file
        // Calculate the 'next free' LSN

        LogLSN nextFree = new LogLSN(logControlDescriptor.nextLSN.extent,
                                     logControlDescriptor.nextLSN.offset + recordSize);

        // Build the record header, initialising with
        // - log record type (recordType passed as input parameter)
        // - log record length (cumulative length of all data buffers)
        // - the LSN of the previous log record (PreviousRecord; log head LSN from
        //   Log_FileDescriptor block)
        // - the LSN of the next log record (NextRecord; the 'next free' LSN value)
        // - the LSN of the record about to be written (ThisRecord)

        LogRecordHeader logRH = new LogRecordHeader();

        logRH.recordType   = recordType;
        logRH.recordLength = record.length;
        logRH.nextLSN      = nextFree;
        logRH.previousLSN  = new LogLSN(logControlDescriptor.headLSN);
        logRH.currentLSN   = new LogLSN(logControlDescriptor.nextLSN);

        // Build the record ending, initialising with
        // the LSN of the record about to be written (ThisRecord)

        LogRecordEnding logRE = new LogRecordEnding();

        logRE.currentLSN = logRH.currentLSN;


        // Initialise an array of iovec structures ready for a WRITEV request
        // (an iovec structure specifies the base address and length of an area in
        // memory from which data should be written)
        // - set the first element to point to the record header, set iovCount=1
        // - LOOP for each buffer in recordPtrList
        //   initialise next iovec element with its address and length
        //   increment iovCount
        //   ENDLOOP
        // - set the next element to point to the record ending, increment iovCount

        byte[] writeBytes = new byte[LogRecordHeader.SIZEOF+record.length+LogRecordEnding.SIZEOF];

        logRH.toBytes(writeBytes,0);
        System.arraycopy(record,0,writeBytes,LogRecordHeader.SIZEOF,record.length);
        logRE.toBytes(writeBytes,LogRecordHeader.SIZEOF+record.length);

        // IF there is enough space in current chunk
        //   Decrease ChunkRemaining by RecordSize

        boolean cushionFreed = false;

        if( chunkRemaining > recordSize )
            chunkRemaining -= recordSize;
        else {

            // CALCULATE the size of disk space to grab

            int grabSize = chunkRemaining + ALLOCATE_SIZE;

            // IF there is NOT enough space in current extent
            //   Set the Grab size to be the size of the remaining extent

            if( grabSize + logControlDescriptor.nextLSN.offset > MAX_EXTENT_SIZE )
                grabSize = MAX_EXTENT_SIZE - logControlDescriptor.nextLSN.offset;


            // Set the Allocate success flag to FALSE;

            boolean allocateSuccess = false;

            do {
                // ALLOCATE the Grab size of disk space
                // IF successful
                //   Set AllocateSuccess to TRUE
                //   BREAK

                try {
                    logEDP.fileHandle.allocFileStorage(grabSize);
                } catch( LogException le ) {

                    // IF the request fails due to lack of storage, i.e.
                    //      ENOSPC - insufficient space left in file system or
                    //      EDQUOT - user or group disk block quota reached
                    //   Call the Log_FreeCushion routine
                    //   IF there was no cushion to free
                    //     Unlock the log file latch
                    //     Return LOG_NO_SPACE
                    //   Move the File pointer back to it's original offset
                    // ELSE
                    //   EXIT LOOP with 'Allocate unsuccessful' status

                    if( le.errorCode == LogException.LOG_NO_SPACE ) {
                        if( cushionExists ) {
                            freeCushion();
                            cushionFreed = true;
                        } else {
                            if( cushionFreed )
                                restoreCushion(false);

                            throw new LogException(null,LogException.LOG_NO_SPACE,11);
                        }

                        try {
                            logEDP = positionFilePointer(logControlDescriptor.nextLSN,0,LogExtent.ACCESSTYPE_WRITE); 
                        } catch( Throwable e ) {};
                    }
                    else
                        allocateSuccess = false;
                }
                allocateSuccess = true;
            }
            while( !allocateSuccess );

            // IF allocate failed
            //   Unlock the log file latch
            //   Return LOG_WRITE_FAILURE

            if( !allocateSuccess )
                throw new LogException(null,LogException.LOG_WRITE_FAILURE,12);

            // SET ChunkRemaining to the Grabbed size - RecordSize

            chunkRemaining = grabSize - recordSize;
        }

        // Issue a WRITEV request to the extent file, specifying the iovec array
        // and iovCount as input
        // IF write failed return the error.

        int bytesWritten = logEDP.fileHandle.fileWrite(writeBytes);

        // Set 'extent written' flag to TRUE

        logEDP.writtenSinceLastForce = true;
        logEDP.cursorPosition += bytesWritten;

        // IF LOG_FORCE was specified
        //   LOOP through each extent chain in the hash table
        //     IF 'extent written' flag is TRUE
        //       Issue FSYNC for extent file descriptor
        //       IF not successful
        //         Unlock the log file latch
        //         Return LOG_ERROR_FORCING_LOG
        //       Set 'extent written' flag to FALSE
        //   ENDLOOP

        if( writeMode == FORCE ) {
            Enumeration extents = extentTable.elements();
            while( extents.hasMoreElements() ) {
                LogExtent nextEDP = (LogExtent)extents.nextElement();
                if( nextEDP.writtenSinceLastForce )
                    try {
                        nextEDP.fileHandle.fileSync();
                        nextEDP.writtenSinceLastForce = false;
                    } catch( LogException le ) {
                        throw new LogException(null,LogException.LOG_ERROR_FORCING_LOG,14);
                    }
            }
        }

        // Update the head LSN and 'next free' LSN in the Log_FileDescriptor
        // block

        logControlDescriptor.headLSN.copy(logRH.currentLSN);
        logControlDescriptor.nextLSN.copy(logRH.nextLSN);

        // Increment the RecordsWritten counter in Log_FileDescriptor block

        recordsWritten++;

        // IF RecordsWritten = LOG_CONTROL_FORCE_INTERVAL or LOG_FORCE was specified
        //   Write the Log_ControlDescriptor structure (embedded in the
        //   Log_FileDescriptor block out to the control file (implied sync)
        //   IF not successful let the error pass to the caller.
        //   Reset the RecordsWritten counter to zero
        //   IF LogCushionOK is FALSE
        //     Call RestoreLogCushion Routine

        if( recordsWritten >= CONTROL_FORCE_INTERVAL ) {
            writeControlFile();
            recordsWritten = 0;
        }

        if( cushionFreed )
            restoreCushion(true);

        // Return the written LSN as the result of the write operation.

        LogLSN result = new LogLSN(logRH.currentLSN);


        return result;
    
synchronized voidwriteRestart(byte[] buffer)
Writes the restart record.

param
buffer The record to be written.
return
exception
LogException The write failed.
see


        // Check BlockValid field in Log_FileDescriptor block pointed to
        // by logHandle parameter, and ensure it is valid
        // IF not valid Log_FileDescriptor
        //   Return LOG_INVALID_FILE_DESCRIPTOR

        if( blockValid != this )
            throw new LogException(null,LogException.LOG_INVALID_FILE_DESCRIPTOR,1);

        // IF not LogInitialised
        //   Return LOG_NOT_INITIALISED

        if( !logControl.logInitialised )
            throw new LogException(null,LogException.LOG_NOT_INITIALISED,2);

        // IF ReadOnly log
        //   Return LOG_READ_ONLY_ACCESS

        if( logControl.logReadOnly )
            throw new LogException(null,LogException.LOG_READ_ONLY_ACCESS,3);

        // IF the bufferLength parameter is greater than LOG_MAX_RESTART_RECORD_SIZE
        //   Return LOG_RECORD_TOO_LARGE

        if( buffer.length > MAX_RESTART_SIZE )
            throw new LogException(null,LogException.LOG_RECORD_TOO_LARGE,4);

        // Check BlockValid field in Log_FileDescriptor block pointed to
        // by logHandle parameter, and ensure it is still valid

        if( blockValid != this )
            throw new LogException(null,LogException.LOG_INVALID_FILE_DESCRIPTOR,5);

        // Use the value in ActiveRestartVersion field showing which is the active
        // to determine which is the alternate restart record
        // Use LSEEK to move the file pointer to its offset

        int alternate = alternateRestart(activeRestartVersion);
        int restartOffset = restartPosition(alternate);
        logFileHandle.fileSeek(restartOffset,LogFileHandle.SEEK_ABSOLUTE);

        // Initialise a Log_RestartDescriptor block with
        // - the current file pointer offset (copied into RestartValid field)
        // - the length of the restart data (DataLength field)
        // - a timestamp obtained from the seconds field of a gettimer call

        LogRestartDescriptor logRD = new LogRestartDescriptor();

        logRD.restartDataLength = buffer.length;
        logRD.timeStamp = (int)new Date().getTime();
        logRD.restartValid = restartOffset;


        // Set up a 3-element iovec array with the first element 'containing'
        // the Log_RestartDescriptor block, the second, the supplied
        // restart data and the third, the Log_RestartDescriptor block again.

        byte[] writeBytes = new byte[LogRestartDescriptor.SIZEOF*2+buffer.length];

        logRD.toBytes(writeBytes,0);
        System.arraycopy(buffer,0,writeBytes,LogRestartDescriptor.SIZEOF,buffer.length);
        logRD.toBytes(writeBytes,LogRestartDescriptor.SIZEOF+buffer.length);

        // Issue a WRITEV request to copy the restart data to the control file
        // IF successful
        //   Data has now been written to permanent storage, so update
        //   RestartDataLength field in Log_FileDescriptor with bufferLength
        //   and indicate (value 1 or 2) in ActiveRestartVersion field that the
        //   alternate has now become the active
        //   Return LOG_SUCCESS
        // ELSE let the error pass to the caller.

        int bytesWritten = logFileHandle.fileWrite(writeBytes);

        activeRestartVersion = alternate;