package com.yiqiniu.easytrans.context;
import java.lang.annotation.Annotation;
import java.util.ArrayList;
import java.util.Date;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;
import com.yiqiniu.easytrans.ExecuteCacheManager;
import com.yiqiniu.easytrans.context.event.DemiLogEventHandler;
import com.yiqiniu.easytrans.context.event.DemiLogEventManager;
import com.yiqiniu.easytrans.context.event.GuardianProcessEndEventHandler;
import com.yiqiniu.easytrans.context.event.GuardianProcessEndEventManager;
import com.yiqiniu.easytrans.datasource.TransStatusLogger;
import com.yiqiniu.easytrans.log.LogCache;
import com.yiqiniu.easytrans.log.TransactionLogWritter;
import com.yiqiniu.easytrans.log.vo.AfterCommit;
import com.yiqiniu.easytrans.log.vo.Content;
import com.yiqiniu.easytrans.log.vo.DemiLeftContent;
import com.yiqiniu.easytrans.log.vo.LogCollection;
import com.yiqiniu.easytrans.protocol.IdempotentTypeDeclare.TransactionId;
public class LogProcessContext{
/**
* final transaction status,null for unknown,true for commit , false for rollback
*/
private Boolean finalMasterTransStatus;
private LogCollection logCollection;
private DemiLogEventManager demiLogManager;
private GuardianProcessEndEventManager processEndManager;
private ExecuteCacheManager executeManager;
private LogCache logCache;
private TransStatusLogger transStatusChecker;
private TransactionLogWritter writer;
private TransactionId transactionId;
private AtomicInteger atomicInger = new AtomicInteger(1);
private ConcurrentHashMap<Object, Object> extendResourceMap = new ConcurrentHashMap<Object, Object>();
public int getAndIncTransUniqueId(){
return atomicInger.getAndIncrement();
}
public ConcurrentHashMap<Object, Object> getExtendResourceMap() {
return extendResourceMap;
}
/**
* for normal processing
* @param transStatusChecker
*/
public LogProcessContext(String appId,String busCode,String trxId,TransactionLogWritter writer, TransStatusLogger transStatusChecker) {
super();
this.logCollection = new LogCollection(appId, busCode, trxId, new ArrayList<Content>(), new Date());
transactionId = new TransactionId(appId, busCode, trxId);
init(false, writer,transStatusChecker);
}
/**
* for crash recover
* @param masterTransCommited
* @param logCollection
* @param writer
*/
public LogProcessContext(LogCollection logCollection,TransactionLogWritter writer, TransStatusLogger transStatusChecker) {
super();
this.logCollection = logCollection;
transactionId = new TransactionId(logCollection.getAppId(), logCollection.getBusCode(), logCollection.getTrxId());
init(null, writer, transStatusChecker);
}
private void init(Boolean masterTransCommited, TransactionLogWritter writer, TransStatusLogger transStatusChecker) {
this.finalMasterTransStatus = masterTransCommited;
this.writer = writer;
demiLogManager = new DemiLogEventManager(this);
processEndManager = new GuardianProcessEndEventManager(this);
executeManager = new ExecuteCacheManager(this);
logCache = new LogCache(this);
this.transStatusChecker = transStatusChecker;
}
public TransactionId getTransactionId(){
return transactionId;
}
public LogCache getLogCache() {
return logCache;
}
public TransactionLogWritter getWriter() {
return writer;
}
public DemiLogEventManager getDemiLogManager() {
return demiLogManager;
}
public GuardianProcessEndEventManager getProcessEndManager() {
return processEndManager;
}
public void registerDemiLogEventListener(DemiLeftContent LeftContent,DemiLogEventHandler handler){
demiLogManager.registerSemiLogEventListener(LeftContent, handler);
}
public void registerProcessEndEventListener(GuardianProcessEndEventHandler handler){
processEndManager.registerSemiLogEventListener(handler);
}
/**
* set the final status of transaction
* @param status true for commit,false for roll back
*/
public void setFinalMasterTransStatus(Boolean status){
finalMasterTransStatus = status;
}
/**
* status true for commit,false for roll back,null for unknown
* @return
*/
public Boolean getFinalMasterTransStatus() {
if(finalMasterTransStatus == null){
//check by logs first
finalMasterTransStatus = checkTransStatusByLogs();
//then check by API
if(finalMasterTransStatus == null){
finalMasterTransStatus = transStatusChecker.checkTransactionStatus(getLogCollection().getAppId(),getLogCollection().getBusCode(), getLogCollection().getTrxId());
}
}
return finalMasterTransStatus;
}
private Boolean checkTransStatusByLogs() {
for(Content c:getLogCollection().getOrderedContents()){
if(isCommitedLog(c.getClass())){
return true;//there is a committed log, so the transaction must be committed
}
if(isRollBackLog(c.getClass())){
return false;//there is a roll back log, so the transaction must be rolled back
}
}
return null;//can't tell
}
private ConcurrentHashMap<Class<?>, Boolean> mapCommitedLogContent = new ConcurrentHashMap<Class<?>, Boolean>();
private boolean isCommitedLog(Class<? extends Content> contentClass){
Boolean isCommitedLogContent = mapCommitedLogContent.get(contentClass);
if(isCommitedLogContent == null){
isCommitedLogContent = hasAnnotion(contentClass,AfterCommit.class);
mapCommitedLogContent.put(contentClass, isCommitedLogContent);
}
return isCommitedLogContent;
}
private ConcurrentHashMap<Class<?>, Boolean> mapRollBackLogContent = new ConcurrentHashMap<Class<?>, Boolean>();
private boolean isRollBackLog(Class<? extends Content> contentClass){
Boolean isRollbackedLogContent = mapRollBackLogContent.get(contentClass);
if(isRollbackedLogContent == null){
isRollbackedLogContent = hasAnnotion(contentClass,AfterCommit.class);
mapRollBackLogContent.put(contentClass, isRollbackedLogContent);
}
return isRollbackedLogContent;
}
private boolean hasAnnotion(Class<? extends Content> checkClass,Class<? extends Annotation> annotation) {
if(checkClass.getAnnotation(annotation) == null){
return false;
}else{
return true;
}
}
public LogCollection getLogCollection() {
return logCollection;
}
public ExecuteCacheManager getExecuteManager() {
return executeManager;
}
}