com.caucho.db.xa.DbTransaction Maven / Gradle / Ivy
/*
* Copyright (c) 1998-2018 Caucho Technology -- all rights reserved
*
* This file is part of Resin(R) Open Source
*
* Each copy or derived work must preserve the copyright notice and this
* notice unmodified.
*
* Resin Open Source 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; either version 2 of the License, or
* (at your option) any later version.
*
* Resin Open Source 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, or any warranty
* of NON-INFRINGEMENT. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License
* along with Resin Open Source; if not, write to the
*
* Free Software Foundation, Inc.
* 59 Temple Place, Suite 330
* Boston, MA 02111-1307 USA
*
* @author Scott Ferguson
*/
package com.caucho.db.xa;
import java.io.IOException;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.logging.Level;
import java.util.logging.Logger;
import com.caucho.db.blob.Inode;
import com.caucho.db.block.Block;
import com.caucho.db.block.BlockStore;
import com.caucho.db.jdbc.ConnectionImpl;
import com.caucho.db.lock.DatabaseLock;
import com.caucho.util.L10N;
import com.caucho.util.SQLExceptionWrapper;
/**
* Represents a single transaction.
*/
public class DbTransaction extends StoreTransaction {
private static final Logger log
= Logger.getLogger(DbTransaction.class.getName());
private static final L10N L = new L10N(DbTransaction.class);
private static long AUTO_COMMIT_TIMEOUT = 30000L;
private boolean _isAutoCommit = true;
private ConnectionImpl _conn;
private ArrayList _readLocks;
private ArrayList _writeLocks;
private ArrayList _updateBlocks;
// inodes that need to be deleted on a commit
private ArrayList _deleteInodes;
// inodes that need to be deleted on a rollback
private ArrayList _addInodes;
// blocks that need deallocating on a commit
private ArrayList _deallocateBlocks;
private boolean _isRollbackOnly;
private SQLException _rollbackExn;
private long _timeout = AUTO_COMMIT_TIMEOUT;
private DbTransaction()
{
}
public static DbTransaction create(ConnectionImpl conn)
{
DbTransaction xa = new DbTransaction();
xa.init(conn);
return xa;
}
public static DbTransaction create()
{
DbTransaction xa = new DbTransaction();
return xa;
}
private void init(ConnectionImpl conn)
{
_conn = conn;
_timeout = AUTO_COMMIT_TIMEOUT;
_isRollbackOnly = false;
_rollbackExn = null;
}
/**
* Sets the transaction timeout.
*/
public void setTimeout(long timeout)
{
_timeout = timeout;
}
public long getTimeout()
{
return _timeout;
}
/**
* Acquires a new read lock.
*/
/*
public void addReadLock(Lock lock)
{
_readLocks.add(lock);
}
*/
/**
* Acquires a new read lock.
*/
public boolean hasReadLock(DatabaseLock lock)
{
return _readLocks.contains(lock);
}
/**
* Returns true for an auto-commit transaction.
*/
public boolean isAutoCommit()
{
return _isAutoCommit;
}
/**
* Returns true for an auto-commit transaction.
*/
public void setAutoCommit(boolean autoCommit)
{
_isAutoCommit = autoCommit;
}
/**
* Acquires a new write lock.
*/
public void lockRead(DatabaseLock lock)
throws SQLException
{
if (_isRollbackOnly) {
if (_rollbackExn != null)
throw _rollbackExn;
else
throw new SQLException(L.l("can't get lock with rollback transaction"));
}
try {
if (_readLocks == null)
_readLocks = new ArrayList();
if (_readLocks.contains(lock))
throw new SQLException(L.l("lockRead must not already have a read lock"));
lock.lockRead(_timeout);
_readLocks.add(lock);
} catch (SQLException e) {
setRollbackOnly(e);
throw e;
}
}
/**
* Acquires a new write lock.
*/
public void lockReadAndWrite(DatabaseLock lock)
throws SQLException
{
if (_isRollbackOnly) {
if (_rollbackExn != null)
throw _rollbackExn;
else
throw new SQLException(L.l("can't get lock with rollback transaction"));
}
try {
if (_readLocks == null)
_readLocks = new ArrayList();
if (_writeLocks == null)
_writeLocks = new ArrayList();
if (_readLocks.contains(lock))
throw new SQLException(L.l("lockReadAndWrite cannot already have a read lock"));
if (_writeLocks.contains(lock))
throw new SQLException(L.l("lockReadAndWrite cannot already have a write lock"));
lock.lockWrite(_timeout);
_readLocks.add(lock);
_writeLocks.add(lock);
} catch (SQLException e) {
setRollbackOnly(e);
throw e;
}
}
/**
* Conditionally a new write lock, if no contention exists.
*/
public boolean lockReadAndWriteNoWait(DatabaseLock lock)
throws SQLException
{
if (_isRollbackOnly) {
if (_rollbackExn != null)
throw _rollbackExn;
else
throw new SQLException(L.l("can't get lock with rollback transaction"));
}
try {
if (_readLocks == null)
_readLocks = new ArrayList();
if (_writeLocks == null)
_writeLocks = new ArrayList();
if (_readLocks.contains(lock))
throw new SQLException(L.l("lockReadAndWrite cannot already have a read lock"));
if (_writeLocks.contains(lock))
throw new SQLException(L.l("lockReadAndWrite cannot already have a write lock"));
/*
if (lock.lockReadAndWriteNoWait()) {
_readLocks.add(lock);
_writeLocks.add(lock);
return true;
}
*/
} catch (SQLException e) {
setRollbackOnly(e);
throw e;
}
return false;
}
/**
* Adds a block for update.
*/
public void addUpdateBlock(Block block)
{
if (block == null)
return;
if (_updateBlocks == null)
_updateBlocks = new ArrayList();
if (_updateBlocks.size() == 0
|| _updateBlocks.get(_updateBlocks.size() - 1) != block)
_updateBlocks.add(block);
}
/**
* If auto-commit, commit the read
*/
public void autoCommitRead(DatabaseLock lock)
throws SQLException
{
unlockRead(lock);
}
public void unlockRead(DatabaseLock lock)
throws SQLException
{
if (_readLocks.remove(lock))
lock.unlockRead();
}
/**
* If auto-commit, commit the write
*/
public void autoCommitWrite(DatabaseLock lock)
throws SQLException
{
_readLocks.remove(lock);
if (_writeLocks.remove(lock)) {
try {
commit();
} finally {
// lock.unlockWrite();
lock.unlockWrite();
}
}
}
public void unlockReadAndWrite(DatabaseLock lock)
throws SQLException
{
_readLocks.remove(lock);
if (_writeLocks.remove(lock)) {
lock.unlockWrite();
}
}
/**
* Returns a read block.
*/
public Block readBlock(BlockStore store, long blockAddress)
throws IOException
{
long blockId = store.addressToBlockId(blockAddress);
Block block = null;
if (block != null)
block.allocate();
else
block = store.readBlock(blockId);
return block;
}
/**
* Returns a read block.
*/
public Block loadBlock(BlockStore store, long blockAddress)
throws IOException
{
long blockId = store.addressToBlockId(blockAddress);
Block block = store.loadBlock(blockId);
return block;
}
/**
* Returns a modified block.
*/
public Block allocateRow(BlockStore store)
throws IOException
{
return store.allocateRow();
}
/**
* Returns a modified block.
*/
public void deallocateBlock(Block block)
throws IOException
{
if (isAutoCommit())
block.getStore().deallocateBlock(block.getBlockId());
else {
if (_deallocateBlocks == null)
_deallocateBlocks = new ArrayList();
_deallocateBlocks.add(block);
}
}
/**
* Adds inode which should be deleted on a commit.
*/
public void addDeleteInode(Inode inode)
{
if (_deleteInodes == null) {
_deleteInodes = new ArrayList();
}
_deleteInodes.add(inode);
}
/**
* Adds inode which should be deleted on a rollback.
*/
public void addAddInode(Inode inode)
{
if (_addInodes == null)
_addInodes = new ArrayList();
_addInodes.add(inode);
}
public void autoCommit()
throws SQLException
{
if (_isAutoCommit) {
ConnectionImpl conn = _conn;
_conn = null;
if (conn != null) {
conn.setTransaction(null);
}
}
}
public void setRollbackOnly(SQLException e)
{
if (_rollbackExn == null)
_rollbackExn = e;
_isRollbackOnly = true;
releaseLocks();
}
public void setRollbackOnly()
{
setRollbackOnly(null);
}
public void commit()
throws SQLException
{
try {
writeData();
} finally {
releaseLocks();
close();
}
}
public void writeData()
throws SQLException
{
ArrayList updateBlocks = _updateBlocks;
if (updateBlocks != null) {
while (updateBlocks.size() > 0) {
Block block = updateBlocks.remove(updateBlocks.size() - 1);
try {
block.getStore().saveAllocation();
} catch (Exception e) {
log.log(Level.WARNING, e.toString(), e);
}
try {
block.commitNoWake();
} catch (Exception e) {
log.log(Level.WARNING, e.toString(), e);
}
}
}
if (_deleteInodes != null) {
while (_deleteInodes.size() > 0) {
Inode inode = _deleteInodes.remove(0);
// XXX: should be allocating based on auto-commit
try {
inode.remove();
} catch (Exception e) {
log.log(Level.WARNING, e.toString(), e);
}
}
}
if (_deallocateBlocks != null) {
while (_deallocateBlocks.size() > 0) {
Block block = _deallocateBlocks.remove(0);
try {
block.getStore().deallocateBlock(block.getBlockId());
} catch (IOException e) {
throw new SQLExceptionWrapper(e);
}
}
}
}
public void rollback()
throws SQLException
{
releaseLocks();
close();
}
private void releaseLocks()
{
// need to unlock write before upgrade to block other threads
if (_writeLocks != null) {
for (int i = 0; i < _writeLocks.size(); i++) {
DatabaseLock lock = _writeLocks.get(i);
if (_readLocks != null)
_readLocks.remove(lock);
try {
lock.unlockWrite();
} catch (Throwable e) {
log.log(Level.WARNING, e.toString(), e);
}
}
_writeLocks.clear();
}
if (_readLocks != null) {
for (int i = 0; i < _readLocks.size(); i++) {
DatabaseLock lock = _readLocks.get(i);
try {
lock.unlockRead();
} catch (Throwable e) {
log.log(Level.WARNING, e.toString(), e);
}
}
_readLocks.clear();
}
}
void close()
{
_isRollbackOnly = false;
_rollbackExn = null;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy