Package bitronix.tm.journal

Source Code of bitronix.tm.journal.TransactionLogCursor

/*
* Copyright (C) 2006-2013 Bitronix Software (http://www.bitronix.be)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package bitronix.tm.journal;

import bitronix.tm.utils.Uid;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.util.HashSet;
import java.util.Set;

/**
* Used to read {@link TransactionLogRecord} objects from a log file.
*
* @author Ludovic Orban
*/
public class TransactionLogCursor {

    private final static Logger log = LoggerFactory.getLogger(TransactionLogCursor.class);

    // private final RandomAccessFile randomAccessFile;
    private FileInputStream fis;
    private FileChannel fileChannel;
    private long currentPosition;
    private long endPosition;
    private ByteBuffer page;

    /**
     * Create a TransactionLogCursor that will read from the specified file.
     * This opens a new read-only file descriptor.
     * @param file the file to read logs from
     * @throws IOException if an I/O error occurs.
     */
    public TransactionLogCursor(File file) throws IOException {
        this.fis = new FileInputStream(file);
        this.fileChannel = fis.getChannel();
        this.page = ByteBuffer.allocate(8192);

        fileChannel.position(TransactionLogHeader.CURRENT_POSITION_HEADER);
        fileChannel.read(page);
        page.rewind();
        endPosition = page.getLong();
        currentPosition = TransactionLogHeader.CURRENT_POSITION_HEADER + 8;
    }

    /**
     * Fetch the next TransactionLogRecord from log, recalculating the CRC and checking it against the stored one.
     * InvalidChecksumException is thrown if the check fails.
     * @return the TransactionLogRecord or null if the end of the log file has been reached
     * @throws IOException if an I/O error occurs.
     */
    public TransactionLogRecord readLog() throws IOException {
        return readLog(false);
    }

    /**
     * Fetch the next TransactionLogRecord from log.
     * @param skipCrcCheck if set to false, the method will thow an InvalidChecksumException if the CRC on disk does
     *        not match the recalculated one. Otherwise, the CRC is not recalculated nor checked agains the stored one.
     * @return the TransactionLogRecord or null if the end of the log file has been reached
     * @throws IOException if an I/O error occurs.
     */
    public TransactionLogRecord readLog(boolean skipCrcCheck) throws IOException {
        if (currentPosition >= endPosition) {
            if (log.isDebugEnabled())
                log.debug("end of transaction log file reached at " + currentPosition);
            return null;
        }

        final int status = page.getInt();
        // currentPosition += 4;
        final int recordLength = page.getInt();
        // currentPosition += 4;
        currentPosition += 8;

        if (page.position() + recordLength + 8 > page.limit()) {
            page.compact();
            fileChannel.read(page);
            page.rewind();
        }

        final int endOfRecordPosition = page.position() + recordLength;
        if (currentPosition + recordLength > endPosition) {
            page.position(page.position() + recordLength);
            currentPosition += recordLength;
            throw new CorruptedTransactionLogException("corrupted log found at position " + currentPosition
                    + " (record terminator outside of file bounds: " + currentPosition + recordLength + " of "
                    + endPosition + ", recordLength: " + recordLength + ")");
        }

        final int headerLength = page.getInt();
        // currentPosition += 4;
        final long time = page.getLong();
        // currentPosition += 8;
        final int sequenceNumber = page.getInt();
        // currentPosition += 4;
        final int crc32 = page.getInt();
        // currentPosition += 4;
        final byte gtridSize = page.get();
        // currentPosition += 1;
        currentPosition += 21;

        // check for log terminator
        page.mark();
        page.position(endOfRecordPosition - 4);
        int endCode = page.getInt();
        page.reset();
        if (endCode != TransactionLogAppender.END_RECORD)
            throw new CorruptedTransactionLogException("corrupted log found at position " + currentPosition + " (no record terminator found)");

        // check that GTRID is not too long
        if (4 + 8 + 4 + 4 + 1 + gtridSize > recordLength) {
            page.position(endOfRecordPosition);
            throw new CorruptedTransactionLogException("corrupted log found at position " + currentPosition
                    + " (GTRID size too long)");
        }

        final byte[] gtridArray = new byte[gtridSize];
        page.get(gtridArray);
        currentPosition += gtridSize;
        Uid gtrid = new Uid(gtridArray);
        final int uniqueNamesCount = page.getInt();
        currentPosition += 4;
        Set<String> uniqueNames = new HashSet<String>();
        int currentReadCount = 4 + 8 + 4 + 4 + 1 + gtridSize + 4;

        for (int i = 0; i < uniqueNamesCount; i++) {
            int length = page.getShort();
            currentPosition += 2;

            // check that names aren't too long
            currentReadCount += 2 + length;
            if (currentReadCount > recordLength) {
                page.position(endOfRecordPosition);
                throw new CorruptedTransactionLogException("corrupted log found at position " + currentPosition
                        + " (unique names too long, " + (i + 1) + " out of " + uniqueNamesCount + ", length: " + length
                        + ", currentReadCount: " + currentReadCount + ", recordLength: " + recordLength + ")");
            }

            byte[] nameBytes = new byte[length];
            page.get(nameBytes);
            currentPosition += length;
            uniqueNames.add(new String(nameBytes, "US-ASCII"));
        }
        final int cEndRecord = page.getInt();
        currentPosition += 4;

        TransactionLogRecord tlog = new TransactionLogRecord(status, recordLength, headerLength, time, sequenceNumber,
                crc32, gtrid, uniqueNames, cEndRecord);

        // check that CRC is okay
        if (!skipCrcCheck && !tlog.isCrc32Correct()) {
            page.position(endOfRecordPosition);
            throw new CorruptedTransactionLogException("corrupted log found at position " + currentPosition
                    + "(invalid CRC, recorded: " + tlog.getCrc32() + ", calculated: " + tlog.calculateCrc32() + ")");
        }

        return tlog;
    }

    /**
     * Close the cursor and the underlying file
     * @throws IOException if an I/O error occurs.
     */
    public void close() throws IOException {
        fis.close();
        fileChannel.close();
    }
}
TOP

Related Classes of bitronix.tm.journal.TransactionLogCursor

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.