com.sun.jts.CosTransactions.LogHandle Maven / Gradle / Ivy
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright (c) 1997-2016 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.
*/
// Portions Copyright [2018] [Payara Foundation]
//----------------------------------------------------------------------------
//
// 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.security.PrivilegedAction;
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/.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 = 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(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(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 );
// 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.
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.
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 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(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.destroy();
// 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 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(){
return LogControl.directory(logFileName,logControl.directoryPath).delete();
}
}
);
// 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(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(
(PrivilegedAction) () -> tmplogEDP.file.delete()
);
if(!isdeleted)
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(extent);
if ( logEDP != null ) {
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(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);
}
/**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(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(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.
*
* 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(){
return logControl.cushionFile.delete();
}
}
);
// End IASRI 4720539
cushionExists = false;
}
}
/**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
*/
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.destroy();
// Start IASRI 4720539
//logControl.cushionFile.delete();
java.security.AccessController.doPrivileged(
new java.security.PrivilegedAction() {
public Object run(){
return logControl.cushionFile.delete();
}
}
);
// 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.destroy();
}
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);
logFileHandle.fileWrite(controlBytes);
}
/**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
*/
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);
// Use the already hashed extent number to find the position in the
// hash table and add it to the chain
extentTable.put(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);
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(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;
}
}