alvinalexander.com | career | drupal | java | mac | mysql | perl | scala | uml | unix  

Glassfish example source code file (LogHandle.java)

This example Glassfish source code file (LogHandle.java) is included in the DevDaily.com "Java Source Code Warehouse" project. The intent of this project is to help you "Learn Java by Example" TM.

Java - Glassfish tags/keywords

boolean, io, logcursor, logexception, logexception, logextent, logextent, logfilehandle, loglsn, loglsn, logrecordheader, logrestartdescriptor, logrestartdescriptor, max_extent_size, object, util

The Glassfish LogHandle.java source code

/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 *
 * Copyright (c) 1997-2010 Oracle and/or its affiliates. All rights reserved.
 *
 * The contents of this file are subject to the terms of either the GNU
 * General Public License Version 2 only ("GPL") or the Common Development
 * and Distribution License("CDDL") (collectively, the "License").  You
 * may not use this file except in compliance with the License.  You can
 * obtain a copy of the License at
 * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html
 * or packager/legal/LICENSE.txt.  See the License for the specific
 * language governing permissions and limitations under the License.
 *
 * When distributing the software, include this License Header Notice in each
 * file and include the License file at packager/legal/LICENSE.txt.
 *
 * GPL Classpath Exception:
 * Oracle designates this particular file as subject to the "Classpath"
 * exception as provided by Oracle in the GPL Version 2 section of the License
 * file that accompanied this code.
 *
 * Modifications:
 * If applicable, add the following below the License Header, with the fields
 * enclosed by brackets [] replaced by your own identifying information:
 * "Portions Copyright [year] [name of copyright owner]"
 *
 * Contributor(s):
 * If you wish your version of this file to be governed by only the CDDL or
 * only the GPL Version 2, indicate your decision by adding "[Contributor]
 * elects to include this software in this distribution under the [CDDL or GPL
 * Version 2] license."  If you don't indicate a single choice of license, a
 * recipient has the option to distribute your version of this file under
 * either the CDDL, the GPL Version 2 or to extend the choice of license to
 * its licensees as provided above.  However, if you add GPL Version 2 code
 * and therefore, elected the GPL Version 2 license, then the option applies
 * only if the new code is made subject to such option by the copyright
 * holder.
 */

//----------------------------------------------------------------------------
//
// Module:      LogHandle.java
//
// Description: Log file handle.
//
// Product:     com.sun.jts.CosTransactions
//
// Author:      Simon Holdsworth
//
// Date:        March, 1997
//
// Copyright (c):   1995-1997 IBM Corp.
//
//   The source code for this program is not published or otherwise divested
//   of its trade secrets, irrespective of what has been deposited with the
//   U.S. Copyright Office.
//
//   This software contains confidential and proprietary information of
//   IBM Corp.
//----------------------------------------------------------------------------

package com.sun.jts.CosTransactions;

// Import required classes.

import com.sun.enterprise.util.i18n.StringManager;
import java.util.*;
import java.io.*;

/**A class containing attributes of an open log file.
 *
 * @version 0.01
 *
 * @author Simon Holdsworth, IBM Corporation
 *
 * @see
*/
//----------------------------------------------------------------------------
// CHANGE HISTORY
//
// Version By     Change Description
//   0.01  SAJH   Initial implementation.
//-----------------------------------------------------------------------------

class LogHandle {
    private static final StringManager sm = StringManager.getManager(LogHandle.class);
    
    // WriteMode identifies the mode in which a system journal record
    // is to be written, and affects the performance overhead of the write.

    /**Buffer the data and return (minimal overhead);
     */
    final static int BUFFER = 0;

    /**Flush and force the data to permanent storage before returning (high
     * overhead) - ie to physically write the record.
     */
    final static int FORCE = 1;

    // This type enumerates the options when truncating a log file.

    /**Don't include tail LSN
     */
    final static int TAIL_NOT_INCLUSIVE = 0;

    /**Include tail LSN
     */
    final static int TAIL_INCLUSIVE = 1;

    // Records written to the Master Log are allocated log record types

    /**Start-of-checkpoint record - internally generated by &damjo.
     */
    final static int START_CKPT = 0;

    /**Checkpoint record from an individual registered module
     */
    final static int INDV_CKPT = 1;

    /**End-of-checkpoint record - internally generated by &damjo
     */
    final static int END_CKPT = 2;

    /**Record of a newly opened journal
     */
    final static int NEW_JRNL = 3;

    /**Upper limit for user specified record type value.
     */
    final static int RECORD_TYPE_MAX = 0xFFFF;

    /**Record type written to local system logs to indicate the position
     * corresponding to the start of the last successful checkpoint.
     */
    final static int MARKER = RECORD_TYPE_MAX;

    /**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).
     */
    final static int LINK = RECORD_TYPE_MAX + 1;

    // Constants used for log files.

    /**The maximum number of extents which can be created for a log file.
     */
    final static int MAX_NUMBER_EXTENTS = 0xFFFFFFFF;

    /**Number of log write operations which will be performed before forcing
     * the control data to permanent storage
     */
    // final static int CONTROL_FORCE_INTERVAL = 20;
    final static int CONTROL_FORCE_INTERVAL = 100;

    /**This determines the size of the largest log record which can be written.
     */
    final static int MAX_EXTENT_SIZE = LogFileHandle.FILESYSTEM_BLOCKSIZE*16;

    /**This is the size of the cushion file used to find if the log is
     * short on space.
     */
    final static int CUSHION_SIZE = MAX_EXTENT_SIZE;

    /**The length of the name assigned to a logfile. This is restricted to
     * 8 to support the FAT file system.
     */
    final static int NAME_LENGTH = 8;

    /**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.
     */
    final static int MAX_NAMES = 4096;

    /**The length of the fixed filename prefix used when allocating new
     * log file names.
     */
    final static int FILENAME_PREFIX_LEN = 5;

    /**The number of entries in each log file descriptor. It is used for
     * performance reason, so we can get to the extent descriptor quickly.
     */
    final static int EXTENT_TABLE_SIZE = 16;

    /**This is used to give the maximum length of a log file name, which also
     * inclues the NULL terminator. 200 for the logname was derived from :
     *   /var/cics_regions/region_name/log/<logname>.extent.00000001
     * We know the maximum region name is 8 chars, therefore every character
     * except the logname added upto 46. Hence 255 (AIX path max) - 46 is 209,
     * however 200 is a nice round (and large number).
     */
    //final static int NAME_MAX_SIZE = 200;

    /**This is the reason why we are calling the calling back function.
     */
    final static int CALLBACK_REASON_SOS = 1;

    /**The offset in the control file for the first restart data record.
     */
    final static int RESTART_OFFSET_1 = LogFileHandle.FILESYSTEM_BLOCKSIZE;

    /**The offset in the control file for the second restart data record.
     */
    final static int RESTART_OFFSET_2 = LogFileHandle.FILESYSTEM_BLOCKSIZE*5;

    /**This is the maximum size of a log record.
     */
    final static int MAX_RECORD_SIZE = MAX_EXTENT_SIZE - 2*LogRecordHeader.SIZEOF - 2*LogRecordEnding.SIZEOF;

    /**The maximum size of a restart record
     */
    final static int MAX_RESTART_SIZE = LogFileHandle.FILESYSTEM_BLOCKSIZE*4 - 2*LogRestartDescriptor.SIZEOF;

    /**The size of a control file which is allocated at open time.
     */
    final static int CONTROL_FILE_SIZE = RESTART_OFFSET_2 + MAX_RESTART_SIZE + 2*LogRestartDescriptor.SIZEOF;

    /**The size of a chunk to allocate from the disk space
     */
    final static int ALLOCATE_SIZE = MAX_EXTENT_SIZE;

    // Instance members

    LogHandle            blockValid = null;
    int                  restartDataLength = 0;
    int                  recordsWritten = 0;
    int                  chunkRemaining = 0;
    int                  activeRestartVersion = 0;
    LogUpcallTarget      upcallTarget = null;
    ArrayList            cursors = null;
    boolean              cushionExists = false;
    boolean              upcallInProgress = false;
    Hashtable            extentTable = null;
    String               logFileName = null;
    LogFileHandle        logFileHandle = null;
    LogControlDescriptor logControlDescriptor = null;
    LogControl           logControl = null;


    /**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
     */
    LogHandle( LogControl      control,
               String          logName,
               LogFileHandle   controlFH,
               LogUpcallTarget upcall )
        throws LogException {

        // 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);

    }

    /**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
     */
    synchronized LogLSN writeRecord( byte[] record,
                                     int    recordType,
                                     int    writeMode )
        throws LogException {

        // 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(LogException.LOG_WRITE_FAILURE, 10, 
                        sm.getString("jts.log_add_link_failed"), le);
            }

            // 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(LogException.LOG_NO_SPACE,11, null, le);
                        }

                        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(LogException.LOG_WRITE_FAILURE, 12,
                        sm.getString("jts.log_allocate_failed"), (Throwable) null);
            }

            // 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(LogException.LOG_ERROR_FORCING_LOG, 14,
                                sm.getString("jts.log_file_sync_failed"), le);
                    }
            }
        }

        // 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;
    }

    /**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
     */
    synchronized byte[] readRecord( LogLSN readLSN,
                                    int[/*1*/] type ) 
        throws LogException {

        // 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(le.errorCode, 6,
                    sm.getString("jts.log_read_header_failed"), le);
        }

        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(le.errorCode,9, sm.getString("jts.log_readvector_failed"), le);
        }

        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];
    }

    /**Writes the restart record.
     *
     * @param buffer The record to be written.
     *
     * @return
     *
     * @exception LogException The write failed.
     *
     * @see
     */
    synchronized void writeRestart( byte[] buffer )
        throws LogException {

        // 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;

    }

    /**Reads the restart record.
     *
     * @param
     *
     * @return  The record read in.
     *
     * @exception LogException The read failed.
     *
     * @see
     */
    synchronized byte[] readRestart()
        throws LogException {
        // 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];
    }

    /**Closes (and optionally deletes) the log file.
     *
     * @param deleteFile Indicates whether file should be deleted.
     *
     * @return
     *
     * @exception LogException The close failed.
     *
     * @see
     */
    synchronized void closeFile( boolean deleteFile ) throws LogException {
        // 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);

    }

    /**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
     */
    synchronized void truncate( LogLSN truncLSN,
                                int    inclusive ) 
        throws LogException {

        // 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(LogException.LOG_READ_FAILURE, 11,
                        sm.getString("jts.log_read_header_failed"), le);
            }

            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;

    }

    /**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
     */
    synchronized void checkLSN( LogLSN chkLSN )
        throws LogException {

        // 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();
        */

    }

    /**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
     */
    synchronized LogCursor openCursor( LogLSN startLSN,
                                       LogLSN endLSN )
        throws LogException {

        // 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;
    }

    /**Closes the cursor.
     *
     * @param LogCursor The cursor to be closed.
     *
     * @return
     *
     * @exception LogException The cursor could not be closed.
     *
     * @see
     */
    synchronized void closeCursor( LogCursor cursor )
        throws LogException {

        // 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();

    }

    /**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
     */
    LogExtent positionFilePointer( LogLSN currentLSN,
                                   int    extra,
                                   int    accessType )
        throws LogException {

        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(LogException.LOG_READ_FAILURE,3, null, le);
            }

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

        // Return the file descriptor for the extent file


        return extent;
    }

    /**Frees the cushion file.
     * <p>
     * This internal method does not need to be synchronized.
     *
     * @param
     *
     * @return
     *
     * @see
     */
    private void freeCushion() {
        // 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;
        }

    }

    /**Restores the cushion file
     * <p>
     * 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
     */
    void restoreCushion( boolean callUpcall )
        throws LogException {

        // 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(LogException.LOG_OPEN_FAILURE,3, null, le);
                }

                // 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(LogException.LOG_OPEN_FAILURE,4, null, le);
                }

                // CLOSE the cushion file

                cushionFH.finalize();
            }

            cushionExists = true;
        }

    }

    /**Writes the control file.
     * This internal method does not need to be synchronized.
     *
     * @param
     *
     * @return
     *
     * @exception LogException The write failed.
     *
     * @see
     */
    void writeControlFile() throws LogException {

        // 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(LogException.LOG_ERROR_FORCING_LOG, 14, null, le);
                }
            }
        }

        // 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);

    }

    /**Opens the given extent.
     * <p>
     * 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
     */
    LogExtent openExtent( int extent ) throws LogException {

        // 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;
    }

    /**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
     */
    void freeFileStorage( LogLSN tailLSN )
        throws LogException {

        // 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();

    }

    /**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
     */
    static void checkRestart( LogFileHandle fileHandle,
                              int           restartNumber,
                              int[/*2*/]    restartInfo )
        throws LogException {

        // 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);
            }
        }

    }

    /**Dumps the state of the object.
     *
     * @param
     *
     * @return
     *
     * @exception LogException  The operation failed.
     *
     * @see
     */
    void dump() throws LogException {
        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();
        }

    }

    /**Removes all extent information from the log file.
     * This internal method does not need to be synchronized.
     *
     * @param
     *
     * @return
     *
     * @see
     */
    void cleanUpExtents() {

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

    }

    /**Returns the alternate restart number.
     *
     * @param restart  The current restart number.
     *
     * @return  The new restart number.
     *
     * @see
     */

    final static int alternateRestart( int restart) {
        return (restart == 1) ? 2 : 1;
    }

    /**Returns the offset of the specified restart number.
     *
     * @param restart  The restart number.
     *
     * @return  The restart position.
     *
     * @see
     */
    final static int restartPosition( int restart ) {
        return (restart == 1) ? RESTART_OFFSET_1 : RESTART_OFFSET_2;
    }

    /**Returns the log file name.
     *
     * @param
     *
     * @return  The log file name.
     *
     * @see
     */
    final String logFileName() {
        return logFileName;
    }
}

Other Glassfish examples (source code examples)

Here is a short list of links related to this Glassfish LogHandle.java source code file:

... this post is sponsored by my books ...

#1 New Release!

FP Best Seller

 

new blog posts

 

Copyright 1998-2021 Alvin Alexander, alvinalexander.com
All Rights Reserved.

A percentage of advertising revenue from
pages under the /java/jwarehouse URI on this website is
paid back to open source projects.