package com.venky.swf.db.jdbc;
import java.sql.SQLException;
import java.util.Stack;
import com.venky.core.checkpoint.Checkpointed;
import com.venky.core.checkpoint.MergeableMap;
import com.venky.core.util.ExceptionUtil;
import com.venky.extension.Registry;
import com.venky.swf.db.Database;
import com.venky.swf.db.Transaction;
import com.venky.swf.exceptions.MultiException;
import com.venky.swf.routing.Config;
/**
* Created by venky on 22/5/16.
*/
public class TransactionManager {
private Stack<Transaction> transactionStack = new Stack<Transaction>();
public Transaction createTransaction() {
Transaction transaction = new Transaction(transactionStack.size(), txnUserAttributes.createCheckpoint());
transactionStack.push(transaction);
return transaction;
}
public boolean isActiveTransactionPresent(){
return !transactionStack.isEmpty();
}
public Transaction getCurrentTransaction(){
if (transactionStack.isEmpty()) {
createTransaction();
}
return transactionStack.peek();
}
public void completeAllTransaction(){
if (!transactionStack.isEmpty()){
Config.instance().getLogger(TransactionManager.class.getName()).warning(transactionStack.size() + " Transactions not closed correctly. Recovering.");
transactionStack.clear();
}
txnUserAttributes.rollback(); //All check points are clear.
txnUserAttributes.getCurrentValue().clear(); // Now restore the initial value to a clear map.
}
public void rollback(Transaction transaction, Throwable th) {
MultiException m = new MultiException();
if (th != null ) m.add(th);
boolean entireTransactionIsRolledBack = false;
for (String pool : transaction.getActivePools()){
entireTransactionIsRolledBack = entireTransactionIsRolledBack || Database.getJdbcTypeHelper(pool).hasTransactionRolledBack(th);
}
if (!entireTransactionIsRolledBack){
try {
transaction.rollbackToSavePoint();
}catch (RuntimeException ex){
Config.instance().getLogger(TransactionManager.class.getName()).warning("Full Transaction seems to be rolled back!!" + ExceptionUtil.getRootCause(ex).getMessage());
m.add(ExceptionUtil.getRootCause(ex));
throw m;
}
}
txnUserAttributes.rollback(transaction.getCheckpoint());
try {
updateTransactionStack(transaction);
}catch(RuntimeException ex){
m.add(ExceptionUtil.getRootCause(ex));
throw m;
}
if (transactionStack.isEmpty()){
try {
Config.instance().getLogger(Database.class.getName()).fine("Connection Rollback" + ":" + Database.getCaller());
for (String pool : transaction.getActivePools()){
Database.getInstance().getConnection(pool,false).rollback();
}
Database.getInstance().registerLockRelease();
} catch (SQLException e) {
m.add(e);
throw m;
} finally {
Database.getInstance().resetTransactionIsolationLevel();
}
}else{
if (entireTransactionIsRolledBack){
throw m;
}
}
}
public void commit(Transaction transaction){
transaction.releaseSavepoint();
transaction.setSavepoint();
registerCommit(transaction);
}
public void registerCommit(Transaction transaction) {
txnUserAttributes.commit(transaction.getCheckpoint());
updateTransactionStack(transaction);
if (transactionStack.isEmpty()){
try {
transactionStack.push(transaction);
Registry.instance().callExtensions("before.commit",transaction);//Still part of current transaction.
transactionStack.pop();
Config.instance().getLogger(Database.class.getName()).fine("Connection.commit:" + Database.getCaller());
for (String pool : transaction.getActivePools()) {
Database.getInstance().getConnection(pool,false).commit();
}
txnUserAttributes.getCurrentValue().clear(); // Now restore the initial value to a clear map.
Database.getInstance().registerLockRelease();
Registry.instance().callExtensions("after.commit",transaction);
} catch (SQLException e) {
throw new RuntimeException(e);
}finally {
Database.getInstance().resetTransactionIsolationLevel();
}
}
}
private void updateTransactionStack(Transaction transaction) {
Transaction completedTransaction = getCurrentTransaction();
if (completedTransaction != transaction) {
throw new RuntimeException("Transaction " + transaction.getTransactionNo() + " Has incomplete nested transactions ");
}
transactionStack.pop();
}
private Checkpointed<MergeableMap<String,Object>> txnUserAttributes = new Checkpointed<MergeableMap<String,Object>>(new MergeableMap<String, Object>());
}