/**
* SqlJetEngine.java
* Copyright (C) 2009-2013 TMate Software Ltd
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 2 of the License.
*
* This program 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.
*
* For information on how to redistribute this software under
* the terms of a license other than GNU General Public License
* contact TMate Software at support@sqljet.com
*/
package org.tmatesoft.sqljet.core.table.engine;
import java.io.File;
import java.util.Collections;
import java.util.EnumSet;
import java.util.Set;
import org.tmatesoft.sqljet.core.ISqlJetMutex;
import org.tmatesoft.sqljet.core.SqlJetErrorCode;
import org.tmatesoft.sqljet.core.SqlJetException;
import org.tmatesoft.sqljet.core.SqlJetTransactionMode;
import org.tmatesoft.sqljet.core.internal.ISqlJetBtree;
import org.tmatesoft.sqljet.core.internal.ISqlJetDbHandle;
import org.tmatesoft.sqljet.core.internal.ISqlJetFile;
import org.tmatesoft.sqljet.core.internal.ISqlJetFileSystem;
import org.tmatesoft.sqljet.core.internal.ISqlJetFileSystemsManager;
import org.tmatesoft.sqljet.core.internal.SqlJetBtreeFlags;
import org.tmatesoft.sqljet.core.internal.SqlJetFileOpenPermission;
import org.tmatesoft.sqljet.core.internal.SqlJetFileType;
import org.tmatesoft.sqljet.core.internal.SqlJetPagerJournalMode;
import org.tmatesoft.sqljet.core.internal.SqlJetSafetyLevel;
import org.tmatesoft.sqljet.core.internal.SqlJetUtility;
import org.tmatesoft.sqljet.core.internal.btree.SqlJetBtree;
import org.tmatesoft.sqljet.core.internal.db.SqlJetDbHandle;
import org.tmatesoft.sqljet.core.internal.fs.SqlJetFileSystemsManager;
import org.tmatesoft.sqljet.core.internal.schema.SqlJetSchema;
import org.tmatesoft.sqljet.core.internal.table.SqlJetOptions;
import org.tmatesoft.sqljet.core.table.ISqlJetBusyHandler;
import org.tmatesoft.sqljet.core.table.ISqlJetOptions;
import org.tmatesoft.sqljet.core.table.ISqlJetTransaction;
import org.tmatesoft.sqljet.core.table.SqlJetDefaultBusyHandler;
/**
* @author TMate Software Ltd.
* @author Sergey Scherbina (sergey.scherbina@gmail.com)
*
*/
public class SqlJetEngine {
private static final Set READ_FLAGS = Collections
.unmodifiableSet(SqlJetUtility.of(SqlJetBtreeFlags.READONLY));
private static final Set READ_PERMISSIONS = Collections
.unmodifiableSet(SqlJetUtility
.of(SqlJetFileOpenPermission.READONLY));
private static final Set WRITE_FLAGS = Collections
.unmodifiableSet(SqlJetUtility.of(SqlJetBtreeFlags.READWRITE,
SqlJetBtreeFlags.CREATE));
private static final Set WRITE_PREMISSIONS = Collections
.unmodifiableSet(SqlJetUtility.of(
SqlJetFileOpenPermission.READWRITE,
SqlJetFileOpenPermission.CREATE));
protected static final ISqlJetFileSystemsManager FILE_SYSTEM_MANAGER = SqlJetFileSystemsManager
.getManager();
protected ISqlJetFileSystem fileSystem = FILE_SYSTEM_MANAGER.find(null);
protected boolean writable;
protected ISqlJetDbHandle dbHandle;
protected ISqlJetBtree btree;
protected boolean open = false;
protected File file;
private boolean transaction;
private SqlJetTransactionMode transactionMode;
/**
*
*/
public SqlJetEngine(final File file, final boolean writable) {
this.writable = writable;
this.file = file;
}
/**
* @param file
* @param writable
* @param fs
*/
public SqlJetEngine(final File file, final boolean writable,
final ISqlJetFileSystem fs) {
this.writable = writable;
this.file = file;
this.fileSystem = fs;
}
/**
* @param file
* @param writable
* @param fsName
* @throws SqlJetException
*/
public SqlJetEngine(final File file, final boolean writable,
final String fsName) throws SqlJetException {
this.writable = writable;
this.file = file;
this.fileSystem = FILE_SYSTEM_MANAGER.find(fsName);
if (this.fileSystem == null) {
throw new SqlJetException(String.format(
"File system '%s' not found", fsName));
}
}
/**
* @param fs
* @param isDefault
* @throws SqlJetException
*/
public void registerFileSystem(final ISqlJetFileSystem fs,
final boolean isDefault) throws SqlJetException {
FILE_SYSTEM_MANAGER.register(fs, isDefault);
}
/**
* @param fs
* @throws SqlJetException
*/
public void unregisterFileSystem(final ISqlJetFileSystem fs)
throws SqlJetException {
FILE_SYSTEM_MANAGER.unregister(fs);
}
/**
* @return database file this engine is created for.
*/
public File getFile() {
return this.file;
}
/**
* Check write access to data base.
*
* @return true if modification is allowed
*/
public boolean isWritable() throws SqlJetException {
return writable;
}
public ISqlJetFileSystem getFileSystem() {
return fileSystem;
}
/**
* Checks is database open.
*
* @return true if database is open.
*/
public boolean isOpen() {
return open;
}
protected void checkOpen() throws SqlJetException {
if (!isOpen())
throw new SqlJetException(SqlJetErrorCode.MISUSE, "Database closed");
}
/**
*
* Opens connection to database. It does not create any locking on database.
* First lock will be created when be called any method which requires real
* access to options or schema.
*
*
* @throws SqlJetException
* if any trouble with access to file or database format.
*/
public synchronized void open() throws SqlJetException {
if (!open) {
dbHandle = new SqlJetDbHandle(fileSystem);
dbHandle.setBusyHandler(new SqlJetDefaultBusyHandler());
btree = new SqlJetBtree();
final Set flags = EnumSet
.copyOf(writable ? WRITE_FLAGS : READ_FLAGS);
final Set permissions = EnumSet
.copyOf(writable ? WRITE_PREMISSIONS : READ_PERMISSIONS);
final SqlJetFileType type = (file != null ? SqlJetFileType.MAIN_DB
: SqlJetFileType.TEMP_DB);
btree.open(file, dbHandle, flags, type, permissions);
// force readonly.
ISqlJetFile file = btree.getPager().getFile();
if (file != null) {
Set realPermissions = btree
.getPager().getFile().getPermissions();
writable = realPermissions
.contains(SqlJetFileOpenPermission.READWRITE);
}
open = true;
} else {
throw new SqlJetException(SqlJetErrorCode.MISUSE,
"Database is open already");
}
}
public Object runSynchronized(ISqlJetEngineSynchronized op)
throws SqlJetException {
checkOpen();
dbHandle.getMutex().enter();
try {
return op.runSynchronized(this);
} finally {
dbHandle.getMutex().leave();
}
}
/**
* Close connection to database. It is safe to call this method if database
* connections is closed already.
*
* @throws SqlJetException
* it is possible to get exception if there is actvie
* transaction and rollback did not success.
*/
public void close() throws SqlJetException {
if (open) {
runSynchronized(new ISqlJetEngineSynchronized() {
public Object runSynchronized(SqlJetEngine engine)
throws SqlJetException {
if (btree != null) {
btree.close();
btree = null;
open = false;
}
closeResources();
return null;
}
});
if (!open) {
dbHandle = null;
}
}
}
protected void closeResources() throws SqlJetException {
}
/*
* (non-Javadoc)
*
* @see java.lang.Object#finalize()
*/
@Override
protected void finalize() throws Throwable {
try {
if (open) {
close();
}
} finally {
super.finalize();
}
}
/**
* Reads database schema and options.
*
* @throws SqlJetException
*/
protected void readSchema() throws SqlJetException {
runSynchronized(new ISqlJetEngineSynchronized() {
public Object runSynchronized(SqlJetEngine engine)
throws SqlJetException {
btree.enter();
try {
dbHandle.setOptions(new SqlJetOptions(btree, dbHandle));
btree.setSchema(new SqlJetSchema(dbHandle, btree));
} finally {
btree.leave();
}
return null;
}
});
}
/**
* Returns database options.
*
* @return options of this database.
*/
public ISqlJetOptions getOptions() throws SqlJetException {
checkOpen();
if (null == btree.getSchema()) {
readSchema();
}
return dbHandle.getOptions();
}
/**
* Refreshes database schema.
*/
public void refreshSchema() throws SqlJetException {
if (null == btree.getSchema()
|| !getOptions().verifySchemaVersion(false)) {
readSchema();
}
}
protected SqlJetSchema getSchemaInternal() throws SqlJetException {
checkOpen();
refreshSchema();
return btree.getSchema();
}
/**
* Get busy handler.
*
* @return the busy handler.
*/
public ISqlJetBusyHandler getBusyHandler() {
return dbHandle.getBusyHandler();
}
/**
* Set busy handler. Busy handler treats situation when database is locked
* by other process or thread.
*
* @param busyHandler
* the busy handler.
*/
public void setBusyHandler(ISqlJetBusyHandler busyHandler) {
dbHandle.setBusyHandler(busyHandler);
}
/**
* Retruns threading synchronization mutex.
*
* @return Semaphore instance used to synchronize database access from
* multiple threads within the same process.
*/
public ISqlJetMutex getMutex() {
return dbHandle.getMutex();
}
/**
* Set cache size (in count of pages).
*
* @param cacheSize
* the count of pages which can hold cache.
*/
public void setCacheSize(final int cacheSize) throws SqlJetException {
checkOpen();
runSynchronized(new ISqlJetEngineSynchronized() {
public Object runSynchronized(SqlJetEngine engine)
throws SqlJetException {
btree.setCacheSize(cacheSize);
return null;
}
});
}
/**
* Get cache size (in count of pages).
*
* @return the count of pages which can hold cache.
*/
public int getCacheSize() throws SqlJetException {
checkOpen();
refreshSchema();
return (Integer) runSynchronized(new ISqlJetEngineSynchronized() {
public Object runSynchronized(SqlJetEngine engine)
throws SqlJetException {
return btree.getCacheSize();
}
});
}
/**
* Set safety level
*
* @param safetyLevel
*
*/
public void setSafetyLevel(final SqlJetSafetyLevel safetyLevel) throws SqlJetException {
checkOpen();
runSynchronized(new ISqlJetEngineSynchronized() {
public Object runSynchronized(SqlJetEngine engine)
throws SqlJetException {
btree.setSafetyLevel(safetyLevel);
return null;
}
});
}
/**
* Set journal mode
*
* @param journalMode
*
*/
public void setJournalMode(final SqlJetPagerJournalMode journalMode) throws SqlJetException {
checkOpen();
runSynchronized(new ISqlJetEngineSynchronized() {
public Object runSynchronized(SqlJetEngine engine)
throws SqlJetException {
btree.setJournalMode(journalMode);
return null;
}
});
}
/**
* Get safety level
*
* @return the safety level set.
*/
public SqlJetSafetyLevel getSafetyLevel() throws SqlJetException {
checkOpen();
refreshSchema();
return (SqlJetSafetyLevel) runSynchronized(new ISqlJetEngineSynchronized() {
public Object runSynchronized(SqlJetEngine engine)
throws SqlJetException {
return btree.getSafetyLevel();
}
});
}
/**
* Get jounrnal mode
*
* @return the safety level set.
*/
public SqlJetPagerJournalMode getJournalMode() throws SqlJetException {
checkOpen();
refreshSchema();
return (SqlJetPagerJournalMode) runSynchronized(new ISqlJetEngineSynchronized() {
public Object runSynchronized(SqlJetEngine engine)
throws SqlJetException {
return btree.getJournalMode();
}
});
}
/**
* Returns true if a transaction is active.
*
* @return true if there is an active running transaction.
*/
public boolean isInTransaction() {
return transaction;
}
public SqlJetTransactionMode getTransactionMode() {
return transactionMode;
}
/**
* Begin transaction.
*
* @param mode
* transaction's mode.
*/
public void beginTransaction(final SqlJetTransactionMode mode)
throws SqlJetException {
checkOpen();
runSynchronized(new ISqlJetEngineSynchronized() {
public Object runSynchronized(SqlJetEngine engine)
throws SqlJetException {
if (!isTransactionStarted(mode)) {
doBeginTransaction(mode);
}
return null;
}
});
}
/**
* Commits transaction.
*
*/
public void commit() throws SqlJetException {
checkOpen();
runSynchronized(new ISqlJetEngineSynchronized() {
public Object runSynchronized(SqlJetEngine engine)
throws SqlJetException {
if (isInTransaction()) {
doCommitTransaction();
}
return null;
}
});
}
/**
* Rolls back transaction.
*
*/
public void rollback() throws SqlJetException {
checkOpen();
runSynchronized(new ISqlJetEngineSynchronized() {
public Object runSynchronized(SqlJetEngine engine)
throws SqlJetException {
doRollbackTransaction();
return null;
}
});
}
/**
* Runs transaction.
*
* @param op
* transaction's body (closure).
* @param mode
* transaction's mode.
* @return result of
* {@link ISqlJetTransaction#run(org.tmatesoft.sqljet.core.table.SqlJetDb)}
* call.
* @throws SqlJetException
*/
protected Object runEngineTransaction(final ISqlJetEngineTransaction op,
final SqlJetTransactionMode mode) throws SqlJetException {
checkOpen();
return runSynchronized(new ISqlJetEngineSynchronized() {
public Object runSynchronized(SqlJetEngine engine)
throws SqlJetException {
if (isTransactionStarted(mode)) {
return op.run(SqlJetEngine.this);
} else {
doBeginTransaction(mode);
boolean success = false;
try {
final Object result = op.run(SqlJetEngine.this);
doCommitTransaction();
success = true;
return result;
} finally {
if (!success) {
doRollbackTransaction();
}
transaction = false;
transactionMode = null;
}
}
}
});
}
private boolean isTransactionStarted(final SqlJetTransactionMode mode) {
return transaction
&& (transactionMode == mode || mode == SqlJetTransactionMode.READ_ONLY);
}
private void doBeginTransaction(final SqlJetTransactionMode mode)
throws SqlJetException {
btree.beginTrans(mode);
refreshSchema();
transaction = true;
transactionMode = mode;
}
private void doCommitTransaction() throws SqlJetException {
btree.closeAllCursors();
btree.commit();
transaction = false;
transactionMode = null;
}
private void doRollbackTransaction() throws SqlJetException {
btree.closeAllCursors();
btree.rollback();
transaction = false;
transactionMode = null;
}
}
Ads help maintain this website.