package com.yiqiniu.easytrans.idempotent; import java.lang.reflect.Method; import java.util.Arrays; import java.util.Date; import java.util.List; import java.util.concurrent.ConcurrentHashMap; import javax.annotation.Resource; import javax.sql.DataSource; import org.springframework.jdbc.core.BeanPropertyRowMapper; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.transaction.PlatformTransactionManager; import com.yiqiniu.easytrans.datasource.DataSourceSelector; import com.yiqiniu.easytrans.filter.EasyTransFilterChain; import com.yiqiniu.easytrans.protocol.BusinessIdentifer; import com.yiqiniu.easytrans.protocol.EasyTransRequest; import com.yiqiniu.easytrans.protocol.ExecuteOrder; import com.yiqiniu.easytrans.provider.factory.ListableProviderFactory; import com.yiqiniu.easytrans.serialization.ObjectSerializer; import com.yiqiniu.easytrans.util.ReflectUtil; public class IdempotentHelper { @Resource private DataSourceSelector selector; @Resource private ObjectSerializer serializer; @Resource private ListableProviderFactory providerFactory; private static final String TRANSACTION_MANAGER = "TRANSACTION_MANAGER"; private static final String DATA_SOURCE = "DATA_SOURCE"; private static final String JDBC_TEMPLATE = "JDBC_TEMPLATE"; public PlatformTransactionManager getTransactionManager(EasyTransFilterChain filterChain, EasyTransRequest<?, ?> reqest){ PlatformTransactionManager transactionManager = filterChain.getResource(TRANSACTION_MANAGER); if(transactionManager == null){ transactionManager = selector.selectTransactionManager(filterChain.getAppId(), filterChain.getBusCode(),reqest); filterChain.bindResource(TRANSACTION_MANAGER, transactionManager); } return transactionManager; } public DataSource getDatasource(EasyTransFilterChain filterChain, EasyTransRequest<?, ?> reqest){ DataSource dataSource = filterChain.getResource(DATA_SOURCE); if(dataSource == null){ dataSource = selector.selectDataSource(filterChain.getAppId(), filterChain.getBusCode(),reqest); filterChain.bindResource(DATA_SOURCE, dataSource); } return dataSource; } public JdbcTemplate getJdbcTemplate(EasyTransFilterChain filterChain, EasyTransRequest<?, ?> reqest){ JdbcTemplate jdbcTemplate = filterChain.getResource(JDBC_TEMPLATE); if(jdbcTemplate == null){ DataSource datasource = getDatasource(filterChain, reqest); jdbcTemplate = new JdbcTemplate(datasource); filterChain.bindResource(JDBC_TEMPLATE, jdbcTemplate); } return jdbcTemplate; } private BeanPropertyRowMapper<IdempotentPo> beanPropertyRowMapper = new BeanPropertyRowMapper<IdempotentPo>(IdempotentPo.class); /** * get execute result from database * @param filterChain * @param reqest * @return */ public IdempotentPo getIdempotentPo(EasyTransFilterChain filterChain, EasyTransRequest<?, ?> reqest){ BusinessIdentifer businessType = ReflectUtil.getBusinessIdentifer(reqest.getClass()); JdbcTemplate jdbcTemplate = getJdbcTemplate(filterChain, reqest); List<IdempotentPo> listQuery = jdbcTemplate.query( "select * from idempotent where src_app_id = ? and src_bus_code = ? and src_trx_id = ? and app_id = ? and bus_code = ? ", new Object[]{ reqest.getParentTrxId().getAppId(), reqest.getParentTrxId().getBusCode(), reqest.getParentTrxId().getTrxId(), businessType.appId(), businessType.busCode()}, beanPropertyRowMapper ); if(listQuery.size() == 1){ return listQuery.get(0); }else if (listQuery.size() == 0){ return null; }else{ throw new RuntimeException("Unkonw Error!" + listQuery); } } private ConcurrentHashMap<String, Object> mapExecuteOrder = new ConcurrentHashMap<String, Object>(); private static final Object NULL_OBJECT = new Object(); public ExecuteOrder getExecuteOrder(String appId, String busCode ,String innerMethod) { if(EasyTransFilterChain.MESSAGE_BUSINESS_FLAG.equals(innerMethod)){ return null; } String key = getKey(appId, busCode, innerMethod); Object object = mapExecuteOrder.get(key); if(object == null){ Class<?> serviceInterface = providerFactory.getServiceInterface(appId, busCode); Method[] methods = serviceInterface.getMethods(); for(Method method:methods){ if(method.getName().equals(innerMethod)){ ExecuteOrder annotation = method.getAnnotation(ExecuteOrder.class); if(annotation == null){ object = NULL_OBJECT; }else{ object = annotation; } mapExecuteOrder.put(key, object); break; } } } if(object == NULL_OBJECT){ return null; }else{ return (ExecuteOrder) object; } } private String getKey(String appId, String busCode ,String innerMethod){ return appId + busCode + innerMethod; } public static class IdempotentPo{ private String srcAppId; private String srcBusCode; private String srcTrxId; private String appId; private String busCode; private String calledMethods; private String md5; private byte[] syncMethodResult; private Date createTime; private Date updateTime; private Integer lockVersion; public String getSrcAppId() { return srcAppId; } public void setSrcAppId(String srcAppId) { this.srcAppId = srcAppId; } public String getSrcBusCode() { return srcBusCode; } public void setSrcBusCode(String srcBusCode) { this.srcBusCode = srcBusCode; } public String getSrcTrxId() { return srcTrxId; } public void setSrcTrxId(String srcTrxId) { this.srcTrxId = srcTrxId; } public String getAppId() { return appId; } public void setAppId(String appId) { this.appId = appId; } public String getBusCode() { return busCode; } public void setBusCode(String busCode) { this.busCode = busCode; } public String getCalledMethods() { return calledMethods; } public void setCalledMethods(String calledMethods) { this.calledMethods = calledMethods; } public String getMd5() { return md5; } public void setMd5(String md5) { this.md5 = md5; } public byte[] getSyncMethodResult() { return syncMethodResult; } public void setSyncMethodResult(byte[] syncMethodResult) { this.syncMethodResult = syncMethodResult; } public Date getCreateTime() { return createTime; } public void setCreateTime(Date createTime) { this.createTime = createTime; } public Date getUpdateTime() { return updateTime; } public void setUpdateTime(Date updateTime) { this.updateTime = updateTime; } public Integer getLockVersion() { return lockVersion; } public void setLockVersion(Integer lockVersion) { this.lockVersion = lockVersion; } @Override public String toString() { return "IdempotentPo [srcAppId=" + srcAppId + ", srcBusCode=" + srcBusCode + ", srcTrxId=" + srcTrxId + ", appId=" + appId + ", busCode=" + busCode + ", calledMethods=" + calledMethods + ", md5=" + md5 + ", syncMethodResult=" + Arrays.toString(syncMethodResult) + ", createTime=" + createTime + ", updateTime=" + updateTime + ", lockVersion=" + lockVersion + "]"; } } public void saveIdempotentPo(EasyTransFilterChain filterChain,IdempotentPo idempotentPo) { JdbcTemplate jdbcTemplate = filterChain.getResource(JDBC_TEMPLATE); int update = jdbcTemplate.update( "INSERT INTO `idempotent` (`src_app_id`, `src_bus_code`, `src_trx_id`, `app_id`, `bus_code`, `called_methods`, `md5`, `sync_method_result`, `create_time`, `update_time` , `lock_version`) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);", idempotentPo.getSrcAppId(), idempotentPo.getSrcBusCode(), idempotentPo.getSrcTrxId(), idempotentPo.getAppId(), idempotentPo.getBusCode(), idempotentPo.getCalledMethods(), idempotentPo.getMd5(), idempotentPo.getSyncMethodResult(), idempotentPo.getCreateTime(), idempotentPo.getUpdateTime(), idempotentPo.getLockVersion() ); if(update != 1){ throw new RuntimeException("update count exception!" + update); } } public void updateIdempotentPo(EasyTransFilterChain filterChain,IdempotentPo idempotentPo) { JdbcTemplate jdbcTemplate = filterChain.getResource(JDBC_TEMPLATE); int update = jdbcTemplate.update( "UPDATE `idempotent` SET `called_methods` = ?, `md5` = ?, `sync_method_result` = ?, `create_time` = ?, `update_time` = ?, `lock_version` = `lock_version` + 1 WHERE `src_app_id` = ? AND `src_bus_code` = ? AND `src_trx_id` = ? AND `app_id` = ? AND `bus_code` = ? AND `lock_version` = ?;", idempotentPo.getCalledMethods(), idempotentPo.getMd5(), idempotentPo.getSyncMethodResult(), idempotentPo.getCreateTime(), idempotentPo.getUpdateTime(), idempotentPo.getSrcAppId(), idempotentPo.getSrcBusCode(), idempotentPo.getSrcTrxId(), idempotentPo.getAppId(), idempotentPo.getBusCode(), idempotentPo.getLockVersion() ); if(update != 1){ throw new RuntimeException("Optimistic Lock Error Occour Or can not find the specific Record!" + idempotentPo); } } }