package com.sleepycat.je.txn;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import javax.transaction.xa.Xid;
import com.sleepycat.je.DatabaseException;
import com.sleepycat.je.LockStats;
import com.sleepycat.je.Transaction;
import com.sleepycat.je.TransactionConfig;
import com.sleepycat.je.dbi.EnvironmentImpl;
import com.sleepycat.je.dbi.MemoryBudget;
import com.sleepycat.je.utilint.DbLsn;
import de.ovgu.cide.jakutil.*;
/**
* Class to manage transactions. Basically a Set of all transactions with add
* and remove methods and a latch around the set.
*/
public class TxnManager {
static final long NULL_TXN_ID=-1;
private static final String DEBUG_NAME=TxnManager.class.getName();
private LockManager lockManager;
private EnvironmentImpl env;
private Set allTxns;
private Map allXATxns;
private Map thread2Txn;
private long lastUsedTxnId;
private int nActiveSerializable;
public TxnManager( EnvironmentImpl env) throws DatabaseException {
this.hook822(env);
this.env=env;
allTxns=new HashSet();
this.hook821(env);
allXATxns=Collections.synchronizedMap(new HashMap());
thread2Txn=Collections.synchronizedMap(new HashMap());
this.hook824();
lastUsedTxnId=0;
}
/**
* Set the txn id sequence.
*/
synchronized public void setLastTxnId( long lastId){
this.lastUsedTxnId=lastId;
}
/**
* Get the last used id, for checkpoint info.
*/
public synchronized long getLastTxnId(){
return lastUsedTxnId;
}
/**
* Get the next transaction id to use.
*/
synchronized long incTxnId(){
return ++lastUsedTxnId;
}
/**
* Create a new transaction.
* @param parentfor nested transactions, not yet supported
* @param txnConfigspecifies txn attributes
* @return the new txn
*/
public Txn txnBegin( Transaction parent, TransactionConfig txnConfig) throws DatabaseException {
if (parent != null) {
throw new DatabaseException("Nested transactions are not supported yet.");
}
return new Txn(env,txnConfig);
}
/**
* Give transactions and environment access to lock manager.
*/
public LockManager getLockManager(){
return lockManager;
}
/**
* Called when txn is created.
*/
void registerTxn( Txn txn) throws DatabaseException {
allTxns.add(txn);
if (txn.isSerializableIsolation()) {
nActiveSerializable++;
}
}
/**
* Called when txn ends.
*/
void unRegisterTxn( Txn txn, boolean isCommit) throws DatabaseException {
allTxns.remove(txn);
this.hook828(txn);
this.hook825(isCommit);
if (txn.isSerializableIsolation()) {
nActiveSerializable--;
}
}
/**
* Called when txn is created.
*/
public void registerXATxn( Xid xid, Txn txn, boolean isPrepare) throws DatabaseException {
if (!allXATxns.containsKey(xid)) {
allXATxns.put(xid,txn);
this.hook829();
}
this.hook826(isPrepare);
}
/**
* Called when txn ends.
*/
void unRegisterXATxn( Xid xid, boolean isCommit) throws DatabaseException {
if (allXATxns.remove(xid) == null) {
throw new DatabaseException("XA Transaction " + xid + " can not be unregistered.");
}
this.hook830();
this.hook827(isCommit);
}
/**
* Retrieve a Txn object from an Xid.
*/
public Txn getTxnFromXid( Xid xid) throws DatabaseException {
return (Txn)allXATxns.get(xid);
}
/**
* Called when txn is assoc'd with this thread.
*/
public void setTxnForThread( Transaction txn){
Thread curThread=Thread.currentThread();
thread2Txn.put(curThread,txn);
}
/**
* Called when txn is assoc'd with this thread.
*/
public Transaction unsetTxnForThread() throws DatabaseException {
Thread curThread=Thread.currentThread();
return (Transaction)thread2Txn.remove(curThread);
}
/**
* Retrieve a Txn object for this Thread.
*/
public Transaction getTxnForThread() throws DatabaseException {
return (Transaction)thread2Txn.get(Thread.currentThread());
}
public Xid[] XARecover() throws DatabaseException {
Set xidSet=allXATxns.keySet();
Xid[] ret=new Xid[xidSet.size()];
ret=(Xid[])xidSet.toArray(ret);
return ret;
}
/**
* Returns whether there are any active serializable transactions, excluding
* the transaction given (if non-null). This is intentionally returned
* without latching, since latching would not make the act of reading an
* integer more atomic than it already is.
*/
public boolean areOtherSerializableTransactionsActive( Locker excludeLocker){
int exclude=(excludeLocker != null && excludeLocker.isSerializableIsolation()) ? 1 : 0;
return (nActiveSerializable - exclude > 0);
}
/**
* Get the earliest LSN of all the active transactions, for checkpoint.
*/
public long getFirstActiveLsn() throws DatabaseException {
long firstActive=DbLsn.NULL_LSN;
firstActive=this.hook823(firstActive);
return firstActive;
}
protected void hook821( EnvironmentImpl env) throws DatabaseException {
}
protected void hook822( EnvironmentImpl env) throws DatabaseException {
if (env.isNoLocking()) {
lockManager=new DummyLockManager(env);
}
else {
lockManager=new SyncedLockManager(env);
}
}
protected long hook823( long firstActive) throws DatabaseException {
Iterator iter=allTxns.iterator();
while (iter.hasNext()) {
long txnFirstActive=((Txn)iter.next()).getFirstActiveLsn();
if (firstActive == DbLsn.NULL_LSN) {
firstActive=txnFirstActive;
}
else if (txnFirstActive != DbLsn.NULL_LSN) {
if (DbLsn.compareTo(txnFirstActive,firstActive) < 0) {
firstActive=txnFirstActive;
}
}
}
return firstActive;
}
protected void hook824() throws DatabaseException {
}
protected void hook825( boolean isCommit) throws DatabaseException {
}
protected void hook826( boolean isPrepare) throws DatabaseException {
}
protected void hook827( boolean isCommit) throws DatabaseException {
}
protected void hook828( Txn txn) throws DatabaseException {
}
protected void hook829() throws DatabaseException {
}
protected void hook830() throws DatabaseException {
}
}