LogControlpublic class LogControl extends Object This class holds the top level information for an instance of the log, |
Fields Summary |
---|
private static final String | CUSHION_NAMEConstants for file name extensions. | private static final String | EXTENT_NAME | private static final String | CONTROL_NAME | public static final String | RECOVERY_STRING_FILE_NAME | private static final String | LOG_EXTENSION | private static final char[] | EXTENT_CHARS | boolean | logInitialisedInternal instance members | boolean | logReadOnly | Vector | logHandles | String | directoryPath | File | controlFile | File | cushionFile |
Methods Summary |
---|
static boolean | checkFileExists(java.lang.String logId, java.lang.String logDirectory)Determines whether the given named log exists in the given directory.
//START IASRI 4730519
if(logDirectory==null)
return false;
//END IASRI 4730519
boolean exists = controlFile(logId,logDirectory).exists();
return exists;
| synchronized void | cleanUp(LogHandle logHandle)Cleans up the given log file.
// IF not LogInitialised Return LOG_NOT_INITIALISED
if( !logInitialised )
throw new LogException(null,LogException.LOG_NOT_INITIALISED,1);
// Check BlockValid field in Log_FileDescriptor block and
// ensure it is valid
// IF not valid Log_FileDescriptor
// Return LOG_INVALID_FILE_DESCRIPTOR
if( logHandle == null || logHandle.blockValid != logHandle )
throw new LogException(null,LogException.LOG_INVALID_FILE_DESCRIPTOR,2);
// Set the block valid to NULL
logHandle.blockValid = null;
// Remove all extent entries from the log file's extent hash table
logHandle.cleanUpExtents();
// Unchain the Log_FileDescriptor block from the RCA chain. The
// mutices contained in the FD latch structure will already have been
// freed up when the AS process which owned them died. We indicate this
// by setting the ProcessOwnsLatch parameter to FALSE.
removeFile(logHandle);
| static void | clearDirectory(java.lang.String logDir)Clears out all log files from the given directory.
// Find each control file in turn and then delete all files that
// begin with the same name. This will delete all files associated
// with that log (including the control file so that it is not found
// again next time through the loop)
File directory = new File(logDir);
String[] allFiles = directory.list();
for( int i = 0; i < allFiles.length; i++ ) {
// Determine if the file is actually a log subdirectory. If so, use it's name
// to delete all associated log files.
if( allFiles[i].endsWith(LOG_EXTENSION) ) {
//Start IASRI 4720539
final File logFileDir = new File(directory,allFiles[i]);
if( logFileDir.isDirectory() ) {
final String[] logFiles = logFileDir.list();
/*
for( int j = 0; j < logFiles.length; j++ )
new File(logFileDir,logFiles[j]).delete();
logFileDir.delete();
*/
java.security.AccessController.doPrivileged(
new java.security.PrivilegedAction() {
public Object run(){
for( int j = 0; j < logFiles.length; j++ ){
new File(logFileDir,logFiles[j]).delete();
}
return null;
}
}
);
java.security.AccessController.doPrivileged(
new java.security.PrivilegedAction() {
public Object run(){
logFileDir.delete();
return null;
}
}
);
}
//End IASRI 4720539
}
}
| static final java.io.File | controlFile(java.lang.String logId, java.lang.String logDir)Builds a log control file.
File result = new File(directory(logId,logDir),CONTROL_NAME);
return result;
| final java.io.File | cushionFile(java.lang.String logId)Builds a log cushion file.
File result = new File(directory(logId,directoryPath),CUSHION_NAME);
return result;
| static final java.io.File | directory(java.lang.String logId, java.lang.String logDir)Builds a log directory file.
//START IASRI 4721336
//START IASRI 4730519
if(logDir==null) //It should not be null
return new File( "." + File.separator + logId + LOG_EXTENSION);
//END IASRI 4730519
return new File(logDir);
//END IASRI 4721336
| void | dump()Dumps the state of the object.
// If the log has been initialised the pointer to the global
// data will have been initialised. So, if the pointer to the
// global data is not NULL, dump out the its contents.
| java.io.File | extentFile(java.lang.String logId, int extent)Builds a log extent file.
char[] buff = (char[])EXTENT_CHARS.clone();
int tmpExtent = extent / LogExtent.EXTENT_RADIX;
int extentLow = extent % LogExtent.EXTENT_RADIX;
int extentMid = tmpExtent % LogExtent.EXTENT_RADIX;
int extentHigh = tmpExtent / LogExtent.EXTENT_RADIX;
buff[7] = (char)(extentHigh + (extentHigh > 9 ? 'A"-10 : '0"));
buff[8] = (char)(extentMid + (extentMid > 9 ? 'A"-10 : '0"));
buff[9] = (char)(extentLow + (extentLow > 9 ? 'A"-10 : '0"));
String fileName = new String(buff);
File result = new File(directory(logId,directoryPath),fileName);
return result;
| synchronized void | initLog(boolean coldStart, boolean readOnly, java.lang.String logDirectory)Initialises the log in the given directory.
//static processSharedLog = GlobLock();
// Trace the global state of the log.
// IF logInitialised
// Unlock the LogGlobalMutex
// Return LOG_SUCCESS
if( logInitialised ) {
return;
}
logReadOnly = readOnly;
// Store the log directory name for use by subsequent log functions.
directoryPath = new String(logDirectory);
// If this is a cold start, then remove all files in the log directory
if( coldStart && !readOnly )
clearDirectory(logDirectory);
// Create the Vector which will hold the LogHandles.
logHandles = new Vector();
// Set the logInitialised flag to TRUE
// Unlock the logGlobalMutex
// Return LOG_SUCCESS
logInitialised = true;
| synchronized LogHandle | openFile(java.lang.String logFileName, LogUpcallTarget upcallTarget, java.lang.String baseNewName, boolean[] newlyCreated)Opens a log file with the given name and upcall.
// IF not LogInitialised
// Return LOG_NOT_INITIALISED
if( !logInitialised )
throw new LogException(null,LogException.LOG_NOT_INITIALISED,1);
// If the logid provided is an Alias name, it could be more than 8
// characters. If so, to support the FAT file system, a unique
// 8 character must be generated
// If the name is an Alias name, then the base new name to use will have
// been passed in as a parameter.
String logName = null;
/* if( baseNewName != null )
{
int i = 0;
// Determine the index to start allocating from based on the base
if( baseNewName.length() != 0 )
{
try
{ i = Integer.parseInt(baseNewName.substring(LogHandle.FILENAME_PREFIX_LEN+1,LogHandle.FILENAME_PREFIX_LEN+3),16); }
catch( Throwable e ) {}
i++;
}
// Generate a new name from the base.
boolean logExists;
for( logExists = true;
i <= LogHandle.MAX_NAMES && logExists;
i++)
{
logName = new String(FILENAME_PREFIX);
if( i < 100 ) logName += "0";
if( i < 10 ) logName += "0";
logName += Integer.toString(i);
logExists = checkFileExists(logName,directoryPath);
}
}
else
*/ {
// If the log Id is provided is not an Alias make make sure it
// meets the FAT file system requirements
/* if( logFileName.length() != LogHandle.NAME_LENGTH )
throw new LogException(null,LogException.LOG_INVALID_FILE_DESCRIPTOR,19);
*/
logName = logFileName;
File logDir = directory(logName,directoryPath);
if( !logDir.exists() )
logDir.mkdir();
}
// Build name of log's control file, e.g. (<logname>.control)
// Issue OPEN request for control file, specifying Read/Write, Create,
// No-share and 'guaranteed write to disk' options
// IF OPEN is not successful
// Return LOG_OPEN_FAILURE
controlFile = controlFile(logName,directoryPath);
cushionFile = cushionFile(logName);
int openOptions = LogFileHandle.OPEN_RDWR |
LogFileHandle.OPEN_CREAT |
LogFileHandle.OPEN_SYNC; // Default open options
if( logReadOnly )
openOptions = LogFileHandle.OPEN_RDONLY;
LogFileHandle controlFH;
try {
controlFH = new LogFileHandle(controlFile,openOptions);
} catch( LogException le ) {
throw new LogException(null,LogException.LOG_OPEN_FAILURE,3);
}
// Allocate a Log_FileDescriptor block and initialise it
// IF allocate fails
// Close the control file
// Return LOG_INSUFFICIENT_MEMORY
LogHandle logHandle = null;
try {
logHandle = new LogHandle(this,logName,controlFH,upcallTarget); }
catch( LogException le ) {
controlFH.finalize();
throw new LogException(null,LogException.LOG_INSUFFICIENT_MEMORY,4);
}
// Call Log_RestoreCushion to create/check the cushion file exists
// for this logfile. If this fails the log cannot be opened.
try {
logHandle.restoreCushion(false);
} catch( LogException le ) {
controlFH.finalize();
throw new LogException(null,LogException.LOG_INSUFFICIENT_MEMORY,9);
}
// Issue a READ request for the control file, specifying the
// Log_ControlDescriptor structure within the Log_FileDescriptor
// block as the input buffer
// IF not successful (rc == -1)
// Close the control file
// Deallocate the Log_FileDescriptor block
// Return LOG_READ_FAILURE
byte[] controlBytes = new byte[LogControlDescriptor.SIZEOF];
int bytesRead = 0;
try {
bytesRead = controlFH.fileRead(controlBytes);
} catch( LogException le ) {
controlFH.finalize();
throw new LogException(null,LogException.LOG_READ_FAILURE,5);
}
if( bytesRead == 0 ) {
// IF the READ returned EOF (rc == 0), continue with initialisation of
// the Log_ControlDescriptor structure within the
// Log_FileDescriptor block
// - Initialise the log head LSN to LOG_NULL_LSN
// - Initialise the log tail LSN to LOG_FIRST_LSN
// - Initialise the next free LSN to LOG_FIRST_LSN
// - Initialise the RestartDataLength in the Log_FileDescriptor
// block to 0
//
// Set NewlyCreated parameter to TRUE
//
// Pre-allocate all the file storage for the control file
//
// Set RecordsWritten to LOG_CONTROL_FORCE_INTERVAL so on
// the first write to the log the newly created control data
// is written to disk. If this is not done then data could be lost.
//
// Write the control data to file
//
// Open the first extent file and allocate the storage it requires.
// If either step fails close and erase the control file and
// return LOG_INSUFFICIENT_STORAGE.
logHandle.logControlDescriptor.headLSN.copy(LogLSN.NULL_LSN);
logHandle.logControlDescriptor.tailLSN.copy(LogLSN.FIRST_LSN);
logHandle.logControlDescriptor.nextLSN.copy(LogLSN.FIRST_LSN);
logHandle.restartDataLength = 0;
logHandle.recordsWritten = LogHandle.CONTROL_FORCE_INTERVAL;
newlyCreated[0] = true;
if( !logReadOnly ) {
int bytesWritten;
try {
controlFH.allocFileStorage(LogHandle.CONTROL_FILE_SIZE);
} catch( LogException le ) {
controlFH.finalize();
throw new LogException(null,LogException.LOG_WRITE_FAILURE,6);
}
logHandle.logControlDescriptor.toBytes(controlBytes,0);
try {
bytesWritten = controlFH.fileWrite(controlBytes);
} catch( LogException le ) {
controlFH.finalize();
throw new LogException(null,LogException.LOG_WRITE_FAILURE,7);
}
LogExtent logEDP = null;
try {
logEDP = logHandle.openExtent(logHandle.logControlDescriptor.nextLSN.extent);
} catch( LogException le ) {
controlFH.finalize();
throw new LogException(null,LogException.LOG_NO_SPACE,10);
}
try {
logEDP.fileHandle.allocFileStorage(LogHandle.ALLOCATE_SIZE);
} catch( LogException le ) {
controlFH.finalize();
throw new LogException(null,LogException.LOG_NO_SPACE,11);
}
logHandle.chunkRemaining = LogHandle.ALLOCATE_SIZE;
}
}
// Otherwise the log already exists.
else {
int timeStampRec1; // The time stamp in restart rec 1
int timeStampRec2; // The time stamp in restart rec 2
int lengthRec1; // The length of restart record 1
int lengthRec2; // The length of restart record 1
// Set NewlyCreated parameter to FALSE
newlyCreated[0] = false;
// Open all of the active extents and
// rebuild the extent table, starting form the extent
// containing the Tail LSN and finishing with the extent
// containing the Head LSN
logHandle.logControlDescriptor = new LogControlDescriptor(controlBytes,0);
LogExtent logEDP = null;
for( int currentExtent = logHandle.logControlDescriptor.tailLSN.extent;
currentExtent <= logHandle.logControlDescriptor.headLSN.extent ||
currentExtent <= logHandle.logControlDescriptor.nextLSN.extent;
currentExtent++)
try {
logEDP = logHandle.openExtent(currentExtent);
} catch( LogException le ) {
controlFH.finalize();
throw new LogException(null,LogException.LOG_OPEN_EXTENT_FAILURE,19);
}
// Read the restart data for restart record one
// If the read failed then
// Close the control file
// Deallocate the Log_FileDescriptor block
// Return LOG_READ_FAILURE
int[] restartValues1 = new int[2];
int[] restartValues2 = new int[2];
try {
logHandle.checkRestart(controlFH,1,restartValues1);
} catch( LogException le ) {
controlFH.finalize();
throw new LogException(null,LogException.LOG_READ_FAILURE,8);
}
// Check that the record length was not equal to zero
// IF the record length was not equal to zero
// READ the next restart record
// IF ( (Length2 !=0) && (Time2 > Time1)
// THEN CurrentValid is 2
// ELSE CurrentValid is 1
// BUGFIX(Ram Jeyaraman) Always check both the restart records,
// even though the first record might have zero data length.
// It is possible, that the second record might
// have non-zero data length with a later
// timestamp, even though the first record has zero data length.
// Fix is to comment out the check below.
//if (restartValues1[0] != 0)
{
// If the read failed then
// Close the control file
// Deallocate the Log_FileDescriptor block
// Return LOG_READ_FAILURE
try {
logHandle.checkRestart(controlFH,2,restartValues2);
} catch( LogException le ) {
controlFH.finalize();
throw new LogException(null,LogException.LOG_READ_FAILURE,9);
}
if( restartValues2[0] != 0 &&
restartValues2[1] > restartValues1[1] ) {
logHandle.activeRestartVersion = 2;
logHandle.restartDataLength = restartValues2[0];
} else {
logHandle.activeRestartVersion = 1;
logHandle.restartDataLength = restartValues1[0];
}
}
if( logHandle.logControlDescriptor.headLSN.isNULL() )
logHandle.recordsWritten = LogHandle.CONTROL_FORCE_INTERVAL;
}
// Add the Log_FileDescriptor block to the head of the
// (LogFileDescriptorHead) chain hung off the RCA and update the backward
// chain pointer of the block which was previously at the head of the chain.
logHandles.addElement(logHandle);
// IF the head LSN in Log_ControlDescriptor != LOG_NULL_LSN
// Build the extent file name from the head LSN
// Issue an OPEN request for the file
if( !logHandle.logControlDescriptor.headLSN.isNULL() ) {
int offset; // Present offset in the open extent
LogRecordHeader extentRec, // An extent record header
headRec, // An extent record header
linkRec; // An extent record header
boolean lastValidRead = false; // Has the last valid record be read
LogExtent logEDP = null;
// IF not successful (rc == -1)
// Close the control file
// Unchain the LogFDP and free up the storage
// Return LOG_OPEN_FAILURE
// Allocate a Log_ExtentDescriptor block and initialise it
// Hash the extent number to find the anchor for this extent in the
// hash table and add it to the head of the chain
// Move the file pointer to the head LSN record (using LSEEK)
try {
logEDP = logHandle.positionFilePointer(logHandle.logControlDescriptor.headLSN,
0,LogExtent.ACCESSTYPE_READ); }
catch( LogException le ) {
controlFH.finalize();
removeFile(logHandle);
throw new LogException(null,LogException.LOG_OPEN_FAILURE,10);
}
// Issue a READ for the record header
// IF the read is not successful (rc <= 0)
// Close the control file
// Deallocate newly acquired control blocks
// Return LOG_READ_FAILURE
//
// Check that the LSN in the record header matches the head LSN
// IF there is a mismatch
// Deallocate newly acquired control blocks
// Return LOG_CORRUPTED
byte[] headerBytes = new byte[LogRecordHeader.SIZEOF];
try {
bytesRead = logEDP.fileHandle.fileRead(headerBytes);
} catch( LogException le ) {
controlFH.finalize();
removeFile(logHandle);
throw new LogException(null,le.errorCode,11);
}
extentRec = new LogRecordHeader(headerBytes,0);
if( !extentRec.currentLSN.equals(logHandle.logControlDescriptor.headLSN) ) {
controlFH.finalize();
removeFile(logHandle);
throw new LogException(null,LogException.LOG_CORRUPTED,12);
}
// Copy the record header to HEADERCOPY
// Move the file pointer to the 'next LSN' in record header
logEDP.cursorPosition += bytesRead;
headRec = new LogRecordHeader();
headRec.copy(extentRec);
offset = headRec.nextLSN.offset;
try {
logEDP = logHandle.positionFilePointer(extentRec.nextLSN,0,LogExtent.ACCESSTYPE_READ);
} catch( LogException le ) {
controlFH.finalize();
removeFile(logHandle);
throw new LogException(null,LogException.LOG_OPEN_FAILURE,13);
}
linkRec = new LogRecordHeader();
// LOOP until last valid log record has been read
lastValidRead = false;
do {
// Issue a read for the record header
offset = extentRec.nextLSN.offset;
try {
bytesRead = logEDP.fileHandle.fileRead(headerBytes);
} catch( LogException le ) {
controlFH.finalize();
removeFile(logHandle);
throw new LogException(null,LogException.LOG_READ_FAILURE,14);
}
if( bytesRead == -1 ) {
controlFH.finalize();
removeFile(logHandle);
throw new LogException(null,LogException.LOG_READ_FAILURE,14);
}
extentRec = new LogRecordHeader(headerBytes,0);
logEDP.cursorPosition += bytesRead;
// IF the LSN in the record header matches the LSN of the
// current position in the extent file
if( extentRec.currentLSN.offset == offset ) {
// IF its a link record
if( extentRec.recordType == LogHandle.LINK ) {
// Copy it to LINKCOPY
// 'Move' the file pointer to the NextRecord value (in the next extent file)
// Iterate.
linkRec.copy(extentRec);
try {
logEDP = logHandle.positionFilePointer(extentRec.nextLSN,0,LogExtent.ACCESSTYPE_READ);
} catch( LogException le ) {
controlFH.finalize();
removeFile(logHandle);
throw new LogException(null,le.errorCode,15);
}
continue;
} else
// IF LINKCOPY is not null AND LINKCOPY.NextRecord LSN &lnot.= ThisRecord LSN
// Set LINKCOPY to null
if( !linkRec.currentLSN.isNULL() &&
!linkRec.nextLSN.equals(extentRec.currentLSN) )
linkRec = new LogRecordHeader();
// Copy the record header to HEADERCOPY
// Use the NextRecord value to move the file pointer to the next record header
headRec.copy(extentRec);
try {
logEDP = logHandle.positionFilePointer(extentRec.nextLSN,0,LogExtent.ACCESSTYPE_READ);
} catch( Throwable e ) {}
} else {
LogRecordEnding endRec; // An ending record type
lastValidRead = true;
// Use the ThisRecord value from HEADERCOPY together with the
// RecordLength value to calculate the position of the previous
// Log_RecordEnding structure
//
// LSEEK to this position and read it
try {
logEDP = logHandle.positionFilePointer(headRec.currentLSN,
LogRecordHeader.SIZEOF+headRec.recordLength,
LogExtent.ACCESSTYPE_READ);
} catch( LogException le ) {
controlFH.finalize();
removeFile(logHandle);
throw new LogException(null,le.errorCode,16);
}
byte[] endingBytes = new byte[LogRecordEnding.SIZEOF];
try {
bytesRead = logEDP.fileHandle.fileRead(endingBytes);
} catch( LogException le ) {
controlFH.finalize();
removeFile(logHandle);
throw new LogException(null,le.errorCode,17);
}
endRec = new LogRecordEnding(endingBytes,0);
logEDP.cursorPosition += bytesRead;
// IF its value is the same as ThisRecord LSN This is the last valid record
// Update head LSN in control data with ThisRecord LSN
// Update next LSN in control data with the NextRecord LSN
if( endRec.currentLSN.equals(headRec.currentLSN) ) {
logHandle.logControlDescriptor.headLSN.copy(headRec.currentLSN);
logHandle.logControlDescriptor.nextLSN.copy(headRec.nextLSN);
}
// This is an invalid record, so record details of previous valid record
// IF LINKCOPY is null
// Update head LSN in control data with the PreviousRecord LSN
// Update next LSN in control data with ThisRecord LSN
else {
if( linkRec.currentLSN.isNULL() ) {
logHandle.logControlDescriptor.headLSN.copy(headRec.previousLSN);
logHandle.logControlDescriptor.nextLSN.copy(headRec.currentLSN);
}
// Otherwise update head LSN in control data with value from LINKCOPY
else {
logHandle.logControlDescriptor.headLSN.copy(linkRec.previousLSN);
logHandle.logControlDescriptor.nextLSN.copy(linkRec.currentLSN);
}
}
}
// Increase the number of records written since the last force
logHandle.recordsWritten++;
}
while( !lastValidRead );
}
// Store the address of the Log_FileDescriptor block into its BlockValid field
logHandle.blockValid = logHandle;
return logHandle;
| static final java.io.File | recoveryIdentifierFile(java.lang.String logId, java.lang.String logDir)
File result = new File(directory(logId,logDir),RECOVERY_STRING_FILE_NAME);
return result;
| synchronized void | removeFile(LogHandle logHandle)Removes the log file from the chain.
// Unchain the Log_FileDescriptor block from the RCA chain
logHandles.removeElement(logHandle);
// Clear the BlockValid field in the Log_FileDescriptor block
logHandle.blockValid = null;
|
|