chore(project): 添加项目配置文件和忽略规则
- 添加 Babel 配置文件支持 ES6+ 语法转换 - 添加 ESLint 忽略规则和配置文件 - 添加 Git 忽略规则文件 - 添加 Travis CI 配置文件 - 添加 1.4.2 版本变更日志文件 - 添加 Helm 图表辅助模板文件 - 添加 Helm 忽略规则文件
This commit is contained in:
69
tcc/README.MD
Normal file
69
tcc/README.MD
Normal file
@@ -0,0 +1,69 @@
|
||||
### TCC
|
||||
|
||||
|
||||
#### TCC 简介
|
||||
|
||||
在2PC(两阶段提交)协议中,事务管理器分两阶段协调资源管理,资源管理器对外提供了3个操作,分别是一阶段的准备操作,二阶段的提交操作和回滚操作;
|
||||
|
||||
TCC服务作为一种事务资源,遵循两阶段提交协议,由业务层面自定义,需要用户根据业务逻辑编码实现;其包含Try、Confirm 和 Cancel 3个操作,其中Try操作对应分布式事务一阶段的准备,Confirm操作对应分布式事务二阶段提交,Cancel对应分布式事务二阶段回滚:
|
||||
|
||||
- Try:
|
||||
|
||||
资源的检查和预留;
|
||||
|
||||
- Confirm:
|
||||
|
||||
使用预留的资源,完成真正的业务操作;要求Try成功Confirm 一定要能成功;
|
||||
|
||||
- Cancel:
|
||||
|
||||
释放预留资源;
|
||||
|
||||
|
||||
TCC的3个方法均由用户根据业务场景编码实现,并对外发布成微服务,供事务管理器调用;事务管理器在一阶段调用TCC的Try方法,在二阶段提交时调用Confirm方法,在二阶段回滚时调用Cancel方法。
|
||||
|
||||
|
||||
#### TCC实现
|
||||
|
||||
##### 1、TCC微服务
|
||||
|
||||
TCC服务由用户编码实现并对外发布成微服务,目前支持3种形式的TCC微服务,分别是:
|
||||
|
||||
- SofaRpc 服务
|
||||
|
||||
用户将实现的TCC操作对外发布成 SofaRpc 服务,事务管理器通过订阅SofaRpc服务,来协调TCC资源;
|
||||
|
||||
- Dubbo 服务
|
||||
|
||||
将TCC发布成dubbo服务,事务管理器订阅dubbo服务,来协调TCC资源;
|
||||
|
||||
- Local TCC
|
||||
|
||||
本地普通的TCC Bean,非远程服务;事务管理器通过本地方法调用,来协调TCC 资源;
|
||||
|
||||
##### 2、TCC资源动态代理
|
||||
|
||||
对TCC服务进行动态代理,GlobalTransactionScanner中当扫描到TCC 服务的 ‘reference’时,会对其进行动态代理;
|
||||
|
||||
TCC 动态代理的主要功能是:生成TCC运行时上下文、透传业务参数、注册分支事务记录;
|
||||
|
||||
#### 模块说明
|
||||
|
||||
seata-tcc 包含TCC主要代码:
|
||||
|
||||
- interceptor:TCC 动态代理;
|
||||
|
||||
- remoting:TCC微服务扫描、RPC协议解析、TCC资源注册;
|
||||
|
||||
- TccResourceManager、RMHandlerTCC: TCC资源管理器;
|
||||
|
||||
|
||||
|
||||
|
||||
#### 其他说明:
|
||||
|
||||
本此为了区分AT、TCC 2种资源类型,在客户端和服务端通信的接口中,均添加了 BranchType 参数;
|
||||
|
||||
|
||||
|
||||
|
||||
49
tcc/pom.xml
Normal file
49
tcc/pom.xml
Normal file
@@ -0,0 +1,49 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
~ Copyright 1999-2019 Seata.io Group.
|
||||
~
|
||||
~ Licensed under the Apache License, Version 2.0 (the "License");
|
||||
~ you may not use this file except in compliance with the License.
|
||||
~ You may obtain a copy of the License at
|
||||
~
|
||||
~ http://www.apache.org/licenses/LICENSE-2.0
|
||||
~
|
||||
~ Unless required by applicable law or agreed to in writing, software
|
||||
~ distributed under the License is distributed on an "AS IS" BASIS,
|
||||
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
~ See the License for the specific language governing permissions and
|
||||
~ limitations under the License.
|
||||
-->
|
||||
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<groupId>io.seata</groupId>
|
||||
<artifactId>seata-parent</artifactId>
|
||||
<version>${revision}</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>seata-tcc</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
<name>seata-tcc ${project.version}</name>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>${project.groupId}</groupId>
|
||||
<artifactId>seata-core</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>${project.groupId}</groupId>
|
||||
<artifactId>seata-rm</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.alibaba</groupId>
|
||||
<artifactId>fastjson</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
|
||||
</project>
|
||||
46
tcc/src/main/java/io/seata/rm/tcc/RMHandlerTCC.java
Normal file
46
tcc/src/main/java/io/seata/rm/tcc/RMHandlerTCC.java
Normal file
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright 1999-2019 Seata.io Group.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package io.seata.rm.tcc;
|
||||
|
||||
import io.seata.core.model.BranchType;
|
||||
import io.seata.core.model.ResourceManager;
|
||||
import io.seata.core.protocol.transaction.UndoLogDeleteRequest;
|
||||
import io.seata.rm.AbstractRMHandler;
|
||||
import io.seata.rm.DefaultResourceManager;
|
||||
|
||||
/**
|
||||
* The type Rm handler tcc.
|
||||
*
|
||||
* @author zhangsen
|
||||
*/
|
||||
public class RMHandlerTCC extends AbstractRMHandler {
|
||||
|
||||
@Override
|
||||
public void handle(UndoLogDeleteRequest request) {
|
||||
//DO nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ResourceManager getResourceManager() {
|
||||
return DefaultResourceManager.get().getResourceManager(BranchType.TCC);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BranchType getBranchType() {
|
||||
return BranchType.TCC;
|
||||
}
|
||||
|
||||
}
|
||||
228
tcc/src/main/java/io/seata/rm/tcc/TCCResource.java
Normal file
228
tcc/src/main/java/io/seata/rm/tcc/TCCResource.java
Normal file
@@ -0,0 +1,228 @@
|
||||
/*
|
||||
* Copyright 1999-2019 Seata.io Group.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package io.seata.rm.tcc;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import io.seata.core.model.BranchType;
|
||||
import io.seata.core.model.Resource;
|
||||
|
||||
/**
|
||||
* The type Tcc resource.
|
||||
*
|
||||
* @author zhangsen
|
||||
*/
|
||||
public class TCCResource implements Resource {
|
||||
|
||||
private String resourceGroupId = "DEFAULT";
|
||||
|
||||
private String appName;
|
||||
|
||||
private String actionName;
|
||||
|
||||
private Object targetBean;
|
||||
|
||||
private Method prepareMethod;
|
||||
|
||||
private String commitMethodName;
|
||||
|
||||
private Method commitMethod;
|
||||
|
||||
private String rollbackMethodName;
|
||||
|
||||
private Method rollbackMethod;
|
||||
|
||||
@Override
|
||||
public String getResourceGroupId() {
|
||||
return resourceGroupId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets resource group id.
|
||||
*
|
||||
* @param resourceGroupId the resource group id
|
||||
*/
|
||||
public void setResourceGroupId(String resourceGroupId) {
|
||||
this.resourceGroupId = resourceGroupId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getResourceId() {
|
||||
return actionName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BranchType getBranchType() {
|
||||
return BranchType.TCC;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets app name.
|
||||
*
|
||||
* @return the app name
|
||||
*/
|
||||
public String getAppName() {
|
||||
return appName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets app name.
|
||||
*
|
||||
* @param appName the app name
|
||||
*/
|
||||
public void setAppName(String appName) {
|
||||
this.appName = appName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets action name.
|
||||
*
|
||||
* @return the action name
|
||||
*/
|
||||
public String getActionName() {
|
||||
return actionName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets action name.
|
||||
*
|
||||
* @param actionName the action name
|
||||
*/
|
||||
public void setActionName(String actionName) {
|
||||
this.actionName = actionName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets target bean.
|
||||
*
|
||||
* @return the target bean
|
||||
*/
|
||||
public Object getTargetBean() {
|
||||
return targetBean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets target bean.
|
||||
*
|
||||
* @param targetBean the target bean
|
||||
*/
|
||||
public void setTargetBean(Object targetBean) {
|
||||
this.targetBean = targetBean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets prepare method.
|
||||
*
|
||||
* @return the prepare method
|
||||
*/
|
||||
public Method getPrepareMethod() {
|
||||
return prepareMethod;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets prepare method.
|
||||
*
|
||||
* @param prepareMethod the prepare method
|
||||
*/
|
||||
public void setPrepareMethod(Method prepareMethod) {
|
||||
this.prepareMethod = prepareMethod;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets commit method.
|
||||
*
|
||||
* @return the commit method
|
||||
*/
|
||||
public Method getCommitMethod() {
|
||||
return commitMethod;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets commit method.
|
||||
*
|
||||
* @param commitMethod the commit method
|
||||
*/
|
||||
public void setCommitMethod(Method commitMethod) {
|
||||
this.commitMethod = commitMethod;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets rollback method.
|
||||
*
|
||||
* @return the rollback method
|
||||
*/
|
||||
public Method getRollbackMethod() {
|
||||
return rollbackMethod;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets rollback method.
|
||||
*
|
||||
* @param rollbackMethod the rollback method
|
||||
*/
|
||||
public void setRollbackMethod(Method rollbackMethod) {
|
||||
this.rollbackMethod = rollbackMethod;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets commit method name.
|
||||
*
|
||||
* @return the commit method name
|
||||
*/
|
||||
public String getCommitMethodName() {
|
||||
return commitMethodName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets commit method name.
|
||||
*
|
||||
* @param commitMethodName the commit method name
|
||||
*/
|
||||
public void setCommitMethodName(String commitMethodName) {
|
||||
this.commitMethodName = commitMethodName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets rollback method name.
|
||||
*
|
||||
* @return the rollback method name
|
||||
*/
|
||||
public String getRollbackMethodName() {
|
||||
return rollbackMethodName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets rollback method name.
|
||||
*
|
||||
* @param rollbackMethodName the rollback method name
|
||||
*/
|
||||
public void setRollbackMethodName(String rollbackMethodName) {
|
||||
this.rollbackMethodName = rollbackMethodName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return actionName.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (!(obj instanceof TCCResource)) {
|
||||
return false;
|
||||
}
|
||||
return this.actionName.equals(((TCCResource)obj).actionName);
|
||||
}
|
||||
}
|
||||
188
tcc/src/main/java/io/seata/rm/tcc/TCCResourceManager.java
Normal file
188
tcc/src/main/java/io/seata/rm/tcc/TCCResourceManager.java
Normal file
@@ -0,0 +1,188 @@
|
||||
/*
|
||||
* Copyright 1999-2019 Seata.io Group.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package io.seata.rm.tcc;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import com.alibaba.fastjson.JSON;
|
||||
|
||||
import io.seata.common.Constants;
|
||||
import io.seata.common.exception.ShouldNeverHappenException;
|
||||
import io.seata.common.util.StringUtils;
|
||||
import io.seata.core.exception.TransactionException;
|
||||
import io.seata.core.model.BranchStatus;
|
||||
import io.seata.core.model.BranchType;
|
||||
import io.seata.core.model.Resource;
|
||||
import io.seata.rm.AbstractResourceManager;
|
||||
import io.seata.rm.tcc.api.BusinessActionContext;
|
||||
|
||||
/**
|
||||
* TCC resource manager
|
||||
*
|
||||
* @author zhangsen
|
||||
*/
|
||||
public class TCCResourceManager extends AbstractResourceManager {
|
||||
|
||||
/**
|
||||
* TCC resource cache
|
||||
*/
|
||||
private Map<String, Resource> tccResourceCache = new ConcurrentHashMap<>();
|
||||
|
||||
/**
|
||||
* Instantiates a new Tcc resource manager.
|
||||
*/
|
||||
public TCCResourceManager() {
|
||||
}
|
||||
|
||||
/**
|
||||
* registry TCC resource
|
||||
*
|
||||
* @param resource The resource to be managed.
|
||||
*/
|
||||
@Override
|
||||
public void registerResource(Resource resource) {
|
||||
TCCResource tccResource = (TCCResource)resource;
|
||||
tccResourceCache.put(tccResource.getResourceId(), tccResource);
|
||||
super.registerResource(tccResource);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Resource> getManagedResources() {
|
||||
return tccResourceCache;
|
||||
}
|
||||
|
||||
/**
|
||||
* TCC branch commit
|
||||
*
|
||||
* @param branchType
|
||||
* @param xid Transaction id.
|
||||
* @param branchId Branch id.
|
||||
* @param resourceId Resource id.
|
||||
* @param applicationData Application data bind with this branch.
|
||||
* @return BranchStatus
|
||||
* @throws TransactionException TransactionException
|
||||
*/
|
||||
@Override
|
||||
public BranchStatus branchCommit(BranchType branchType, String xid, long branchId, String resourceId,
|
||||
String applicationData) throws TransactionException {
|
||||
TCCResource tccResource = (TCCResource)tccResourceCache.get(resourceId);
|
||||
if (tccResource == null) {
|
||||
throw new ShouldNeverHappenException(String.format("TCC resource is not exist, resourceId: %s", resourceId));
|
||||
}
|
||||
Object targetTCCBean = tccResource.getTargetBean();
|
||||
Method commitMethod = tccResource.getCommitMethod();
|
||||
if (targetTCCBean == null || commitMethod == null) {
|
||||
throw new ShouldNeverHappenException(String.format("TCC resource is not available, resourceId: %s", resourceId));
|
||||
}
|
||||
try {
|
||||
//BusinessActionContext
|
||||
BusinessActionContext businessActionContext = getBusinessActionContext(xid, branchId, resourceId,
|
||||
applicationData);
|
||||
Object ret = commitMethod.invoke(targetTCCBean, businessActionContext);
|
||||
LOGGER.info("TCC resource commit result : {}, xid: {}, branchId: {}, resourceId: {}", ret, xid, branchId, resourceId);
|
||||
boolean result;
|
||||
if (ret != null) {
|
||||
if (ret instanceof TwoPhaseResult) {
|
||||
result = ((TwoPhaseResult)ret).isSuccess();
|
||||
} else {
|
||||
result = (boolean)ret;
|
||||
}
|
||||
} else {
|
||||
result = true;
|
||||
}
|
||||
return result ? BranchStatus.PhaseTwo_Committed : BranchStatus.PhaseTwo_CommitFailed_Retryable;
|
||||
} catch (Throwable t) {
|
||||
String msg = String.format("commit TCC resource error, resourceId: %s, xid: %s.", resourceId, xid);
|
||||
LOGGER.error(msg, t);
|
||||
return BranchStatus.PhaseTwo_CommitFailed_Retryable;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* TCC branch rollback
|
||||
*
|
||||
* @param branchType the branch type
|
||||
* @param xid Transaction id.
|
||||
* @param branchId Branch id.
|
||||
* @param resourceId Resource id.
|
||||
* @param applicationData Application data bind with this branch.
|
||||
* @return BranchStatus
|
||||
* @throws TransactionException TransactionException
|
||||
*/
|
||||
@Override
|
||||
public BranchStatus branchRollback(BranchType branchType, String xid, long branchId, String resourceId,
|
||||
String applicationData) throws TransactionException {
|
||||
TCCResource tccResource = (TCCResource)tccResourceCache.get(resourceId);
|
||||
if (tccResource == null) {
|
||||
throw new ShouldNeverHappenException(String.format("TCC resource is not exist, resourceId: %s", resourceId));
|
||||
}
|
||||
Object targetTCCBean = tccResource.getTargetBean();
|
||||
Method rollbackMethod = tccResource.getRollbackMethod();
|
||||
if (targetTCCBean == null || rollbackMethod == null) {
|
||||
throw new ShouldNeverHappenException(String.format("TCC resource is not available, resourceId: %s", resourceId));
|
||||
}
|
||||
try {
|
||||
//BusinessActionContext
|
||||
BusinessActionContext businessActionContext = getBusinessActionContext(xid, branchId, resourceId,
|
||||
applicationData);
|
||||
Object ret = rollbackMethod.invoke(targetTCCBean, businessActionContext);
|
||||
LOGGER.info("TCC resource rollback result : {}, xid: {}, branchId: {}, resourceId: {}", ret, xid, branchId, resourceId);
|
||||
boolean result;
|
||||
if (ret != null) {
|
||||
if (ret instanceof TwoPhaseResult) {
|
||||
result = ((TwoPhaseResult)ret).isSuccess();
|
||||
} else {
|
||||
result = (boolean)ret;
|
||||
}
|
||||
} else {
|
||||
result = true;
|
||||
}
|
||||
return result ? BranchStatus.PhaseTwo_Rollbacked : BranchStatus.PhaseTwo_RollbackFailed_Retryable;
|
||||
} catch (Throwable t) {
|
||||
String msg = String.format("rollback TCC resource error, resourceId: %s, xid: %s.", resourceId, xid);
|
||||
LOGGER.error(msg, t);
|
||||
return BranchStatus.PhaseTwo_RollbackFailed_Retryable;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* transfer tcc applicationData to BusinessActionContext
|
||||
*
|
||||
* @param xid the xid
|
||||
* @param branchId the branch id
|
||||
* @param resourceId the resource id
|
||||
* @param applicationData the application data
|
||||
* @return business action context
|
||||
*/
|
||||
protected BusinessActionContext getBusinessActionContext(String xid, long branchId, String resourceId,
|
||||
String applicationData) {
|
||||
//transfer tcc applicationData to Context
|
||||
Map tccContext = StringUtils.isBlank(applicationData) ? new HashMap() : (Map)JSON.parse(applicationData);
|
||||
Map actionContextMap = (Map)tccContext.get(Constants.TCC_ACTION_CONTEXT);
|
||||
BusinessActionContext businessActionContext = new BusinessActionContext(
|
||||
xid, String.valueOf(branchId), actionContextMap);
|
||||
businessActionContext.setActionName(resourceId);
|
||||
return businessActionContext;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BranchType getBranchType() {
|
||||
return BranchType.TCC;
|
||||
}
|
||||
}
|
||||
95
tcc/src/main/java/io/seata/rm/tcc/TwoPhaseResult.java
Normal file
95
tcc/src/main/java/io/seata/rm/tcc/TwoPhaseResult.java
Normal file
@@ -0,0 +1,95 @@
|
||||
/*
|
||||
* Copyright 1999-2019 Seata.io Group.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package io.seata.rm.tcc;
|
||||
|
||||
import io.seata.common.util.StringUtils;
|
||||
|
||||
/**
|
||||
* the TCC method result
|
||||
*
|
||||
* @author zhangsen
|
||||
*/
|
||||
public class TwoPhaseResult {
|
||||
|
||||
/**
|
||||
* is Success ?
|
||||
*/
|
||||
private boolean isSuccess;
|
||||
|
||||
/**
|
||||
* result message
|
||||
*/
|
||||
private String message;
|
||||
|
||||
/**
|
||||
* Instantiates a new Two phase result.
|
||||
*
|
||||
* @param isSuccess the is success
|
||||
* @param msg the msg
|
||||
*/
|
||||
public TwoPhaseResult(boolean isSuccess, String msg) {
|
||||
this.isSuccess = isSuccess;
|
||||
this.message = msg;
|
||||
}
|
||||
|
||||
/**
|
||||
* Is success boolean.
|
||||
*
|
||||
* @return the boolean
|
||||
*/
|
||||
public boolean isSuccess() {
|
||||
return isSuccess;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets success.
|
||||
*
|
||||
* @param isSuccess the is success
|
||||
*/
|
||||
public void setSuccess(boolean isSuccess) {
|
||||
this.isSuccess = isSuccess;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets message.
|
||||
*
|
||||
* @return the message
|
||||
*/
|
||||
public String getMessage() {
|
||||
return message;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets message.
|
||||
*
|
||||
* @param msg the message
|
||||
*/
|
||||
public void setMessage(String msg) {
|
||||
this.message = msg;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("[");
|
||||
sb.append("isSuccess:").append(isSuccess);
|
||||
if (StringUtils.isNotBlank(message)) {
|
||||
sb.append(", msg").append(message);
|
||||
}
|
||||
sb.append("]");
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
155
tcc/src/main/java/io/seata/rm/tcc/api/BusinessActionContext.java
Normal file
155
tcc/src/main/java/io/seata/rm/tcc/api/BusinessActionContext.java
Normal file
@@ -0,0 +1,155 @@
|
||||
/*
|
||||
* Copyright 1999-2019 Seata.io Group.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package io.seata.rm.tcc.api;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* The type Business action context.
|
||||
*/
|
||||
public class BusinessActionContext implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 6539226288677737991L;
|
||||
|
||||
private String xid;
|
||||
|
||||
private String branchId;
|
||||
|
||||
private String actionName;
|
||||
|
||||
private Map<String, Object> actionContext;
|
||||
|
||||
/**
|
||||
* Instantiates a new Business action context.
|
||||
*/
|
||||
public BusinessActionContext() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiates a new Business action context.
|
||||
*
|
||||
* @param xid the xid
|
||||
* @param branchId the branch id
|
||||
* @param actionContext the action context
|
||||
*/
|
||||
public BusinessActionContext(String xid, String branchId, Map<String, Object> actionContext) {
|
||||
this.xid = xid;
|
||||
this.branchId = branchId;
|
||||
this.setActionContext(actionContext);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets action context.
|
||||
*
|
||||
* @param key the key
|
||||
* @return the action context
|
||||
*/
|
||||
public Object getActionContext(String key) {
|
||||
return actionContext.get(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets branch id.
|
||||
*
|
||||
* @return the branch id
|
||||
*/
|
||||
public long getBranchId() {
|
||||
return branchId != null ? Long.parseLong(branchId) : -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets branch id.
|
||||
*
|
||||
* @param branchId the branch id
|
||||
*/
|
||||
public void setBranchId(long branchId) {
|
||||
this.branchId = String.valueOf(branchId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets action context.
|
||||
*
|
||||
* @return the action context
|
||||
*/
|
||||
public Map<String, Object> getActionContext() {
|
||||
return actionContext;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets action context.
|
||||
*
|
||||
* @param actionContext the action context
|
||||
*/
|
||||
public void setActionContext(Map<String, Object> actionContext) {
|
||||
this.actionContext = actionContext;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets xid.
|
||||
*
|
||||
* @return the xid
|
||||
*/
|
||||
public String getXid() {
|
||||
return xid;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets xid.
|
||||
*
|
||||
* @param xid the xid
|
||||
*/
|
||||
public void setXid(String xid) {
|
||||
this.xid = xid;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets branch id.
|
||||
*
|
||||
* @param branchId the branch id
|
||||
*/
|
||||
public void setBranchId(String branchId) {
|
||||
this.branchId = branchId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets action name.
|
||||
*
|
||||
* @return the action name
|
||||
*/
|
||||
public String getActionName() {
|
||||
return actionName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets action name.
|
||||
*
|
||||
* @param actionName the action name
|
||||
*/
|
||||
public void setActionName(String actionName) {
|
||||
this.actionName = actionName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("[xid:").append(xid)
|
||||
.append(",branch_Id:").append(branchId).append(",action_name:").append(actionName)
|
||||
.append(",action_context:")
|
||||
.append(actionContext).append("]");
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* Copyright 1999-2019 Seata.io Group.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package io.seata.rm.tcc.api;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* the TCC parameters that need to be passed to the BusinessActivityContext;
|
||||
* <p>
|
||||
* add this annotation on the parameters of the try method, and the parameters will be passed to the
|
||||
* BusinessActivityContext
|
||||
*
|
||||
* @author zhangsen
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ElementType.PARAMETER, ElementType.FIELD})
|
||||
public @interface BusinessActionContextParameter {
|
||||
|
||||
/**
|
||||
* parameter's name
|
||||
*
|
||||
* @return the string
|
||||
*/
|
||||
String paramName() default "";
|
||||
|
||||
/**
|
||||
* if it is a sharding param ?
|
||||
*
|
||||
* @return boolean boolean
|
||||
*/
|
||||
boolean isShardingParam() default false;
|
||||
|
||||
/**
|
||||
* Specify the index of the parameter in the List
|
||||
*
|
||||
* @return int int
|
||||
*/
|
||||
int index() default -1;
|
||||
|
||||
/**
|
||||
* if get the parameter from the property of the object ?
|
||||
*
|
||||
* @return boolean boolean
|
||||
*/
|
||||
boolean isParamInProperty() default false;
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
/*
|
||||
* Copyright 1999-2019 Seata.io Group.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package io.seata.rm.tcc.api;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import io.seata.common.Constants;
|
||||
|
||||
/**
|
||||
* The type Business activity context.
|
||||
*
|
||||
* @author zhangsen
|
||||
*/
|
||||
public class BusinessActivityContext implements Serializable {
|
||||
|
||||
/** */
|
||||
private static final long serialVersionUID = 6539226288677737992L;
|
||||
|
||||
private Map<String, Object> context = new HashMap<>();
|
||||
|
||||
/**
|
||||
* Instantiates a new Business activity context.
|
||||
*/
|
||||
public BusinessActivityContext() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiates a new Business activity context.
|
||||
*
|
||||
* @param context the context
|
||||
*/
|
||||
public BusinessActivityContext(Map<String, Object> context) {
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add context.
|
||||
*
|
||||
* @param key the key
|
||||
* @param value the value
|
||||
*/
|
||||
public void addContext(String key, Object value) {
|
||||
context.put(key, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch start time long.
|
||||
*
|
||||
* @return the long
|
||||
*/
|
||||
public Long fetchStartTime() {
|
||||
return (Long)context.get(Constants.START_TIME);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get context object.
|
||||
*
|
||||
* @param key the key
|
||||
* @return the object
|
||||
*/
|
||||
public Object getContext(String key) {
|
||||
return context.get(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets context.
|
||||
*
|
||||
* @return the context
|
||||
*/
|
||||
public Map<String, Object> getContext() {
|
||||
return context;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets context.
|
||||
*
|
||||
* @param context the context
|
||||
*/
|
||||
public void setContext(Map<String, Object> context) {
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return context.toString();
|
||||
}
|
||||
|
||||
}
|
||||
35
tcc/src/main/java/io/seata/rm/tcc/api/LocalTCC.java
Normal file
35
tcc/src/main/java/io/seata/rm/tcc/api/LocalTCC.java
Normal file
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright 1999-2019 Seata.io Group.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package io.seata.rm.tcc.api;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Inherited;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* Local TCC bean annotation, add on the TCC interface
|
||||
*
|
||||
* @author zhangsen
|
||||
* @see io.seata.spring.annotation.GlobalTransactionScanner#wrapIfNecessary(Object, String, Object) // the scanner for TM, GlobalLock, and TCC mode
|
||||
* @see io.seata.rm.tcc.remoting.parser.LocalTCCRemotingParser // the RemotingParser impl for LocalTCC
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.TYPE)
|
||||
@Inherited
|
||||
public @interface LocalTCC {
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* Copyright 1999-2019 Seata.io Group.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package io.seata.rm.tcc.api;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Inherited;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* TCC annotation.
|
||||
* Define a TCC interface, which added on the try method.
|
||||
* Must be used with `@LocalTCC`.
|
||||
*
|
||||
* @author zhangsen
|
||||
* @see io.seata.rm.tcc.api.LocalTCC // TCC annotation, which added on the TCC interface. It can't be left out.
|
||||
* @see io.seata.spring.annotation.GlobalTransactionScanner#wrapIfNecessary(Object, String, Object) // the scanner for TM, GlobalLock, and TCC mode
|
||||
* @see io.seata.spring.tcc.TccActionInterceptor // the interceptor of TCC mode
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ElementType.METHOD})
|
||||
@Inherited
|
||||
public @interface TwoPhaseBusinessAction {
|
||||
|
||||
/**
|
||||
* TCC bean name, must be unique
|
||||
*
|
||||
* @return the string
|
||||
*/
|
||||
String name();
|
||||
|
||||
/**
|
||||
* commit methed name
|
||||
*
|
||||
* @return the string
|
||||
*/
|
||||
String commitMethod() default "commit";
|
||||
|
||||
/**
|
||||
* rollback method name
|
||||
*
|
||||
* @return the string
|
||||
*/
|
||||
String rollbackMethod() default "rollback";
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright 1999-2019 Seata.io Group.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package io.seata.rm.tcc.interceptor;
|
||||
|
||||
import io.seata.rm.tcc.api.BusinessActionContextParameter;
|
||||
|
||||
/**
|
||||
* The interface Action context filter.
|
||||
*
|
||||
* @author zhangsen
|
||||
*/
|
||||
public interface ActionContextFilter {
|
||||
|
||||
/**
|
||||
* Need filter boolean.
|
||||
*
|
||||
* @param parameter the parameter
|
||||
* @return the boolean
|
||||
*/
|
||||
boolean needFilter(BusinessActionContextParameter parameter);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
/*
|
||||
* Copyright 1999-2019 Seata.io Group.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package io.seata.rm.tcc.interceptor;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import io.seata.common.exception.FrameworkException;
|
||||
import io.seata.common.util.StringUtils;
|
||||
import io.seata.rm.tcc.api.BusinessActionContextParameter;
|
||||
|
||||
/**
|
||||
* Extracting TCC Context from Method
|
||||
*
|
||||
* @author zhangsen
|
||||
*/
|
||||
public class ActionContextUtil {
|
||||
|
||||
private ActionContextUtil() {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracting context data from parameters
|
||||
*
|
||||
* @param targetParam the target param
|
||||
* @return map map
|
||||
*/
|
||||
public static Map<String, Object> fetchContextFromObject(Object targetParam) {
|
||||
try {
|
||||
Map<String, Object> context = new HashMap<>(8);
|
||||
List<Field> fields = new ArrayList<>();
|
||||
getAllField(targetParam.getClass(), fields);
|
||||
|
||||
for (Field f : fields) {
|
||||
String fieldName = f.getName();
|
||||
BusinessActionContextParameter annotation = f.getAnnotation(BusinessActionContextParameter.class);
|
||||
|
||||
if (annotation != null) {
|
||||
f.setAccessible(true);
|
||||
Object paramObject = f.get(targetParam);
|
||||
int index = annotation.index();
|
||||
if (annotation.isParamInProperty()) {
|
||||
if (index >= 0) {
|
||||
@SuppressWarnings("unchecked")
|
||||
Object targetObject = ((List<Object>) paramObject).get(index);
|
||||
context.putAll(fetchContextFromObject(targetObject));
|
||||
} else {
|
||||
context.putAll(fetchContextFromObject(paramObject));
|
||||
}
|
||||
} else {
|
||||
if (StringUtils.isBlank(annotation.paramName())) {
|
||||
context.put(fieldName, paramObject);
|
||||
} else {
|
||||
context.put(annotation.paramName(), paramObject);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return context;
|
||||
} catch (Throwable t) {
|
||||
throw new FrameworkException(t, "fetchContextFromObject failover");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all field.
|
||||
*
|
||||
* @param interFace the inter face
|
||||
* @param fields the fields
|
||||
*/
|
||||
public static void getAllField(Class<?> interFace, List<Field> fields) {
|
||||
if (interFace == Object.class || interFace.isInterface()) {
|
||||
return;
|
||||
}
|
||||
Field[] field = interFace.getDeclaredFields();
|
||||
fields.addAll(Arrays.asList(field));
|
||||
getAllField(interFace.getSuperclass(), fields);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,207 @@
|
||||
/*
|
||||
* Copyright 1999-2019 Seata.io Group.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package io.seata.rm.tcc.interceptor;
|
||||
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import io.seata.common.Constants;
|
||||
import io.seata.common.exception.FrameworkException;
|
||||
import io.seata.common.executor.Callback;
|
||||
import io.seata.common.util.NetUtil;
|
||||
import io.seata.core.context.RootContext;
|
||||
import io.seata.core.model.BranchType;
|
||||
import io.seata.rm.DefaultResourceManager;
|
||||
import io.seata.rm.tcc.api.BusinessActionContext;
|
||||
import io.seata.rm.tcc.api.BusinessActionContextParameter;
|
||||
import io.seata.rm.tcc.api.TwoPhaseBusinessAction;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.slf4j.MDC;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Handler the TCC Participant Aspect : Setting Context, Creating Branch Record
|
||||
*
|
||||
* @author zhangsen
|
||||
*/
|
||||
public class ActionInterceptorHandler {
|
||||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(ActionInterceptorHandler.class);
|
||||
|
||||
/**
|
||||
* Handler the TCC Aspect
|
||||
*
|
||||
* @param method the method
|
||||
* @param arguments the arguments
|
||||
* @param businessAction the business action
|
||||
* @param targetCallback the target callback
|
||||
* @return map map
|
||||
* @throws Throwable the throwable
|
||||
*/
|
||||
public Map<String, Object> proceed(Method method, Object[] arguments, String xid, TwoPhaseBusinessAction businessAction,
|
||||
Callback<Object> targetCallback) throws Throwable {
|
||||
Map<String, Object> ret = new HashMap<>(4);
|
||||
|
||||
//TCC name
|
||||
String actionName = businessAction.name();
|
||||
BusinessActionContext actionContext = new BusinessActionContext();
|
||||
actionContext.setXid(xid);
|
||||
//set action name
|
||||
actionContext.setActionName(actionName);
|
||||
|
||||
//Creating Branch Record
|
||||
String branchId = doTccActionLogStore(method, arguments, businessAction, actionContext);
|
||||
actionContext.setBranchId(branchId);
|
||||
//MDC put branchId
|
||||
MDC.put(RootContext.MDC_KEY_BRANCH_ID, branchId);
|
||||
|
||||
//set the parameter whose type is BusinessActionContext
|
||||
Class<?>[] types = method.getParameterTypes();
|
||||
int argIndex = 0;
|
||||
for (Class<?> cls : types) {
|
||||
if (cls.getName().equals(BusinessActionContext.class.getName())) {
|
||||
arguments[argIndex] = actionContext;
|
||||
break;
|
||||
}
|
||||
argIndex++;
|
||||
}
|
||||
//the final parameters of the try method
|
||||
ret.put(Constants.TCC_METHOD_ARGUMENTS, arguments);
|
||||
//the final result
|
||||
ret.put(Constants.TCC_METHOD_RESULT, targetCallback.execute());
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creating Branch Record
|
||||
*
|
||||
* @param method the method
|
||||
* @param arguments the arguments
|
||||
* @param businessAction the business action
|
||||
* @param actionContext the action context
|
||||
* @return the string
|
||||
*/
|
||||
protected String doTccActionLogStore(Method method, Object[] arguments, TwoPhaseBusinessAction businessAction,
|
||||
BusinessActionContext actionContext) {
|
||||
String actionName = actionContext.getActionName();
|
||||
String xid = actionContext.getXid();
|
||||
//
|
||||
Map<String, Object> context = fetchActionRequestContext(method, arguments);
|
||||
context.put(Constants.ACTION_START_TIME, System.currentTimeMillis());
|
||||
|
||||
//init business context
|
||||
initBusinessContext(context, method, businessAction);
|
||||
//Init running environment context
|
||||
initFrameworkContext(context);
|
||||
actionContext.setActionContext(context);
|
||||
|
||||
//init applicationData
|
||||
Map<String, Object> applicationContext = new HashMap<>(4);
|
||||
applicationContext.put(Constants.TCC_ACTION_CONTEXT, context);
|
||||
String applicationContextStr = JSON.toJSONString(applicationContext);
|
||||
try {
|
||||
//registry branch record
|
||||
Long branchId = DefaultResourceManager.get().branchRegister(BranchType.TCC, actionName, null, xid,
|
||||
applicationContextStr, null);
|
||||
return String.valueOf(branchId);
|
||||
} catch (Throwable t) {
|
||||
String msg = String.format("TCC branch Register error, xid: %s", xid);
|
||||
LOGGER.error(msg, t);
|
||||
throw new FrameworkException(t, msg);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Init running environment context
|
||||
*
|
||||
* @param context the context
|
||||
*/
|
||||
protected void initFrameworkContext(Map<String, Object> context) {
|
||||
try {
|
||||
context.put(Constants.HOST_NAME, NetUtil.getLocalIp());
|
||||
} catch (Throwable t) {
|
||||
LOGGER.warn("getLocalIP error", t);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Init business context
|
||||
*
|
||||
* @param context the context
|
||||
* @param method the method
|
||||
* @param businessAction the business action
|
||||
*/
|
||||
protected void initBusinessContext(Map<String, Object> context, Method method,
|
||||
TwoPhaseBusinessAction businessAction) {
|
||||
if (method != null) {
|
||||
//the phase one method name
|
||||
context.put(Constants.PREPARE_METHOD, method.getName());
|
||||
}
|
||||
if (businessAction != null) {
|
||||
//the phase two method name
|
||||
context.put(Constants.COMMIT_METHOD, businessAction.commitMethod());
|
||||
context.put(Constants.ROLLBACK_METHOD, businessAction.rollbackMethod());
|
||||
context.put(Constants.ACTION_NAME, businessAction.name());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracting context data from parameters, add them to the context
|
||||
*
|
||||
* @param method the method
|
||||
* @param arguments the arguments
|
||||
* @return map map
|
||||
*/
|
||||
protected Map<String, Object> fetchActionRequestContext(Method method, Object[] arguments) {
|
||||
Map<String, Object> context = new HashMap<>(8);
|
||||
|
||||
Annotation[][] parameterAnnotations = method.getParameterAnnotations();
|
||||
for (int i = 0; i < parameterAnnotations.length; i++) {
|
||||
for (int j = 0; j < parameterAnnotations[i].length; j++) {
|
||||
if (parameterAnnotations[i][j] instanceof BusinessActionContextParameter) {
|
||||
BusinessActionContextParameter param = (BusinessActionContextParameter)parameterAnnotations[i][j];
|
||||
if (arguments[i] == null) {
|
||||
throw new IllegalArgumentException("@BusinessActionContextParameter 's params can not null");
|
||||
}
|
||||
Object paramObject = arguments[i];
|
||||
int index = param.index();
|
||||
//List, get by index
|
||||
if (index >= 0) {
|
||||
@SuppressWarnings("unchecked")
|
||||
Object targetParam = ((List<Object>)paramObject).get(index);
|
||||
if (param.isParamInProperty()) {
|
||||
context.putAll(ActionContextUtil.fetchContextFromObject(targetParam));
|
||||
} else {
|
||||
context.put(param.paramName(), targetParam);
|
||||
}
|
||||
} else {
|
||||
if (param.isParamInProperty()) {
|
||||
context.putAll(ActionContextUtil.fetchContextFromObject(paramObject));
|
||||
} else {
|
||||
context.put(param.paramName(), paramObject);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return context;
|
||||
}
|
||||
|
||||
}
|
||||
49
tcc/src/main/java/io/seata/rm/tcc/remoting/Protocols.java
Normal file
49
tcc/src/main/java/io/seata/rm/tcc/remoting/Protocols.java
Normal file
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Copyright 1999-2019 Seata.io Group.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package io.seata.rm.tcc.remoting;
|
||||
|
||||
/**
|
||||
* remoting protocols enum
|
||||
*
|
||||
* @author zhangsen
|
||||
*/
|
||||
public interface Protocols {
|
||||
|
||||
/**
|
||||
* sofa-rpc service
|
||||
*/
|
||||
short SOFA_RPC = 2;
|
||||
|
||||
/**
|
||||
* dubbo service
|
||||
*/
|
||||
short DUBBO = 3;
|
||||
|
||||
/**
|
||||
* restful service
|
||||
*/
|
||||
short RESTFUL = 4;
|
||||
|
||||
/**
|
||||
* local bean
|
||||
*/
|
||||
short IN_JVM = 5;
|
||||
|
||||
/**
|
||||
* hsf service
|
||||
*/
|
||||
short HSF = 8;
|
||||
}
|
||||
185
tcc/src/main/java/io/seata/rm/tcc/remoting/RemotingDesc.java
Normal file
185
tcc/src/main/java/io/seata/rm/tcc/remoting/RemotingDesc.java
Normal file
@@ -0,0 +1,185 @@
|
||||
/*
|
||||
* Copyright 1999-2019 Seata.io Group.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package io.seata.rm.tcc.remoting;
|
||||
|
||||
/**
|
||||
* remoting bean info
|
||||
*
|
||||
* @author zhangsen
|
||||
*/
|
||||
public class RemotingDesc {
|
||||
|
||||
/**
|
||||
* is referenc bean ?
|
||||
*/
|
||||
private boolean isReference = false;
|
||||
|
||||
/**
|
||||
* rpc target bean, the service bean has this property
|
||||
*/
|
||||
private Object targetBean;
|
||||
|
||||
/**
|
||||
* the tcc interface tyep
|
||||
*/
|
||||
private Class<?> interfaceClass;
|
||||
|
||||
/**
|
||||
* interface class name
|
||||
*/
|
||||
private String interfaceClassName;
|
||||
|
||||
/**
|
||||
* rpc uniqueId: hsf, dubbo's version, sofa-rpc's uniqueId
|
||||
*/
|
||||
private String uniqueId;
|
||||
|
||||
/**
|
||||
* dubbo/hsf 's group
|
||||
*/
|
||||
private String group;
|
||||
|
||||
/**
|
||||
* protocol: sofa-rpc, dubbo, injvm etc.
|
||||
*/
|
||||
private short protocol;
|
||||
|
||||
/**
|
||||
* Gets target bean.
|
||||
*
|
||||
* @return the target bean
|
||||
*/
|
||||
public Object getTargetBean() {
|
||||
return targetBean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets target bean.
|
||||
*
|
||||
* @param targetBean the target bean
|
||||
*/
|
||||
public void setTargetBean(Object targetBean) {
|
||||
this.targetBean = targetBean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets interface class.
|
||||
*
|
||||
* @return the interface class
|
||||
*/
|
||||
public Class<?> getInterfaceClass() {
|
||||
return interfaceClass;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets interface class.
|
||||
*
|
||||
* @param interfaceClass the interface class
|
||||
*/
|
||||
public void setInterfaceClass(Class<?> interfaceClass) {
|
||||
this.interfaceClass = interfaceClass;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets interface class name.
|
||||
*
|
||||
* @return the interface class name
|
||||
*/
|
||||
public String getInterfaceClassName() {
|
||||
return interfaceClassName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets interface class name.
|
||||
*
|
||||
* @param interfaceClassName the interface class name
|
||||
*/
|
||||
public void setInterfaceClassName(String interfaceClassName) {
|
||||
this.interfaceClassName = interfaceClassName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets unique id.
|
||||
*
|
||||
* @return the unique id
|
||||
*/
|
||||
public String getUniqueId() {
|
||||
return uniqueId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets unique id.
|
||||
*
|
||||
* @param uniqueId the unique id
|
||||
*/
|
||||
public void setUniqueId(String uniqueId) {
|
||||
this.uniqueId = uniqueId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets group.
|
||||
*
|
||||
* @return the group
|
||||
*/
|
||||
public String getGroup() {
|
||||
return group;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets group.
|
||||
*
|
||||
* @param group the group
|
||||
*/
|
||||
public void setGroup(String group) {
|
||||
this.group = group;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets protocol.
|
||||
*
|
||||
* @return the protocol
|
||||
*/
|
||||
public short getProtocol() {
|
||||
return protocol;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets protocol.
|
||||
*
|
||||
* @param protocol the protocol
|
||||
*/
|
||||
public void setProtocol(short protocol) {
|
||||
this.protocol = protocol;
|
||||
}
|
||||
|
||||
/**
|
||||
* Is reference boolean.
|
||||
*
|
||||
* @return the boolean
|
||||
*/
|
||||
public boolean isReference() {
|
||||
return isReference;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets reference.
|
||||
*
|
||||
* @param reference the reference
|
||||
*/
|
||||
public void setReference(boolean reference) {
|
||||
isReference = reference;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
/*
|
||||
* Copyright 1999-2019 Seata.io Group.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package io.seata.rm.tcc.remoting;
|
||||
|
||||
import io.seata.common.exception.FrameworkException;
|
||||
|
||||
/**
|
||||
* extract remoting bean info
|
||||
*
|
||||
* @author zhangsen
|
||||
*/
|
||||
public interface RemotingParser {
|
||||
|
||||
/**
|
||||
* if it is remoting bean ?
|
||||
*
|
||||
* @param bean the bean
|
||||
* @param beanName the bean name
|
||||
* @return boolean boolean
|
||||
* @throws FrameworkException the framework exception
|
||||
*/
|
||||
boolean isRemoting(Object bean, String beanName) throws FrameworkException;
|
||||
|
||||
/**
|
||||
* if it is reference bean ?
|
||||
*
|
||||
* @param bean the bean
|
||||
* @param beanName the bean name
|
||||
* @return boolean boolean
|
||||
* @throws FrameworkException the framework exception
|
||||
*/
|
||||
boolean isReference(Object bean, String beanName) throws FrameworkException;
|
||||
|
||||
/**
|
||||
* if it is service bean ?
|
||||
*
|
||||
* @param bean the bean
|
||||
* @param beanName the bean name
|
||||
* @return boolean boolean
|
||||
* @throws FrameworkException the framework exception
|
||||
*/
|
||||
boolean isService(Object bean, String beanName) throws FrameworkException;
|
||||
|
||||
/**
|
||||
* get the remoting bean info
|
||||
*
|
||||
* @param bean the bean
|
||||
* @param beanName the bean name
|
||||
* @return service desc
|
||||
* @throws FrameworkException the framework exception
|
||||
*/
|
||||
RemotingDesc getServiceDesc(Object bean, String beanName) throws FrameworkException;
|
||||
|
||||
/**
|
||||
* the remoting protocol
|
||||
*
|
||||
* @return protocol
|
||||
*/
|
||||
short getProtocol();
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Copyright 1999-2019 Seata.io Group.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package io.seata.rm.tcc.remoting.parser;
|
||||
|
||||
import io.seata.common.exception.FrameworkException;
|
||||
import io.seata.rm.tcc.remoting.RemotingParser;
|
||||
|
||||
/**
|
||||
* The type Abstracted remoting parser.
|
||||
*
|
||||
* @author zhangsen
|
||||
*/
|
||||
public abstract class AbstractedRemotingParser implements RemotingParser {
|
||||
|
||||
|
||||
@Override
|
||||
public boolean isRemoting(Object bean, String beanName) throws FrameworkException {
|
||||
return isReference(bean, beanName) || isService(bean, beanName);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,216 @@
|
||||
/*
|
||||
* Copyright 1999-2019 Seata.io Group.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package io.seata.rm.tcc.remoting.parser;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import io.seata.common.exception.FrameworkException;
|
||||
import io.seata.common.loader.EnhancedServiceLoader;
|
||||
import io.seata.common.util.CollectionUtils;
|
||||
import io.seata.common.util.ReflectionUtil;
|
||||
import io.seata.rm.DefaultResourceManager;
|
||||
import io.seata.rm.tcc.TCCResource;
|
||||
import io.seata.rm.tcc.api.BusinessActionContext;
|
||||
import io.seata.rm.tcc.api.TwoPhaseBusinessAction;
|
||||
import io.seata.rm.tcc.remoting.RemotingDesc;
|
||||
import io.seata.rm.tcc.remoting.RemotingParser;
|
||||
|
||||
/**
|
||||
* parsing remoting bean
|
||||
*
|
||||
* @author zhangsen
|
||||
*/
|
||||
public class DefaultRemotingParser {
|
||||
|
||||
/**
|
||||
* all remoting bean parser
|
||||
*/
|
||||
protected static List<RemotingParser> allRemotingParsers = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* all remoting beans beanName -> RemotingDesc
|
||||
*/
|
||||
protected static Map<String, RemotingDesc> remotingServiceMap = new ConcurrentHashMap<>();
|
||||
|
||||
private static class SingletonHolder {
|
||||
private static final DefaultRemotingParser INSTANCE = new DefaultRemotingParser();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get resource manager.
|
||||
*
|
||||
* @return the resource manager
|
||||
*/
|
||||
public static DefaultRemotingParser get() {
|
||||
return DefaultRemotingParser.SingletonHolder.INSTANCE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiates a new Default remoting parser.
|
||||
*/
|
||||
protected DefaultRemotingParser() {
|
||||
initRemotingParser();
|
||||
}
|
||||
|
||||
/**
|
||||
* init parsers
|
||||
*/
|
||||
protected void initRemotingParser() {
|
||||
//init all resource managers
|
||||
List<RemotingParser> remotingParsers = EnhancedServiceLoader.loadAll(RemotingParser.class);
|
||||
if (CollectionUtils.isNotEmpty(remotingParsers)) {
|
||||
allRemotingParsers.addAll(remotingParsers);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* is remoting bean ?
|
||||
*
|
||||
* @param bean the bean
|
||||
* @param beanName the bean name
|
||||
* @return boolean boolean
|
||||
*/
|
||||
public RemotingParser isRemoting(Object bean, String beanName) {
|
||||
for (RemotingParser remotingParser : allRemotingParsers) {
|
||||
if (remotingParser.isRemoting(bean, beanName)) {
|
||||
return remotingParser;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* is reference bean?
|
||||
*
|
||||
* @param bean the bean
|
||||
* @param beanName the bean name
|
||||
* @return boolean boolean
|
||||
*/
|
||||
public boolean isReference(Object bean, String beanName) {
|
||||
for (RemotingParser remotingParser : allRemotingParsers) {
|
||||
if (remotingParser.isReference(bean, beanName)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* is service bean ?
|
||||
*
|
||||
* @param bean the bean
|
||||
* @param beanName the bean name
|
||||
* @return boolean boolean
|
||||
*/
|
||||
public boolean isService(Object bean, String beanName) {
|
||||
for (RemotingParser remotingParser : allRemotingParsers) {
|
||||
if (remotingParser.isService(bean, beanName)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* get the remoting Service desc
|
||||
*
|
||||
* @param bean the bean
|
||||
* @param beanName the bean name
|
||||
* @return service desc
|
||||
*/
|
||||
public RemotingDesc getServiceDesc(Object bean, String beanName) {
|
||||
List<RemotingDesc> ret = new ArrayList<>();
|
||||
for (RemotingParser remotingParser : allRemotingParsers) {
|
||||
RemotingDesc s = remotingParser.getServiceDesc(bean, beanName);
|
||||
if (s != null) {
|
||||
ret.add(s);
|
||||
}
|
||||
}
|
||||
if (ret.size() == 1) {
|
||||
return ret.get(0);
|
||||
} else if (ret.size() > 1) {
|
||||
throw new FrameworkException(String.format("More than one RemotingParser for bean: %s", beanName));
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* parse the remoting bean info
|
||||
*
|
||||
* @param bean the bean
|
||||
* @param beanName the bean name
|
||||
* @param remotingParser the remoting parser
|
||||
* @return remoting desc
|
||||
*/
|
||||
public RemotingDesc parserRemotingServiceInfo(Object bean, String beanName, RemotingParser remotingParser) {
|
||||
RemotingDesc remotingBeanDesc = remotingParser.getServiceDesc(bean, beanName);
|
||||
if (remotingBeanDesc == null) {
|
||||
return null;
|
||||
}
|
||||
remotingServiceMap.put(beanName, remotingBeanDesc);
|
||||
|
||||
Class<?> interfaceClass = remotingBeanDesc.getInterfaceClass();
|
||||
Method[] methods = interfaceClass.getMethods();
|
||||
if (remotingParser.isService(bean, beanName)) {
|
||||
try {
|
||||
//service bean, registry resource
|
||||
Object targetBean = remotingBeanDesc.getTargetBean();
|
||||
for (Method m : methods) {
|
||||
TwoPhaseBusinessAction twoPhaseBusinessAction = m.getAnnotation(TwoPhaseBusinessAction.class);
|
||||
if (twoPhaseBusinessAction != null) {
|
||||
TCCResource tccResource = new TCCResource();
|
||||
tccResource.setActionName(twoPhaseBusinessAction.name());
|
||||
tccResource.setTargetBean(targetBean);
|
||||
tccResource.setPrepareMethod(m);
|
||||
tccResource.setCommitMethodName(twoPhaseBusinessAction.commitMethod());
|
||||
tccResource.setCommitMethod(ReflectionUtil
|
||||
.getMethod(interfaceClass, twoPhaseBusinessAction.commitMethod(),
|
||||
new Class[] {BusinessActionContext.class}));
|
||||
tccResource.setRollbackMethodName(twoPhaseBusinessAction.rollbackMethod());
|
||||
tccResource.setRollbackMethod(ReflectionUtil
|
||||
.getMethod(interfaceClass, twoPhaseBusinessAction.rollbackMethod(),
|
||||
new Class[] {BusinessActionContext.class}));
|
||||
//registry tcc resource
|
||||
DefaultResourceManager.get().registerResource(tccResource);
|
||||
}
|
||||
}
|
||||
} catch (Throwable t) {
|
||||
throw new FrameworkException(t, "parser remoting service error");
|
||||
}
|
||||
}
|
||||
if (remotingParser.isReference(bean, beanName)) {
|
||||
//reference bean, TCC proxy
|
||||
remotingBeanDesc.setReference(true);
|
||||
}
|
||||
return remotingBeanDesc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get remoting bean desc remoting desc.
|
||||
*
|
||||
* @param beanName the bean name
|
||||
* @return the remoting desc
|
||||
*/
|
||||
public RemotingDesc getRemotingBeanDesc(String beanName) {
|
||||
return remotingServiceMap.get(beanName);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
* Copyright 1999-2019 Seata.io Group.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package io.seata.rm.tcc.remoting.parser;
|
||||
|
||||
import io.seata.common.exception.FrameworkException;
|
||||
import io.seata.common.util.ReflectionUtil;
|
||||
import io.seata.rm.tcc.remoting.Protocols;
|
||||
import io.seata.rm.tcc.remoting.RemotingDesc;
|
||||
|
||||
/**
|
||||
* dubbo remoting bean parsing
|
||||
*
|
||||
* @author zhangsen
|
||||
*/
|
||||
public class DubboRemotingParser extends AbstractedRemotingParser {
|
||||
|
||||
@Override
|
||||
public boolean isReference(Object bean, String beanName) throws FrameworkException {
|
||||
Class<?> c = bean.getClass();
|
||||
return "com.alibaba.dubbo.config.spring.ReferenceBean".equals(c.getName())
|
||||
|| "org.apache.dubbo.config.spring.ReferenceBean".equals(c.getName());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isService(Object bean, String beanName) throws FrameworkException {
|
||||
Class<?> c = bean.getClass();
|
||||
return "com.alibaba.dubbo.config.spring.ServiceBean".equals(c.getName())
|
||||
|| "org.apache.dubbo.config.spring.ServiceBean".equals(c.getName());
|
||||
}
|
||||
|
||||
@Override
|
||||
public RemotingDesc getServiceDesc(Object bean, String beanName) throws FrameworkException {
|
||||
if (!this.isRemoting(bean, beanName)) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
RemotingDesc serviceBeanDesc = new RemotingDesc();
|
||||
Class<?> interfaceClass = (Class<?>)ReflectionUtil.invokeMethod(bean, "getInterfaceClass");
|
||||
String interfaceClassName = (String)ReflectionUtil.getFieldValue(bean, "interfaceName");
|
||||
String version = (String)ReflectionUtil.invokeMethod(bean, "getVersion");
|
||||
String group = (String)ReflectionUtil.invokeMethod(bean, "getGroup");
|
||||
serviceBeanDesc.setInterfaceClass(interfaceClass);
|
||||
serviceBeanDesc.setInterfaceClassName(interfaceClassName);
|
||||
serviceBeanDesc.setUniqueId(version);
|
||||
serviceBeanDesc.setGroup(group);
|
||||
serviceBeanDesc.setProtocol(Protocols.DUBBO);
|
||||
if (isService(bean, beanName)) {
|
||||
Object targetBean = ReflectionUtil.getFieldValue(bean, "ref");
|
||||
serviceBeanDesc.setTargetBean(targetBean);
|
||||
}
|
||||
return serviceBeanDesc;
|
||||
} catch (Throwable t) {
|
||||
throw new FrameworkException(t);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public short getProtocol() {
|
||||
return Protocols.DUBBO;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
* Copyright 1999-2019 Seata.io Group.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package io.seata.rm.tcc.remoting.parser;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
|
||||
import io.seata.common.util.ReflectionUtil;
|
||||
|
||||
/**
|
||||
* dubbo attribute analysis
|
||||
*
|
||||
* @author zhangsen
|
||||
*/
|
||||
public class DubboUtil {
|
||||
|
||||
private DubboUtil() {
|
||||
|
||||
}
|
||||
|
||||
private static final String ALIBABA_DUBBO_PROXY_NAME_PREFIX = "com.alibaba.dubbo.common.bytecode.proxy";
|
||||
private static final String APACHE_DUBBO_PROXY_NAME_PREFIX = "org.apache.dubbo.common.bytecode.proxy";
|
||||
|
||||
/**
|
||||
* get the interface class of the dubbo proxy which be generated by javaassist
|
||||
*
|
||||
* @param proxyBean the proxy bean
|
||||
* @return the assist interface
|
||||
* @throws NoSuchFieldException the no such field exception
|
||||
* @throws SecurityException the security exception
|
||||
* @throws IllegalArgumentException the illegal argument exception
|
||||
* @throws IllegalAccessException the illegal access exception
|
||||
* @throws NoSuchMethodException the no such method exception
|
||||
* @throws InvocationTargetException the invocation target exception
|
||||
*/
|
||||
public static Class<?> getAssistInterface(Object proxyBean)
|
||||
throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException,
|
||||
NoSuchMethodException, InvocationTargetException {
|
||||
if (proxyBean == null) {
|
||||
return null;
|
||||
}
|
||||
if (!isDubboProxyName(proxyBean.getClass().getName())) {
|
||||
return null;
|
||||
}
|
||||
Field handlerField = proxyBean.getClass().getDeclaredField("handler");
|
||||
handlerField.setAccessible(true);
|
||||
Object invokerInvocationHandler = handlerField.get(proxyBean);
|
||||
Field invokerField = invokerInvocationHandler.getClass().getDeclaredField("invoker");
|
||||
invokerField.setAccessible(true);
|
||||
Object invoker = invokerField.get(invokerInvocationHandler);
|
||||
Field failoverClusterInvokerField = invoker.getClass().getDeclaredField("invoker");
|
||||
failoverClusterInvokerField.setAccessible(true);
|
||||
Object failoverClusterInvoker = failoverClusterInvokerField.get(invoker);
|
||||
return (Class<?>)ReflectionUtil.invokeMethod(failoverClusterInvoker,
|
||||
"getInterface");
|
||||
}
|
||||
|
||||
public static boolean isDubboProxyName(String name) {
|
||||
return name.startsWith(ALIBABA_DUBBO_PROXY_NAME_PREFIX) || name.startsWith(APACHE_DUBBO_PROXY_NAME_PREFIX);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,119 @@
|
||||
/*
|
||||
* Copyright 1999-2019 Seata.io Group.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package io.seata.rm.tcc.remoting.parser;
|
||||
|
||||
import io.seata.common.exception.FrameworkException;
|
||||
import io.seata.common.util.ReflectionUtil;
|
||||
import io.seata.rm.tcc.remoting.Protocols;
|
||||
import io.seata.rm.tcc.remoting.RemotingDesc;
|
||||
|
||||
/**
|
||||
* HSF Remote Bean Parser
|
||||
*
|
||||
* @author ppf@jiumao.org
|
||||
*/
|
||||
public class HSFRemotingParser extends AbstractedRemotingParser {
|
||||
|
||||
/**
|
||||
* is HSF env
|
||||
*/
|
||||
private static volatile boolean isHsf;
|
||||
|
||||
static {
|
||||
// check HSF runtime env
|
||||
try {
|
||||
Class.forName("com.taobao.hsf.app.api.util.HSFApiConsumerBean");
|
||||
Class.forName("com.taobao.hsf.app.api.util.HSFApiProviderBean");
|
||||
|
||||
Class.forName("com.taobao.hsf.app.spring.util.HSFSpringConsumerBean");
|
||||
Class.forName("com.taobao.hsf.app.spring.util.HSFSpringProviderBean");
|
||||
|
||||
isHsf = true;
|
||||
} catch (ClassNotFoundException e) {
|
||||
isHsf = false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isRemoting(Object bean, String beanName) {
|
||||
return isHsf && (isReference(bean, beanName) || isService(bean, beanName));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isReference(Object bean, String beanName) {
|
||||
String beanClassName = bean.getClass().getName();
|
||||
return isHsf && ("com.taobao.hsf.app.spring.util.HSFSpringConsumerBean".equals(beanClassName) || "org.springframework.beans.factory.FactoryBean".equals(beanClassName));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isService(Object bean, String beanName) {
|
||||
String beanClassName = bean.getClass().getName();
|
||||
return isHsf && "com.taobao.hsf.app.spring.util.HSFSpringProviderBean".equals(beanClassName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public RemotingDesc getServiceDesc(Object bean, String beanName) throws FrameworkException {
|
||||
if (!this.isRemoting(bean, beanName)) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
if (isReference(bean, beanName)) {
|
||||
Object consumerBean = ReflectionUtil.getFieldValue(bean, "consumerBean");
|
||||
Object metadata = ReflectionUtil.invokeMethod(consumerBean, "getMetadata");
|
||||
|
||||
Class<?> interfaceClass = (Class<?>) ReflectionUtil.invokeMethod(metadata, "getIfClazz");
|
||||
String interfaceClassName = (String) ReflectionUtil.invokeMethod(metadata, "getInterfaceName");
|
||||
String uniqueId = (String) ReflectionUtil.invokeMethod(metadata, "getVersion");
|
||||
String group = (String) ReflectionUtil.invokeMethod(metadata, "getGroup");
|
||||
|
||||
RemotingDesc serviceBeanDesc = new RemotingDesc();
|
||||
serviceBeanDesc.setInterfaceClass(interfaceClass);
|
||||
serviceBeanDesc.setInterfaceClassName(interfaceClassName);
|
||||
serviceBeanDesc.setUniqueId(uniqueId);
|
||||
serviceBeanDesc.setGroup(group);
|
||||
serviceBeanDesc.setProtocol(Protocols.HSF);
|
||||
return serviceBeanDesc;
|
||||
} else if (isService(bean, beanName)) {
|
||||
Object consumerBean = ReflectionUtil.getFieldValue(bean, "providerBean");
|
||||
Object metadata = ReflectionUtil.invokeMethod(consumerBean, "getMetadata");
|
||||
|
||||
String interfaceClassName = (String) ReflectionUtil.invokeMethod(metadata, "getInterfaceName");
|
||||
Class<?> interfaceClass = (Class<?>) Class.forName(interfaceClassName);
|
||||
String uniqueId = (String) ReflectionUtil.invokeMethod(metadata, "getVersion");
|
||||
String group = (String) ReflectionUtil.invokeMethod(metadata, "getGroup");
|
||||
RemotingDesc serviceBeanDesc = new RemotingDesc();
|
||||
serviceBeanDesc.setInterfaceClass(interfaceClass);
|
||||
serviceBeanDesc.setInterfaceClassName(interfaceClassName);
|
||||
serviceBeanDesc.setUniqueId(uniqueId);
|
||||
serviceBeanDesc.setGroup(group);
|
||||
|
||||
Object targetBean = ReflectionUtil.getFieldValue(metadata, "target");
|
||||
serviceBeanDesc.setTargetBean(targetBean);
|
||||
serviceBeanDesc.setProtocol(Protocols.HSF);
|
||||
return serviceBeanDesc;
|
||||
}
|
||||
} catch (Throwable t) {
|
||||
throw new FrameworkException(t);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public short getProtocol() {
|
||||
return Protocols.HSF;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
* Copyright 1999-2019 Seata.io Group.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package io.seata.rm.tcc.remoting.parser;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
import io.seata.common.exception.FrameworkException;
|
||||
import io.seata.common.util.ReflectionUtil;
|
||||
import io.seata.rm.tcc.api.LocalTCC;
|
||||
import io.seata.rm.tcc.remoting.Protocols;
|
||||
import io.seata.rm.tcc.remoting.RemotingDesc;
|
||||
|
||||
/**
|
||||
* local tcc bean parsing
|
||||
*
|
||||
* @author zhangsen
|
||||
*/
|
||||
public class LocalTCCRemotingParser extends AbstractedRemotingParser {
|
||||
|
||||
@Override
|
||||
public boolean isReference(Object bean, String beanName) {
|
||||
Class<?> classType = bean.getClass();
|
||||
Set<Class<?>> interfaceClasses = ReflectionUtil.getInterfaces(classType);
|
||||
for (Class<?> interClass : interfaceClasses) {
|
||||
if (interClass.isAnnotationPresent(LocalTCC.class)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isService(Object bean, String beanName) {
|
||||
Class<?> classType = bean.getClass();
|
||||
Set<Class<?>> interfaceClasses = ReflectionUtil.getInterfaces(classType);
|
||||
for (Class<?> interClass : interfaceClasses) {
|
||||
if (interClass.isAnnotationPresent(LocalTCC.class)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RemotingDesc getServiceDesc(Object bean, String beanName) throws FrameworkException {
|
||||
if (!this.isRemoting(bean, beanName)) {
|
||||
return null;
|
||||
}
|
||||
RemotingDesc remotingDesc = new RemotingDesc();
|
||||
remotingDesc.setReference(true);
|
||||
remotingDesc.setProtocol(Protocols.IN_JVM);
|
||||
Class<?> classType = bean.getClass();
|
||||
Set<Class<?>> interfaceClasses = ReflectionUtil.getInterfaces(classType);
|
||||
for (Class<?> interClass : interfaceClasses) {
|
||||
if (interClass.isAnnotationPresent(LocalTCC.class)) {
|
||||
remotingDesc.setInterfaceClassName(interClass.getName());
|
||||
remotingDesc.setInterfaceClass(interClass);
|
||||
remotingDesc.setTargetBean(bean);
|
||||
return remotingDesc;
|
||||
}
|
||||
}
|
||||
throw new FrameworkException("Couldn't parser any Remoting info");
|
||||
}
|
||||
|
||||
@Override
|
||||
public short getProtocol() {
|
||||
return Protocols.IN_JVM;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
* Copyright 1999-2019 Seata.io Group.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package io.seata.rm.tcc.remoting.parser;
|
||||
|
||||
import io.seata.common.exception.FrameworkException;
|
||||
import io.seata.common.util.ReflectionUtil;
|
||||
import io.seata.rm.tcc.remoting.Protocols;
|
||||
import io.seata.rm.tcc.remoting.RemotingDesc;
|
||||
|
||||
/**
|
||||
* sofa-rpc remoting bean parsing
|
||||
*
|
||||
* @author zhangsen
|
||||
*/
|
||||
public class SofaRpcRemotingParser extends AbstractedRemotingParser {
|
||||
|
||||
|
||||
@Override
|
||||
public boolean isReference(Object bean, String beanName)
|
||||
throws FrameworkException {
|
||||
String beanClassName = bean.getClass().getName();
|
||||
return "com.alipay.sofa.runtime.spring.factory.ReferenceFactoryBean".equals(beanClassName);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean isService(Object bean, String beanName) throws FrameworkException {
|
||||
String beanClassName = bean.getClass().getName();
|
||||
return "com.alipay.sofa.runtime.spring.factory.ServiceFactoryBean".equals(beanClassName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public RemotingDesc getServiceDesc(Object bean, String beanName) throws FrameworkException {
|
||||
if (!this.isRemoting(bean, beanName)) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
RemotingDesc serviceBeanDesc = new RemotingDesc();
|
||||
Class<?> interfaceClass = (Class<?>)ReflectionUtil.invokeMethod(bean, "getInterfaceClass");
|
||||
String interfaceClassName = (String)ReflectionUtil.getFieldValue(bean, "interfaceType");
|
||||
String uniqueId = (String)ReflectionUtil.getFieldValue(bean, "uniqueId");
|
||||
serviceBeanDesc.setInterfaceClass(interfaceClass);
|
||||
serviceBeanDesc.setInterfaceClassName(interfaceClassName);
|
||||
serviceBeanDesc.setUniqueId(uniqueId);
|
||||
serviceBeanDesc.setProtocol(Protocols.SOFA_RPC);
|
||||
if (isService(bean, beanName)) {
|
||||
Object targetBean = ReflectionUtil.getFieldValue(bean, "ref");
|
||||
serviceBeanDesc.setTargetBean(targetBean);
|
||||
}
|
||||
return serviceBeanDesc;
|
||||
} catch (Throwable t) {
|
||||
throw new FrameworkException(t);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public short getProtocol() {
|
||||
return Protocols.SOFA_RPC;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
io.seata.rm.tcc.TCCResourceManager
|
||||
@@ -0,0 +1 @@
|
||||
io.seata.rm.tcc.RMHandlerTCC
|
||||
@@ -0,0 +1,4 @@
|
||||
io.seata.rm.tcc.remoting.parser.DubboRemotingParser
|
||||
io.seata.rm.tcc.remoting.parser.LocalTCCRemotingParser
|
||||
io.seata.rm.tcc.remoting.parser.SofaRpcRemotingParser
|
||||
io.seata.rm.tcc.remoting.parser.HSFRemotingParser
|
||||
63
tcc/src/test/java/io/seata/rm/tcc/TccAction.java
Normal file
63
tcc/src/test/java/io/seata/rm/tcc/TccAction.java
Normal file
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* Copyright 1999-2019 Seata.io Group.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package io.seata.rm.tcc;
|
||||
|
||||
import io.seata.rm.tcc.api.BusinessActionContext;
|
||||
import io.seata.rm.tcc.api.BusinessActionContextParameter;
|
||||
import io.seata.rm.tcc.api.LocalTCC;
|
||||
import io.seata.rm.tcc.api.TwoPhaseBusinessAction;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* The interface Tcc action.
|
||||
*
|
||||
* @author zhangsen
|
||||
*/
|
||||
@LocalTCC
|
||||
public interface TccAction {
|
||||
|
||||
/**
|
||||
* Prepare boolean.
|
||||
*
|
||||
* @param actionContext the action context
|
||||
* @param a the a
|
||||
* @param b the b
|
||||
* @param tccParam the tcc param
|
||||
* @return the boolean
|
||||
*/
|
||||
@TwoPhaseBusinessAction(name = "tccActionForTest" , commitMethod = "commit", rollbackMethod = "rollback")
|
||||
public boolean prepare(BusinessActionContext actionContext,
|
||||
@BusinessActionContextParameter(paramName = "a") int a,
|
||||
@BusinessActionContextParameter(paramName = "b", index = 0) List b,
|
||||
@BusinessActionContextParameter(isParamInProperty = true) TccParam tccParam);
|
||||
|
||||
/**
|
||||
* Commit boolean.
|
||||
*
|
||||
* @param actionContext the action context
|
||||
* @return the boolean
|
||||
*/
|
||||
public boolean commit(BusinessActionContext actionContext);
|
||||
|
||||
/**
|
||||
* Rollback boolean.
|
||||
*
|
||||
* @param actionContext the action context
|
||||
* @return the boolean
|
||||
*/
|
||||
public boolean rollback(BusinessActionContext actionContext);
|
||||
}
|
||||
46
tcc/src/test/java/io/seata/rm/tcc/TccActionImpl.java
Normal file
46
tcc/src/test/java/io/seata/rm/tcc/TccActionImpl.java
Normal file
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright 1999-2019 Seata.io Group.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package io.seata.rm.tcc;
|
||||
|
||||
import io.seata.rm.tcc.api.BusinessActionContext;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* The type Tcc action.
|
||||
*
|
||||
* @author zhangsen
|
||||
*/
|
||||
public class TccActionImpl implements TccAction {
|
||||
|
||||
@Override
|
||||
public boolean prepare(BusinessActionContext actionContext,
|
||||
int a,
|
||||
List b,
|
||||
TccParam TccParam ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean commit(BusinessActionContext actionContext) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean rollback(BusinessActionContext actionContext) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
66
tcc/src/test/java/io/seata/rm/tcc/TccParam.java
Normal file
66
tcc/src/test/java/io/seata/rm/tcc/TccParam.java
Normal file
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
* Copyright 1999-2019 Seata.io Group.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package io.seata.rm.tcc;
|
||||
|
||||
import io.seata.rm.tcc.api.BusinessActionContextParameter;
|
||||
|
||||
/**
|
||||
* The type Tcc param.
|
||||
*
|
||||
* @author zhangsen
|
||||
*/
|
||||
public class TccParam {
|
||||
|
||||
/**
|
||||
* The Num.
|
||||
*/
|
||||
protected int num;
|
||||
|
||||
/**
|
||||
* The Email.
|
||||
*/
|
||||
@BusinessActionContextParameter(paramName = "email")
|
||||
protected String email;
|
||||
|
||||
/**
|
||||
* Instantiates a new Tcc param.
|
||||
*
|
||||
* @param num the num
|
||||
* @param email the email
|
||||
*/
|
||||
public TccParam(int num, String email) {
|
||||
this.num = num;
|
||||
this.email = email;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets num.
|
||||
*
|
||||
* @return the num
|
||||
*/
|
||||
public int getNum() {
|
||||
return num;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets num.
|
||||
*
|
||||
* @param num the num
|
||||
*/
|
||||
public void setNum(int num) {
|
||||
this.num = num;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* Copyright 1999-2019 Seata.io Group.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package io.seata.rm.tcc.interceptor;
|
||||
|
||||
import io.seata.rm.tcc.TccAction;
|
||||
import io.seata.rm.tcc.TccParam;
|
||||
import io.seata.rm.tcc.api.BusinessActionContext;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* The type Action interceptor handler test.
|
||||
*
|
||||
* @author zhangsen
|
||||
*/
|
||||
public class ActionInterceptorHandlerTest {
|
||||
|
||||
/**
|
||||
* The Action interceptor handler.
|
||||
*/
|
||||
protected ActionInterceptorHandler actionInterceptorHandler = new ActionInterceptorHandler();
|
||||
|
||||
/**
|
||||
* Test business action context.
|
||||
*
|
||||
* @throws NoSuchMethodException the no such method exception
|
||||
*/
|
||||
@Test
|
||||
public void testBusinessActionContext() throws NoSuchMethodException {
|
||||
Method prepareMethod = TccAction.class.getDeclaredMethod("prepare",
|
||||
BusinessActionContext.class, int.class, List.class, TccParam.class);
|
||||
List<Object> list = new ArrayList<>();
|
||||
list.add("b");
|
||||
TccParam tccParam = new TccParam (1, "abc@ali.com");
|
||||
|
||||
Map<String, Object> paramContext = actionInterceptorHandler.fetchActionRequestContext(prepareMethod,
|
||||
new Object[]{null, 10, list, tccParam});
|
||||
System.out.println(paramContext);
|
||||
|
||||
Assertions.assertEquals(10, paramContext.get("a"));
|
||||
Assertions.assertEquals("b", paramContext.get("b"));
|
||||
Assertions.assertEquals("abc@ali.com", paramContext.get("email"));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
* Copyright 1999-2019 Seata.io Group.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package io.seata.rm.tcc.remoting.parser;
|
||||
|
||||
import io.seata.rm.tcc.TccAction;
|
||||
import io.seata.rm.tcc.TccActionImpl;
|
||||
import io.seata.rm.tcc.remoting.RemotingDesc;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
|
||||
/**
|
||||
* The type Local tcc remoting parser test.
|
||||
*
|
||||
* @author zhangsen
|
||||
*/
|
||||
public class LocalTCCRemotingParserTest {
|
||||
|
||||
/**
|
||||
* The Local tcc remoting parser.
|
||||
*/
|
||||
LocalTCCRemotingParser localTCCRemotingParser = new LocalTCCRemotingParser();
|
||||
|
||||
/**
|
||||
* Test service parser.
|
||||
*/
|
||||
@Test
|
||||
public void testServiceParser(){
|
||||
TccActionImpl tccAction = new TccActionImpl();
|
||||
|
||||
boolean result = localTCCRemotingParser.isService(tccAction, "a");
|
||||
Assertions.assertTrue(result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test reference parser.
|
||||
*/
|
||||
@Test
|
||||
public void testReferenceParser(){
|
||||
TccActionImpl tccAction = new TccActionImpl();
|
||||
|
||||
boolean result = localTCCRemotingParser.isReference(tccAction, "b");
|
||||
Assertions.assertTrue(result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test service desc.
|
||||
*/
|
||||
@Test
|
||||
public void testServiceDesc(){
|
||||
TccActionImpl tccAction = new TccActionImpl();
|
||||
|
||||
RemotingDesc remotingDesc = localTCCRemotingParser.getServiceDesc(tccAction, "c");
|
||||
Assertions.assertNotNull(remotingDesc);
|
||||
|
||||
Assertions.assertEquals(remotingDesc.getInterfaceClassName(), "io.seata.rm.tcc.TccAction");
|
||||
Assertions.assertEquals(remotingDesc.getInterfaceClass(), TccAction.class);
|
||||
Assertions.assertEquals(remotingDesc.getTargetBean(), tccAction);
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user