/* This file is part of the db4o object database http://www.db4o.com Copyright (C) 2004 - 2011 Versant Corporation http://www.versant.com db4o is free software; you can redistribute it and/or modify it under the terms of version 3 of the GNU General Public License as published by the Free Software Foundation. db4o is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see http://www.gnu.org/licenses/. */ package com.db4o.internal.transactionlog; import com.db4o.foundation.*; import com.db4o.foundation.io.*; import com.db4o.internal.*; import com.db4o.internal.slots.*; import com.db4o.io.*; /** * @exclude */ public class FileBasedTransactionLogHandler extends TransactionLogHandler { static final int LOCK_INT = Integer.MAX_VALUE - 1; private Bin _lockFile; private Bin _logFile; private final String _fileName; public FileBasedTransactionLogHandler(LocalObjectContainer container, String fileName) { super(container); _fileName = fileName; } public static String logFileName(String fileName) { return fileName + ".log"; } public static String lockFileName(String fileName) { return fileName + ".lock"; } private Bin openBin(String fileName) { return new FileStorage().open(new BinConfiguration(fileName, _container.config().lockFile(), 0, false)); } public void completeInterruptedTransaction(int transactionId1, int transactionId2) { if(!File4.exists(lockFileName(_fileName))){ return; } if( ! lockFileSignalsInterruptedTransaction()){ return; } ByteArrayBuffer buffer = new ByteArrayBuffer(Const4.INT_LENGTH); openLogFile(); read(_logFile, buffer); int length = buffer.readInt(); if(length > 0){ buffer = new ByteArrayBuffer(length); read(_logFile, buffer); buffer.incrementOffset(Const4.INT_LENGTH); readWriteSlotChanges(buffer); } deleteLockFile(); closeLogFile(); deleteLogFile(); } private boolean lockFileSignalsInterruptedTransaction() { openLockFile(); ByteArrayBuffer buffer = newLockFileBuffer(); read(_lockFile, buffer); for (int i = 0; i < 2; i++) { int checkInt = buffer.readInt(); if(checkInt != LOCK_INT){ closeLockFile(); return false; } } closeLockFile(); return true; } public void close() { if(!logsOpened()){ return; } closeLockFile(); closeLogFile(); deleteLockFile(); deleteLogFile(); } private void closeLockFile() { syncAndClose(_lockFile); _lockFile = null; } private void syncAndClose(Bin bin) { try { bin.sync(); } finally { bin.close(); } } private void closeLogFile() { syncAndClose(_logFile); _logFile = null; } private void deleteLockFile() { File4.delete(lockFileName(_fileName)); } private void deleteLogFile() { File4.delete(logFileName(_fileName)); } @Override public Slot allocateSlot(boolean append, int slotChangeCount) { // do nothing return null; } @Override public void applySlotChanges(Visitable<SlotChange> slotChangeTree, int slotChangeCount, Slot reservedSlot) { if(slotChangeCount < 1){ return; } Runnable commitHook = _container.commitHook(); flushDatabaseFile(); ensureLogAndLock(); int length = transactionLogSlotLength(slotChangeCount); ByteArrayBuffer logBuffer = new ByteArrayBuffer(length); logBuffer.writeInt(length); logBuffer.writeInt(slotChangeCount); appendSlotChanges(logBuffer, slotChangeTree); write(_logFile, logBuffer); _logFile.sync(); writeToLockFile(LOCK_INT); writeSlots(slotChangeTree); commitHook.run(); flushDatabaseFile(); writeToLockFile(0); } private void writeToLockFile(int lockSignal) { ByteArrayBuffer lockBuffer = newLockFileBuffer(); lockBuffer.writeInt(lockSignal); lockBuffer.writeInt(lockSignal); write(_lockFile, lockBuffer); _lockFile.sync(); } private ByteArrayBuffer newLockFileBuffer() { return new ByteArrayBuffer(lockFileBufferLength()); } private int lockFileBufferLength() { return Const4.LONG_LENGTH * 2; } private void ensureLogAndLock() { if(_container.config().isReadOnly()){ return; } if(logsOpened()){ return; } openLockFile(); openLogFile(); } private void openLogFile() { _logFile = openBin(logFileName(_fileName)); } private void openLockFile() { _lockFile = openBin(lockFileName(_fileName)); } private boolean logsOpened() { return _lockFile != null; } private void read(Bin storage, ByteArrayBuffer buffer) { storage.read(0, buffer._buffer, buffer.length()); } private void write(Bin storage, ByteArrayBuffer buffer) { storage.write(0, buffer._buffer, buffer.length()); } }