package com.ctrip.platform.dal.dao.client;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import com.ctrip.platform.dal.dao.DalClientFactory;
import com.ctrip.platform.dal.exceptions.DalException;
import com.ctrip.platform.dal.exceptions.ErrorCode;
public class DalTransaction {
private String logicDbName;
private DalConnection connHolder;
private List<DalTransactionListener> listeners;
private int level = 0;
private boolean rolledBack = false;
private boolean completed = false;
private DalLogger logger;
public DalTransaction(DalConnection connHolder, String logicDbName) throws SQLException{
this.logicDbName = logicDbName;
this.connHolder = connHolder;
connHolder.getConn().setAutoCommit(false);
this.logger = DalClientFactory.getDalLogger();
}
public void validate(String logicDbName) throws SQLException {
if(logicDbName == null || logicDbName.length() == 0)
throw new DalException(ErrorCode.LogicDbEmpty);
if(!logicDbName.equals(this.logicDbName))
throw new DalException(ErrorCode.TransactionDistributed, this.logicDbName, logicDbName);
}
public String getLogicDbName() {
return logicDbName;
}
public DalConnection getConnection() {
return connHolder;
}
public void register(DalTransactionListener listener) {
if(listeners == null)
listeners = new ArrayList<>();
listeners.add(listener);
}
public List<DalTransactionListener> getListeners() {
return listeners;
}
public int getLevel() {
return level;
}
public boolean isRolledBack() {
return rolledBack;
}
public int startTransaction() throws SQLException {
if(rolledBack || completed)
throw new DalException(ErrorCode.TransactionState);
return level++;
}
public void endTransaction(int startLevel) throws SQLException {
if(rolledBack || completed)
throw new DalException(ErrorCode.TransactionState);
if(startLevel != (level - 1)) {
rollbackTransaction();
throw new DalException(ErrorCode.TransactionLevelMatch, (level - 1), startLevel);
}
if(level > 1) {
level--;
return;
}
// Back to the first transaction, about to commit
beforeCommit();
level = 0;
completed = true;
cleanup(true);
afterCommit();
}
public void rollbackTransaction() throws SQLException {
if(rolledBack)
return;
beforeRollback();
rolledBack = true;
// Even the rollback fails, we still set the flag to true;
cleanup(false);
afterRollback();
}
private void cleanup(boolean commit) {
Connection conn = connHolder.getConn();
try {
if(commit)
conn.commit();
else
conn.rollback();
} catch (Throwable e) {
logger.error("Can not commit or rollback on current connection", e);
}
try {
conn.setAutoCommit(true);
} catch (Throwable e) {
e.printStackTrace();
}
connHolder.close();
DalTransactionManager.clearCurrentTransaction();
}
private void beforeCommit() throws SQLException {
if(listeners == null)
return;
// The before commit can cause transaction termination by throwing exception
for(DalTransactionListener listener: listeners)
listener.beforeCommit();
}
private void beforeRollback() {
if(listeners == null)
return;
for(DalTransactionListener listener: listeners) {
try{
listener.beforeRollback();
}catch(Throwable e) {
logError(e);
}
}
}
private void afterCommit() {
if(listeners == null)
return;
for(DalTransactionListener listener: listeners) {
try{
listener.afterCommit();
}catch(Throwable e) {
logError(e);
}
}
}
private void afterRollback() {
if(listeners == null)
return;
for(DalTransactionListener listener: listeners) {
try{
listener.afterRollback();
}catch(Throwable e) {
logError(e);
}
}
}
private void logError(Throwable e) {
try {
logger.error(e.getMessage(), e);
} catch (Throwable e2) {
System.err.println(e2);
}
}
}