/* 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.fileheader; import com.db4o.*; import com.db4o.ext.*; import com.db4o.foundation.*; import com.db4o.internal.*; import com.db4o.io.*; /** * @sharpen.ignore */ public class TimerFileLockEnabled extends TimerFileLock{ private final BlockAwareBin _timerFile; private final Object _timerLock; private byte[] _longBytes = new byte[Const4.LONG_LENGTH]; private byte[] _intBytes = new byte[Const4.INT_LENGTH]; private int _headerLockOffset = 2 + Const4.INT_LENGTH; private final long _opentime; private int _baseAddress = -1; private int _openTimeOffset; private int _accessTimeOffset; private boolean _closed = false; public TimerFileLockEnabled(IoAdaptedObjectContainer file) { _timerLock = new Object(); _timerFile = file.timerFile(); _opentime = uniqueOpenTime(); } public void checkHeaderLock() { long openTime = readInt(0, _headerLockOffset); if( ((int)_opentime) != openTime){ throw new DatabaseFileLockedException(_timerFile.toString()); } writeHeaderLock(); } public void checkOpenTime() { long readOpenTime = readLong(_baseAddress, _openTimeOffset); if (_opentime != readOpenTime) { throw new DatabaseFileLockedException(_timerFile.toString()); } writeOpenTime(); } public void checkIfOtherSessionAlive(LocalObjectContainer container, int address, int offset, long lastAccessTime) throws Db4oIOException { if(_timerFile == null) { // need to check? return; } long waitTime = Const4.LOCK_TIME_INTERVAL * 5; long currentTime = System.currentTimeMillis(); // If someone changes the system clock here, he is out of luck. while (System.currentTimeMillis() < currentTime + waitTime) { Runtime4.sleep(waitTime); } long currentAccessTime = readLong(address, offset); if ((currentAccessTime > lastAccessTime)) { throw new DatabaseFileLockedException(container.toString()); } } public void close() throws Db4oIOException { synchronized (_timerLock) { writeAccessTime(true); _closed = true; _timerLock.notifyAll(); } } public boolean lockFile() { return true; } public long openTime() { return _opentime; } public void run() { while (true) { synchronized (_timerLock) { if (_closed) { return; } try { writeAccessTime(false); } catch (Db4oIOException e) { return; } try { _timerLock.wait(Const4.LOCK_TIME_INTERVAL); } catch (Exception e) { } } } } public void setAddresses(int baseAddress, int openTimeOffset, int accessTimeOffset){ _baseAddress = baseAddress; _openTimeOffset = openTimeOffset; _accessTimeOffset = accessTimeOffset; } public void start() throws Db4oIOException{ writeAccessTime(false); checkOpenTime(); } private long uniqueOpenTime(){ return System.currentTimeMillis(); // TODO: More security is possible here to make this time unique // to other processes. } private boolean writeAccessTime(boolean closing) throws Db4oIOException { if(noAddressSet()){ return true; } long time = closing ? 0 : System.currentTimeMillis(); boolean ret = writeLong(_baseAddress, _accessTimeOffset, time); sync(); return ret; } private boolean noAddressSet() { return _baseAddress < 0; } public void writeHeaderLock(){ writeInt(0, _headerLockOffset, (int)_opentime); sync(); } public void writeOpenTime() { writeLong(_baseAddress, _openTimeOffset, _opentime); sync(); } private boolean writeLong(int address, int offset, long time) throws Db4oIOException { synchronized (_timerLock) { if(_timerFile == null){ return false; } if (Deploy.debug) { ByteArrayBuffer lockBytes = new ByteArrayBuffer(Const4.LONG_LENGTH); lockBytes.writeLong(time); _timerFile.blockWrite(address, offset, lockBytes._buffer); } else { PrimitiveCodec.writeLong(_longBytes, time); _timerFile.blockWrite(address, offset, _longBytes); } return true; } } private long readLong(int address, int offset) throws Db4oIOException { synchronized (_timerLock) { if(_timerFile == null){ return 0; } if (Deploy.debug) { ByteArrayBuffer lockBytes = new ByteArrayBuffer(Const4.LONG_LENGTH); _timerFile.syncRead(address + offset, lockBytes._buffer, Const4.LONG_LENGTH); return lockBytes.readLong(); } _timerFile.syncRead(address + offset, _longBytes, Const4.LONG_LENGTH); return PrimitiveCodec.readLong(_longBytes, 0); } } private boolean writeInt(int address, int offset, int time) { synchronized (_timerLock) { if(_timerFile == null){ return false; } if (Deploy.debug) { ByteArrayBuffer lockBytes = new ByteArrayBuffer(Const4.INT_LENGTH); lockBytes.writeInt(time); _timerFile.blockWrite(address, offset, lockBytes._buffer); } else { PrimitiveCodec.writeInt(_intBytes, 0, time); _timerFile.blockWrite(address, offset, _intBytes); } return true; } } private long readInt(int address, int offset) { synchronized (_timerLock) { if(_timerFile == null){ return 0; } if (Deploy.debug) { ByteArrayBuffer lockBytes = new ByteArrayBuffer(Const4.INT_LENGTH); _timerFile.syncRead(address + offset, lockBytes._buffer, Const4.INT_LENGTH); return lockBytes.readInt(); } _timerFile.syncRead(address + offset, _longBytes, Const4.LONG_LENGTH); return PrimitiveCodec.readInt(_longBytes, 0); } } private void sync() throws Db4oIOException { try{ _timerFile.sync(); } catch(EmergencyShutdownReadOnlyException ex){ // ignore this one, emergency shutdown in progress } } }