/* 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;
import java.io.*;
import com.db4o.*;
import com.db4o.config.*;
import com.db4o.ext.*;
import com.db4o.foundation.*;
import com.db4o.internal.slots.*;
import com.db4o.io.*;
/**
* @exclude
*/
public class IoAdaptedObjectContainer extends LocalObjectContainer implements EmbeddedObjectContainer{
private final String _fileName;
private BlockAwareBin _file;
private volatile BlockAwareBin _backupFile;
private Object _fileLock;
private final FreespaceFiller _freespaceFiller;
IoAdaptedObjectContainer(Configuration config, String fileName) throws OldFormatException {
super(config);
_fileLock = new Object();
_fileName = fileName;
_freespaceFiller=createFreespaceFiller();
open();
}
protected final void openImpl() throws OldFormatException,
DatabaseReadOnlyException {
Config4Impl configImpl = configImpl();
final Storage storage = configImpl.storage();
boolean isNew = !storage.exists(fileName());
if (isNew) {
logMsg(14, fileName());
checkReadOnly();
_handlers.oldEncryptionOff();
}
boolean readOnly = configImpl.isReadOnly();
boolean lockFile = Debug4.lockFile && configImpl.lockFile()
&& (!readOnly);
if (needsLockFileThread()) {
Bin fileBin = storage.open(new BinConfiguration(fileName(), false, 0, false, configImpl.blockSize()));
Bin synchronizedBin = new SynchronizedBin(fileBin);
_file = new BlockAwareBin(synchronizedBin);
} else {
Bin bin = storage.open(new BinConfiguration(fileName(), lockFile, 0, readOnly, configImpl.blockSize()));
if(configImpl.asynchronousSync()){
bin = new ThreadedSyncBin(bin);
}
_file = new BlockAwareBin(bin);
}
if (isNew) {
configureNewFile();
if (configImpl.reservedStorageSpace() > 0) {
reserve(configImpl.reservedStorageSpace());
}
commitTransaction();
writeHeader(true, false);
} else {
readThis();
}
}
public void backup(final Storage targetStorage, final String path) throws DatabaseClosedException, Db4oIOException {
withEnvironment(new Runnable() { public void run() {
synchronized (_lock) {
checkClosed();
if (_backupFile != null) {
throw new BackupInProgressException();
}
_backupFile = new BlockAwareBin(targetStorage.open(new BinConfiguration(path, true,_file.length(), false, _blockConverter.blocksToBytes(1))));
}
long pos = 0;
byte[] buffer = new byte[8192];
while (true) {
synchronized (_lock) {
int read = _file.read(pos, buffer);
if (read <= 0) {
break;
}
_backupFile.write(pos, buffer, read);
pos += read;
}
// Let the database engine continue to do
// some work if it likes to.
Runtime4.sleep(1);
}
synchronized (_lock) {
try {
syncAndClose(_backupFile);
}
finally {
_backupFile = null;
}
}
}});
}
public void blockSize(int size){
createBlockConverter(size);
_file.blockSize(size);
}
public byte blockSize() {
return (byte) _file.blockSize();
}
protected void shutdownDataStorage() {
synchronized (_fileLock) {
try{
closeFileHeader();
} finally{
closeDatabaseFile();
}
}
}
private void closeDatabaseFile() {
try {
syncAndClose(_file);
} finally {
_file = null;
}
}
private static void syncAndClose(Bin bin) {
if (bin != null) {
try {
bin.sync();
}
finally {
bin.close();
}
}
}
private void closeFileHeader() {
try {
if (_fileHeader != null) {
_fileHeader.close();
}
} finally {
_fileHeader = null;
}
}
@Override
public void closeTransaction(Transaction transaction, boolean isSystemTransaction, boolean rollbackOnClose) {
transaction.close(rollbackOnClose);
}
public void commit1(Transaction trans) {
ensureLastSlotWritten();
super.commit1(trans);
}
private void checkXBytes(int newAddress, int newAddressOffset, int length) {
if (Debug4.xbytes && Deploy.overwrite) {
try {
byte[] checkXBytes = new byte[length];
_file.blockRead(newAddress, newAddressOffset, checkXBytes);
for (int i = 0; i < checkXBytes.length; i++) {
if (checkXBytes[i] != Const4.XBYTE) {
String msg = "XByte corruption adress:" + newAddress + " length:"
+ length + " starting:" + i;
throw new Db4oException(msg);
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
public long fileLength() {
return _file.length();
}
public String fileName() {
return _fileName;
}
public void readBytes(byte[] bytes, int address, int length) throws Db4oIOException {
readBytes(bytes, address, 0, length);
}
public void readBytes(byte[] bytes, int address, int addressOffset,
int length) throws Db4oIOException {
if (DTrace.enabled) {
DTrace.READ_BYTES.logLength(address + addressOffset, length);
}
int bytesRead = _file.blockRead(address, addressOffset, bytes, length);
checkReadCount(bytesRead, length);
}
private void checkReadCount(int bytesRead, int expected) {
if (bytesRead != expected) {
throw new IncompatibleFileFormatException();
}
}
public void reserve(int byteCount) throws DatabaseReadOnlyException {
checkReadOnly();
synchronized (_lock) {
Slot slot = allocateSlot(byteCount);
zeroReservedSlot(slot);
free(slot);
}
}
private void zeroReservedSlot(Slot slot) {
zeroFile(_file, slot);
zeroFile(_backupFile, slot);
}
private void zeroFile(BlockAwareBin io, Slot slot) {
if(io == null) {
return;
}
byte[] zeroBytes = new byte[1024];
int left = slot.length();
int offset = 0;
while (left > zeroBytes.length) {
io.blockWrite(slot.address(), offset, zeroBytes, zeroBytes.length);
offset += zeroBytes.length;
left -= zeroBytes.length;
}
if(left > 0) {
io.blockWrite(slot.address(), offset, zeroBytes, left);
}
}
public void syncFiles() {
_file.sync();
}
public void syncFiles(Runnable runnable){
_file.sync(runnable);
}
public void writeBytes(ByteArrayBuffer buffer, int blockedAddress, int addressOffset) {
if (Deploy.debug && !Deploy.flush) {
return;
}
if (Debug4.xbytes && Deploy.overwrite) {
if (buffer.checkXBytes()) {
checkXBytes(blockedAddress, addressOffset, buffer.length());
} else {
buffer.checkXBytes(true);
}
}
if (DTrace.enabled) {
DTrace.WRITE_BYTES.logLength(blockedAddress + addressOffset, buffer
.length());
}
_file.blockWrite(blockedAddress, addressOffset, buffer._buffer, buffer.length());
if (_backupFile != null) {
_backupFile.blockWrite(blockedAddress, addressOffset, buffer._buffer, buffer.length());
}
}
public void overwriteDeletedBytes(int address, int length) {
if (!Deploy.flush) {
return;
}
if (_freespaceFiller == null) {
return;
}
if (address > 0 && length > 0) {
if (DTrace.enabled) {
DTrace.WRITE_XBYTES.logLength(address, length);
}
BlockAwareBinWindow window = new BlockAwareBinWindow(_file, address, length);
try {
createFreespaceFiller().fill(window);
} catch (IOException e) {
e.printStackTrace();
} finally {
window.disable();
}
}
}
public BlockAwareBin timerFile() {
return _file;
}
private FreespaceFiller createFreespaceFiller() {
if(Debug4.xbytes) {
return new XByteFreespaceFiller();
}
return config().freespaceFiller();
}
private static class XByteFreespaceFiller implements FreespaceFiller {
public void fill(BlockAwareBinWindow io) throws IOException {
io.write(0,xBytes(io.length()));
}
private byte[] xBytes(int len) {
byte[] bytes = new byte[len];
for (int i = 0; i < len; i++) {
bytes[i]=Const4.XBYTE;
}
return bytes;
}
}
@Override
protected void fatalStorageShutdown() {
if(_file != null) {
_file.close();
}
}
}