All Downloads are FREE. Search and download functionalities are using the official Maven repository.

mq5.0-source.main.persist.txnlog.src.main.java.com.sun.messaging.jmq.io.txnlog.file.FileTransactionLogWriter Maven / Gradle / Ivy

There is a newer version: 5.1
Show newest version
/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 *
 * Copyright (c) 2000-2012 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.
 */

/*
 * @(#)FileTransactionLogWriter.java	1.3 06/29/07
 */ 

package com.sun.messaging.jmq.io.txnlog.file;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.util.Date;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.zip.Adler32;
import java.util.zip.Checksum;

import com.sun.messaging.jmq.util.MQThread;
import com.sun.messaging.jmq.io.txnlog.CheckPointListener;
import com.sun.messaging.jmq.io.txnlog.TransactionLogRecord;
import com.sun.messaging.jmq.io.txnlog.TransactionLogType;
import com.sun.messaging.jmq.io.txnlog.TransactionLogWriter;

/**
 * This class is used to write FileTransactionLogRecord to the transaction log file.
 * 

* Each transaction is written as a transaction log record. Each record written is * synced to the disk. Broker does not need to sync its message stores until * a check point request (CheckPointListener is called). *

* (maxSize - checkPointOffset), //this logwriter will reset itself and start from the beginning private long cpOffset = 500 * 1024; private long cpSize = maxSize - cpOffset; private CheckPointListener callback = null; private boolean isListenerCalled = false; //The txn file. private RandomAccessFile raf = null; private File file = null; //read/write/sync mode. public static final String RWD_MODE = "rwd"; /** * "rws" file mode -- for testing use only */ private static final String RWS_MODE = "rws"; /** * read-write mode -- call FileChannel.force() to sync every transaction. **/ public static final String RW_MODE = "rw"; private String fileMode = RWD_MODE; /** * This is used only if "useFileChannelSync" flag is true. */ private FileChannel fchannel = null; /** * This is set to true only when RW_MODE is used. */ private boolean useFileChannelSync = false; //check point position, 0 means this log is synced with message store. private long checkPointPosition = FILE_HEADER_SIZE; private boolean debug = false; //sync object for operations to txn file. private Object txnLogSyncObj = new Object(); //to calculate checksum for each record written to txn log. private Checksum checksumEngine = new Adler32(); /** * flag to indicate if playback of this file is required. */ private boolean playBackRequired = false; /** * flag to indicate if file id corrupted. This flag is set to true if * FileTransactionLogWriter#readFileHeaderdetects * that the header cannot be validated. Broker MUST call * FileTransactionLogWriter#reset to reset the log file. */ private boolean isFileCorrupted = false; //check point sequence number. This number is put in the file header //and each log record. The value in the file header indicates the last //check point sequence number. Each log record from the last check point //to the next one contains the same check point sequence number. //the number is increased by one for each check point synced. private long checkpointSequence = 0; //This number is increased by 1 for each record added //to the log file. This number is reset to 0 when a check //point is called. private long entrySequence = 0; //last entry written to the log file private TransactionLogRecord lastEntry = null; //back up txn log file name extension. public static final String TXNLOG_BACKUP_EXT = ".1"; //flag to determine if we want to do txnlog file backup. default to false //some systems do not support the OP. //Set imq.txnlog.backup to true to turn it on private boolean doFileBackup = false; private boolean doAsyncWrites = false; private boolean closed; private MQThread asyncWriteThread = null; private boolean synch = true; // the following three variables are used to debug the average number of records in a compound record private int sampleNum; private int sampleCount=1000; private int[] numrecordsArray = new int[sampleCount]; // a version number passed in by the application that will be stored in the header when the file is first created private long currentAppCookie = 0; // the version number read from the file when the file already exists. private long existingAppCookie = 0; // a collection holding log records due for processing private List transactionLogRecordList = new LinkedList(); // a mutex controlling access to the transactionLogRecordList private Object recordListMutex = new Object(); public FileTransactionLogWriter(File parent, String fileName, long size) throws IOException { init(parent, fileName, size); } public FileTransactionLogWriter(String fileName) throws IOException { init(null, fileName, DEFAULT_MAX_SIZE); } /** * Constructor. * */ public FileTransactionLogWriter(File parent, String fileName, long size, String mode, long applicationCookie) throws IOException { this(parent,fileName,size,mode,true, false, applicationCookie); } /** * Constructor. * */ public FileTransactionLogWriter(File parent, String fileName, long size, String mode, boolean sync, boolean groupCommit, long applicationCookie) throws IOException { if (RW_MODE.equals(mode)) { this.useFileChannelSync = true; fileMode = RW_MODE; } else if (RWS_MODE.equals(mode)) { fileMode = RWS_MODE; } else if (RWD_MODE.equals(mode) == false) { throw new java.lang.IllegalArgumentException( "This file mode is not supported: " + mode); } this.currentAppCookie = applicationCookie; this.synch = sync; this.doAsyncWrites = groupCommit; init(parent, fileName, size); } private void init(File parent, String child, long size) throws IOException { this.debug = Boolean.getBoolean (TXNLOG_DEBUG_PROP_NAME); this.doFileBackup = Boolean.getBoolean (TXNLOG_BACKUP_PROP_NAME); file = new File(parent, child); setMaximumSize(size); if (raf != null) { raf.close(); } if(doAsyncWrites) { log ("starting asyncwrite"); asyncWriteThread = new MQThread(this, child+ "AsyncWrite"); asyncWriteThread.setPriority(Thread.NORM_PRIORITY-1); asyncWriteThread.start(); } /** * The default file mode is RWD_MODE ("rwd"). If this class is instantiated with a file mode, * the file mode is set to the specified mode ("rw", "rws", "rwd" only). */ log ("Using file mode: " + fileMode); raf = new RandomAccessFile(file, fileMode); /** * If "rw" mode is specified, useFileChannelSync flag is set to true. */ if ( this.useFileChannelSync) { log("File Channel Sync flag is on ..."); fchannel = raf.getChannel(); } if ( raf.length() > 0 ) { if (debug) { log("file exists: " + file.getAbsolutePath() + ", file size: " + raf.length()); } readFileHeader(); if (this.playBackRequired == false) { writeFileHeader(FILE_STATUS_CHK_POINT_UPDATED, FILE_HEADER_SIZE); } } else { //do create header for now. existingAppCookie = currentAppCookie; initNewFile(); } } private void initNewFile() throws IOException { //always rewind. raf.seek(0); raf.setLength((int)maxSize); //allocate size for perf. reason. //raf.write( new byte [(int)maxSize] ); doWrite (new byte [(int)maxSize]); //init check point sequence this.checkpointSequence = 0; //init entry sequence this.entrySequence = 0; //write file header. //fileStatus=CREATE_NORMAL, cpPosition=0 writeFileHeader(FILE_STATUS_CREATE_NORMAL, FILE_HEADER_SIZE); if (debug) { log ("file created: " + file.getAbsolutePath () + ", size: " + raf.length ()); } } /** * Read and validate file header. * @throws IOException */ private void readFileHeader() throws IOException { raf.seek(0); byte[] bytes = new byte[FILE_HEADER_SIZE]; int size = raf.read(bytes); //1. check header size if ( size != FILE_HEADER_SIZE ) { //set flag this.isFileCorrupted = true; throw new FileCorruptedException("File Header size mismatch. Expected: " + FILE_HEADER_SIZE + ", read: " + size); } ByteBuffer buf = ByteBuffer.wrap(bytes); int magic = buf.getInt(); short fversion = buf.getShort(); short shutdownState = buf.getShort(); long cpPosition = buf.getLong(); long timestamp = buf.getLong(); long cpseq = buf.getLong(); //cp seq long chksum = buf.getLong(); long fileCookie = buf.getLong(); long calculated = calculateCheckSum(bytes, 0, FILE_CHECK_SUM_POSITION); if (debug) { log ("read file header, magic=" + magic + ", fversion=" + fversion + ", status="+shutdownState+ ", cpPosition="+cpPosition + ", timestamp=" + timestamp + ", cpSequence="+cpseq + ", chksum="+chksum); } //2. validate check sum if ( chksum != calculated ) { //set flag this.isFileCorrupted = true; throw new FileCorruptedException("File Header checksum mismatch. Expected: " + chksum + ", calculated: " + calculated); } //3. validate magic number/version if ((magic != FILE_MAGIC_NUMBER) || fversion != FILE_VERSION) { //set flag this.isFileCorrupted = true; throw new FileCorruptedException("File Magic number/version mismatch: " + magic +":" + fversion); } //4. check if playback is required. if ( shutdownState != FILE_STATUS_SHUTDOWN_NORMAL) { //we need to playback. set flag to true this.playBackRequired = true; } if (debug) { log ("playbackRequired="+playBackRequired); } //set chk position position. this.checkPointPosition = cpPosition; //set check point sequence this.checkpointSequence = cpseq; //move to the end of file header. raf.seek(FILE_HEADER_SIZE); //fchannel.position(FILE_HEADER_SIZE); // check application cookie matches this.existingAppCookie = fileCookie; if(this.existingAppCookie != currentAppCookie) { log("application cookies do not match! Existing file has version="+this.existingAppCookie + " Current version of software=" + this.currentAppCookie); } } /** * Write file header into ByteBuffer. * * This file has a 48 byte fixed header as specified below. */ private void writeFileHeader(short fileStatus, long cpPosition) throws IOException { //increase check point sequence. this value is used so that all entries in each //check point contains the same (and unique) checkPointSequence number. // if (checkpointSequence > Long.MAX_VALUE -1) { //reset checkpointSequence = 0; } else { checkpointSequence ++; } //reset entry seq number when check point seq is increased entrySequence = 0; //rewind. raf.seek(0); byte[] bytes = new byte[FILE_HEADER_SIZE]; ByteBuffer buf = ByteBuffer.wrap(bytes); //1. Magic number buf.putInt(FILE_MAGIC_NUMBER); //0-3 //2. version buf.putShort(FILE_VERSION); //4-5 //3. status. buf.putShort(fileStatus);//6-7 //4. check point position buf.putLong(cpPosition);//8-15 //5. timestamp long timestamp = System.currentTimeMillis(); buf.putLong(timestamp); //16-23 //6. check point sequence number buf.putLong(checkpointSequence); //24-31 long chksum = this.calculateCheckSum(bytes, 0, FILE_CHECK_SUM_POSITION); //6. check sum buf.putLong(chksum); //32-39 //7. reserve buf.putLong(existingAppCookie); //40-47 //raf.write(bytes); doWrite (bytes); if (debug) { log ("write file header. magic=" + FILE_MAGIC_NUMBER + ", fversion=" + FILE_VERSION + ", status="+ fileStatus+ ", cpPosition="+cpPosition + ", timestamp=" + timestamp + ", cpSequence="+checkpointSequence + ", chksum="+chksum); } } /** * sets the point where a checkpoint automatically * occurs * XXX: should we remove this? */ public void setCheckpointSize(long bytes) { this.cpSize = bytes; } /** * sets the maximum size of all State logs. When the maximum * is hit, the log is checkpointed and renamed as a back up file. */ public void setMaximumSize(long bytes) { this.maxSize = bytes; this.cpSize = maxSize - this.cpOffset; if (this.cpSize < (FILE_HEADER_SIZE +RECORD_HEADER_SIZE)) { this.cpSize = FILE_HEADER_SIZE +RECORD_HEADER_SIZE; } } /** * Set check point offset. When the file pointer position reached * to (maxSize-offset), a check point is triggerred. */ public void setCheckPointOffset(long offset) { this.cpOffset = offset; this.cpSize = maxSize - offset; if (this.cpSize < (FILE_HEADER_SIZE +RECORD_HEADER_SIZE)) { this.cpSize = FILE_HEADER_SIZE +RECORD_HEADER_SIZE; } } /** * registeres the checkpoint CB. The callback is * triggered when checkpoint is called whether because * the system hit a size limit or the application explicitly * called checkpoint(); * @see checkpoint(); */ public void setCheckPointListener(CheckPointListener cb) { this.callback = cb; } public void writeAsyncRecord(TransactionLogRecord entry) throws IOException { // add record to list synchronized (recordListMutex) { // log("adding record"); transactionLogRecordList.add(entry); // log("notifying recordListMutex"); recordListMutex.notify(); } // now wait for record to be processed by logger thread synchronized (entry) { try { while(!entry.isWritten()) { // log("waiting for record to be written"); entry.wait(); // log("woken up from waiting for record to be written"); } // log("record now written"); } catch (InterruptedException e) { } } } private void processTransactionLogRecordList() { TransactionLogRecord[] records = null; synchronized (recordListMutex) { while (transactionLogRecordList.size() == 0 && !closed) { try { // log("waiting for transactionLogRecordList to be >0 "); recordListMutex.wait(1000); // log("waking up from recordListMutex.wait()"); } catch (InterruptedException e) { } } records = new TransactionLogRecord[transactionLogRecordList.size()]; records = transactionLogRecordList.toArray(records); transactionLogRecordList.clear(); } // log("processing " + records.length + " records"); // there should be something in the list now. if (debug) { numrecordsArray[sampleNum] = records.length; sampleNum++; int totalCount = 0; if (sampleNum == sampleCount) { sampleNum = 0; for (int i = 0; i < sampleCount; i++) { totalCount += numrecordsArray[i]; System.out.print(numrecordsArray[i] + ","); } float averageCount = ((float) totalCount) / sampleCount; log(" average records in compound txn record = " + averageCount); } } if (records.length == 1) { // log("transactionLogRecordList ==1 "); // only a single record so just save it normally TransactionLogRecord entry = records[0]; try { writeRecord(entry); } catch (IOException e) { entry.setException(e); } // wake up waiting thread // log("synchronized (entry) "); synchronized (entry) { // log("notifying entry "); entry.setWritten(true); entry.notify(); } return; } try { writeCompoundRecord(records); } catch (IOException ioe) { for (int i = 0; i < records.length; i++) { TransactionLogRecord e = records[i]; e.setException(ioe); } } for (int i = 0; i < records.length; i++) { TransactionLogRecord e = records[i]; // log("synchronized (entry) "); synchronized (e) { // log("notifying entry "); e.setWritten(true); e.notify(); } } } public void run() { log("run called "); while (!closed) { try { processTransactionLogRecordList(); } catch (Exception e) { } } log("run ending "); } void writeCompoundRecord(TransactionLogRecord[] records) throws IOException { synchronized (txnLogSyncObj) { if(closed) { // don't write record if txnLog has been closed return; } int numRecords = records.length; // log("writeCompoundRecord list size = "+records.length); TransactionLogRecord entry = new FileTransactionLogRecord(); entry.setType(TransactionLogType.COMPOUND_TRANSACTION); // get bytes // calculate compoundBody size; int compoundBodySize = 4; for (int i = 0; i < numRecords; i++) { compoundBodySize += 8; compoundBodySize += records[i].getBody().length; } byte[] compoundBody = new byte[compoundBodySize]; ByteBuffer subBuf = ByteBuffer.wrap(compoundBody); subBuf.putInt(numRecords); for (int i = 0; i < numRecords; i++) { subBuf.putInt(records[i].getType()); subBuf.putInt(records[i].getBody().length); subBuf.put(records[i].getBody()); } entry.setBody(compoundBody); // write entry, we need to sync from this point // First come first serve entry.setCheckPointSequence(checkpointSequence); // set timestamp entry.setTimestamp(System.currentTimeMillis()); // set entry sequence. increase 1 after addition. entry.setSequence(entrySequence++); // 1. calculate record size int size = RECORD_HEADER_SIZE + entry.getBody().length; // 2. allocate byte buffer byte[] bytes = new byte[size]; ByteBuffer buf = ByteBuffer.wrap(bytes); // 3. write record header writeRecordHeader(buf, entry); // 4. write body buf.put(entry.getBody()); // write (and sync) bytes to disk // raf.write(bytes); doWrite(bytes); // check if we need to notify CP listener. if (raf.getFilePointer() > cpSize) { // we only call the listener once if (this.isListenerCalled == false) { if (debug) { log("calling check point listener, fpointer: " + raf.getFilePointer()); } callback.checkpoint(); // set flag to true so that we only call the listener once. this.isListenerCalled = true; } } // last record added to the log file this.lastEntry = entry; // log("wroteCompoundRecord list "); } } /* * public void write(TransactionLogRecord entry) throws IOException { * * synchronized (txnLogSyncObj) { * * if (isFileCorrupted) { throw new IllegalStateException("File is * corrupted. You must reset the log file to continue."); } * * if (playBackRequired) { throw new IllegalStateException("File not synced. * You must call Iterator to play back log file."); } * * if (callback == null) { throw new IllegalStateException("Check point * listener not set. You must set a CheckPointListener before writing * TransactionLogRecords."); } * // write entry, we need to sync from this point // First come first * serve entry.setCheckPointSequence(checkpointSequence); * * //set timestamp entry.setTimestamp (System.currentTimeMillis ()); * * //set entry sequence. increase 1 after addition. entry.setSequence * (entrySequence++); * // 1. calculate record size int size = RECORD_HEADER_SIZE + * entry.getBody().length; * // 2. allocate byte buffer byte[] bytes = new byte[size]; ByteBuffer buf = * ByteBuffer.wrap(bytes); * // 3. write record header writeRecordHeader(buf, entry); * // 4. write body buf.put(entry.getBody()); * // write (and sync) bytes to disk //raf.write(bytes); doWrite (bytes); * * //check if we need to notify CP listener. if (raf.getFilePointer() > * cpSize) { * * //we only call the listener once if ( this.isListenerCalled == false) { * * if (debug) { log ("calling check point listener, fpointer: " + * raf.getFilePointer ()); } * * callback.checkpoint(); * * //set flag to true so that we only call the listener once. * this.isListenerCalled = true; } } * * //last record added to the log file this.lastEntry = entry; } } * */ public void write(TransactionLogRecord entry) throws IOException { if (doAsyncWrites) { writeAsyncRecord(entry); } else { writeRecord(entry); } } public void writeRecord(TransactionLogRecord entry) throws IOException { synchronized (txnLogSyncObj) { if(closed) { // don't attempt to write record if txnLog has been closed return; } if (isFileCorrupted) { throw new IllegalStateException("File is corrupted. You must reset the log file to continue."); } if (playBackRequired) { throw new IllegalStateException("File not synced. You must call Iterator to play back log file."); } if (callback == null) { throw new IllegalStateException("Check point listener not set. You must set a CheckPointListener before writing TransactionLogRecords."); } // write entry, we need to sync from this point // First come first serve entry.setCheckPointSequence(checkpointSequence); //set timestamp entry.setTimestamp (System.currentTimeMillis ()); //set entry sequence. increase 1 after addition. entry.setSequence (entrySequence++); // 1. calculate record size int size = RECORD_HEADER_SIZE + entry.getBody().length; // 2. allocate byte buffer byte[] bytes = new byte[size]; ByteBuffer buf = ByteBuffer.wrap(bytes); // 3. write record header writeRecordHeader(buf, entry); // 4. write body buf.put(entry.getBody()); // write (and sync) bytes to disk //raf.write(bytes); doWrite (bytes); //check if we need to notify CP listener. if (raf.getFilePointer() > cpSize) { //we only call the listener once if ( this.isListenerCalled == false) { if (debug) { log ("calling check point listener, fpointer: " + raf.getFilePointer ()); } callback.checkpoint(); //set flag to true so that we only call the listener once. this.isListenerCalled = true; } } //last record added to the log file this.lastEntry = entry; } } /** * header size - 32 bytes. * @param buf * @param entry * XXX: NEED SEQUENCE NUMBER? */ private void writeRecordHeader(ByteBuffer buf, TransactionLogRecord entry) { //1. record magic number buf.putInt(RECORD_MAGIC_NUMBER); //0-3 //2. record type buf.putInt(entry.getType());//4-7 //3. body size buf.putInt(entry.getBody().length);//8-11 //4. timestamp buf.putLong(entry.getTimestamp());//12-19 //5. entry seq buf.putLong(entry.getSequence()); //20-28 //6. check point sequence buf.putLong(entry.getCheckPointSequence () ); //29-36 //7. calculate and write body check sum long value = this.calculateCheckSum(entry.getBody()); buf.putLong(value); //37-44, check sum //8. header reserve buf.putInt(RECORD_HEADER_RESERVE);//45-48 } /** * Writes bytes to the file. */ private void doWrite (byte[] bytes) throws IOException { raf.write(bytes); if ( this.useFileChannelSync && synch) { //this.fchannel.force(false); raf.getFD().sync(); } } long calculateCheckSum(byte[] body) { long value = calculateCheckSum(body, 0, body.length); return value; } /** * Calculate check sum. A simple perf showed that average time spent * here is about 0.001 (total-checksum-time/total-time) when writing * 2000 records to a synced file (hard drive cache off). * * @param body * @param offset * @param length * @return */ long calculateCheckSum(byte[] body, int offset, int length) { long value = -1; synchronized (txnLogSyncObj) { checksumEngine.update(body, offset, length); value = checksumEngine.getValue(); checksumEngine.reset(); } return value; } /** * * @return the next check point sequence number. */ public TransactionLogRecord checkpoint() throws IOException { synchronized (this.txnLogSyncObj) { long pos = raf.getFilePointer(); if ( pos > cpSize ) { //rewind -- update file header only. writeFileHeader(FILE_STATUS_CHK_POINT_UPDATED, FILE_HEADER_SIZE); } else { //update file header only writeFileHeader(FILE_STATUS_CHK_POINT_UPDATED, pos); raf.seek(pos); } //reset this flag this.isListenerCalled = false; return this.lastEntry; } } /** * iterates from the last checkpoint */ public Iterator iterator() throws IOException { Iterator it = new FileLogRecordIterator(this); return it; } /** * Rewind/Refresh the log file. * * This method should be called if one of the following conditions occurs. *

* 1. When broker starts up, and detects that playback is required. The broker should call the * iterator() method and playback the transaction entries from the iterator API. The broker then * calls reset to clear the state of the transaction log file. *

* 2. */ public void reset() throws IOException { log("Reseting txn log file ..."); synchronized (this.txnLogSyncObj) { if (this.doFileBackup) { this.backupLogFile (); } if ( this.isFileCorrupted) { this.initNewFile(); } else { writeFileHeader(FILE_STATUS_CHK_POINT_UPDATED, FILE_HEADER_SIZE); } /** * reset check point position. */ this.checkPointPosition = FILE_HEADER_SIZE; this.playBackRequired = false; this.isFileCorrupted = false; this.isListenerCalled = false; } } /** * The last entry added to the log file. * * XXX: only works when the txn log is in writing/inserting mode. */ public TransactionLogRecord getLastEntry() { synchronized (this.txnLogSyncObj) { return this.lastEntry; } } public void close(boolean clean) throws IOException { if(clean) { close(); return; } synchronized (this.txnLogSyncObj) { // close file. raf.close(); closed = true; } } /** * 1. set SHUTDOWN_NORMAL. * 2. set cpPosition to current file position. */ public void close() throws IOException { synchronized (this.txnLogSyncObj) { //get current file pointer long pos = raf.getFilePointer(); //set file status and cpPosition. writeFileHeader(FILE_STATUS_SHUTDOWN_NORMAL, FILE_HEADER_SIZE); //close file. raf.close(); closed = true; } } /** * XXX chiaming; Not required to sync the class since we have a cp sequence. */ public TransactionLogRecord newTransactionLogRecord() { return new FileTransactionLogRecord(); } RandomAccessFile getRAF() { return this.raf; } long getCPPosition() { return this.checkPointPosition; } public synchronized long getCPSequence() { return this.checkpointSequence; } private void backupLogFile() throws IOException { synchronized (this.txnLogSyncObj) { /** * Get txn log file name */ String txnlogName = file.getCanonicalPath (); this.deleteBackUpFile (txnlogName); //rename the current file copyFile (txnlogName); } } /** * copy txnlogName contents to txnlogName.1 * */ private void copyFile (String txnlogName) throws IOException { String bname = txnlogName + TXNLOG_BACKUP_EXT; File bfile = new File (bname); if (bfile.exists ()) { throw new IOException ("Cannot backup txnlog. You must remove the back up file to continue: " + bname); } long savedPosition = raf.getFilePointer (); if (this.useFileChannelSync == false) { fchannel = raf.getChannel (); } fchannel.position (0); FileChannel fc2 = new FileOutputStream (bfile).getChannel (); fc2.transferFrom (fchannel, 0, fchannel.size ()); fc2.close (); if (this.useFileChannelSync == false) { fchannel.close (); raf.close (); //reopen the file. raf = new RandomAccessFile(file, fileMode); } raf.seek (savedPosition); } /** * delete the old back up file if exists. * */ private void deleteBackUpFile (String txnLogName) throws IOException { //1. remove txnLogName.2 file if exists. String backupName2 = txnLogName + TXNLOG_BACKUP_EXT; File bfile2 = new File (backupName2); if (bfile2.exists ()) { bfile2.delete (); } //2. move txnLogName.1 to txnLogName.2 //String backupName1 = txnLogName +".1"; //File bfile1 = new File (backupName1); //if (bfile1.exists ()) { // bfile1.renameTo (bfile2); //} } public boolean playBackRequired() { return this.playBackRequired; } private void log(String msg) { if ( debug ) { System.out.println(new Date() + " " + Thread.currentThread() + ": " + msg); } } public long getExistingAppCookie() { return existingAppCookie; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy