From c7fba75c4c282db5b3d243d819269c88434cd2e6 Mon Sep 17 00:00:00 2001 From: sunrui1225 Date: Tue, 31 Jan 2023 15:17:15 +0800 Subject: [PATCH] feature: optimize TCC structure, supporting API access (#5165) --- all/pom.xml | 8 +- build/pom.xml | 2 +- changes/en-us/2.0.0.md | 2 +- changes/zh-cn/2.0.0.md | 2 +- .../main/java/io/seata/common/Constants.java | 11 +- .../java/io/seata/common/DefaultValues.java | 19 +- .../java/io/seata/core/protocol/Version.java | 2 +- dependencies/pom.xml | 2 +- integration-tx-api/pom.xml | 64 +++++ .../api}/annotation/AspectTransactional.java | 2 +- .../BusinessActionContextParameterDesc.java | 73 ++++++ .../tx/api}/event/DegradeCheckEvent.java | 2 +- .../api/fence/DefaultCommonFenceHandler.java | 72 ++++++ .../tx/api/fence/FenceHandler.java | 37 +++ .../api/fence/config/CommonFenceConfig.java | 82 ++----- .../fence/constant/CommonFenceConstant.java | 10 +- .../fence/exception/CommonFenceException.java | 20 +- .../tx/api/fence/store/CommonFenceDO.java | 10 +- .../tx/api/fence/store/CommonFenceStore.java | 31 ++- .../store/db/CommonFenceStoreDataBaseDAO.java | 74 +++--- .../store/db/sql/CommonFenceStoreSqls.java | 14 +- .../api}/interceptor/ActionContextFilter.java | 2 +- .../api}/interceptor/ActionContextUtil.java | 14 +- .../interceptor/ActionInterceptorHandler.java | 88 ++++--- .../interceptor/DefaultInvocationWrapper.java | 65 +++++ .../tx/api/interceptor/InvocationWrapper.java | 37 +++ .../TwoPhaseBusinessActionParam.java | 78 ++++++ .../tx/api/interceptor/TxBeanParserUtils.java | 69 ++++++ .../AbstractProxyInvocationHandler.java | 37 +++ .../handler/DefaultInvocationHandler.java | 56 +++++ ...GlobalTransactionalInterceptorHandler.java | 172 +++++--------- .../handler/ProxyInvocationHandler.java | 32 +++ .../parser/DefaultInterfaceParser.java | 67 ++++++ .../parser/DefaultResourceRegisterParser.java | 61 +++++ .../parser/DefaultTargetClassParser.java | 76 ++++++ .../GlobalTransactionalInterceptorParser.java | 92 +++++++ .../interceptor/parser/InterfaceParser.java | 29 +++ .../parser/RegisterResourceParser.java | 26 ++ .../interceptor/parser/TargetClassParser.java | 28 +++ .../tx/api/json/DefaultJsonParser.java | 76 ++++++ .../integration/tx/api/json/JsonParser.java | 32 +++ .../tx/api}/remoting/Protocols.java | 2 +- .../tx/api}/remoting/RemotingDesc.java | 25 +- .../tx/api}/remoting/RemotingParser.java | 2 +- .../tx/api/remoting}/TwoPhaseResult.java | 4 +- .../parser/AbstractedRemotingParser.java | 6 +- .../parser/DefaultRemotingParser.java | 91 +------ .../remoting/parser/DubboRemotingParser.java | 14 +- .../remoting/parser/HSFRemotingParser.java | 12 +- .../parser/SofaRpcRemotingParser.java | 12 +- .../integration/tx/api/util}/DubboUtil.java | 2 +- .../integration/tx/api/util/JsonUtil.java | 34 +++ .../integration/tx/api/util/ProxyUtil.java | 63 +++++ .../rm/tcc/api/BusinessActionContext.java | 24 +- .../api/BusinessActionContextParameter.java | 14 +- .../rm/tcc/api/BusinessActionContextUtil.java | 58 ++++- .../java/io/seata/rm/tcc/api/ParamType.java | 0 .../seata/spring/annotation/GlobalLock.java | 13 - .../annotation/GlobalTransactional.java | 13 +- ....tx.api.interceptor.parser.InterfaceParser | 1 + ...integration.tx.api.remoting.RemotingParser | 3 + .../ActionInterceptorHandlerTest.java | 20 +- .../tx/api/interceptor/TestAction.java | 34 +++ .../tx/api/interceptor/TestParam.java | 66 ++++++ .../tx/api/interceptor/parser/Business.java | 29 +++ .../api/interceptor/parser/BusinessImpl.java | 34 +++ ...balTransactionalInterceptorParserTest.java | 44 ++++ .../ProxyUtilsGlobalTransactionalTest.java | 84 +++++++ pom.xml | 1 + .../SeataClientEnvironmentPostProcessor.java | 7 +- .../SeataTCCFenceAutoConfiguration.java | 10 +- ...itional-spring-configuration-metadata.json | 4 +- spring/pom.xml | 17 +- .../io/seata/rm/fence/SpringFenceConfig.java | 66 ++++++ .../io/seata/rm/fence/SpringFenceHandler.java | 144 +++++------ .../seata/spring/SpringTargetClassParser.java | 35 +++ .../AspectTransactionalInterceptor.java | 30 ++- .../annotation/GlobalTransactionScanner.java | 224 ++---------------- .../parser/RemotingFactoryBeanParser.java | 85 +++++++ .../spring/tcc/TccActionInterceptor.java | 181 -------------- .../spring/tcc/TccAnnotationProcessor.java | 9 +- .../seata/spring/util/SpringProxyUtils.java | 6 +- .../seata/spring/util/TCCBeanParserUtils.java | 210 ---------------- ...x.api.interceptor.parser.TargetClassParser | 1 + ...integration.tx.api.remoting.RemotingParser | 1 + .../GlobalTransactionScannerTest.java | 130 ---------- .../spring/annotation/MethodDescTest.java | 9 +- tcc/pom.xml | 7 +- .../io/seata/rm/tcc/TCCResourceManager.java | 68 ++---- .../java/io/seata/rm/tcc/api/LocalTCC.java | 4 +- .../rm/tcc/api/TwoPhaseBusinessAction.java | 4 +- .../TccActionInterceptorHandler.java | 132 +++++++++++ .../parser/TccActionInterceptorParser.java | 97 ++++++++ .../io/seata/rm/tcc/json/FastJsonParser.java | 36 +++ .../parser/LocalTCCRemotingParser.java | 14 +- .../parser/TccRegisterResourceParser.java | 120 ++++++++++ ....tx.api.interceptor.parser.InterfaceParser | 1 + ....interceptor.parser.RegisterResourceParser | 1 + ...o.seata.integration.tx.api.json.JsonParser | 1 + ...integration.tx.api.remoting.RemotingParser | 1 + .../io.seata.rm.tcc.remoting.RemotingParser | 4 - .../java/io/seata/rm/tcc/NormalTccAction.java | 64 +++++ .../io/seata/rm/tcc/NormalTccActionImpl.java | 48 ++++ .../test/java/io/seata/rm/tcc/TccAction.java | 2 +- .../rm/tcc/interceptor/ProxyUtilsTccTest.java | 130 ++++++++++ .../TccActionInterceptorParserTest.java | 43 ++++ .../parser/LocalTCCRemotingParserTest.java | 2 +- .../TccRegisterResourceParserTest.java} | 21 +- .../io/seata/rm/tcc/spring}/Business.java | 2 +- .../io/seata/rm/tcc/spring}/BusinessImpl.java | 3 +- .../seata/rm/tcc/spring}/BusinessProxy.java | 2 +- .../spring/GlobalTransactionScannerTest.java | 130 ++++++++++ .../rm/tcc}/spring/tcc/LocalTccAction.java | 2 +- .../tcc}/spring/tcc/LocalTccActionImpl.java | 2 +- .../seata/rm/tcc}/spring/tcc/TccAction.java | 2 +- .../rm/tcc}/spring/tcc/TccActionImpl.java | 2 +- .../tm/api/DefaultFailureHandlerImpl.java | 10 +- .../io/seata/tm/api/FailureHandlerHolder.java | 36 +++ .../tm/api/DefaultFailureHandlerImplTest.java | 4 +- 119 files changed, 3149 insertions(+), 1396 deletions(-) create mode 100644 integration-tx-api/pom.xml rename {spring/src/main/java/io/seata/spring => integration-tx-api/src/main/java/io/seata/integration/tx/api}/annotation/AspectTransactional.java (99%) create mode 100644 integration-tx-api/src/main/java/io/seata/integration/tx/api/annotation/BusinessActionContextParameterDesc.java rename {spring/src/main/java/io/seata/spring => integration-tx-api/src/main/java/io/seata/integration/tx/api}/event/DegradeCheckEvent.java (95%) create mode 100644 integration-tx-api/src/main/java/io/seata/integration/tx/api/fence/DefaultCommonFenceHandler.java create mode 100644 integration-tx-api/src/main/java/io/seata/integration/tx/api/fence/FenceHandler.java rename tcc/src/main/java/io/seata/rm/tcc/config/TCCFenceConfig.java => integration-tx-api/src/main/java/io/seata/integration/tx/api/fence/config/CommonFenceConfig.java (58%) rename tcc/src/main/java/io/seata/rm/tcc/constant/TCCFenceConstant.java => integration-tx-api/src/main/java/io/seata/integration/tx/api/fence/constant/CommonFenceConstant.java (88%) rename tcc/src/main/java/io/seata/rm/tcc/exception/TCCFenceException.java => integration-tx-api/src/main/java/io/seata/integration/tx/api/fence/exception/CommonFenceException.java (64%) rename tcc/src/main/java/io/seata/rm/tcc/store/TCCFenceDO.java => integration-tx-api/src/main/java/io/seata/integration/tx/api/fence/store/CommonFenceDO.java (93%) rename tcc/src/main/java/io/seata/rm/tcc/store/TCCFenceStore.java => integration-tx-api/src/main/java/io/seata/integration/tx/api/fence/store/CommonFenceStore.java (72%) rename tcc/src/main/java/io/seata/rm/tcc/store/db/TCCFenceStoreDataBaseDAO.java => integration-tx-api/src/main/java/io/seata/integration/tx/api/fence/store/db/CommonFenceStoreDataBaseDAO.java (65%) rename tcc/src/main/java/io/seata/rm/tcc/store/db/sql/TCCFenceStoreSqls.java => integration-tx-api/src/main/java/io/seata/integration/tx/api/fence/store/db/sql/CommonFenceStoreSqls.java (88%) rename {tcc/src/main/java/io/seata/rm/tcc => integration-tx-api/src/main/java/io/seata/integration/tx/api}/interceptor/ActionContextFilter.java (95%) rename {tcc/src/main/java/io/seata/rm/tcc => integration-tx-api/src/main/java/io/seata/integration/tx/api}/interceptor/ActionContextUtil.java (97%) rename {tcc/src/main/java/io/seata/rm/tcc => integration-tx-api/src/main/java/io/seata/integration/tx/api}/interceptor/ActionInterceptorHandler.java (75%) create mode 100644 integration-tx-api/src/main/java/io/seata/integration/tx/api/interceptor/DefaultInvocationWrapper.java create mode 100644 integration-tx-api/src/main/java/io/seata/integration/tx/api/interceptor/InvocationWrapper.java create mode 100644 integration-tx-api/src/main/java/io/seata/integration/tx/api/interceptor/TwoPhaseBusinessActionParam.java create mode 100644 integration-tx-api/src/main/java/io/seata/integration/tx/api/interceptor/TxBeanParserUtils.java create mode 100644 integration-tx-api/src/main/java/io/seata/integration/tx/api/interceptor/handler/AbstractProxyInvocationHandler.java create mode 100644 integration-tx-api/src/main/java/io/seata/integration/tx/api/interceptor/handler/DefaultInvocationHandler.java rename spring/src/main/java/io/seata/spring/annotation/GlobalTransactionalInterceptor.java => integration-tx-api/src/main/java/io/seata/integration/tx/api/interceptor/handler/GlobalTransactionalInterceptorHandler.java (73%) create mode 100644 integration-tx-api/src/main/java/io/seata/integration/tx/api/interceptor/handler/ProxyInvocationHandler.java create mode 100644 integration-tx-api/src/main/java/io/seata/integration/tx/api/interceptor/parser/DefaultInterfaceParser.java create mode 100644 integration-tx-api/src/main/java/io/seata/integration/tx/api/interceptor/parser/DefaultResourceRegisterParser.java create mode 100644 integration-tx-api/src/main/java/io/seata/integration/tx/api/interceptor/parser/DefaultTargetClassParser.java create mode 100644 integration-tx-api/src/main/java/io/seata/integration/tx/api/interceptor/parser/GlobalTransactionalInterceptorParser.java create mode 100644 integration-tx-api/src/main/java/io/seata/integration/tx/api/interceptor/parser/InterfaceParser.java create mode 100644 integration-tx-api/src/main/java/io/seata/integration/tx/api/interceptor/parser/RegisterResourceParser.java create mode 100644 integration-tx-api/src/main/java/io/seata/integration/tx/api/interceptor/parser/TargetClassParser.java create mode 100644 integration-tx-api/src/main/java/io/seata/integration/tx/api/json/DefaultJsonParser.java create mode 100644 integration-tx-api/src/main/java/io/seata/integration/tx/api/json/JsonParser.java rename {tcc/src/main/java/io/seata/rm/tcc => integration-tx-api/src/main/java/io/seata/integration/tx/api}/remoting/Protocols.java (95%) rename {tcc/src/main/java/io/seata/rm/tcc => integration-tx-api/src/main/java/io/seata/integration/tx/api}/remoting/RemotingDesc.java (89%) rename {tcc/src/main/java/io/seata/rm/tcc => integration-tx-api/src/main/java/io/seata/integration/tx/api}/remoting/RemotingParser.java (97%) rename {tcc/src/main/java/io/seata/rm/tcc => integration-tx-api/src/main/java/io/seata/integration/tx/api/remoting}/TwoPhaseResult.java (97%) rename {tcc/src/main/java/io/seata/rm/tcc => integration-tx-api/src/main/java/io/seata/integration/tx/api}/remoting/parser/AbstractedRemotingParser.java (89%) rename {tcc/src/main/java/io/seata/rm/tcc => integration-tx-api/src/main/java/io/seata/integration/tx/api}/remoting/parser/DefaultRemotingParser.java (52%) rename {tcc/src/main/java/io/seata/rm/tcc => integration-tx-api/src/main/java/io/seata/integration/tx/api}/remoting/parser/DubboRemotingParser.java (84%) rename {tcc/src/main/java/io/seata/rm/tcc => integration-tx-api/src/main/java/io/seata/integration/tx/api}/remoting/parser/HSFRemotingParser.java (90%) rename {tcc/src/main/java/io/seata/rm/tcc => integration-tx-api/src/main/java/io/seata/integration/tx/api}/remoting/parser/SofaRpcRemotingParser.java (87%) rename {tcc/src/main/java/io/seata/rm/tcc/remoting/parser => integration-tx-api/src/main/java/io/seata/integration/tx/api/util}/DubboUtil.java (98%) create mode 100644 integration-tx-api/src/main/java/io/seata/integration/tx/api/util/JsonUtil.java create mode 100644 integration-tx-api/src/main/java/io/seata/integration/tx/api/util/ProxyUtil.java rename {tcc => integration-tx-api}/src/main/java/io/seata/rm/tcc/api/BusinessActionContext.java (92%) rename {tcc => integration-tx-api}/src/main/java/io/seata/rm/tcc/api/BusinessActionContextParameter.java (85%) rename {tcc => integration-tx-api}/src/main/java/io/seata/rm/tcc/api/BusinessActionContextUtil.java (66%) rename {tcc => integration-tx-api}/src/main/java/io/seata/rm/tcc/api/ParamType.java (100%) rename {spring => integration-tx-api}/src/main/java/io/seata/spring/annotation/GlobalLock.java (83%) rename {spring => integration-tx-api}/src/main/java/io/seata/spring/annotation/GlobalTransactional.java (95%) create mode 100644 integration-tx-api/src/main/resources/META-INF/services/io.seata.integration.tx.api.interceptor.parser.InterfaceParser create mode 100644 integration-tx-api/src/main/resources/META-INF/services/io.seata.integration.tx.api.remoting.RemotingParser rename {tcc/src/test/java/io/seata/rm/tcc => integration-tx-api/src/test/java/io/seata/integration/tx/api}/interceptor/ActionInterceptorHandlerTest.java (83%) create mode 100644 integration-tx-api/src/test/java/io/seata/integration/tx/api/interceptor/TestAction.java create mode 100644 integration-tx-api/src/test/java/io/seata/integration/tx/api/interceptor/TestParam.java create mode 100644 integration-tx-api/src/test/java/io/seata/integration/tx/api/interceptor/parser/Business.java create mode 100644 integration-tx-api/src/test/java/io/seata/integration/tx/api/interceptor/parser/BusinessImpl.java create mode 100644 integration-tx-api/src/test/java/io/seata/integration/tx/api/interceptor/parser/GlobalTransactionalInterceptorParserTest.java create mode 100644 integration-tx-api/src/test/java/io/seata/integration/tx/api/interceptor/parser/ProxyUtilsGlobalTransactionalTest.java create mode 100644 spring/src/main/java/io/seata/rm/fence/SpringFenceConfig.java rename tcc/src/main/java/io/seata/rm/tcc/TCCFenceHandler.java => spring/src/main/java/io/seata/rm/fence/SpringFenceHandler.java (70%) create mode 100644 spring/src/main/java/io/seata/spring/SpringTargetClassParser.java create mode 100644 spring/src/main/java/io/seata/spring/remoting/parser/RemotingFactoryBeanParser.java delete mode 100644 spring/src/main/java/io/seata/spring/tcc/TccActionInterceptor.java delete mode 100644 spring/src/main/java/io/seata/spring/util/TCCBeanParserUtils.java create mode 100644 spring/src/main/resources/META-INF/services/io.seata.integration.tx.api.interceptor.parser.TargetClassParser create mode 100644 spring/src/main/resources/META-INF/services/io.seata.integration.tx.api.remoting.RemotingParser delete mode 100644 spring/src/test/java/io/seata/spring/annotation/GlobalTransactionScannerTest.java create mode 100644 tcc/src/main/java/io/seata/rm/tcc/interceptor/TccActionInterceptorHandler.java create mode 100644 tcc/src/main/java/io/seata/rm/tcc/interceptor/parser/TccActionInterceptorParser.java create mode 100644 tcc/src/main/java/io/seata/rm/tcc/json/FastJsonParser.java create mode 100644 tcc/src/main/java/io/seata/rm/tcc/resource/parser/TccRegisterResourceParser.java create mode 100644 tcc/src/main/resources/META-INF/services/io.seata.integration.tx.api.interceptor.parser.InterfaceParser create mode 100644 tcc/src/main/resources/META-INF/services/io.seata.integration.tx.api.interceptor.parser.RegisterResourceParser create mode 100644 tcc/src/main/resources/META-INF/services/io.seata.integration.tx.api.json.JsonParser create mode 100644 tcc/src/main/resources/META-INF/services/io.seata.integration.tx.api.remoting.RemotingParser delete mode 100644 tcc/src/main/resources/META-INF/services/io.seata.rm.tcc.remoting.RemotingParser create mode 100644 tcc/src/test/java/io/seata/rm/tcc/NormalTccAction.java create mode 100644 tcc/src/test/java/io/seata/rm/tcc/NormalTccActionImpl.java create mode 100644 tcc/src/test/java/io/seata/rm/tcc/interceptor/ProxyUtilsTccTest.java create mode 100644 tcc/src/test/java/io/seata/rm/tcc/interceptor/parser/TccActionInterceptorParserTest.java rename tcc/src/test/java/io/seata/rm/tcc/{remoting/parser/DefaultRemotingParserTest.java => resource/parser/TccRegisterResourceParserTest.java} (67%) rename {spring/src/test/java/io/seata/spring/annotation => tcc/src/test/java/io/seata/rm/tcc/spring}/Business.java (95%) rename {spring/src/test/java/io/seata/spring/annotation => tcc/src/test/java/io/seata/rm/tcc/spring}/BusinessImpl.java (92%) rename {spring/src/test/java/io/seata/spring/annotation => tcc/src/test/java/io/seata/rm/tcc/spring}/BusinessProxy.java (97%) create mode 100644 tcc/src/test/java/io/seata/rm/tcc/spring/GlobalTransactionScannerTest.java rename {spring/src/test/java/io/seata => tcc/src/test/java/io/seata/rm/tcc}/spring/tcc/LocalTccAction.java (95%) rename {spring/src/test/java/io/seata => tcc/src/test/java/io/seata/rm/tcc}/spring/tcc/LocalTccActionImpl.java (95%) rename {spring/src/test/java/io/seata => tcc/src/test/java/io/seata/rm/tcc}/spring/tcc/TccAction.java (97%) rename {spring/src/test/java/io/seata => tcc/src/test/java/io/seata/rm/tcc}/spring/tcc/TccActionImpl.java (96%) create mode 100644 tm/src/main/java/io/seata/tm/api/FailureHandlerHolder.java diff --git a/all/pom.xml b/all/pom.xml index e0215683f9e..c9b499ebef2 100644 --- a/all/pom.xml +++ b/all/pom.xml @@ -421,10 +421,6 @@ edas-sdk provided - - net.bytebuddy - byte-buddy - aopalliance aopalliance @@ -600,6 +596,10 @@ lz4-java provided + + net.bytebuddy + byte-buddy + diff --git a/build/pom.xml b/build/pom.xml index 0e0e2514849..c354e83fb69 100644 --- a/build/pom.xml +++ b/build/pom.xml @@ -63,7 +63,7 @@ - 1.7.0-SNAPSHOT + 2.0.0-SNAPSHOT 1.8 diff --git a/changes/en-us/2.0.0.md b/changes/en-us/2.0.0.md index 4593f9bae53..d37630d02f6 100644 --- a/changes/en-us/2.0.0.md +++ b/changes/en-us/2.0.0.md @@ -15,7 +15,7 @@ Seata is an easy-to-use, high-performance, open source distributed transaction s The version is updated as follows: ### feature: - - [[#1234](https://github.com/seata/seata/pull/1234)] Please delete the sample later + - [[#5165](https://github.com/seata/seata/pull/5165)] optimize TCC structure, supporting API access. add integration layer module(seata-integration-tx-api) for transaction process definition and proxy enhancement. ### bugfix: - [[#1234](https://github.com/seata/seata/pull/1234)] Please delete the sample later diff --git a/changes/zh-cn/2.0.0.md b/changes/zh-cn/2.0.0.md index 591880a4071..f1a64501825 100644 --- a/changes/zh-cn/2.0.0.md +++ b/changes/zh-cn/2.0.0.md @@ -15,7 +15,7 @@ Seata 是一款开源的分布式事务解决方案,提供高性能和简单 此版本更新如下: ### feature: - - [[#1234](https://github.com/seata/seata/pull/1234)] 样例,后续请删除 + - [[#5165](https://github.com/seata/seata/pull/5165)] TCC结构拆分,支持API方式接入。增加集成层模块(seata-integration-tx-api),对事务流程定义以及代理部分增强。 ### bugfix: - [[#1234](https://github.com/seata/seata/pull/1234)] 样例,后续请删除 diff --git a/common/src/main/java/io/seata/common/Constants.java b/common/src/main/java/io/seata/common/Constants.java index 08bb175eadd..f5a4507dcc7 100644 --- a/common/src/main/java/io/seata/common/Constants.java +++ b/common/src/main/java/io/seata/common/Constants.java @@ -69,7 +69,7 @@ public interface Constants { /** * Use TCC fence */ - String USE_TCC_FENCE = "useTCCFence"; + String USE_COMMON_FENCE = "useTCCFence"; /** * phase one method name @@ -94,7 +94,7 @@ public interface Constants { /** * branch context */ - String TCC_ACTION_CONTEXT = "actionContext"; + String TX_ACTION_CONTEXT = "actionContext"; /** * default charset name @@ -165,5 +165,10 @@ public interface Constants { * The constant REGISTRY_TYPE_SPLIT_CHAR. */ String REGISTRY_TYPE_SPLIT_CHAR = ","; + + /** + * phase two compensation method name + */ + String COMPENSATION_METHOD = "sys::compensation"; -} +} \ No newline at end of file diff --git a/common/src/main/java/io/seata/common/DefaultValues.java b/common/src/main/java/io/seata/common/DefaultValues.java index bf12a6c3668..7851b3ac676 100644 --- a/common/src/main/java/io/seata/common/DefaultValues.java +++ b/common/src/main/java/io/seata/common/DefaultValues.java @@ -147,23 +147,28 @@ public interface DefaultValues { */ int TCC_ACTION_INTERCEPTOR_ORDER = Integer.MIN_VALUE + 1000; + /** + * the constant SAGA_ACTION_INTERCEPTOR_ORDER + */ + int SAGA_ACTION_INTERCEPTOR_ORDER = Integer.MIN_VALUE + 1000; + /** * the constant DEFAULT_DISTRIBUTED_LOCK_EXPIRE */ int DEFAULT_DISTRIBUTED_LOCK_EXPIRE = 10000; /** - * the constant DEFAULT_TCC_FENCE_CLEAN_PERIOD + * the constant DEFAULT_COMMON_FENCE_CLEAN_PERIOD */ - int DEFAULT_TCC_FENCE_CLEAN_PERIOD = 1; + int DEFAULT_COMMON_FENCE_CLEAN_PERIOD = 1; /** - * the constant DEFAULT_TCC_FENCE_LOG_TABLE_NAME + * the constant DEFAULT_COMMON_FENCE_LOG_TABLE_NAME */ - String DEFAULT_TCC_FENCE_LOG_TABLE_NAME = "tcc_fence_log"; + String DEFAULT_COMMON_FENCE_LOG_TABLE_NAME = "tcc_fence_log"; /** - * the constant TCC_FENCE_BEAN_NAME + * the constant COMMON_FENCE_BEAN_NAME */ - String TCC_FENCE_BEAN_NAME = "tccFenceConfig"; + String COMMON_FENCE_BEAN_NAME = "tccFenceConfig"; /** * the constant DEFAULT_RPC_RM_REQUEST_TIMEOUT @@ -286,4 +291,4 @@ public interface DefaultValues { * Default druid location in classpath */ String DRUID_LOCATION = "lib/sqlparser/druid.jar"; -} +} \ No newline at end of file diff --git a/core/src/main/java/io/seata/core/protocol/Version.java b/core/src/main/java/io/seata/core/protocol/Version.java index 56e96f47c9e..0ed4bbd15c7 100644 --- a/core/src/main/java/io/seata/core/protocol/Version.java +++ b/core/src/main/java/io/seata/core/protocol/Version.java @@ -36,7 +36,7 @@ public class Version { /** * The constant CURRENT. */ - private static final String CURRENT = "1.7.0-SNAPSHOT"; + private static final String CURRENT = "2.0.0-SNAPSHOT"; private static final String VERSION_0_7_1 = "0.7.1"; private static final String VERSION_1_5_0 = "1.5.0"; private static final int MAX_VERSION_DOT = 3; diff --git a/dependencies/pom.xml b/dependencies/pom.xml index e53f3475364..9c11cce0f28 100644 --- a/dependencies/pom.xml +++ b/dependencies/pom.xml @@ -35,7 +35,6 @@ 1.0.2 2.5.9 1.8.3 - 1.12.13 2.6.10 5.5.3 1.2.83 @@ -114,6 +113,7 @@ 2.23.4 3.12.2 9.4.38.v20210224 + 1.12.17 diff --git a/integration-tx-api/pom.xml b/integration-tx-api/pom.xml new file mode 100644 index 00000000000..12cc16bc2d6 --- /dev/null +++ b/integration-tx-api/pom.xml @@ -0,0 +1,64 @@ + + + + + io.seata + seata-parent + ${revision} + + 4.0.0 + seata-integration-tx-api + seata-integration-tx-api ${project.version} + jar + + + + + ${project.groupId} + seata-tm + ${project.version} + + + ${project.groupId} + seata-rm-datasource + ${project.version} + + + ${project.groupId} + seata-rm + ${project.version} + + + ${project.groupId} + seata-serializer-all + ${project.version} + + + ${project.groupId} + seata-common + ${project.version} + + + net.bytebuddy + byte-buddy + + + + + diff --git a/spring/src/main/java/io/seata/spring/annotation/AspectTransactional.java b/integration-tx-api/src/main/java/io/seata/integration/tx/api/annotation/AspectTransactional.java similarity index 99% rename from spring/src/main/java/io/seata/spring/annotation/AspectTransactional.java rename to integration-tx-api/src/main/java/io/seata/integration/tx/api/annotation/AspectTransactional.java index b603bd8c8e2..b51b0f2c147 100644 --- a/spring/src/main/java/io/seata/spring/annotation/AspectTransactional.java +++ b/integration-tx-api/src/main/java/io/seata/integration/tx/api/annotation/AspectTransactional.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.seata.spring.annotation; +package io.seata.integration.tx.api.annotation; import io.seata.common.DefaultValues; import io.seata.common.LockStrategyMode; diff --git a/integration-tx-api/src/main/java/io/seata/integration/tx/api/annotation/BusinessActionContextParameterDesc.java b/integration-tx-api/src/main/java/io/seata/integration/tx/api/annotation/BusinessActionContextParameterDesc.java new file mode 100644 index 00000000000..693c3807a03 --- /dev/null +++ b/integration-tx-api/src/main/java/io/seata/integration/tx/api/annotation/BusinessActionContextParameterDesc.java @@ -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.integration.tx.api.annotation; + +import io.seata.common.util.StringUtils; +import io.seata.rm.tcc.api.BusinessActionContextParameter; + +import java.lang.annotation.Annotation; + +/** + * @author leezongjie + * @date 2022/12/23 + */ +public class BusinessActionContextParameterDesc { + private String paramName; + private int index; + private boolean isParamInProperty; + + private BusinessActionContextParameterDesc() { + } + + public static BusinessActionContextParameterDesc createFromBusinessActionContextParameter(Annotation annotation) { + if (annotation == null) { + return null; + } + BusinessActionContextParameterDesc businessActionContextParameterDesc = null; + if (annotation instanceof BusinessActionContextParameter) { + businessActionContextParameterDesc = new BusinessActionContextParameterDesc(); + BusinessActionContextParameter businessActionContextParameter = (BusinessActionContextParameter) annotation; + businessActionContextParameterDesc.setIndex(businessActionContextParameter.index()); + businessActionContextParameterDesc.setParamInProperty(businessActionContextParameter.isParamInProperty()); + businessActionContextParameterDesc.setParamName(StringUtils.isNotBlank(businessActionContextParameter.paramName()) ? businessActionContextParameter.paramName() : businessActionContextParameter.value()); + } + return businessActionContextParameterDesc; + } + + public String getParamName() { + return paramName; + } + + public void setParamName(String paramName) { + this.paramName = paramName; + } + + public int getIndex() { + return index; + } + + public void setIndex(int index) { + this.index = index; + } + + public boolean isParamInProperty() { + return isParamInProperty; + } + + public void setParamInProperty(boolean paramInProperty) { + isParamInProperty = paramInProperty; + } +} diff --git a/spring/src/main/java/io/seata/spring/event/DegradeCheckEvent.java b/integration-tx-api/src/main/java/io/seata/integration/tx/api/event/DegradeCheckEvent.java similarity index 95% rename from spring/src/main/java/io/seata/spring/event/DegradeCheckEvent.java rename to integration-tx-api/src/main/java/io/seata/integration/tx/api/event/DegradeCheckEvent.java index 2d1c6d3ad01..17eaba69196 100644 --- a/spring/src/main/java/io/seata/spring/event/DegradeCheckEvent.java +++ b/integration-tx-api/src/main/java/io/seata/integration/tx/api/event/DegradeCheckEvent.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.seata.spring.event; +package io.seata.integration.tx.api.event; import io.seata.core.event.Event; diff --git a/integration-tx-api/src/main/java/io/seata/integration/tx/api/fence/DefaultCommonFenceHandler.java b/integration-tx-api/src/main/java/io/seata/integration/tx/api/fence/DefaultCommonFenceHandler.java new file mode 100644 index 00000000000..e853b46c9bb --- /dev/null +++ b/integration-tx-api/src/main/java/io/seata/integration/tx/api/fence/DefaultCommonFenceHandler.java @@ -0,0 +1,72 @@ +/* + * 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.integration.tx.api.fence; + +import io.seata.common.executor.Callback; + +import java.lang.reflect.Method; +import java.util.Date; + +/** + * @author leezongjie + * @date 2022/12/17 + */ +public class DefaultCommonFenceHandler implements FenceHandler { + + private FenceHandler fenceHandler; + + private static class SingletonHolder { + private static final DefaultCommonFenceHandler INSTANCE = new DefaultCommonFenceHandler(); + } + + public static DefaultCommonFenceHandler get() { + return DefaultCommonFenceHandler.SingletonHolder.INSTANCE; + } + + public void setFenceHandler(FenceHandler fenceHandler) { + this.fenceHandler = fenceHandler; + } + + private void check() { + if (fenceHandler == null) { + throw new RuntimeException("fenceHandler is null, need to set a fenceHandler implement"); + } + } + + @Override + public Object prepareFence(String xid, Long branchId, String actionName, Callback targetCallback) { + check(); + return fenceHandler.prepareFence(xid, branchId, actionName, targetCallback); + } + + @Override + public boolean commitFence(Method commitMethod, Object targetTCCBean, String xid, Long branchId, Object[] args) { + check(); + return fenceHandler.commitFence(commitMethod, targetTCCBean, xid, branchId, args); + } + + @Override + public boolean rollbackFence(Method rollbackMethod, Object targetTCCBean, String xid, Long branchId, Object[] args, String actionName) { + check(); + return fenceHandler.rollbackFence(rollbackMethod, targetTCCBean, xid, branchId, args, actionName); + } + + @Override + public int deleteFenceByDate(Date datetime) { + check(); + return fenceHandler.deleteFenceByDate(datetime); + } +} diff --git a/integration-tx-api/src/main/java/io/seata/integration/tx/api/fence/FenceHandler.java b/integration-tx-api/src/main/java/io/seata/integration/tx/api/fence/FenceHandler.java new file mode 100644 index 00000000000..70a54773b9a --- /dev/null +++ b/integration-tx-api/src/main/java/io/seata/integration/tx/api/fence/FenceHandler.java @@ -0,0 +1,37 @@ +/* + * 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.integration.tx.api.fence; + +import io.seata.common.executor.Callback; + +import java.lang.reflect.Method; +import java.util.Date; + +/** + * @author leezongjie + * @date 2022/12/17 + */ +public interface FenceHandler { + + Object prepareFence(String xid, Long branchId, String actionName, Callback targetCallback); + + boolean commitFence(Method commitMethod, Object targetTCCBean, String xid, Long branchId, Object[] args); + + boolean rollbackFence(Method rollbackMethod, Object targetTCCBean, String xid, Long branchId, Object[] args, String actionName); + + int deleteFenceByDate(Date datetime); + +} diff --git a/tcc/src/main/java/io/seata/rm/tcc/config/TCCFenceConfig.java b/integration-tx-api/src/main/java/io/seata/integration/tx/api/fence/config/CommonFenceConfig.java similarity index 58% rename from tcc/src/main/java/io/seata/rm/tcc/config/TCCFenceConfig.java rename to integration-tx-api/src/main/java/io/seata/integration/tx/api/fence/config/CommonFenceConfig.java index fffc08ab993..1023fa16916 100644 --- a/tcc/src/main/java/io/seata/rm/tcc/config/TCCFenceConfig.java +++ b/integration-tx-api/src/main/java/io/seata/integration/tx/api/fence/config/CommonFenceConfig.java @@ -13,89 +13,61 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.seata.rm.tcc.config; +package io.seata.integration.tx.api.fence.config; import java.time.Duration; import java.util.Date; +import java.util.Random; import java.util.concurrent.ScheduledThreadPoolExecutor; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; -import javax.sql.DataSource; - import io.seata.common.DefaultValues; -import io.seata.common.exception.FrameworkErrorCode; import io.seata.common.thread.NamedThreadFactory; import io.seata.core.rpc.Disposable; -import io.seata.rm.tcc.TCCFenceHandler; -import io.seata.rm.tcc.exception.TCCFenceException; -import io.seata.rm.tcc.store.db.TCCFenceStoreDataBaseDAO; +import io.seata.integration.tx.api.fence.DefaultCommonFenceHandler; +import io.seata.integration.tx.api.fence.store.db.CommonFenceStoreDataBaseDAO; import org.apache.commons.lang.time.DateUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.InitializingBean; -import org.springframework.transaction.PlatformTransactionManager; -import org.springframework.transaction.support.TransactionTemplate; /** - * TCC Fence Config + * Common Fence Config * * @author kaka2code */ -public class TCCFenceConfig implements InitializingBean, Disposable { +public class CommonFenceConfig implements Disposable { - private static final Logger LOGGER = LoggerFactory.getLogger(TCCFenceConfig.class); + private static final Logger LOGGER = LoggerFactory.getLogger(CommonFenceConfig.class); private final AtomicBoolean initialized = new AtomicBoolean(false); /** - * TCC fence clean period max value. maximum interval is 68 years + * Common fence clean period max value. maximum interval is 68 years */ private static final Duration MAX_PERIOD = Duration.ofSeconds(Integer.MAX_VALUE); /** - * TCC fence clean period. only duration type format are supported + * Common fence clean period. only duration type format are supported */ - private Duration cleanPeriod = Duration.ofDays(DefaultValues.DEFAULT_TCC_FENCE_CLEAN_PERIOD); + private Duration cleanPeriod = Duration.ofDays(DefaultValues.DEFAULT_COMMON_FENCE_CLEAN_PERIOD); /** - * TCC fence log table name + * Common fence log table name */ - private String logTableName = DefaultValues.DEFAULT_TCC_FENCE_LOG_TABLE_NAME; + private String logTableName = DefaultValues.DEFAULT_COMMON_FENCE_LOG_TABLE_NAME; - /** - * TCC fence datasource - */ - private final DataSource dataSource; - - /** - * TCC fence transactionManager - */ - private final PlatformTransactionManager transactionManager; /** - * TCC fence clean scheduled thread pool + * Common fence clean scheduled thread pool */ private final ScheduledThreadPoolExecutor tccFenceClean = new ScheduledThreadPoolExecutor(1, new NamedThreadFactory("tccFenceClean", 1)); - public TCCFenceConfig(final DataSource dataSource, final PlatformTransactionManager transactionManager) { - this.dataSource = dataSource; - this.transactionManager = transactionManager; - } - public AtomicBoolean getInitialized() { return initialized; } - public DataSource getDataSource() { - return dataSource; - } - - public PlatformTransactionManager getTransactionManager() { - return transactionManager; - } - public void setCleanPeriod(Duration cleanPeriod) { this.cleanPeriod = cleanPeriod; } @@ -115,8 +87,8 @@ public void initCleanTask() { tccFenceClean.scheduleWithFixedDelay(() -> { Date timeBefore = null; try { - timeBefore = DateUtils.addSeconds(new Date(), -(int)periodSeconds); - int deletedRowCount = TCCFenceHandler.deleteFenceByDate(timeBefore); + timeBefore = DateUtils.addSeconds(new Date(), -(int) periodSeconds); + int deletedRowCount = DefaultCommonFenceHandler.get().deleteFenceByDate(timeBefore); if (deletedRowCount > 0) { LOGGER.info("TCC fence clean task executed success, timeBefore: {}, deleted row count: {}", timeBefore, deletedRowCount); @@ -124,7 +96,8 @@ public void initCleanTask() { } catch (RuntimeException e) { LOGGER.error("Delete tcc fence log failed, timeBefore: {}", timeBefore, e); } - }, 0, periodSeconds, TimeUnit.SECONDS); + }, new Random(System.currentTimeMillis()).nextInt(60), periodSeconds, TimeUnit.SECONDS); + LOGGER.info("TCC fence log clean task start success, cleanPeriod:{}", cleanPeriod); } catch (NumberFormatException e) { LOGGER.error("TCC fence log clean period only supports positive integers, clean task start failed"); @@ -137,24 +110,13 @@ public void destroy() { tccFenceClean.shutdown(); } - @Override - public void afterPropertiesSet() { + public void init() { // set log table name if (logTableName != null) { - TCCFenceStoreDataBaseDAO.getInstance().setLogTableName(logTableName); - } - if (dataSource != null) { - // set dataSource - TCCFenceHandler.setDataSource(dataSource); - } else { - throw new TCCFenceException(FrameworkErrorCode.DateSourceNeedInjected); - } - if (transactionManager != null) { - // set transaction template - TCCFenceHandler.setTransactionTemplate(new TransactionTemplate(transactionManager)); - } else { - throw new TCCFenceException(FrameworkErrorCode.TransactionManagerNeedInjected); + CommonFenceStoreDataBaseDAO.getInstance().setLogTableName(logTableName); } + initCleanTask(); } -} + +} diff --git a/tcc/src/main/java/io/seata/rm/tcc/constant/TCCFenceConstant.java b/integration-tx-api/src/main/java/io/seata/integration/tx/api/fence/constant/CommonFenceConstant.java similarity index 88% rename from tcc/src/main/java/io/seata/rm/tcc/constant/TCCFenceConstant.java rename to integration-tx-api/src/main/java/io/seata/integration/tx/api/fence/constant/CommonFenceConstant.java index 7c9a2b416cf..a7cb0277eeb 100644 --- a/tcc/src/main/java/io/seata/rm/tcc/constant/TCCFenceConstant.java +++ b/integration-tx-api/src/main/java/io/seata/integration/tx/api/fence/constant/CommonFenceConstant.java @@ -13,16 +13,16 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.seata.rm.tcc.constant; +package io.seata.integration.tx.api.fence.constant; /** - * TCC Fence Constant + * Common Fence Constant * * @author kaka2code */ -public class TCCFenceConstant { +public class CommonFenceConstant { - private TCCFenceConstant() { + private CommonFenceConstant() { throw new IllegalStateException("Utility class"); } @@ -45,4 +45,4 @@ private TCCFenceConstant() { * Suspended status. */ public static final int STATUS_SUSPENDED = 4; -} +} \ No newline at end of file diff --git a/tcc/src/main/java/io/seata/rm/tcc/exception/TCCFenceException.java b/integration-tx-api/src/main/java/io/seata/integration/tx/api/fence/exception/CommonFenceException.java similarity index 64% rename from tcc/src/main/java/io/seata/rm/tcc/exception/TCCFenceException.java rename to integration-tx-api/src/main/java/io/seata/integration/tx/api/fence/exception/CommonFenceException.java index 716da66ed1f..c77fa639381 100644 --- a/tcc/src/main/java/io/seata/rm/tcc/exception/TCCFenceException.java +++ b/integration-tx-api/src/main/java/io/seata/integration/tx/api/fence/exception/CommonFenceException.java @@ -13,40 +13,40 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.seata.rm.tcc.exception; +package io.seata.integration.tx.api.fence.exception; import io.seata.common.exception.FrameworkErrorCode; import io.seata.common.exception.FrameworkException; /** - * TCC Fence Exception + * Common Fence Exception * * @author kaka2code */ -public class TCCFenceException extends FrameworkException { +public class CommonFenceException extends FrameworkException { - public TCCFenceException(FrameworkErrorCode err) { + public CommonFenceException(FrameworkErrorCode err) { super(err); } - public TCCFenceException(String msg) { + public CommonFenceException(String msg) { super(msg); } - public TCCFenceException(String msg, FrameworkErrorCode errCode) { + public CommonFenceException(String msg, FrameworkErrorCode errCode) { super(msg, errCode); } - public TCCFenceException(Throwable cause, String msg, FrameworkErrorCode errCode) { + public CommonFenceException(Throwable cause, String msg, FrameworkErrorCode errCode) { super(cause, msg, errCode); } - public TCCFenceException(Throwable th) { + public CommonFenceException(Throwable th) { super(th); } - public TCCFenceException(Throwable th, String msg) { + public CommonFenceException(Throwable th, String msg) { super(th, msg); } -} +} \ No newline at end of file diff --git a/tcc/src/main/java/io/seata/rm/tcc/store/TCCFenceDO.java b/integration-tx-api/src/main/java/io/seata/integration/tx/api/fence/store/CommonFenceDO.java similarity index 93% rename from tcc/src/main/java/io/seata/rm/tcc/store/TCCFenceDO.java rename to integration-tx-api/src/main/java/io/seata/integration/tx/api/fence/store/CommonFenceDO.java index 6878a9a0c74..caa33cd9b1f 100644 --- a/tcc/src/main/java/io/seata/rm/tcc/store/TCCFenceDO.java +++ b/integration-tx-api/src/main/java/io/seata/integration/tx/api/fence/store/CommonFenceDO.java @@ -13,16 +13,16 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.seata.rm.tcc.store; +package io.seata.integration.tx.api.fence.store; import java.util.Date; /** - * TCC Fence Domain + * Common Fence Domain * * @author kaka2code */ -public class TCCFenceDO { +public class CommonFenceDO { /** * the global transaction id @@ -40,7 +40,7 @@ public class TCCFenceDO { private String actionName; /** - * the tcc fence status + * the common fence status * tried: 1; committed: 2; rollbacked: 3; suspended: 4 */ private Integer status; @@ -103,4 +103,4 @@ public void setGmtModified(Date gmtModified) { this.gmtModified = gmtModified; } -} +} \ No newline at end of file diff --git a/tcc/src/main/java/io/seata/rm/tcc/store/TCCFenceStore.java b/integration-tx-api/src/main/java/io/seata/integration/tx/api/fence/store/CommonFenceStore.java similarity index 72% rename from tcc/src/main/java/io/seata/rm/tcc/store/TCCFenceStore.java rename to integration-tx-api/src/main/java/io/seata/integration/tx/api/fence/store/CommonFenceStore.java index 81b64202203..4dea7452bf3 100644 --- a/tcc/src/main/java/io/seata/rm/tcc/store/TCCFenceStore.java +++ b/integration-tx-api/src/main/java/io/seata/integration/tx/api/fence/store/CommonFenceStore.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.seata.rm.tcc.store; +package io.seata.integration.tx.api.fence.store; import java.sql.Connection; import java.util.Date; @@ -21,20 +21,20 @@ import java.util.Set; /** - * The TCC Fence Store + * The common Fence Store * * @author kaka2code */ -public interface TCCFenceStore { +public interface CommonFenceStore { /** - * Query tcc fence do. + * Query common fence do. * @param conn the connection * @param xid the global transaction id * @param branchId the branch transaction id - * @return the tcc fence do + * @return the common fence do */ - TCCFenceDO queryTCCFenceDO(Connection conn, String xid, Long branchId); + CommonFenceDO queryCommonFenceDO(Connection conn, String xid, Long branchId); /** * Query xid. @@ -46,15 +46,15 @@ public interface TCCFenceStore { Set queryEndStatusXidsByDate(Connection conn, Date datetime, int limit); /** - * Insert tcc fence do boolean. + * Insert common fence do boolean. * @param conn the connection - * @param tccFenceDO the tcc fence do + * @param commonFenceDO the common fence do * @return the boolean */ - boolean insertTCCFenceDO(Connection conn, TCCFenceDO tccFenceDO); + boolean insertCommonFenceDO(Connection conn, CommonFenceDO commonFenceDO); /** - * Update tcc fence do boolean. + * Update common fence do boolean. * @param conn the connection * @param xid the global transaction id * @param branchId the branch transaction id @@ -62,16 +62,16 @@ public interface TCCFenceStore { * @param oldStatus the old status * @return the boolean */ - boolean updateTCCFenceDO(Connection conn, String xid, Long branchId, int newStatus, int oldStatus); + boolean updateCommonFenceDO(Connection conn, String xid, Long branchId, int newStatus, int oldStatus); /** - * Delete tcc fence do boolean. + * Delete common fence do boolean. * @param conn the connection * @param xid the global transaction id * @param branchId the branch transaction id * @return the boolean */ - boolean deleteTCCFenceDO(Connection conn, String xid, Long branchId); + boolean deleteCommonFenceDO(Connection conn, String xid, Long branchId); /** * Delete tcc fence do boolean. @@ -82,13 +82,12 @@ public interface TCCFenceStore { int deleteTCCFenceDO(Connection conn, List xids); /** - * Delete tcc fence by datetime. + * Delete common fence by datetime. * @param conn the connection * @param datetime datetime * @return the deleted row count */ - int deleteTCCFenceDOByDate(Connection conn, Date datetime); - + int deleteCommonFenceDOByDate(Connection conn, Date datetime); /** * Set LogTable Name diff --git a/tcc/src/main/java/io/seata/rm/tcc/store/db/TCCFenceStoreDataBaseDAO.java b/integration-tx-api/src/main/java/io/seata/integration/tx/api/fence/store/db/CommonFenceStoreDataBaseDAO.java similarity index 65% rename from tcc/src/main/java/io/seata/rm/tcc/store/db/TCCFenceStoreDataBaseDAO.java rename to integration-tx-api/src/main/java/io/seata/integration/tx/api/fence/store/db/CommonFenceStoreDataBaseDAO.java index bee3f09b7c3..589f1e0a323 100644 --- a/tcc/src/main/java/io/seata/rm/tcc/store/db/TCCFenceStoreDataBaseDAO.java +++ b/integration-tx-api/src/main/java/io/seata/integration/tx/api/fence/store/db/CommonFenceStoreDataBaseDAO.java @@ -13,17 +13,17 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.seata.rm.tcc.store.db; +package io.seata.integration.tx.api.fence.store.db; import io.seata.common.DefaultValues; import io.seata.common.exception.DataAccessException; import io.seata.common.exception.FrameworkErrorCode; import io.seata.common.exception.StoreException; import io.seata.common.util.IOUtil; -import io.seata.rm.tcc.exception.TCCFenceException; -import io.seata.rm.tcc.store.TCCFenceDO; -import io.seata.rm.tcc.store.TCCFenceStore; -import io.seata.rm.tcc.store.db.sql.TCCFenceStoreSqls; +import io.seata.integration.tx.api.fence.store.db.sql.CommonFenceStoreSqls; +import io.seata.integration.tx.api.fence.exception.CommonFenceException; +import io.seata.integration.tx.api.fence.store.CommonFenceDO; +import io.seata.integration.tx.api.fence.store.CommonFenceStore; import java.sql.Connection; import java.sql.PreparedStatement; @@ -37,26 +37,26 @@ import java.util.Set; /** - * The type TCC Fence store data base dao + * The type Common Fence store data base dao * * @author kaka2code */ -public class TCCFenceStoreDataBaseDAO implements TCCFenceStore { +public class CommonFenceStoreDataBaseDAO implements CommonFenceStore { /** - * TCC fence log table name + * Common fence log table name */ - private String logTableName = DefaultValues.DEFAULT_TCC_FENCE_LOG_TABLE_NAME; + private String logTableName = DefaultValues.DEFAULT_COMMON_FENCE_LOG_TABLE_NAME; - private static volatile TCCFenceStoreDataBaseDAO instance = null; + private static volatile CommonFenceStoreDataBaseDAO instance = null; - private TCCFenceStoreDataBaseDAO() {} + private CommonFenceStoreDataBaseDAO() {} - public static TCCFenceStore getInstance() { + public static CommonFenceStore getInstance() { if (instance == null) { - synchronized (TCCFenceStore.class) { + synchronized (CommonFenceStore.class) { if (instance == null) { - instance = new TCCFenceStoreDataBaseDAO(); + instance = new CommonFenceStoreDataBaseDAO(); } } } @@ -64,21 +64,21 @@ public static TCCFenceStore getInstance() { } @Override - public TCCFenceDO queryTCCFenceDO(Connection conn, String xid, Long branchId) { + public CommonFenceDO queryCommonFenceDO(Connection conn, String xid, Long branchId) { PreparedStatement ps = null; ResultSet rs = null; try { - String sql = TCCFenceStoreSqls.getQuerySQLByBranchIdAndXid(logTableName); + String sql = CommonFenceStoreSqls.getQuerySQLByBranchIdAndXid(logTableName); ps = conn.prepareStatement(sql); ps.setString(1, xid); ps.setLong(2, branchId); rs = ps.executeQuery(); if (rs.next()) { - TCCFenceDO tccFenceDO = new TCCFenceDO(); - tccFenceDO.setXid(rs.getString("xid")); - tccFenceDO.setBranchId(rs.getLong("branch_id")); - tccFenceDO.setStatus(rs.getInt("status")); - return tccFenceDO; + CommonFenceDO commonFenceDO = new CommonFenceDO(); + commonFenceDO.setXid(rs.getString("xid")); + commonFenceDO.setBranchId(rs.getLong("branch_id")); + commonFenceDO.setStatus(rs.getInt("status")); + return commonFenceDO; } else { return null; } @@ -94,7 +94,7 @@ public Set queryEndStatusXidsByDate(Connection conn, Date datetime, int PreparedStatement ps = null; ResultSet rs = null; try { - String sql = TCCFenceStoreSqls.getQueryEndStatusSQLByDate(logTableName); + String sql = CommonFenceStoreSqls.getQueryEndStatusSQLByDate(logTableName); ps = conn.prepareStatement(sql); ps.setTimestamp(1, new Timestamp(datetime.getTime())); ps.setInt(2, limit); @@ -112,22 +112,22 @@ public Set queryEndStatusXidsByDate(Connection conn, Date datetime, int } @Override - public boolean insertTCCFenceDO(Connection conn, TCCFenceDO tccFenceDO) { + public boolean insertCommonFenceDO(Connection conn, CommonFenceDO commonFenceDO) { PreparedStatement ps = null; try { Timestamp now = new Timestamp(System.currentTimeMillis()); - String sql = TCCFenceStoreSqls.getInsertLocalTCCLogSQL(logTableName); + String sql = CommonFenceStoreSqls.getInsertLocalTCCLogSQL(logTableName); ps = conn.prepareStatement(sql); - ps.setString(1, tccFenceDO.getXid()); - ps.setLong(2, tccFenceDO.getBranchId()); - ps.setString(3, tccFenceDO.getActionName()); - ps.setInt(4, tccFenceDO.getStatus()); + ps.setString(1, commonFenceDO.getXid()); + ps.setLong(2, commonFenceDO.getBranchId()); + ps.setString(3, commonFenceDO.getActionName()); + ps.setInt(4, commonFenceDO.getStatus()); ps.setTimestamp(5, now); ps.setTimestamp(6, now); return ps.executeUpdate() > 0; } catch (SQLIntegrityConstraintViolationException e) { - throw new TCCFenceException(String.format("Insert tcc fence record duplicate key exception. xid= %s, branchId= %s", tccFenceDO.getXid(), tccFenceDO.getBranchId()), + throw new CommonFenceException(String.format("Insert tcc fence record duplicate key exception. xid= %s, branchId= %s", commonFenceDO.getXid(), commonFenceDO.getBranchId()), FrameworkErrorCode.DuplicateKeyException); } catch (SQLException e) { throw new StoreException(e); @@ -137,10 +137,10 @@ public boolean insertTCCFenceDO(Connection conn, TCCFenceDO tccFenceDO) { } @Override - public boolean updateTCCFenceDO(Connection conn, String xid, Long branchId, int newStatus, int oldStatus) { + public boolean updateCommonFenceDO(Connection conn, String xid, Long branchId, int newStatus, int oldStatus) { PreparedStatement ps = null; try { - String sql = TCCFenceStoreSqls.getUpdateStatusSQLByBranchIdAndXid(logTableName); + String sql = CommonFenceStoreSqls.getUpdateStatusSQLByBranchIdAndXid(logTableName); ps = conn.prepareStatement(sql); ps.setInt(1, newStatus); // gmt_modified @@ -157,10 +157,10 @@ public boolean updateTCCFenceDO(Connection conn, String xid, Long branchId, int } @Override - public boolean deleteTCCFenceDO(Connection conn, String xid, Long branchId) { + public boolean deleteCommonFenceDO(Connection conn, String xid, Long branchId) { PreparedStatement ps = null; try { - String sql = TCCFenceStoreSqls.getDeleteSQLByBranchIdAndXid(logTableName); + String sql = CommonFenceStoreSqls.getDeleteSQLByBranchIdAndXid(logTableName); ps = conn.prepareStatement(sql); ps.setString(1, xid); ps.setLong(2, branchId); @@ -178,7 +178,7 @@ public int deleteTCCFenceDO(Connection conn, List xids) { PreparedStatement ps = null; try { String paramsPlaceHolder = org.apache.commons.lang.StringUtils.repeat("?", ",", xids.size()); - String sql = TCCFenceStoreSqls.getDeleteSQLByXids(logTableName, paramsPlaceHolder); + String sql = CommonFenceStoreSqls.getDeleteSQLByXids(logTableName, paramsPlaceHolder); ps = conn.prepareStatement(sql); for (int i = 0; i < xids.size(); i++) { ps.setString(i + 1, xids.get(i)); @@ -192,10 +192,10 @@ public int deleteTCCFenceDO(Connection conn, List xids) { } @Override - public int deleteTCCFenceDOByDate(Connection conn, Date datetime) { + public int deleteCommonFenceDOByDate(Connection conn, Date datetime) { PreparedStatement ps = null; try { - String sql = TCCFenceStoreSqls.getDeleteSQLByDateAndStatus(logTableName); + String sql = CommonFenceStoreSqls.getDeleteSQLByDateAndStatus(logTableName); ps = conn.prepareStatement(sql); ps.setTimestamp(1, new Timestamp(datetime.getTime())); return ps.executeUpdate(); @@ -210,4 +210,4 @@ public int deleteTCCFenceDOByDate(Connection conn, Date datetime) { public void setLogTableName(String logTableName) { this.logTableName = logTableName; } -} +} \ No newline at end of file diff --git a/tcc/src/main/java/io/seata/rm/tcc/store/db/sql/TCCFenceStoreSqls.java b/integration-tx-api/src/main/java/io/seata/integration/tx/api/fence/store/db/sql/CommonFenceStoreSqls.java similarity index 88% rename from tcc/src/main/java/io/seata/rm/tcc/store/db/sql/TCCFenceStoreSqls.java rename to integration-tx-api/src/main/java/io/seata/integration/tx/api/fence/store/db/sql/CommonFenceStoreSqls.java index 9ea6c28c0ca..842202a2366 100644 --- a/tcc/src/main/java/io/seata/rm/tcc/store/db/sql/TCCFenceStoreSqls.java +++ b/integration-tx-api/src/main/java/io/seata/integration/tx/api/fence/store/db/sql/CommonFenceStoreSqls.java @@ -13,18 +13,18 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.seata.rm.tcc.store.db.sql; +package io.seata.integration.tx.api.fence.store.db.sql; -import io.seata.rm.tcc.constant.TCCFenceConstant; +import io.seata.integration.tx.api.fence.constant.CommonFenceConstant; /** * TCC Fence Store Sqls * * @author kaka2code */ -public class TCCFenceStoreSqls { +public class CommonFenceStoreSqls { - private TCCFenceStoreSqls() { + private CommonFenceStoreSqls() { throw new IllegalStateException("Utility class"); } @@ -59,7 +59,7 @@ private TCCFenceStoreSqls() { protected static final String QUERY_END_STATUS_BY_DATE = "select xid, branch_id, status, gmt_create, gmt_modified " + "from " + LOCAL_TCC_LOG_PLACEHOLD + " where gmt_modified < ? " - + " and status in (" + TCCFenceConstant.STATUS_COMMITTED + " , " + TCCFenceConstant.STATUS_ROLLBACKED + " , " + TCCFenceConstant.STATUS_SUSPENDED + ")" + + " and status in (" + CommonFenceConstant.STATUS_COMMITTED + " , " + CommonFenceConstant.STATUS_ROLLBACKED + " , " + CommonFenceConstant.STATUS_SUSPENDED + ")" + " limit ?"; /** @@ -84,7 +84,7 @@ private TCCFenceStoreSqls() { */ protected static final String DELETE_BY_DATE_AND_STATUS = "delete from " + LOCAL_TCC_LOG_PLACEHOLD + " where gmt_modified < ? " - + " and status in (" + TCCFenceConstant.STATUS_COMMITTED + " , " + TCCFenceConstant.STATUS_ROLLBACKED + " , " + TCCFenceConstant.STATUS_SUSPENDED + ")"; + + " and status in (" + CommonFenceConstant.STATUS_COMMITTED + " , " + CommonFenceConstant.STATUS_ROLLBACKED + " , " + CommonFenceConstant.STATUS_SUSPENDED + ")"; public static String getInsertLocalTCCLogSQL(String localTccTable) { return INSERT_LOCAL_TCC_LOG.replace(LOCAL_TCC_LOG_PLACEHOLD, localTccTable); @@ -115,4 +115,4 @@ public static String getDeleteSQLByDateAndStatus(String localTccTable) { return DELETE_BY_DATE_AND_STATUS.replace(LOCAL_TCC_LOG_PLACEHOLD, localTccTable); } -} +} \ No newline at end of file diff --git a/tcc/src/main/java/io/seata/rm/tcc/interceptor/ActionContextFilter.java b/integration-tx-api/src/main/java/io/seata/integration/tx/api/interceptor/ActionContextFilter.java similarity index 95% rename from tcc/src/main/java/io/seata/rm/tcc/interceptor/ActionContextFilter.java rename to integration-tx-api/src/main/java/io/seata/integration/tx/api/interceptor/ActionContextFilter.java index 32a6f492161..60f64fffc8c 100644 --- a/tcc/src/main/java/io/seata/rm/tcc/interceptor/ActionContextFilter.java +++ b/integration-tx-api/src/main/java/io/seata/integration/tx/api/interceptor/ActionContextFilter.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.seata.rm.tcc.interceptor; +package io.seata.integration.tx.api.interceptor; import io.seata.rm.tcc.api.BusinessActionContextParameter; diff --git a/tcc/src/main/java/io/seata/rm/tcc/interceptor/ActionContextUtil.java b/integration-tx-api/src/main/java/io/seata/integration/tx/api/interceptor/ActionContextUtil.java similarity index 97% rename from tcc/src/main/java/io/seata/rm/tcc/interceptor/ActionContextUtil.java rename to integration-tx-api/src/main/java/io/seata/integration/tx/api/interceptor/ActionContextUtil.java index 8b50b52bce9..cf0ca183cbb 100644 --- a/tcc/src/main/java/io/seata/rm/tcc/interceptor/ActionContextUtil.java +++ b/integration-tx-api/src/main/java/io/seata/integration/tx/api/interceptor/ActionContextUtil.java @@ -13,21 +13,21 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.seata.rm.tcc.interceptor; +package io.seata.integration.tx.api.interceptor; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; import java.lang.reflect.Field; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; -import com.alibaba.fastjson.JSON; import io.seata.common.exception.FrameworkException; import io.seata.common.util.CollectionUtils; import io.seata.common.util.ReflectionUtil; import io.seata.common.util.StringUtils; +import io.seata.integration.tx.api.util.JsonUtil; import io.seata.rm.tcc.api.BusinessActionContext; import io.seata.rm.tcc.api.BusinessActionContextParameter; import io.seata.rm.tcc.api.ParamType; @@ -247,7 +247,7 @@ public static Object handleActionContext(@Nonnull Object actionContext) { || actionContext instanceof Character) { return actionContext; } else { - return JSON.toJSONString(actionContext); + return JsonUtil.toJSONString(actionContext); } } @@ -283,9 +283,9 @@ public static T convertActionContext(String key, @Nullable Object value, @No // JSON to Object try { if (value instanceof CharSequence || value instanceof Character) { - return JSON.parseObject(value.toString(), targetClazz); + return JsonUtil.parseObject(value.toString(), targetClazz); } else { - return JSON.parseObject(JSON.toJSONString(value), targetClazz); + return JsonUtil.parseObject(JsonUtil.toJSONString(value), targetClazz); } } catch (RuntimeException e) { String errorMsg = String.format("Failed to convert the action context with key '%s' from '%s' to '%s'.", diff --git a/tcc/src/main/java/io/seata/rm/tcc/interceptor/ActionInterceptorHandler.java b/integration-tx-api/src/main/java/io/seata/integration/tx/api/interceptor/ActionInterceptorHandler.java similarity index 75% rename from tcc/src/main/java/io/seata/rm/tcc/interceptor/ActionInterceptorHandler.java rename to integration-tx-api/src/main/java/io/seata/integration/tx/api/interceptor/ActionInterceptorHandler.java index 3fdec847b63..1f850f564f1 100644 --- a/tcc/src/main/java/io/seata/rm/tcc/interceptor/ActionInterceptorHandler.java +++ b/integration-tx-api/src/main/java/io/seata/integration/tx/api/interceptor/ActionInterceptorHandler.java @@ -13,17 +13,16 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.seata.rm.tcc.interceptor; +package io.seata.integration.tx.api.interceptor; +import javax.annotation.Nonnull; import java.lang.annotation.Annotation; import java.lang.reflect.Method; import java.lang.reflect.UndeclaredThrowableException; import java.util.Collections; import java.util.HashMap; import java.util.Map; -import javax.annotation.Nonnull; -import com.alibaba.fastjson.JSON; import io.seata.common.Constants; import io.seata.common.exception.FrameworkException; import io.seata.common.exception.SkipCallbackWrapperException; @@ -31,20 +30,19 @@ import io.seata.common.util.CollectionUtils; import io.seata.common.util.NetUtil; import io.seata.core.context.RootContext; -import io.seata.core.model.BranchType; +import io.seata.integration.tx.api.fence.DefaultCommonFenceHandler; +import io.seata.integration.tx.api.util.JsonUtil; import io.seata.rm.DefaultResourceManager; -import io.seata.rm.tcc.TCCFenceHandler; import io.seata.rm.tcc.api.BusinessActionContext; import io.seata.rm.tcc.api.BusinessActionContextParameter; import io.seata.rm.tcc.api.BusinessActionContextUtil; import io.seata.rm.tcc.api.ParamType; -import io.seata.rm.tcc.api.TwoPhaseBusinessAction; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.slf4j.MDC; /** - * Handler the TCC Participant Aspect : Setting Context, Creating Branch Record + * Handler the Tx Participant Aspect : Setting Context, Creating Branch Record * * @author zhangsen */ @@ -53,31 +51,33 @@ public class ActionInterceptorHandler { private static final Logger LOGGER = LoggerFactory.getLogger(ActionInterceptorHandler.class); /** - * Handler the TCC Aspect + * Handler the Tx Aspect * - * @param method the method - * @param arguments the arguments - * @param xid the xid - * @param businessAction the business action - * @param targetCallback the target callback + * @param method the method + * @param arguments the arguments + * @param xid the xid + * @param businessActionParam the business action params + * @param targetCallback the target callback * @return the business result * @throws Throwable the throwable */ - public Object proceed(Method method, Object[] arguments, String xid, TwoPhaseBusinessAction businessAction, - Callback targetCallback) throws Throwable { + public Object proceed(Method method, Object[] arguments, String xid, TwoPhaseBusinessActionParam businessActionParam, + Callback targetCallback) throws Throwable { //Get action context from arguments, or create a new one and then reset to arguments BusinessActionContext actionContext = getOrCreateActionContextAndResetToArguments(method.getParameterTypes(), arguments); //Set the xid actionContext.setXid(xid); //Set the action name - String actionName = businessAction.name(); + String actionName = businessActionParam.getActionName(); actionContext.setActionName(actionName); //Set the delay report - actionContext.setDelayReport(businessAction.isDelayReport()); + actionContext.setDelayReport(businessActionParam.getDelayReport()); + //Set branch type + actionContext.setBranchType(businessActionParam.getBranchType()); //Creating Branch Record - String branchId = doTccActionLogStore(method, arguments, businessAction, actionContext); + String branchId = doTxActionLogStore(method, arguments, businessActionParam, actionContext); actionContext.setBranchId(branchId); //MDC put branchId MDC.put(RootContext.MDC_KEY_BRANCH_ID, branchId); @@ -88,14 +88,14 @@ public Object proceed(Method method, Object[] arguments, String xid, TwoPhaseBus //share actionContext implicitly BusinessActionContextUtil.setContext(actionContext); - if (businessAction.useTCCFence()) { + if (businessActionParam.getUseCommonFence()) { try { - // Use TCC Fence, and return the business result - return TCCFenceHandler.prepareFence(xid, Long.valueOf(branchId), actionName, targetCallback); + // Use common Fence, and return the business result + return DefaultCommonFenceHandler.get().prepareFence(xid, Long.valueOf(branchId), actionName, targetCallback); } catch (SkipCallbackWrapperException | UndeclaredThrowableException e) { Throwable originException = e.getCause(); if (originException instanceof FrameworkException) { - LOGGER.error("[{}] prepare TCC fence error: {}", xid, originException.getMessage()); + LOGGER.error("[{}] prepare common fence error: {}", xid, originException.getMessage()); } throw originException; } @@ -135,7 +135,7 @@ protected BusinessActionContext getOrCreateActionContextAndResetToArguments(Clas int argIndex = 0; for (Class parameterType : parameterTypes) { if (BusinessActionContext.class.isAssignableFrom(parameterType)) { - actionContext = (BusinessActionContext)arguments[argIndex]; + actionContext = (BusinessActionContext) arguments[argIndex]; if (actionContext == null) { // If the action context exists in arguments but is null, create a new one and reset the action context to the arguments actionContext = new BusinessActionContext(); @@ -159,14 +159,14 @@ protected BusinessActionContext getOrCreateActionContextAndResetToArguments(Clas /** * Creating Branch Record * - * @param method the method - * @param arguments the arguments - * @param businessAction the business action - * @param actionContext the action context + * @param method the method + * @param arguments the arguments + * @param businessActionParam the business action param + * @param actionContext the action context * @return the branchId */ - protected String doTccActionLogStore(Method method, Object[] arguments, TwoPhaseBusinessAction businessAction, - BusinessActionContext actionContext) { + protected String doTxActionLogStore(Method method, Object[] arguments, TwoPhaseBusinessActionParam businessActionParam, + BusinessActionContext actionContext) { String actionName = actionContext.getActionName(); String xid = actionContext.getXid(); @@ -176,7 +176,7 @@ protected String doTccActionLogStore(Method method, Object[] arguments, TwoPhase context.put(Constants.ACTION_START_TIME, System.currentTimeMillis()); //Init business context - initBusinessContext(context, method, businessAction); + initBusinessContext(context, method, businessActionParam.getBusinessActionContext()); //Init running environment context initFrameworkContext(context); @@ -193,15 +193,15 @@ protected String doTccActionLogStore(Method method, Object[] arguments, TwoPhase //endregion //Init applicationData - Map applicationContext = Collections.singletonMap(Constants.TCC_ACTION_CONTEXT, context); - String applicationContextStr = JSON.toJSONString(applicationContext); + Map applicationContext = Collections.singletonMap(Constants.TX_ACTION_CONTEXT, context); + String applicationContextStr = JsonUtil.toJSONString(applicationContext); try { //registry branch record - Long branchId = DefaultResourceManager.get().branchRegister(BranchType.TCC, actionName, null, xid, + Long branchId = DefaultResourceManager.get().branchRegister(businessActionParam.getBranchType(), actionName, null, xid, applicationContextStr, null); return String.valueOf(branchId); } catch (Throwable t) { - String msg = String.format("TCC branch Register error, xid: %s", xid); + String msg = String.format("%s branch Register error, xid: %s", businessActionParam.getBranchType(), xid); LOGGER.error(msg, t); throw new FrameworkException(t, msg); } @@ -223,22 +223,19 @@ protected void initFrameworkContext(Map context) { /** * Init business context * - * @param context the context - * @param method the method - * @param businessAction the business action + * @param context the context + * @param method the method + * @param businessActionContext the business action map */ protected void initBusinessContext(Map context, Method method, - TwoPhaseBusinessAction businessAction) { + Map businessActionContext) { if (method != null) { //the phase one method name context.put(Constants.PREPARE_METHOD, method.getName()); } - if (businessAction != null) { + if (businessActionContext != null && businessActionContext.size() > 0) { //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()); - context.put(Constants.USE_TCC_FENCE, businessAction.useTCCFence()); + context.putAll(businessActionContext); } } @@ -257,7 +254,7 @@ protected Map fetchActionRequestContext(Method method, Object[] for (int j = 0; j < parameterAnnotations[i].length; j++) { if (parameterAnnotations[i][j] instanceof BusinessActionContextParameter) { // get annotation - BusinessActionContextParameter annotation = (BusinessActionContextParameter)parameterAnnotations[i][j]; + BusinessActionContextParameter annotation = (BusinessActionContextParameter) parameterAnnotations[i][j]; if (arguments[i] == null) { throw new IllegalArgumentException("@BusinessActionContextParameter 's params can not null"); } @@ -275,4 +272,5 @@ protected Map fetchActionRequestContext(Method method, Object[] } return context; } -} + +} \ No newline at end of file diff --git a/integration-tx-api/src/main/java/io/seata/integration/tx/api/interceptor/DefaultInvocationWrapper.java b/integration-tx-api/src/main/java/io/seata/integration/tx/api/interceptor/DefaultInvocationWrapper.java new file mode 100644 index 00000000000..a6bef4e6eeb --- /dev/null +++ b/integration-tx-api/src/main/java/io/seata/integration/tx/api/interceptor/DefaultInvocationWrapper.java @@ -0,0 +1,65 @@ +/* + * 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.integration.tx.api.interceptor; + +import java.lang.reflect.Method; + +/** + * @author leezongjie + * @date 2022/11/26 + */ +public class DefaultInvocationWrapper implements InvocationWrapper { + private Object proxy; + private Object delegate; + private Method method; + private Object[] args; + + public DefaultInvocationWrapper(Object proxy, Object delegate, Method method, Object[] args) { + this.proxy = proxy; + this.delegate = delegate; + this.method = method; + this.args = args; + } + + @Override + public Method getMethod() { + return method; + } + + @Override + public Object getProxy() { + return proxy; + } + + @Override + public Object getTarget() { + return delegate; + } + + @Override + public Object[] getArguments() { + return args; + } + + @Override + public Object proceed() { + try { + return method.invoke(delegate, args); + } catch (Throwable t) { + throw new RuntimeException(t); + } + } +} diff --git a/integration-tx-api/src/main/java/io/seata/integration/tx/api/interceptor/InvocationWrapper.java b/integration-tx-api/src/main/java/io/seata/integration/tx/api/interceptor/InvocationWrapper.java new file mode 100644 index 00000000000..c7ca1a74190 --- /dev/null +++ b/integration-tx-api/src/main/java/io/seata/integration/tx/api/interceptor/InvocationWrapper.java @@ -0,0 +1,37 @@ +/* + * 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.integration.tx.api.interceptor; + +import java.lang.reflect.Method; + +/** + * @author leezongjie + * @date 2022/11/26 + */ +public interface InvocationWrapper { + + Method getMethod(); + + Object getProxy(); + + Object getTarget(); + + Object[] getArguments(); + + Object proceed(); + + +} diff --git a/integration-tx-api/src/main/java/io/seata/integration/tx/api/interceptor/TwoPhaseBusinessActionParam.java b/integration-tx-api/src/main/java/io/seata/integration/tx/api/interceptor/TwoPhaseBusinessActionParam.java new file mode 100644 index 00000000000..4f0eb0e4ac5 --- /dev/null +++ b/integration-tx-api/src/main/java/io/seata/integration/tx/api/interceptor/TwoPhaseBusinessActionParam.java @@ -0,0 +1,78 @@ +/* + * 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.integration.tx.api.interceptor; + +import io.seata.core.model.BranchType; + +import java.util.Map; + +/** + * The two phase business action parameters. + * + * @author ruishansun + */ +public class TwoPhaseBusinessActionParam { + + private String actionName; + + private Boolean isDelayReport; + + private Boolean useCommonFence; + + private Map businessActionContext; + + private BranchType branchType; + + public String getActionName() { + return actionName; + } + + public void setActionName(String actionName) { + this.actionName = actionName; + } + + public Boolean getDelayReport() { + return isDelayReport; + } + + public void setDelayReport(Boolean delayReport) { + isDelayReport = delayReport; + } + + public Boolean getUseCommonFence() { + return useCommonFence; + } + + public void setUseCommonFence(Boolean useCommonFence) { + this.useCommonFence = useCommonFence; + } + + public Map getBusinessActionContext() { + return businessActionContext; + } + + public void setBusinessActionContext(Map businessActionContext) { + this.businessActionContext = businessActionContext; + } + + public BranchType getBranchType() { + return branchType; + } + + public void setBranchType(BranchType branchType) { + this.branchType = branchType; + } +} \ No newline at end of file diff --git a/integration-tx-api/src/main/java/io/seata/integration/tx/api/interceptor/TxBeanParserUtils.java b/integration-tx-api/src/main/java/io/seata/integration/tx/api/interceptor/TxBeanParserUtils.java new file mode 100644 index 00000000000..3824a67a53d --- /dev/null +++ b/integration-tx-api/src/main/java/io/seata/integration/tx/api/interceptor/TxBeanParserUtils.java @@ -0,0 +1,69 @@ +/* + * 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.integration.tx.api.interceptor; + +import io.seata.integration.tx.api.remoting.RemotingDesc; +import io.seata.integration.tx.api.remoting.RemotingParser; +import io.seata.integration.tx.api.remoting.parser.DefaultRemotingParser; + +/** + * parser transaction bean + * + * @author zhangsen + */ +public class TxBeanParserUtils { + + private TxBeanParserUtils() { + } + + /** + * is auto proxy transaction bean + * + * @param bean the bean + * @param beanName the bean name + * @return boolean boolean + */ + public static boolean isTxRemotingBean(Object bean, String beanName) { + return parserRemotingServiceInfo(bean, beanName); + } + + + /** + * get remoting bean info: sofa:service, sofa:reference, dubbo:reference, dubbo:service + * + * @param bean the bean + * @param beanName the bean name + * @return if sofa:service, sofa:reference, dubbo:reference, dubbo:service return true, else return false + */ + public static boolean parserRemotingServiceInfo(Object bean, String beanName) { + RemotingParser remotingParser = DefaultRemotingParser.get().isRemoting(bean, beanName); + if (remotingParser != null) { + return DefaultRemotingParser.get().parserRemotingServiceInfo(bean, beanName, remotingParser) != null; + } + return false; + } + + /** + * get the remoting description of Tx bean + * + * @param beanName the bean name + * @return remoting desc + */ + public static RemotingDesc getRemotingDesc(String beanName) { + return DefaultRemotingParser.get().getRemotingBeanDesc(beanName); + } + +} \ No newline at end of file diff --git a/integration-tx-api/src/main/java/io/seata/integration/tx/api/interceptor/handler/AbstractProxyInvocationHandler.java b/integration-tx-api/src/main/java/io/seata/integration/tx/api/interceptor/handler/AbstractProxyInvocationHandler.java new file mode 100644 index 00000000000..bc2adb4be1f --- /dev/null +++ b/integration-tx-api/src/main/java/io/seata/integration/tx/api/interceptor/handler/AbstractProxyInvocationHandler.java @@ -0,0 +1,37 @@ +/* + * 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.integration.tx.api.interceptor.handler; + +import io.seata.common.util.CollectionUtils; +import io.seata.integration.tx.api.interceptor.InvocationWrapper; + +/** + * @author leezongjie + * @date 2022/11/26 + */ +public abstract class AbstractProxyInvocationHandler implements ProxyInvocationHandler { + + protected abstract Object doInvoke(InvocationWrapper invocation) throws Throwable; + + @Override + public Object invoke(InvocationWrapper invocation) throws Throwable { + if (CollectionUtils.isNotEmpty(getMethodsToProxy()) && !getMethodsToProxy().contains(invocation.getMethod().getName())) { + return invocation.proceed(); + } + return doInvoke(invocation); + } + +} diff --git a/integration-tx-api/src/main/java/io/seata/integration/tx/api/interceptor/handler/DefaultInvocationHandler.java b/integration-tx-api/src/main/java/io/seata/integration/tx/api/interceptor/handler/DefaultInvocationHandler.java new file mode 100644 index 00000000000..0784a5be100 --- /dev/null +++ b/integration-tx-api/src/main/java/io/seata/integration/tx/api/interceptor/handler/DefaultInvocationHandler.java @@ -0,0 +1,56 @@ +/* + * 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.integration.tx.api.interceptor.handler; + +import io.seata.integration.tx.api.interceptor.DefaultInvocationWrapper; +import io.seata.integration.tx.api.interceptor.InvocationWrapper; + +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Method; + +/** + * @author leezongjie + * @date 2022/11/26 + */ +public class DefaultInvocationHandler implements InvocationHandler { + + private ProxyInvocationHandler proxyInvocationHandler; + private Object delegate; + + public DefaultInvocationHandler(ProxyInvocationHandler proxyInvocationHandler, Object delegate) { + this.proxyInvocationHandler = proxyInvocationHandler; + this.delegate = delegate; + } + + /** + * Dynamic proxy calls methods + * + * @param proxy The generated proxy object + * @param method Method of agency + * @param args Method parameter + */ + @Override + public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { + InvocationWrapper invocation = new DefaultInvocationWrapper(proxy, delegate, method, args); + Object result; + if (proxyInvocationHandler != null) { + result = proxyInvocationHandler.invoke(invocation); + } else { + result = invocation.proceed(); + } + return result; + } +} diff --git a/spring/src/main/java/io/seata/spring/annotation/GlobalTransactionalInterceptor.java b/integration-tx-api/src/main/java/io/seata/integration/tx/api/interceptor/handler/GlobalTransactionalInterceptorHandler.java similarity index 73% rename from spring/src/main/java/io/seata/spring/annotation/GlobalTransactionalInterceptor.java rename to integration-tx-api/src/main/java/io/seata/integration/tx/api/interceptor/handler/GlobalTransactionalInterceptorHandler.java index 7bc8609ecad..525d4c9392b 100644 --- a/spring/src/main/java/io/seata/spring/annotation/GlobalTransactionalInterceptor.java +++ b/integration-tx-api/src/main/java/io/seata/integration/tx/api/interceptor/handler/GlobalTransactionalInterceptorHandler.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.seata.spring.annotation; +package io.seata.integration.tx.api.interceptor.handler; import java.lang.annotation.Annotation; import java.lang.reflect.Method; @@ -35,63 +35,63 @@ import io.seata.core.event.EventBus; import io.seata.core.event.GuavaEventBus; import io.seata.core.model.GlobalLockConfig; +import io.seata.integration.tx.api.annotation.AspectTransactional; +import io.seata.integration.tx.api.event.DegradeCheckEvent; +import io.seata.integration.tx.api.interceptor.InvocationWrapper; import io.seata.rm.GlobalLockExecutor; import io.seata.rm.GlobalLockTemplate; -import io.seata.spring.event.DegradeCheckEvent; +import io.seata.spring.annotation.GlobalLock; +import io.seata.spring.annotation.GlobalTransactional; import io.seata.tm.TransactionManagerHolder; -import io.seata.tm.api.DefaultFailureHandlerImpl; import io.seata.tm.api.FailureHandler; +import io.seata.tm.api.FailureHandlerHolder; import io.seata.tm.api.TransactionalExecutor; import io.seata.tm.api.TransactionalTemplate; import io.seata.tm.api.transaction.NoRollbackRule; import io.seata.tm.api.transaction.RollbackRule; import io.seata.tm.api.transaction.TransactionInfo; - -import com.google.common.eventbus.Subscribe; -import org.aopalliance.intercept.MethodInterceptor; -import org.aopalliance.intercept.MethodInvocation; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.springframework.aop.support.AopUtils; -import org.springframework.core.BridgeMethodResolver; -import org.springframework.util.ClassUtils; import static io.seata.common.DefaultValues.DEFAULT_DISABLE_GLOBAL_TRANSACTION; import static io.seata.common.DefaultValues.DEFAULT_GLOBAL_TRANSACTION_TIMEOUT; import static io.seata.common.DefaultValues.DEFAULT_TM_DEGRADE_CHECK; import static io.seata.common.DefaultValues.DEFAULT_TM_DEGRADE_CHECK_ALLOW_TIMES; import static io.seata.common.DefaultValues.DEFAULT_TM_DEGRADE_CHECK_PERIOD; -import static io.seata.common.DefaultValues.TM_INTERCEPTOR_ORDER; + /** - * The type Global transactional interceptor. + * The type Global transactional interceptor handler. * * @author slievrly + * @author leezongjie */ -public class GlobalTransactionalInterceptor implements ConfigurationChangeListener, MethodInterceptor, SeataInterceptor { +public class GlobalTransactionalInterceptorHandler extends AbstractProxyInvocationHandler implements ConfigurationChangeListener { - private static final Logger LOGGER = LoggerFactory.getLogger(GlobalTransactionalInterceptor.class); - private static final FailureHandler DEFAULT_FAIL_HANDLER = new DefaultFailureHandlerImpl(); + private static final Logger LOGGER = LoggerFactory.getLogger(GlobalTransactionalInterceptorHandler.class); private final TransactionalTemplate transactionalTemplate = new TransactionalTemplate(); private final GlobalLockTemplate globalLockTemplate = new GlobalLockTemplate(); - private final FailureHandler failureHandler; + + private Set methodsToProxy; + private volatile boolean disable; - private int order; - protected AspectTransactional aspectTransactional; - private static int degradeCheckPeriod; private static final AtomicBoolean ATOMIC_DEGRADE_CHECK = new AtomicBoolean(false); - private static int degradeCheckAllowTimes; private static volatile Integer degradeNum = 0; private static volatile Integer reachNum = 0; - private static final EventBus EVENT_BUS = new GuavaEventBus("degradeCheckEventBus", true); - private static volatile ScheduledThreadPoolExecutor executor; - //region DEFAULT_GLOBAL_TRANSACTION_TIMEOUT + private static int degradeCheckAllowTimes; + protected AspectTransactional aspectTransactional; + private static int degradeCheckPeriod; private static int defaultGlobalTransactionTimeout = 0; + private final FailureHandler failureHandler; + + private static final EventBus EVENT_BUS = new GuavaEventBus("degradeCheckEventBus", true); + private static volatile ScheduledThreadPoolExecutor executor; + private void initDefaultGlobalTransactionTimeout() { - if (GlobalTransactionalInterceptor.defaultGlobalTransactionTimeout <= 0) { + if (GlobalTransactionalInterceptorHandler.defaultGlobalTransactionTimeout <= 0) { int defaultGlobalTransactionTimeout; try { defaultGlobalTransactionTimeout = ConfigurationFactory.getInstance().getInt( @@ -105,30 +105,18 @@ private void initDefaultGlobalTransactionTimeout() { defaultGlobalTransactionTimeout, DEFAULT_GLOBAL_TRANSACTION_TIMEOUT); defaultGlobalTransactionTimeout = DEFAULT_GLOBAL_TRANSACTION_TIMEOUT; } - GlobalTransactionalInterceptor.defaultGlobalTransactionTimeout = defaultGlobalTransactionTimeout; + GlobalTransactionalInterceptorHandler.defaultGlobalTransactionTimeout = defaultGlobalTransactionTimeout; } } - public GlobalTransactionalInterceptor() { - this(null); - } - - //endregion - - /** - * Instantiates a new Global transactional interceptor. - * - * @param failureHandler - * the failure handler - */ - public GlobalTransactionalInterceptor(FailureHandler failureHandler) { - this.failureHandler = failureHandler == null ? DEFAULT_FAIL_HANDLER : failureHandler; + public GlobalTransactionalInterceptorHandler(FailureHandler failureHandler, Set methodsToProxy) { + this.failureHandler = failureHandler == null ? FailureHandlerHolder.getFailureHandler() : failureHandler; + this.methodsToProxy = methodsToProxy; this.disable = ConfigurationFactory.getInstance().getBoolean(ConfigurationKeys.DISABLE_GLOBAL_TRANSACTION, - DEFAULT_DISABLE_GLOBAL_TRANSACTION); - this.order = - ConfigurationFactory.getInstance().getInt(ConfigurationKeys.TM_INTERCEPTOR_ORDER, TM_INTERCEPTOR_ORDER); + DEFAULT_DISABLE_GLOBAL_TRANSACTION); + boolean degradeCheck = ConfigurationFactory.getInstance().getBoolean(ConfigurationKeys.CLIENT_DEGRADE_CHECK, - DEFAULT_TM_DEGRADE_CHECK); + DEFAULT_TM_DEGRADE_CHECK); degradeCheckPeriod = ConfigurationFactory.getInstance() .getInt(ConfigurationKeys.CLIENT_DEGRADE_CHECK_PERIOD, DEFAULT_TM_DEGRADE_CHECK_PERIOD); degradeCheckAllowTimes = ConfigurationFactory.getInstance() @@ -141,15 +129,23 @@ public GlobalTransactionalInterceptor(FailureHandler failureHandler) { this.initDefaultGlobalTransactionTimeout(); } + public GlobalTransactionalInterceptorHandler(FailureHandler failureHandler, Set methodsToProxy, AspectTransactional aspectTransactional) { + this(failureHandler, methodsToProxy); + this.aspectTransactional = aspectTransactional; + } + + @Override + public Set getMethodsToProxy() { + return methodsToProxy; + } + @Override - public Object invoke(final MethodInvocation methodInvocation) throws Throwable { - Class targetClass = - methodInvocation.getThis() != null ? AopUtils.getTargetClass(methodInvocation.getThis()) : null; - Method specificMethod = ClassUtils.getMostSpecificMethod(methodInvocation.getMethod(), targetClass); + protected Object doInvoke(InvocationWrapper invocation) throws Throwable { + Class targetClass = invocation.getTarget().getClass(); + Method specificMethod = invocation.getMethod(); if (specificMethod != null && !specificMethod.getDeclaringClass().equals(Object.class)) { - final Method method = BridgeMethodResolver.findBridgedMethod(specificMethod); - final GlobalTransactional globalTransactionalAnnotation = - getAnnotation(method, targetClass, GlobalTransactional.class); + final Method method = invocation.getMethod(); + final GlobalTransactional globalTransactionalAnnotation = getAnnotation(method, targetClass, GlobalTransactional.class); final GlobalLock globalLockAnnotation = getAnnotation(method, targetClass, GlobalLock.class); boolean localDisable = disable || (ATOMIC_DEGRADE_CHECK.get() && degradeNum >= degradeCheckAllowTimes); if (!localDisable) { @@ -157,27 +153,28 @@ public Object invoke(final MethodInvocation methodInvocation) throws Throwable { AspectTransactional transactional; if (globalTransactionalAnnotation != null) { transactional = new AspectTransactional(globalTransactionalAnnotation.timeoutMills(), - globalTransactionalAnnotation.name(), globalTransactionalAnnotation.rollbackFor(), - globalTransactionalAnnotation.rollbackForClassName(), - globalTransactionalAnnotation.noRollbackFor(), - globalTransactionalAnnotation.noRollbackForClassName(), - globalTransactionalAnnotation.propagation(), - globalTransactionalAnnotation.lockRetryInterval(), - globalTransactionalAnnotation.lockRetryTimes(), - globalTransactionalAnnotation.lockStrategyMode()); + globalTransactionalAnnotation.name(), globalTransactionalAnnotation.rollbackFor(), + globalTransactionalAnnotation.rollbackForClassName(), + globalTransactionalAnnotation.noRollbackFor(), + globalTransactionalAnnotation.noRollbackForClassName(), + globalTransactionalAnnotation.propagation(), + globalTransactionalAnnotation.lockRetryInterval(), + globalTransactionalAnnotation.lockRetryTimes(), + globalTransactionalAnnotation.lockStrategyMode()); } else { transactional = this.aspectTransactional; } - return handleGlobalTransaction(methodInvocation, transactional); + return handleGlobalTransaction(invocation, transactional); } else if (globalLockAnnotation != null) { - return handleGlobalLock(methodInvocation, globalLockAnnotation); + return handleGlobalLock(invocation, globalLockAnnotation); } } } - return methodInvocation.proceed(); + return invocation.proceed(); } - private Object handleGlobalLock(final MethodInvocation methodInvocation, final GlobalLock globalLockAnno) throws Throwable { + + private Object handleGlobalLock(final InvocationWrapper methodInvocation, final GlobalLock globalLockAnno) throws Throwable { return globalLockTemplate.execute(new GlobalLockExecutor() { @Override public Object execute() throws Throwable { @@ -194,8 +191,8 @@ public GlobalLockConfig getGlobalLockConfig() { }); } - Object handleGlobalTransaction(final MethodInvocation methodInvocation, - final AspectTransactional aspectTransactional) throws Throwable { + Object handleGlobalTransaction(final InvocationWrapper methodInvocation, + final AspectTransactional aspectTransactional) throws Throwable { boolean succeed = true; try { return transactionalTemplate.execute(new TransactionalExecutor() { @@ -277,9 +274,10 @@ public TransactionInfo getTransactionInfo() { } } + public T getAnnotation(Method method, Class targetClass, Class annotationClass) { return Optional.ofNullable(method).map(m -> m.getAnnotation(annotationClass)) - .orElse(Optional.ofNullable(targetClass).map(t -> t.getAnnotation(annotationClass)).orElse(null)); + .orElse(Optional.ofNullable(targetClass).map(t -> t.getAnnotation(annotationClass)).orElse(null)); } private String formatMethod(Method method) { @@ -348,48 +346,4 @@ private static void startDegradeCheck() { } }, degradeCheckPeriod, degradeCheckPeriod, TimeUnit.MILLISECONDS); } - - @Subscribe - public static void onDegradeCheck(DegradeCheckEvent event) { - if (event.isRequestSuccess()) { - if (degradeNum >= degradeCheckAllowTimes) { - reachNum++; - if (reachNum >= degradeCheckAllowTimes) { - reachNum = 0; - degradeNum = 0; - if (LOGGER.isInfoEnabled()) { - LOGGER.info("the current global transaction has been restored"); - } - } - } else if (degradeNum != 0) { - degradeNum = 0; - } - } else { - if (degradeNum < degradeCheckAllowTimes) { - degradeNum++; - if (degradeNum >= degradeCheckAllowTimes) { - if (LOGGER.isWarnEnabled()) { - LOGGER.warn("the current global transaction has been automatically downgraded"); - } - } - } else if (reachNum != 0) { - reachNum = 0; - } - } - } - - @Override - public int getOrder() { - return order; - } - - @Override - public void setOrder(int order) { - this.order = order; - } - - @Override - public SeataInterceptorPosition getPosition() { - return SeataInterceptorPosition.BeforeTransaction; - } } diff --git a/integration-tx-api/src/main/java/io/seata/integration/tx/api/interceptor/handler/ProxyInvocationHandler.java b/integration-tx-api/src/main/java/io/seata/integration/tx/api/interceptor/handler/ProxyInvocationHandler.java new file mode 100644 index 00000000000..cda4754d798 --- /dev/null +++ b/integration-tx-api/src/main/java/io/seata/integration/tx/api/interceptor/handler/ProxyInvocationHandler.java @@ -0,0 +1,32 @@ +/* + * 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.integration.tx.api.interceptor.handler; + +import java.util.Set; + +import io.seata.integration.tx.api.interceptor.InvocationWrapper; + +/** + * @author leezongjie + * @date 2022/11/26 + */ +public interface ProxyInvocationHandler { + + Set getMethodsToProxy(); + + Object invoke(InvocationWrapper invocation) throws Throwable; + +} diff --git a/integration-tx-api/src/main/java/io/seata/integration/tx/api/interceptor/parser/DefaultInterfaceParser.java b/integration-tx-api/src/main/java/io/seata/integration/tx/api/interceptor/parser/DefaultInterfaceParser.java new file mode 100644 index 00000000000..c47b361e97b --- /dev/null +++ b/integration-tx-api/src/main/java/io/seata/integration/tx/api/interceptor/parser/DefaultInterfaceParser.java @@ -0,0 +1,67 @@ +/* + * 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.integration.tx.api.interceptor.parser; + +import io.seata.common.loader.EnhancedServiceLoader; +import io.seata.common.util.CollectionUtils; +import io.seata.integration.tx.api.interceptor.handler.ProxyInvocationHandler; + +import java.util.ArrayList; +import java.util.List; + +/** + * @author leezongjie + * @date 2022/11/26 + */ +public class DefaultInterfaceParser implements InterfaceParser { + + protected static final List ALL_INTERFACE_PARSERS = new ArrayList<>(); + + + private static class SingletonHolder { + private static final DefaultInterfaceParser INSTANCE = new DefaultInterfaceParser(); + } + + public static DefaultInterfaceParser get() { + return DefaultInterfaceParser.SingletonHolder.INSTANCE; + } + + protected DefaultInterfaceParser() { + initInterfaceParser(); + } + + /** + * init parsers + */ + protected void initInterfaceParser() { + List interfaceParsers = EnhancedServiceLoader.loadAll(InterfaceParser.class); + if (CollectionUtils.isNotEmpty(interfaceParsers)) { + ALL_INTERFACE_PARSERS.addAll(interfaceParsers); + } + } + + @Override + public ProxyInvocationHandler parserInterfaceToProxy(Object target) throws Exception { + for (InterfaceParser interfaceParser : ALL_INTERFACE_PARSERS) { + ProxyInvocationHandler proxyInvocationHandler = interfaceParser.parserInterfaceToProxy(target); + if (proxyInvocationHandler != null) { + return proxyInvocationHandler; + } + } + return null; + } + +} \ No newline at end of file diff --git a/integration-tx-api/src/main/java/io/seata/integration/tx/api/interceptor/parser/DefaultResourceRegisterParser.java b/integration-tx-api/src/main/java/io/seata/integration/tx/api/interceptor/parser/DefaultResourceRegisterParser.java new file mode 100644 index 00000000000..8e9451c2982 --- /dev/null +++ b/integration-tx-api/src/main/java/io/seata/integration/tx/api/interceptor/parser/DefaultResourceRegisterParser.java @@ -0,0 +1,61 @@ +/* + * 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.integration.tx.api.interceptor.parser; + +import io.seata.common.loader.EnhancedServiceLoader; +import io.seata.common.util.CollectionUtils; + +import java.util.ArrayList; +import java.util.List; + +/** + * @author leezongjie + * @date 2022/11/26 + */ +public class DefaultResourceRegisterParser { + + protected static List allRegisterResourceParsers = new ArrayList<>(); + + public void registerResource(Object target) { + for (RegisterResourceParser registerResourceParser : allRegisterResourceParsers) { + registerResourceParser.registerResource(target); + } + } + + private static class SingletonHolder { + private static final DefaultResourceRegisterParser INSTANCE = new DefaultResourceRegisterParser(); + } + + public static DefaultResourceRegisterParser get() { + return DefaultResourceRegisterParser.SingletonHolder.INSTANCE; + } + + protected DefaultResourceRegisterParser() { + initResourceRegisterParser(); + } + + /** + * init parsers + */ + protected void initResourceRegisterParser() { + List registerResourceParsers = EnhancedServiceLoader.loadAll(RegisterResourceParser.class); + if (CollectionUtils.isNotEmpty(registerResourceParsers)) { + allRegisterResourceParsers.addAll(registerResourceParsers); + } + } + + +} \ No newline at end of file diff --git a/integration-tx-api/src/main/java/io/seata/integration/tx/api/interceptor/parser/DefaultTargetClassParser.java b/integration-tx-api/src/main/java/io/seata/integration/tx/api/interceptor/parser/DefaultTargetClassParser.java new file mode 100644 index 00000000000..502a39138c8 --- /dev/null +++ b/integration-tx-api/src/main/java/io/seata/integration/tx/api/interceptor/parser/DefaultTargetClassParser.java @@ -0,0 +1,76 @@ +/* + * 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.integration.tx.api.interceptor.parser; + +import io.seata.common.loader.EnhancedServiceLoader; +import io.seata.common.util.CollectionUtils; + +import java.util.ArrayList; +import java.util.List; + +/** + * @author leezongjie + * @date 2022/12/17 + */ +public class DefaultTargetClassParser implements TargetClassParser { + + protected static List allTargetClassParsers = new ArrayList<>(); + + + private static class SingletonHolder { + private static final DefaultTargetClassParser INSTANCE = new DefaultTargetClassParser(); + } + + public static DefaultTargetClassParser get() { + return DefaultTargetClassParser.SingletonHolder.INSTANCE; + } + + protected DefaultTargetClassParser() { + initTargetClassParser(); + } + + /** + * init parsers + */ + protected void initTargetClassParser() { + List targetClassParsers = EnhancedServiceLoader.loadAll(TargetClassParser.class); + if (CollectionUtils.isNotEmpty(targetClassParsers)) { + allTargetClassParsers.addAll(targetClassParsers); + } + } + + @Override + public Class findTargetClass(Object target) throws Exception { + for (TargetClassParser targetClassParser : allTargetClassParsers) { + Class result = targetClassParser.findTargetClass(target); + if (result != null) { + return result; + } + } + return target.getClass(); + } + + @Override + public Class[] findInterfaces(Object target) throws Exception { + for (TargetClassParser targetClassParser : allTargetClassParsers) { + Class[] result = targetClassParser.findInterfaces(target); + if (result != null) { + return result; + } + } + return target.getClass().getInterfaces(); + } +} diff --git a/integration-tx-api/src/main/java/io/seata/integration/tx/api/interceptor/parser/GlobalTransactionalInterceptorParser.java b/integration-tx-api/src/main/java/io/seata/integration/tx/api/interceptor/parser/GlobalTransactionalInterceptorParser.java new file mode 100644 index 00000000000..7c19e07020b --- /dev/null +++ b/integration-tx-api/src/main/java/io/seata/integration/tx/api/interceptor/parser/GlobalTransactionalInterceptorParser.java @@ -0,0 +1,92 @@ +/* + * 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.integration.tx.api.interceptor.parser; + +import java.lang.reflect.Method; +import java.util.HashSet; +import java.util.Set; + +import io.seata.common.ConfigurationKeys; +import io.seata.common.util.CollectionUtils; +import io.seata.config.ConfigurationCache; +import io.seata.config.ConfigurationChangeListener; +import io.seata.integration.tx.api.interceptor.handler.GlobalTransactionalInterceptorHandler; +import io.seata.integration.tx.api.interceptor.handler.ProxyInvocationHandler; +import io.seata.spring.annotation.GlobalLock; +import io.seata.spring.annotation.GlobalTransactional; +import io.seata.tm.api.FailureHandlerHolder; + +/** + * @author leezongjie + * @date 2022/11/26 + */ +public class GlobalTransactionalInterceptorParser implements InterfaceParser { + + private final Set methodsToProxy = new HashSet<>(); + + /** + * @param target + * @return + * @throws Exception + * @see GlobalTransactional // TM annotation + *

+ * GlobalLock: + * @see GlobalLock // GlobalLock annotation + */ + @Override + public ProxyInvocationHandler parserInterfaceToProxy(Object target) throws Exception { + Class serviceInterface = DefaultTargetClassParser.get().findTargetClass(target); + Class[] interfacesIfJdk = DefaultTargetClassParser.get().findInterfaces(target); + + if (existsAnnotation(serviceInterface) || existsAnnotation(interfacesIfJdk)) { + ProxyInvocationHandler proxyInvocationHandler = new GlobalTransactionalInterceptorHandler(FailureHandlerHolder.getFailureHandler(), methodsToProxy); + ConfigurationCache.addConfigListener(ConfigurationKeys.DISABLE_GLOBAL_TRANSACTION, (ConfigurationChangeListener) proxyInvocationHandler); + return proxyInvocationHandler; + } + + return null; + } + + private boolean existsAnnotation(Class... classes) { + boolean result = false; + if (CollectionUtils.isNotEmpty(classes)) { + for (Class clazz : classes) { + if (clazz == null) { + continue; + } + GlobalTransactional trxAnno = clazz.getAnnotation(GlobalTransactional.class); + if (trxAnno != null) { + return true; + } + Method[] methods = clazz.getMethods(); + for (Method method : methods) { + trxAnno = method.getAnnotation(GlobalTransactional.class); + if (trxAnno != null) { + methodsToProxy.add(method.getName()); + result = true; + } + + GlobalLock lockAnno = method.getAnnotation(GlobalLock.class); + if (lockAnno != null) { + methodsToProxy.add(method.getName()); + result = true; + } + } + } + } + return result; + } +} diff --git a/integration-tx-api/src/main/java/io/seata/integration/tx/api/interceptor/parser/InterfaceParser.java b/integration-tx-api/src/main/java/io/seata/integration/tx/api/interceptor/parser/InterfaceParser.java new file mode 100644 index 00000000000..c7f4ce84e1f --- /dev/null +++ b/integration-tx-api/src/main/java/io/seata/integration/tx/api/interceptor/parser/InterfaceParser.java @@ -0,0 +1,29 @@ +/* + * 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.integration.tx.api.interceptor.parser; + +import io.seata.integration.tx.api.interceptor.handler.ProxyInvocationHandler; + +/** + * @author leezongjie + * @date 2022/11/26 + */ +public interface InterfaceParser { + + ProxyInvocationHandler parserInterfaceToProxy(Object target) throws Exception; + + +} diff --git a/integration-tx-api/src/main/java/io/seata/integration/tx/api/interceptor/parser/RegisterResourceParser.java b/integration-tx-api/src/main/java/io/seata/integration/tx/api/interceptor/parser/RegisterResourceParser.java new file mode 100644 index 00000000000..9faf017e6d5 --- /dev/null +++ b/integration-tx-api/src/main/java/io/seata/integration/tx/api/interceptor/parser/RegisterResourceParser.java @@ -0,0 +1,26 @@ +/* + * 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.integration.tx.api.interceptor.parser; + +/** + * @author leezongjie + * @date 2022/12/17 + */ +public interface RegisterResourceParser { + + void registerResource(Object target); + +} diff --git a/integration-tx-api/src/main/java/io/seata/integration/tx/api/interceptor/parser/TargetClassParser.java b/integration-tx-api/src/main/java/io/seata/integration/tx/api/interceptor/parser/TargetClassParser.java new file mode 100644 index 00000000000..c011f2a6bcd --- /dev/null +++ b/integration-tx-api/src/main/java/io/seata/integration/tx/api/interceptor/parser/TargetClassParser.java @@ -0,0 +1,28 @@ +/* + * 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.integration.tx.api.interceptor.parser; + +/** + * @author leezongjie + * @date 2022/12/17 + */ +public interface TargetClassParser { + + Class findTargetClass(Object target) throws Exception; + + Class[] findInterfaces(Object target) throws Exception; + +} diff --git a/integration-tx-api/src/main/java/io/seata/integration/tx/api/json/DefaultJsonParser.java b/integration-tx-api/src/main/java/io/seata/integration/tx/api/json/DefaultJsonParser.java new file mode 100644 index 00000000000..cc5a450538a --- /dev/null +++ b/integration-tx-api/src/main/java/io/seata/integration/tx/api/json/DefaultJsonParser.java @@ -0,0 +1,76 @@ +/* + * 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.integration.tx.api.json; + +import io.seata.common.loader.EnhancedServiceLoader; +import io.seata.common.util.CollectionUtils; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; + +/** + * @author leezongjie + * @date 2023/1/13 + */ +public class DefaultJsonParser implements JsonParser { + + protected static List allJsonParsers = new ArrayList<>(); + + private static class SingletonHolder { + private static final DefaultJsonParser INSTANCE = new DefaultJsonParser(); + } + + private DefaultJsonParser() { + initJsonParser(); + } + + public static DefaultJsonParser get() { + return DefaultJsonParser.SingletonHolder.INSTANCE; + } + + private void initJsonParser() { + List jsonParsers = EnhancedServiceLoader.loadAll(JsonParser.class); + if (CollectionUtils.isNotEmpty(jsonParsers)) { + allJsonParsers.addAll(jsonParsers); + } + Collections.sort(allJsonParsers, Comparator.comparingInt(JsonParser::order)); + } + + @Override + public String toJSONString(Object object) { + for (JsonParser jsonParser : allJsonParsers) { + String result = jsonParser.toJSONString(object); + if (result != null) { + return result; + } + } + return null; + } + + @Override + public T parseObject(String text, Class clazz) { + for (JsonParser jsonParser : allJsonParsers) { + T result = jsonParser.parseObject(text, clazz); + if (result != null) { + return result; + } + } + return null; + } + +} diff --git a/integration-tx-api/src/main/java/io/seata/integration/tx/api/json/JsonParser.java b/integration-tx-api/src/main/java/io/seata/integration/tx/api/json/JsonParser.java new file mode 100644 index 00000000000..bb812285a11 --- /dev/null +++ b/integration-tx-api/src/main/java/io/seata/integration/tx/api/json/JsonParser.java @@ -0,0 +1,32 @@ +/* + * 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.integration.tx.api.json; + +/** + * @author leezongjie + * @date 2023/1/13 + */ +public interface JsonParser { + + String toJSONString(Object object); + + T parseObject(String text, Class clazz); + + default int order() { + return 0; + } + +} diff --git a/tcc/src/main/java/io/seata/rm/tcc/remoting/Protocols.java b/integration-tx-api/src/main/java/io/seata/integration/tx/api/remoting/Protocols.java similarity index 95% rename from tcc/src/main/java/io/seata/rm/tcc/remoting/Protocols.java rename to integration-tx-api/src/main/java/io/seata/integration/tx/api/remoting/Protocols.java index 3d38f71e82a..8751af079bb 100644 --- a/tcc/src/main/java/io/seata/rm/tcc/remoting/Protocols.java +++ b/integration-tx-api/src/main/java/io/seata/integration/tx/api/remoting/Protocols.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.seata.rm.tcc.remoting; +package io.seata.integration.tx.api.remoting; /** * remoting protocols enum diff --git a/tcc/src/main/java/io/seata/rm/tcc/remoting/RemotingDesc.java b/integration-tx-api/src/main/java/io/seata/integration/tx/api/remoting/RemotingDesc.java similarity index 89% rename from tcc/src/main/java/io/seata/rm/tcc/remoting/RemotingDesc.java rename to integration-tx-api/src/main/java/io/seata/integration/tx/api/remoting/RemotingDesc.java index 73b97c97ca9..6e9ba588d67 100644 --- a/tcc/src/main/java/io/seata/rm/tcc/remoting/RemotingDesc.java +++ b/integration-tx-api/src/main/java/io/seata/integration/tx/api/remoting/RemotingDesc.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.seata.rm.tcc.remoting; +package io.seata.integration.tx.api.remoting; /** * remoting bean info @@ -27,6 +27,11 @@ public class RemotingDesc { */ private boolean isReference = false; + /** + * is service bean ? + */ + private boolean isService = false; + /** * rpc target bean, the service bean has this property */ @@ -183,4 +188,20 @@ public void setReference(boolean reference) { isReference = reference; } -} + /** + * Is service boolean. + * + * @return the boolean + */ + public boolean isService() { + return isService; + } + + /** + * Sets service. + * @param service the service + */ + public void setService(boolean service) { + isService = service; + } +} \ No newline at end of file diff --git a/tcc/src/main/java/io/seata/rm/tcc/remoting/RemotingParser.java b/integration-tx-api/src/main/java/io/seata/integration/tx/api/remoting/RemotingParser.java similarity index 97% rename from tcc/src/main/java/io/seata/rm/tcc/remoting/RemotingParser.java rename to integration-tx-api/src/main/java/io/seata/integration/tx/api/remoting/RemotingParser.java index 2ff32ca4980..4d65ca8362b 100644 --- a/tcc/src/main/java/io/seata/rm/tcc/remoting/RemotingParser.java +++ b/integration-tx-api/src/main/java/io/seata/integration/tx/api/remoting/RemotingParser.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.seata.rm.tcc.remoting; +package io.seata.integration.tx.api.remoting; import io.seata.common.exception.FrameworkException; diff --git a/tcc/src/main/java/io/seata/rm/tcc/TwoPhaseResult.java b/integration-tx-api/src/main/java/io/seata/integration/tx/api/remoting/TwoPhaseResult.java similarity index 97% rename from tcc/src/main/java/io/seata/rm/tcc/TwoPhaseResult.java rename to integration-tx-api/src/main/java/io/seata/integration/tx/api/remoting/TwoPhaseResult.java index 54b4c3139ee..91f5ac43df5 100644 --- a/tcc/src/main/java/io/seata/rm/tcc/TwoPhaseResult.java +++ b/integration-tx-api/src/main/java/io/seata/integration/tx/api/remoting/TwoPhaseResult.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.seata.rm.tcc; +package io.seata.integration.tx.api.remoting; import io.seata.common.util.StringUtils; @@ -92,4 +92,4 @@ public String toString() { sb.append("]"); return sb.toString(); } -} +} \ No newline at end of file diff --git a/tcc/src/main/java/io/seata/rm/tcc/remoting/parser/AbstractedRemotingParser.java b/integration-tx-api/src/main/java/io/seata/integration/tx/api/remoting/parser/AbstractedRemotingParser.java similarity index 89% rename from tcc/src/main/java/io/seata/rm/tcc/remoting/parser/AbstractedRemotingParser.java rename to integration-tx-api/src/main/java/io/seata/integration/tx/api/remoting/parser/AbstractedRemotingParser.java index 153326688ea..497fae5f0c3 100644 --- a/tcc/src/main/java/io/seata/rm/tcc/remoting/parser/AbstractedRemotingParser.java +++ b/integration-tx-api/src/main/java/io/seata/integration/tx/api/remoting/parser/AbstractedRemotingParser.java @@ -13,10 +13,10 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.seata.rm.tcc.remoting.parser; +package io.seata.integration.tx.api.remoting.parser; import io.seata.common.exception.FrameworkException; -import io.seata.rm.tcc.remoting.RemotingParser; +import io.seata.integration.tx.api.remoting.RemotingParser; /** * The type Abstracted remoting parser. @@ -31,4 +31,4 @@ public boolean isRemoting(Object bean, String beanName) throws FrameworkExceptio return isReference(bean, beanName) || isService(bean, beanName); } -} +} \ No newline at end of file diff --git a/tcc/src/main/java/io/seata/rm/tcc/remoting/parser/DefaultRemotingParser.java b/integration-tx-api/src/main/java/io/seata/integration/tx/api/remoting/parser/DefaultRemotingParser.java similarity index 52% rename from tcc/src/main/java/io/seata/rm/tcc/remoting/parser/DefaultRemotingParser.java rename to integration-tx-api/src/main/java/io/seata/integration/tx/api/remoting/parser/DefaultRemotingParser.java index 16c1455a4b3..5b3a28baf94 100644 --- a/tcc/src/main/java/io/seata/rm/tcc/remoting/parser/DefaultRemotingParser.java +++ b/integration-tx-api/src/main/java/io/seata/integration/tx/api/remoting/parser/DefaultRemotingParser.java @@ -13,10 +13,8 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.seata.rm.tcc.remoting.parser; +package io.seata.integration.tx.api.remoting.parser; -import java.lang.annotation.Annotation; -import java.lang.reflect.Method; import java.util.ArrayList; import java.util.List; import java.util.Map; @@ -25,14 +23,8 @@ import io.seata.common.exception.FrameworkException; import io.seata.common.loader.EnhancedServiceLoader; import io.seata.common.util.CollectionUtils; -import io.seata.rm.DefaultResourceManager; -import io.seata.rm.tcc.TCCResource; -import io.seata.rm.tcc.api.BusinessActionContext; -import io.seata.rm.tcc.api.BusinessActionContextParameter; -import io.seata.rm.tcc.api.TwoPhaseBusinessAction; -import io.seata.rm.tcc.interceptor.ActionContextUtil; -import io.seata.rm.tcc.remoting.RemotingDesc; -import io.seata.rm.tcc.remoting.RemotingParser; +import io.seata.integration.tx.api.remoting.RemotingDesc; +import io.seata.integration.tx.api.remoting.RemotingParser; /** * parsing remoting bean @@ -50,7 +42,7 @@ public class DefaultRemotingParser { /** * all remoting beans beanName -> RemotingDesc */ - protected static Map remotingServiceMap = new ConcurrentHashMap<>(); + protected static Map remotingServiceMap = new ConcurrentHashMap<>(); private static class SingletonHolder { private static final DefaultRemotingParser INSTANCE = new DefaultRemotingParser(); @@ -164,47 +156,14 @@ public RemotingDesc getServiceDesc(Object bean, String beanName) { * @return remoting desc */ public RemotingDesc parserRemotingServiceInfo(Object bean, String beanName, RemotingParser remotingParser) { + if (remotingServiceMap.containsKey(bean)) { + return remotingServiceMap.get(bean); + } RemotingDesc remotingBeanDesc = remotingParser.getServiceDesc(bean, beanName); if (remotingBeanDesc == null) { return null; } - remotingServiceMap.put(beanName, remotingBeanDesc); - - Class serviceClass = remotingBeanDesc.getServiceClass(); - Method[] methods = serviceClass.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(serviceClass.getMethod(twoPhaseBusinessAction.commitMethod(), - twoPhaseBusinessAction.commitArgsClasses())); - tccResource.setRollbackMethodName(twoPhaseBusinessAction.rollbackMethod()); - tccResource.setRollbackMethod(serviceClass.getMethod(twoPhaseBusinessAction.rollbackMethod(), - twoPhaseBusinessAction.rollbackArgsClasses())); - // set argsClasses - tccResource.setCommitArgsClasses(twoPhaseBusinessAction.commitArgsClasses()); - tccResource.setRollbackArgsClasses(twoPhaseBusinessAction.rollbackArgsClasses()); - // set phase two method's keys - tccResource.setPhaseTwoCommitKeys(this.getTwoPhaseArgs(tccResource.getCommitMethod(), - twoPhaseBusinessAction.commitArgsClasses())); - tccResource.setPhaseTwoRollbackKeys(this.getTwoPhaseArgs(tccResource.getRollbackMethod(), - twoPhaseBusinessAction.rollbackArgsClasses())); - //registry tcc resource - DefaultResourceManager.get().registerResource(tccResource); - } - } - } catch (Throwable t) { - throw new FrameworkException(t, "parser remoting service error"); - } - } + remotingServiceMap.put(bean, remotingBeanDesc); if (remotingParser.isReference(bean, beanName)) { //reference bean, TCC proxy remotingBeanDesc.setReference(true); @@ -212,40 +171,14 @@ public RemotingDesc parserRemotingServiceInfo(Object bean, String beanName, Remo return remotingBeanDesc; } - protected String[] getTwoPhaseArgs(Method method, Class[] argsClasses) { - Annotation[][] parameterAnnotations = method.getParameterAnnotations(); - String[] keys = new String[parameterAnnotations.length]; - /* - * get parameter's key - * if method's parameter list is like - * (BusinessActionContext, @BusinessActionContextParameter("a") A a, @BusinessActionContextParameter("b") B b) - * the keys will be [null, a, b] - */ - 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]; - String key = ActionContextUtil.getParamNameFromAnnotation(param); - keys[i] = key; - break; - } - } - if (keys[i] == null && !(argsClasses[i].equals(BusinessActionContext.class))) { - throw new IllegalArgumentException("non-BusinessActionContext parameter should use annotation " + - "BusinessActionContextParameter"); - } - } - return keys; - } - /** * Get remoting bean desc remoting desc. * - * @param beanName the bean name + * @param bean the bean * @return the remoting desc */ - public RemotingDesc getRemotingBeanDesc(String beanName) { - return remotingServiceMap.get(beanName); + public RemotingDesc getRemotingBeanDesc(Object bean) { + return remotingServiceMap.get(bean); } -} +} \ No newline at end of file diff --git a/tcc/src/main/java/io/seata/rm/tcc/remoting/parser/DubboRemotingParser.java b/integration-tx-api/src/main/java/io/seata/integration/tx/api/remoting/parser/DubboRemotingParser.java similarity index 84% rename from tcc/src/main/java/io/seata/rm/tcc/remoting/parser/DubboRemotingParser.java rename to integration-tx-api/src/main/java/io/seata/integration/tx/api/remoting/parser/DubboRemotingParser.java index 8ded4d41ae5..03ebda17b0e 100644 --- a/tcc/src/main/java/io/seata/rm/tcc/remoting/parser/DubboRemotingParser.java +++ b/integration-tx-api/src/main/java/io/seata/integration/tx/api/remoting/parser/DubboRemotingParser.java @@ -13,12 +13,12 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.seata.rm.tcc.remoting.parser; +package io.seata.integration.tx.api.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; +import io.seata.integration.tx.api.remoting.Protocols; +import io.seata.integration.tx.api.remoting.RemotingDesc; /** * dubbo remoting bean parsing @@ -31,14 +31,14 @@ public class DubboRemotingParser extends AbstractedRemotingParser { 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()); + || "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()); + || "org.apache.dubbo.config.spring.ServiceBean".equals(c.getName()); } @Override @@ -61,6 +61,8 @@ public RemotingDesc getServiceDesc(Object bean, String beanName) throws Framewor Object targetBean = ReflectionUtil.getFieldValue(bean, "ref"); serviceBeanDesc.setTargetBean(targetBean); } + serviceBeanDesc.setReference(this.isReference(bean, beanName)); + serviceBeanDesc.setService(this.isService(bean, beanName)); return serviceBeanDesc; } catch (Throwable t) { throw new FrameworkException(t); @@ -71,4 +73,4 @@ public RemotingDesc getServiceDesc(Object bean, String beanName) throws Framewor public short getProtocol() { return Protocols.DUBBO; } -} +} \ No newline at end of file diff --git a/tcc/src/main/java/io/seata/rm/tcc/remoting/parser/HSFRemotingParser.java b/integration-tx-api/src/main/java/io/seata/integration/tx/api/remoting/parser/HSFRemotingParser.java similarity index 90% rename from tcc/src/main/java/io/seata/rm/tcc/remoting/parser/HSFRemotingParser.java rename to integration-tx-api/src/main/java/io/seata/integration/tx/api/remoting/parser/HSFRemotingParser.java index ae3e7db0f61..995be2d33a3 100644 --- a/tcc/src/main/java/io/seata/rm/tcc/remoting/parser/HSFRemotingParser.java +++ b/integration-tx-api/src/main/java/io/seata/integration/tx/api/remoting/parser/HSFRemotingParser.java @@ -13,12 +13,12 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.seata.rm.tcc.remoting.parser; +package io.seata.integration.tx.api.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; +import io.seata.integration.tx.api.remoting.Protocols; +import io.seata.integration.tx.api.remoting.RemotingDesc; /** * HSF Remote Bean Parser @@ -85,6 +85,8 @@ public RemotingDesc getServiceDesc(Object bean, String beanName) throws Framewor serviceBeanDesc.setUniqueId(uniqueId); serviceBeanDesc.setGroup(group); serviceBeanDesc.setProtocol(Protocols.HSF); + serviceBeanDesc.setReference(this.isReference(bean, beanName)); + serviceBeanDesc.setService(this.isService(bean, beanName)); return serviceBeanDesc; } else if (isService(bean, beanName)) { Object consumerBean = ReflectionUtil.getFieldValue(bean, "providerBean"); @@ -103,6 +105,8 @@ public RemotingDesc getServiceDesc(Object bean, String beanName) throws Framewor Object targetBean = ReflectionUtil.getFieldValue(metadata, "target"); serviceBeanDesc.setTargetBean(targetBean); serviceBeanDesc.setProtocol(Protocols.HSF); + serviceBeanDesc.setReference(this.isReference(bean, beanName)); + serviceBeanDesc.setService(this.isService(bean, beanName)); return serviceBeanDesc; } } catch (Throwable t) { @@ -116,4 +120,4 @@ public RemotingDesc getServiceDesc(Object bean, String beanName) throws Framewor public short getProtocol() { return Protocols.HSF; } -} +} \ No newline at end of file diff --git a/tcc/src/main/java/io/seata/rm/tcc/remoting/parser/SofaRpcRemotingParser.java b/integration-tx-api/src/main/java/io/seata/integration/tx/api/remoting/parser/SofaRpcRemotingParser.java similarity index 87% rename from tcc/src/main/java/io/seata/rm/tcc/remoting/parser/SofaRpcRemotingParser.java rename to integration-tx-api/src/main/java/io/seata/integration/tx/api/remoting/parser/SofaRpcRemotingParser.java index 5debae1c3d5..af7af02a898 100644 --- a/tcc/src/main/java/io/seata/rm/tcc/remoting/parser/SofaRpcRemotingParser.java +++ b/integration-tx-api/src/main/java/io/seata/integration/tx/api/remoting/parser/SofaRpcRemotingParser.java @@ -13,12 +13,12 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.seata.rm.tcc.remoting.parser; +package io.seata.integration.tx.api.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; +import io.seata.integration.tx.api.remoting.Protocols; +import io.seata.integration.tx.api.remoting.RemotingDesc; /** * sofa-rpc remoting bean parsing @@ -30,7 +30,7 @@ public class SofaRpcRemotingParser extends AbstractedRemotingParser { @Override public boolean isReference(Object bean, String beanName) - throws FrameworkException { + throws FrameworkException { String beanClassName = bean.getClass().getName(); return "com.alipay.sofa.runtime.spring.factory.ReferenceFactoryBean".equals(beanClassName); } @@ -60,6 +60,8 @@ public RemotingDesc getServiceDesc(Object bean, String beanName) throws Framewor Object targetBean = ReflectionUtil.getFieldValue(bean, "ref"); serviceBeanDesc.setTargetBean(targetBean); } + serviceBeanDesc.setReference(this.isReference(bean, beanName)); + serviceBeanDesc.setService(this.isService(bean, beanName)); return serviceBeanDesc; } catch (Throwable t) { throw new FrameworkException(t); @@ -70,4 +72,4 @@ public RemotingDesc getServiceDesc(Object bean, String beanName) throws Framewor public short getProtocol() { return Protocols.SOFA_RPC; } -} +} \ No newline at end of file diff --git a/tcc/src/main/java/io/seata/rm/tcc/remoting/parser/DubboUtil.java b/integration-tx-api/src/main/java/io/seata/integration/tx/api/util/DubboUtil.java similarity index 98% rename from tcc/src/main/java/io/seata/rm/tcc/remoting/parser/DubboUtil.java rename to integration-tx-api/src/main/java/io/seata/integration/tx/api/util/DubboUtil.java index c33651ca09b..eb4c381ba11 100644 --- a/tcc/src/main/java/io/seata/rm/tcc/remoting/parser/DubboUtil.java +++ b/integration-tx-api/src/main/java/io/seata/integration/tx/api/util/DubboUtil.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.seata.rm.tcc.remoting.parser; +package io.seata.integration.tx.api.util; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; diff --git a/integration-tx-api/src/main/java/io/seata/integration/tx/api/util/JsonUtil.java b/integration-tx-api/src/main/java/io/seata/integration/tx/api/util/JsonUtil.java new file mode 100644 index 00000000000..11259637ce9 --- /dev/null +++ b/integration-tx-api/src/main/java/io/seata/integration/tx/api/util/JsonUtil.java @@ -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.integration.tx.api.util; + +import io.seata.integration.tx.api.json.DefaultJsonParser; + +/** + * @author leezongjie + * @date 2023/1/13 + */ +public class JsonUtil { + + public static String toJSONString(Object object) { + return DefaultJsonParser.get().toJSONString(object); + } + + public static T parseObject(String text, Class clazz) { + return DefaultJsonParser.get().parseObject(text, clazz); + } + +} diff --git a/integration-tx-api/src/main/java/io/seata/integration/tx/api/util/ProxyUtil.java b/integration-tx-api/src/main/java/io/seata/integration/tx/api/util/ProxyUtil.java new file mode 100644 index 00000000000..86658bd44f0 --- /dev/null +++ b/integration-tx-api/src/main/java/io/seata/integration/tx/api/util/ProxyUtil.java @@ -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.integration.tx.api.util; + +import io.seata.integration.tx.api.interceptor.handler.DefaultInvocationHandler; +import io.seata.integration.tx.api.interceptor.handler.ProxyInvocationHandler; +import io.seata.integration.tx.api.interceptor.parser.DefaultInterfaceParser; +import net.bytebuddy.ByteBuddy; +import net.bytebuddy.implementation.InvocationHandlerAdapter; + +import java.util.HashMap; +import java.util.Map; + +import static net.bytebuddy.matcher.ElementMatchers.isDeclaredBy; + +/** + * @author leezongjie + * @date 2022/11/26 + */ +public class ProxyUtil { + + private static final Map PROXYED_SET = new HashMap<>(); + + public static T createProxy(T target) { + try { + synchronized (PROXYED_SET) { + if (PROXYED_SET.containsKey(target)) { + return (T) PROXYED_SET.get(target); + } + ProxyInvocationHandler proxyInvocationHandler = DefaultInterfaceParser.get().parserInterfaceToProxy(target); + if (proxyInvocationHandler == null) { + return target; + } + T proxy = (T) new ByteBuddy().subclass(target.getClass()) + .method(isDeclaredBy(target.getClass())) + .intercept(InvocationHandlerAdapter.of(new DefaultInvocationHandler(proxyInvocationHandler, target))) + .make() + .load(target.getClass().getClassLoader()) + .getLoaded() + .getDeclaredConstructor() + .newInstance(); + PROXYED_SET.put(target, proxy); + return proxy; + } + } catch (Throwable t) { + throw new RuntimeException("error occurs when create seata proxy", t); + } + } + +} diff --git a/tcc/src/main/java/io/seata/rm/tcc/api/BusinessActionContext.java b/integration-tx-api/src/main/java/io/seata/rm/tcc/api/BusinessActionContext.java similarity index 92% rename from tcc/src/main/java/io/seata/rm/tcc/api/BusinessActionContext.java rename to integration-tx-api/src/main/java/io/seata/rm/tcc/api/BusinessActionContext.java index f11b3249940..98ddb5f2961 100644 --- a/tcc/src/main/java/io/seata/rm/tcc/api/BusinessActionContext.java +++ b/integration-tx-api/src/main/java/io/seata/rm/tcc/api/BusinessActionContext.java @@ -20,7 +20,9 @@ import javax.annotation.Nonnull; import javax.annotation.Nullable; -import io.seata.rm.tcc.interceptor.ActionContextUtil; +import io.seata.core.model.BranchType; +import io.seata.integration.tx.api.interceptor.ActionContextUtil; + /** * The type Business action context. @@ -38,7 +40,7 @@ public class BusinessActionContext implements Serializable { /** * delay branch report while sharing params to tcc phase 2 to enhance performance * - * @see io.seata.rm.tcc.api.BusinessActionContextUtil + * @see BusinessActionContextUtil * @see io.seata.rm.tcc.api.TwoPhaseBusinessAction */ private Boolean isDelayReport; @@ -46,10 +48,15 @@ public class BusinessActionContext implements Serializable { /** * mark that actionContext has been updated by business * - * @see io.seata.rm.tcc.api.BusinessActionContextUtil + * @see BusinessActionContextUtil */ private Boolean isUpdated; + /** + * branch Type + */ + private BranchType branchType; + /** * action context */ @@ -219,6 +226,14 @@ public void setUpdated(Boolean updated) { isUpdated = updated; } + public BranchType getBranchType() { + return branchType; + } + + public void setBranchType(BranchType branchType) { + this.branchType = branchType; + } + @Override public String toString() { StringBuilder sb = new StringBuilder(); @@ -227,8 +242,9 @@ public String toString() { .append(",action_name:").append(actionName) .append(",is_delay_report:").append(isDelayReport) .append(",is_updated:").append(isDelayReport) + .append(",branch_type:").append(branchType) .append(",action_context:") .append(actionContext).append("]"); return sb.toString(); } -} +} \ No newline at end of file diff --git a/tcc/src/main/java/io/seata/rm/tcc/api/BusinessActionContextParameter.java b/integration-tx-api/src/main/java/io/seata/rm/tcc/api/BusinessActionContextParameter.java similarity index 85% rename from tcc/src/main/java/io/seata/rm/tcc/api/BusinessActionContextParameter.java rename to integration-tx-api/src/main/java/io/seata/rm/tcc/api/BusinessActionContextParameter.java index 518a7c5dec4..4406b177d04 100644 --- a/tcc/src/main/java/io/seata/rm/tcc/api/BusinessActionContextParameter.java +++ b/integration-tx-api/src/main/java/io/seata/rm/tcc/api/BusinessActionContextParameter.java @@ -26,7 +26,7 @@ * add this annotation on the parameters of the try method, and the parameters will be passed to the action context * * @author zhangsen - * @see io.seata.rm.tcc.interceptor.ActionContextUtil + * @see io.seata.spring.interceptor.ActionContextUtil */ @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.PARAMETER, ElementType.FIELD}) @@ -36,7 +36,7 @@ * parameter's name. Synonym for {@link #paramName()}. * * @return the name of the param or field - * @see io.seata.rm.tcc.interceptor.ActionContextUtil#getParamNameFromAnnotation + * @see io.seata.spring.interceptor.ActionContextUtil#getParamNameFromAnnotation */ String value() default ""; @@ -44,7 +44,7 @@ * parameter's name. Synonym for {@link #value()}. * * @return the name of the param or field - * @see io.seata.rm.tcc.interceptor.ActionContextUtil#getParamNameFromAnnotation + * @see io.seata.spring.interceptor.ActionContextUtil#getParamNameFromAnnotation */ String paramName() default ""; @@ -61,7 +61,7 @@ * Specify the index of the parameter in the List * * @return the index of the List - * @see io.seata.rm.tcc.interceptor.ActionContextUtil#getByIndex + * @see io.seata.spring.interceptor.ActionContextUtil#getByIndex */ int index() default -1; @@ -70,8 +70,8 @@ * if {@code index >= 0}, the object get from the List and then do get the parameter from the property of the object * * @return the boolean - * @see io.seata.rm.tcc.interceptor.ActionContextUtil#loadParamByAnnotationAndPutToContext - * @see io.seata.rm.tcc.interceptor.ActionContextUtil#fetchContextFromObject + * @see io.seata.spring.interceptor.ActionContextUtil#loadParamByAnnotationAndPutToContext + * @see io.seata.spring.interceptor.ActionContextUtil#fetchContextFromObject */ boolean isParamInProperty() default false; -} +} \ No newline at end of file diff --git a/tcc/src/main/java/io/seata/rm/tcc/api/BusinessActionContextUtil.java b/integration-tx-api/src/main/java/io/seata/rm/tcc/api/BusinessActionContextUtil.java similarity index 66% rename from tcc/src/main/java/io/seata/rm/tcc/api/BusinessActionContextUtil.java rename to integration-tx-api/src/main/java/io/seata/rm/tcc/api/BusinessActionContextUtil.java index 3bb0158df9d..65d8933c5bf 100644 --- a/tcc/src/main/java/io/seata/rm/tcc/api/BusinessActionContextUtil.java +++ b/integration-tx-api/src/main/java/io/seata/rm/tcc/api/BusinessActionContextUtil.java @@ -15,21 +15,22 @@ */ package io.seata.rm.tcc.api; -import java.util.Collections; -import java.util.Map; - -import com.alibaba.fastjson.JSON; import io.seata.common.Constants; import io.seata.common.exception.FrameworkException; import io.seata.common.util.CollectionUtils; +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.integration.tx.api.interceptor.ActionContextUtil; +import io.seata.integration.tx.api.util.JsonUtil; import io.seata.rm.DefaultResourceManager; -import io.seata.rm.tcc.interceptor.ActionContextUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + /** * the api of sharing business action context to tcc phase 2 * @@ -105,11 +106,11 @@ public static boolean reportContext(BusinessActionContext actionContext) { try { // branch report DefaultResourceManager.get().branchReport( - BranchType.TCC, + actionContext.getBranchType(), actionContext.getXid(), actionContext.getBranchId(), BranchStatus.Registered, - JSON.toJSONString(Collections.singletonMap(Constants.TCC_ACTION_CONTEXT, actionContext.getActionContext())) + JsonUtil.toJSONString(Collections.singletonMap(Constants.TX_ACTION_CONTEXT, actionContext.getActionContext())) ); // reset to un_updated @@ -133,4 +134,43 @@ public static void setContext(BusinessActionContext context) { public static void clear() { CONTEXT_HOLDER.remove(); } -} + + /** + * 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 + */ + public static BusinessActionContext getBusinessActionContext(String xid, long branchId, String resourceId, + String applicationData) { + Map actionContextMap = null; + if (StringUtils.isNotBlank(applicationData)) { + Map tccContext = JsonUtil.parseObject(applicationData, Map.class); + actionContextMap = (Map) tccContext.get(Constants.TX_ACTION_CONTEXT); + } + if (actionContextMap == null) { + actionContextMap = new HashMap<>(2); + } + + //instance the action context + BusinessActionContext businessActionContext = new BusinessActionContext( + xid, String.valueOf(branchId), actionContextMap); + businessActionContext.setActionName(resourceId); + return businessActionContext; + } + + public static Object[] getTwoPhaseMethodParams(String[] keys, Class[] argsClasses, BusinessActionContext businessActionContext) { + Object[] args = new Object[argsClasses.length]; + for (int i = 0; i < argsClasses.length; i++) { + if (argsClasses[i].equals(BusinessActionContext.class)) { + args[i] = businessActionContext; + } else { + args[i] = businessActionContext.getActionContext(keys[i], argsClasses[i]); + } + } + return args; + } +} \ No newline at end of file diff --git a/tcc/src/main/java/io/seata/rm/tcc/api/ParamType.java b/integration-tx-api/src/main/java/io/seata/rm/tcc/api/ParamType.java similarity index 100% rename from tcc/src/main/java/io/seata/rm/tcc/api/ParamType.java rename to integration-tx-api/src/main/java/io/seata/rm/tcc/api/ParamType.java diff --git a/spring/src/main/java/io/seata/spring/annotation/GlobalLock.java b/integration-tx-api/src/main/java/io/seata/spring/annotation/GlobalLock.java similarity index 83% rename from spring/src/main/java/io/seata/spring/annotation/GlobalLock.java rename to integration-tx-api/src/main/java/io/seata/spring/annotation/GlobalLock.java index 2cd8b3e71e9..561d15a4ccb 100644 --- a/spring/src/main/java/io/seata/spring/annotation/GlobalLock.java +++ b/integration-tx-api/src/main/java/io/seata/spring/annotation/GlobalLock.java @@ -21,8 +21,6 @@ import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; -import org.aopalliance.intercept.MethodInvocation; -import org.springframework.core.annotation.AliasFor; /** * declare the transaction only execute in single local RM @@ -47,16 +45,6 @@ */ int lockRetryInterval() default 0; - /** - * customized global lock retry interval(unit: ms) - * you may use this to override global config of "client.rm.lock.retryInterval" - * note: 0 or negative number will take no effect(which mean fall back to global config) - * @return lock retry interval - */ - @Deprecated - @AliasFor("lockRetryInterval") - int lockRetryInternal() default 0; - /** * customized global lock retry times * you may use this to override global config of "client.rm.lock.retryTimes" @@ -64,5 +52,4 @@ * @return lock retry times */ int lockRetryTimes() default -1; - } diff --git a/spring/src/main/java/io/seata/spring/annotation/GlobalTransactional.java b/integration-tx-api/src/main/java/io/seata/spring/annotation/GlobalTransactional.java similarity index 95% rename from spring/src/main/java/io/seata/spring/annotation/GlobalTransactional.java rename to integration-tx-api/src/main/java/io/seata/spring/annotation/GlobalTransactional.java index 2e1dbd2d9bb..3e80fda88b1 100644 --- a/spring/src/main/java/io/seata/spring/annotation/GlobalTransactional.java +++ b/integration-tx-api/src/main/java/io/seata/spring/annotation/GlobalTransactional.java @@ -15,18 +15,16 @@ */ package io.seata.spring.annotation; +import io.seata.common.DefaultValues; +import io.seata.common.LockStrategyMode; +import io.seata.tm.api.transaction.Propagation; + 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; -import io.seata.common.DefaultValues; -import io.seata.common.LockStrategyMode; -import io.seata.tm.api.transaction.Propagation; -import org.aopalliance.intercept.MethodInvocation; -import org.springframework.core.annotation.AliasFor; - /** * The interface Global transactional. * @@ -41,7 +39,7 @@ * @see io.seata.spring.annotation.datasource.SeataAutoDataSourceProxyAdvice#invoke(MethodInvocation) io.seata.spring * .annotation.datasource.SeataAutoDataSourceProxyAdvice#invoke(MethodInvocation)// RM: the interceptor of * GlobalLockLogic and AT/XA mode - * @see io.seata.spring.tcc.TccActionInterceptor#invoke(MethodInvocation) io.seata.spring.tcc + * @see io.seata.rm.tcc.interceptor.TccActionInterceptor#invoke(MethodInvocation) io.seata.spring.tcc * .TccActionInterceptor#invoke(MethodInvocation)// RM: the interceptor of TCC mode */ @Retention(RetentionPolicy.RUNTIME) @@ -117,7 +115,6 @@ * @return int */ @Deprecated - @AliasFor("lockRetryInterval") int lockRetryInternal() default 0; /** diff --git a/integration-tx-api/src/main/resources/META-INF/services/io.seata.integration.tx.api.interceptor.parser.InterfaceParser b/integration-tx-api/src/main/resources/META-INF/services/io.seata.integration.tx.api.interceptor.parser.InterfaceParser new file mode 100644 index 00000000000..b9e1043dc3e --- /dev/null +++ b/integration-tx-api/src/main/resources/META-INF/services/io.seata.integration.tx.api.interceptor.parser.InterfaceParser @@ -0,0 +1 @@ +io.seata.integration.tx.api.interceptor.parser.GlobalTransactionalInterceptorParser \ No newline at end of file diff --git a/integration-tx-api/src/main/resources/META-INF/services/io.seata.integration.tx.api.remoting.RemotingParser b/integration-tx-api/src/main/resources/META-INF/services/io.seata.integration.tx.api.remoting.RemotingParser new file mode 100644 index 00000000000..3bb1adeec5f --- /dev/null +++ b/integration-tx-api/src/main/resources/META-INF/services/io.seata.integration.tx.api.remoting.RemotingParser @@ -0,0 +1,3 @@ +io.seata.integration.tx.api.remoting.parser.DubboRemotingParser +io.seata.integration.tx.api.remoting.parser.SofaRpcRemotingParser +io.seata.integration.tx.api.remoting.parser.HSFRemotingParser \ No newline at end of file diff --git a/tcc/src/test/java/io/seata/rm/tcc/interceptor/ActionInterceptorHandlerTest.java b/integration-tx-api/src/test/java/io/seata/integration/tx/api/interceptor/ActionInterceptorHandlerTest.java similarity index 83% rename from tcc/src/test/java/io/seata/rm/tcc/interceptor/ActionInterceptorHandlerTest.java rename to integration-tx-api/src/test/java/io/seata/integration/tx/api/interceptor/ActionInterceptorHandlerTest.java index 1fb8f1b5fb8..15f883ceb31 100644 --- a/tcc/src/test/java/io/seata/rm/tcc/interceptor/ActionInterceptorHandlerTest.java +++ b/integration-tx-api/src/test/java/io/seata/integration/tx/api/interceptor/ActionInterceptorHandlerTest.java @@ -13,19 +13,17 @@ * 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; +package io.seata.integration.tx.api.interceptor; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.List; import java.util.Map; +import io.seata.rm.tcc.api.BusinessActionContext; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + /** * The type Action interceptor handler test. * @@ -36,7 +34,7 @@ public class ActionInterceptorHandlerTest { /** * The Action interceptor handler. */ - protected ActionInterceptorHandler actionInterceptorHandler = new ActionInterceptorHandler(); + protected ActionInterceptorHandler actionInterceptorHandler = new ActionInterceptorHandler(); /** * Test business action context. @@ -45,11 +43,11 @@ public class ActionInterceptorHandlerTest { */ @Test public void testBusinessActionContext() throws NoSuchMethodException { - Method prepareMethod = TccAction.class.getDeclaredMethod("prepare", - BusinessActionContext.class, int.class, List.class, TccParam.class); + Method prepareMethod = TestAction.class.getDeclaredMethod("prepare", + BusinessActionContext.class, int.class, List.class, TestParam.class); List list = new ArrayList<>(); list.add("b"); - TccParam tccParam = new TccParam (1, "abc@ali.com"); + TestParam tccParam = new TestParam(1, "abc@ali.com"); Map paramContext = actionInterceptorHandler.fetchActionRequestContext(prepareMethod, new Object[]{null, 10, list, tccParam}); diff --git a/integration-tx-api/src/test/java/io/seata/integration/tx/api/interceptor/TestAction.java b/integration-tx-api/src/test/java/io/seata/integration/tx/api/interceptor/TestAction.java new file mode 100644 index 00000000000..52592dcf3b1 --- /dev/null +++ b/integration-tx-api/src/test/java/io/seata/integration/tx/api/interceptor/TestAction.java @@ -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.integration.tx.api.interceptor; + +import java.util.List; + +import io.seata.rm.tcc.api.BusinessActionContext; +import io.seata.rm.tcc.api.BusinessActionContextParameter; + +/** + * @author ruishansun + */ +public interface TestAction { + + + boolean prepare(BusinessActionContext actionContext, + @BusinessActionContextParameter("a") int a, + @BusinessActionContextParameter(paramName = "b", index = 0) List b, + @BusinessActionContextParameter(isParamInProperty = true) TestParam tccParam); + +} diff --git a/integration-tx-api/src/test/java/io/seata/integration/tx/api/interceptor/TestParam.java b/integration-tx-api/src/test/java/io/seata/integration/tx/api/interceptor/TestParam.java new file mode 100644 index 00000000000..3c4d082414a --- /dev/null +++ b/integration-tx-api/src/test/java/io/seata/integration/tx/api/interceptor/TestParam.java @@ -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.integration.tx.api.interceptor; + +import io.seata.rm.tcc.api.BusinessActionContextParameter; + +/** + * The type Tcc param. + * + * @author zhangsen + */ +public class TestParam { + + /** + * 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 TestParam(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; + } +} diff --git a/integration-tx-api/src/test/java/io/seata/integration/tx/api/interceptor/parser/Business.java b/integration-tx-api/src/test/java/io/seata/integration/tx/api/interceptor/parser/Business.java new file mode 100644 index 00000000000..16f80401c7b --- /dev/null +++ b/integration-tx-api/src/test/java/io/seata/integration/tx/api/interceptor/parser/Business.java @@ -0,0 +1,29 @@ +/* + * 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.integration.tx.api.interceptor.parser; + +/** + * The interface Business. + */ +public interface Business { + /** + * Do biz string. + * + * @param msg the msg + * @return the string + */ + String doBiz(String msg); +} diff --git a/integration-tx-api/src/test/java/io/seata/integration/tx/api/interceptor/parser/BusinessImpl.java b/integration-tx-api/src/test/java/io/seata/integration/tx/api/interceptor/parser/BusinessImpl.java new file mode 100644 index 00000000000..bb23eb8a758 --- /dev/null +++ b/integration-tx-api/src/test/java/io/seata/integration/tx/api/interceptor/parser/BusinessImpl.java @@ -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.integration.tx.api.interceptor.parser; + +import io.seata.spring.annotation.GlobalTransactional; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * The type Business. + */ +@GlobalTransactional(timeoutMills = 300000, name = "busi-doBiz") +public class BusinessImpl implements Business { + private static final Logger LOGGER = LoggerFactory.getLogger(BusinessImpl.class); + + @Override + public String doBiz(String msg) { + LOGGER.info("Business doBiz"); + return "hello " + msg; + } +} diff --git a/integration-tx-api/src/test/java/io/seata/integration/tx/api/interceptor/parser/GlobalTransactionalInterceptorParserTest.java b/integration-tx-api/src/test/java/io/seata/integration/tx/api/interceptor/parser/GlobalTransactionalInterceptorParserTest.java new file mode 100644 index 00000000000..afb98feee46 --- /dev/null +++ b/integration-tx-api/src/test/java/io/seata/integration/tx/api/interceptor/parser/GlobalTransactionalInterceptorParserTest.java @@ -0,0 +1,44 @@ +/* + * 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.integration.tx.api.interceptor.parser; + +import io.seata.integration.tx.api.interceptor.handler.ProxyInvocationHandler; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +/** + * @author leezongjie + * @date 2022/12/15 + */ +class GlobalTransactionalInterceptorParserTest { + + @Test + void parserInterfaceToProxy() throws Exception { + + //given + BusinessImpl business = new BusinessImpl(); + + GlobalTransactionalInterceptorParser globalTransactionalInterceptorParser = new GlobalTransactionalInterceptorParser(); + + //when + ProxyInvocationHandler proxyInvocationHandler = globalTransactionalInterceptorParser.parserInterfaceToProxy(business); + + //then + Assertions.assertNotNull(proxyInvocationHandler); + + + } +} \ No newline at end of file diff --git a/integration-tx-api/src/test/java/io/seata/integration/tx/api/interceptor/parser/ProxyUtilsGlobalTransactionalTest.java b/integration-tx-api/src/test/java/io/seata/integration/tx/api/interceptor/parser/ProxyUtilsGlobalTransactionalTest.java new file mode 100644 index 00000000000..1ec608506ca --- /dev/null +++ b/integration-tx-api/src/test/java/io/seata/integration/tx/api/interceptor/parser/ProxyUtilsGlobalTransactionalTest.java @@ -0,0 +1,84 @@ +/* + * 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.integration.tx.api.interceptor.parser; + +import io.seata.integration.tx.api.util.ProxyUtil; +import io.seata.core.exception.TransactionException; +import io.seata.core.model.GlobalStatus; +import io.seata.core.model.TransactionManager; +import io.seata.tm.TransactionManagerHolder; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import java.util.concurrent.atomic.AtomicReference; + +/** + * @author leezongjie + * @date 2022/12/14 + */ +public class ProxyUtilsGlobalTransactionalTest { + + private final String DEFAULT_XID = "default_xid"; + + + @Test + public void testTcc() { + //given + BusinessImpl business = new BusinessImpl(); + + AtomicReference branchReference = new AtomicReference(); + + Business businessProxy = ProxyUtil.createProxy(business); + + TransactionManager mockTransactionManager = new TransactionManager() { + @Override + public String begin(String applicationId, String transactionServiceGroup, String name, int timeout) throws TransactionException { + return DEFAULT_XID; + } + + @Override + public GlobalStatus commit(String xid) throws TransactionException { + return GlobalStatus.Committed; + } + + @Override + public GlobalStatus rollback(String xid) throws TransactionException { + return null; + } + + @Override + public GlobalStatus getStatus(String xid) throws TransactionException { + return null; + } + + @Override + public GlobalStatus globalReport(String xid, GlobalStatus globalStatus) throws TransactionException { + return null; + } + }; + + TransactionManagerHolder.set(mockTransactionManager); + + //when + String result = businessProxy.doBiz("test"); + + //then + Assertions.assertNotNull(result); + + } + + +} diff --git a/pom.xml b/pom.xml index 90e31432011..d555e04d93a 100644 --- a/pom.xml +++ b/pom.xml @@ -65,6 +65,7 @@ sqlparser server ext/apm-seata-skywalking-plugin + integration-tx-api diff --git a/seata-spring-autoconfigure/seata-spring-autoconfigure-client/src/main/java/io/seata/spring/boot/autoconfigure/SeataClientEnvironmentPostProcessor.java b/seata-spring-autoconfigure/seata-spring-autoconfigure-client/src/main/java/io/seata/spring/boot/autoconfigure/SeataClientEnvironmentPostProcessor.java index 5939248427a..1730ebaa3de 100644 --- a/seata-spring-autoconfigure/seata-spring-autoconfigure-client/src/main/java/io/seata/spring/boot/autoconfigure/SeataClientEnvironmentPostProcessor.java +++ b/seata-spring-autoconfigure/seata-spring-autoconfigure-client/src/main/java/io/seata/spring/boot/autoconfigure/SeataClientEnvironmentPostProcessor.java @@ -16,7 +16,7 @@ package io.seata.spring.boot.autoconfigure; import io.seata.common.holder.ObjectHolder; -import io.seata.rm.tcc.config.TCCFenceConfig; +import io.seata.rm.fence.SpringFenceConfig; import io.seata.saga.engine.StateMachineConfig; import io.seata.spring.boot.autoconfigure.properties.SagaAsyncThreadPoolProperties; import io.seata.spring.boot.autoconfigure.properties.SeataProperties; @@ -46,6 +46,7 @@ import static io.seata.spring.boot.autoconfigure.StarterConstants.TCC_FENCE_PREFIX; import static io.seata.spring.boot.autoconfigure.StarterConstants.UNDO_PREFIX; + /** * @author xingfudeshi@gmail.com * @author wang.liang @@ -64,7 +65,7 @@ public void postProcessEnvironment(ConfigurableEnvironment environment, SpringAp PROPERTY_BEAN_MAP.put(UNDO_PREFIX, UndoProperties.class); PROPERTY_BEAN_MAP.put(COMPRESS_PREFIX, UndoCompressProperties.class); PROPERTY_BEAN_MAP.put(LOAD_BALANCE_PREFIX, LoadBalanceProperties.class); - PROPERTY_BEAN_MAP.put(TCC_FENCE_PREFIX, TCCFenceConfig.class); + PROPERTY_BEAN_MAP.put(TCC_FENCE_PREFIX, SpringFenceConfig.class); PROPERTY_BEAN_MAP.put(SAGA_STATE_MACHINE_PREFIX, StateMachineConfig.class); PROPERTY_BEAN_MAP.put(SAGA_ASYNC_THREAD_POOL_PREFIX, SagaAsyncThreadPoolProperties.class); } @@ -73,4 +74,4 @@ public void postProcessEnvironment(ConfigurableEnvironment environment, SpringAp public int getOrder() { return Ordered.HIGHEST_PRECEDENCE; } -} +} \ No newline at end of file diff --git a/seata-spring-autoconfigure/seata-spring-autoconfigure-client/src/main/java/io/seata/spring/boot/autoconfigure/SeataTCCFenceAutoConfiguration.java b/seata-spring-autoconfigure/seata-spring-autoconfigure-client/src/main/java/io/seata/spring/boot/autoconfigure/SeataTCCFenceAutoConfiguration.java index 34b88497be9..7ce9a2e97cc 100644 --- a/seata-spring-autoconfigure/seata-spring-autoconfigure-client/src/main/java/io/seata/spring/boot/autoconfigure/SeataTCCFenceAutoConfiguration.java +++ b/seata-spring-autoconfigure/seata-spring-autoconfigure-client/src/main/java/io/seata/spring/boot/autoconfigure/SeataTCCFenceAutoConfiguration.java @@ -15,7 +15,7 @@ */ package io.seata.spring.boot.autoconfigure; -import io.seata.rm.tcc.config.TCCFenceConfig; +import io.seata.rm.fence.SpringFenceConfig; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.boot.autoconfigure.AutoConfigureAfter; @@ -38,7 +38,7 @@ */ @ConditionalOnExpression("${seata.enabled:true}") @ConditionalOnBean(type = {"javax.sql.DataSource", "org.springframework.transaction.PlatformTransactionManager"}) -@ConditionalOnMissingBean(TCCFenceConfig.class) +@ConditionalOnMissingBean(SpringFenceConfig.class) @AutoConfigureAfter({SeataCoreAutoConfiguration.class, TransactionAutoConfiguration.class}) @AutoConfigureOrder(Ordered.LOWEST_PRECEDENCE) public class SeataTCCFenceAutoConfiguration { @@ -48,13 +48,13 @@ public class SeataTCCFenceAutoConfiguration { @Bean @ConfigurationProperties(StarterConstants.TCC_FENCE_PREFIX) - public TCCFenceConfig tccFenceConfig( + public SpringFenceConfig tccFenceConfig( DataSource dataSource, PlatformTransactionManager transactionManager, @Qualifier(TCC_FENCE_DATA_SOURCE_BEAN_NAME) @Autowired(required = false) DataSource tccFenceDataSource, @Qualifier(TCC_FENCE_TRANSACTION_MANAGER_BEAN_NAME) @Autowired(required = false) PlatformTransactionManager tccFenceTransactionManager) { - return new TCCFenceConfig(tccFenceDataSource != null ? tccFenceDataSource : dataSource, + return new SpringFenceConfig(tccFenceDataSource != null ? tccFenceDataSource : dataSource, tccFenceTransactionManager != null ? tccFenceTransactionManager : transactionManager); } -} +} \ No newline at end of file diff --git a/seata-spring-autoconfigure/seata-spring-autoconfigure-client/src/main/resources/META-INF/additional-spring-configuration-metadata.json b/seata-spring-autoconfigure/seata-spring-autoconfigure-client/src/main/resources/META-INF/additional-spring-configuration-metadata.json index 6e13dff145a..0b20cfc4f20 100644 --- a/seata-spring-autoconfigure/seata-spring-autoconfigure-client/src/main/resources/META-INF/additional-spring-configuration-metadata.json +++ b/seata-spring-autoconfigure/seata-spring-autoconfigure-client/src/main/resources/META-INF/additional-spring-configuration-metadata.json @@ -248,14 +248,14 @@ "name": "seata.tcc.fence.log-table-name", "type": "java.lang.String", "description": "TCC fence log table name.", - "sourceType": "io.seata.rm.tcc.config.TCCFenceConfig", + "sourceType": "io.seata.rm.fence.SpringFenceConfig", "defaultValue": "tcc_fence_log" }, { "name": "seata.tcc.fence.clean-period", "type": "java.time.Duration", "description": "TCC fence log clean period. only duration type format are supported.", - "sourceType": "io.seata.rm.tcc.config.TCCFenceConfig", + "sourceType": "io.seata.rm.fence.SpringFenceConfig", "defaultValue": "1d" }, { diff --git a/spring/pom.xml b/spring/pom.xml index 7b36f4864dc..73b71d26b1c 100644 --- a/spring/pom.xml +++ b/spring/pom.xml @@ -41,12 +41,12 @@ ${project.groupId} - seata-tcc + seata-rm ${project.version} ${project.groupId} - seata-rm + seata-integration-tx-api ${project.version} @@ -54,12 +54,21 @@ seata-serializer-all ${project.version} + + ${project.groupId} + seata-tcc + ${project.version} + org.springframework spring-context + + org.springframework + spring-jdbc + org.jetbrains.kotlin kotlin-stdlib-jdk8 @@ -74,8 +83,6 @@ org.jetbrains.kotlinx kotlinx-coroutines-core - - @@ -98,4 +105,4 @@ - + \ No newline at end of file diff --git a/spring/src/main/java/io/seata/rm/fence/SpringFenceConfig.java b/spring/src/main/java/io/seata/rm/fence/SpringFenceConfig.java new file mode 100644 index 00000000000..aab8842ef80 --- /dev/null +++ b/spring/src/main/java/io/seata/rm/fence/SpringFenceConfig.java @@ -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.fence; + +import io.seata.common.exception.FrameworkErrorCode; +import io.seata.integration.tx.api.fence.config.CommonFenceConfig; +import io.seata.integration.tx.api.fence.exception.CommonFenceException; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.transaction.PlatformTransactionManager; +import org.springframework.transaction.support.TransactionTemplate; + +import javax.sql.DataSource; + +/** + * @author leezongjie + * @date 2022/12/17 + */ +public class SpringFenceConfig extends CommonFenceConfig implements InitializingBean { + + /** + * Common fence datasource + */ + private final DataSource dataSource; + + /** + * Common fence transactionManager + */ + private final PlatformTransactionManager transactionManager; + + public SpringFenceConfig(DataSource dataSource, PlatformTransactionManager transactionManager) { + this.dataSource = dataSource; + this.transactionManager = transactionManager; + } + + @Override + public void afterPropertiesSet() { + if (dataSource != null) { + // set dataSource + SpringFenceHandler.setDataSource(dataSource); + init(); + } else { + throw new CommonFenceException(FrameworkErrorCode.DateSourceNeedInjected); + } + if (transactionManager != null) { + // set transaction template + SpringFenceHandler.setTransactionTemplate(new TransactionTemplate(transactionManager)); + } else { + throw new CommonFenceException(FrameworkErrorCode.TransactionManagerNeedInjected); + } + } + + +} diff --git a/tcc/src/main/java/io/seata/rm/tcc/TCCFenceHandler.java b/spring/src/main/java/io/seata/rm/fence/SpringFenceHandler.java similarity index 70% rename from tcc/src/main/java/io/seata/rm/tcc/TCCFenceHandler.java rename to spring/src/main/java/io/seata/rm/fence/SpringFenceHandler.java index 40c3cf44e5d..8e6669018cb 100644 --- a/tcc/src/main/java/io/seata/rm/tcc/TCCFenceHandler.java +++ b/spring/src/main/java/io/seata/rm/fence/SpringFenceHandler.java @@ -13,8 +13,9 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.seata.rm.tcc; +package io.seata.rm.fence; +import javax.sql.DataSource; import java.lang.reflect.Method; import java.sql.Connection; import java.sql.SQLException; @@ -25,17 +26,19 @@ import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; -import javax.sql.DataSource; import io.seata.common.exception.FrameworkErrorCode; import io.seata.common.exception.SkipCallbackWrapperException; import io.seata.common.executor.Callback; import io.seata.common.thread.NamedThreadFactory; -import io.seata.rm.tcc.constant.TCCFenceConstant; -import io.seata.rm.tcc.exception.TCCFenceException; -import io.seata.rm.tcc.store.TCCFenceDO; -import io.seata.rm.tcc.store.TCCFenceStore; -import io.seata.rm.tcc.store.db.TCCFenceStoreDataBaseDAO; +import io.seata.integration.tx.api.fence.DefaultCommonFenceHandler; +import io.seata.integration.tx.api.fence.FenceHandler; +import io.seata.integration.tx.api.fence.constant.CommonFenceConstant; +import io.seata.integration.tx.api.fence.exception.CommonFenceException; +import io.seata.integration.tx.api.fence.store.CommonFenceDO; +import io.seata.integration.tx.api.fence.store.CommonFenceStore; +import io.seata.integration.tx.api.fence.store.db.CommonFenceStoreDataBaseDAO; +import io.seata.integration.tx.api.remoting.TwoPhaseResult; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.jdbc.datasource.DataSourceUtils; @@ -43,19 +46,15 @@ import org.springframework.transaction.support.TransactionTemplate; /** - * TCC Fence Handler(idempotent, non_rollback, suspend) + * Common Fence Handler(idempotent, non_rollback, suspend) * * @author kaka2code */ -public class TCCFenceHandler { +public class SpringFenceHandler implements FenceHandler { - private TCCFenceHandler() { - throw new IllegalStateException("Utility class"); - } + private static final Logger LOGGER = LoggerFactory.getLogger(SpringFenceHandler.class); - private static final Logger LOGGER = LoggerFactory.getLogger(TCCFenceHandler.class); - - private static final TCCFenceStore TCC_FENCE_DAO = TCCFenceStoreDataBaseDAO.getInstance(); + private static final CommonFenceStore COMMON_FENCE_DAO = CommonFenceStoreDataBaseDAO.getInstance(); private static DataSource dataSource; @@ -79,25 +78,26 @@ private TCCFenceHandler() { static { try { initLogCleanExecutor(); + DefaultCommonFenceHandler.get().setFenceHandler(new SpringFenceHandler()); } catch (Exception e) { LOGGER.error("init fence log clean executor error", e); } } public static DataSource getDataSource() { - return TCCFenceHandler.dataSource; + return SpringFenceHandler.dataSource; } public static void setDataSource(DataSource dataSource) { - TCCFenceHandler.dataSource = dataSource; + SpringFenceHandler.dataSource = dataSource; } public static void setTransactionTemplate(TransactionTemplate transactionTemplate) { - TCCFenceHandler.transactionTemplate = transactionTemplate; + SpringFenceHandler.transactionTemplate = transactionTemplate; } /** - * tcc prepare method enhanced + * common prepare method enhanced * * @param xid the global transaction id * @param branchId the branch transaction id @@ -105,19 +105,20 @@ public static void setTransactionTemplate(TransactionTemplate transactionTemplat * @param targetCallback the target callback * @return the boolean */ - public static Object prepareFence(String xid, Long branchId, String actionName, Callback targetCallback) { + @Override + public Object prepareFence(String xid, Long branchId, String actionName, Callback targetCallback) { return transactionTemplate.execute(status -> { try { Connection conn = DataSourceUtils.getConnection(dataSource); - boolean result = insertTCCFenceLog(conn, xid, branchId, actionName, TCCFenceConstant.STATUS_TRIED); - LOGGER.info("TCC fence prepare result: {}. xid: {}, branchId: {}", result, xid, branchId); + boolean result = insertCommonFenceLog(conn, xid, branchId, actionName, CommonFenceConstant.STATUS_TRIED); + LOGGER.info("Common fence prepare result: {}. xid: {}, branchId: {}", result, xid, branchId); if (result) { return targetCallback.execute(); } else { - throw new TCCFenceException(String.format("Insert tcc fence record error, prepare fence failed. xid= %s, branchId= %s", xid, branchId), + throw new CommonFenceException(String.format("Insert common fence record error, prepare fence failed. xid= %s, branchId= %s", xid, branchId), FrameworkErrorCode.InsertRecordError); } - } catch (TCCFenceException e) { + } catch (CommonFenceException e) { if (e.getErrcode() == FrameworkErrorCode.DuplicateKeyException) { LOGGER.error("Branch transaction has already rollbacked before,prepare fence failed. xid= {},branchId = {}", xid, branchId); addToLogCleanQueue(xid, branchId); @@ -132,36 +133,37 @@ public static Object prepareFence(String xid, Long branchId, String actionName, } /** - * tcc commit method enhanced + * common commit method enhanced * * @param commitMethod commit method - * @param targetTCCBean target tcc bean + * @param targetTCCBean target common bean * @param xid the global transaction id * @param branchId the branch transaction id * @param args commit method's parameters * @return the boolean */ - public static boolean commitFence(Method commitMethod, Object targetTCCBean, + @Override + public boolean commitFence(Method commitMethod, Object targetTCCBean, String xid, Long branchId, Object[] args) { return transactionTemplate.execute(status -> { try { Connection conn = DataSourceUtils.getConnection(dataSource); - TCCFenceDO tccFenceDO = TCC_FENCE_DAO.queryTCCFenceDO(conn, xid, branchId); - if (tccFenceDO == null) { - throw new TCCFenceException(String.format("TCC fence record not exists, commit fence method failed. xid= %s, branchId= %s", xid, branchId), + CommonFenceDO commonFenceDO = COMMON_FENCE_DAO.queryCommonFenceDO(conn, xid, branchId); + if (commonFenceDO == null) { + throw new CommonFenceException(String.format("Common fence record not exists, commit fence method failed. xid= %s, branchId= %s", xid, branchId), FrameworkErrorCode.RecordNotExists); } - if (TCCFenceConstant.STATUS_COMMITTED == tccFenceDO.getStatus()) { - LOGGER.info("Branch transaction has already committed before. idempotency rejected. xid: {}, branchId: {}, status: {}", xid, branchId, tccFenceDO.getStatus()); + if (CommonFenceConstant.STATUS_COMMITTED == commonFenceDO.getStatus()) { + LOGGER.info("Branch transaction has already committed before. idempotency rejected. xid: {}, branchId: {}, status: {}", xid, branchId, commonFenceDO.getStatus()); return true; } - if (TCCFenceConstant.STATUS_ROLLBACKED == tccFenceDO.getStatus() || TCCFenceConstant.STATUS_SUSPENDED == tccFenceDO.getStatus()) { + if (CommonFenceConstant.STATUS_ROLLBACKED == commonFenceDO.getStatus() || CommonFenceConstant.STATUS_SUSPENDED == commonFenceDO.getStatus()) { if (LOGGER.isWarnEnabled()) { - LOGGER.warn("Branch transaction status is unexpected. xid: {}, branchId: {}, status: {}", xid, branchId, tccFenceDO.getStatus()); + LOGGER.warn("Branch transaction status is unexpected. xid: {}, branchId: {}, status: {}", xid, branchId, commonFenceDO.getStatus()); } return false; } - return updateStatusAndInvokeTargetMethod(conn, commitMethod, targetTCCBean, xid, branchId, TCCFenceConstant.STATUS_COMMITTED, status, args); + return updateStatusAndInvokeTargetMethod(conn, commitMethod, targetTCCBean, xid, branchId, CommonFenceConstant.STATUS_COMMITTED, status, args); } catch (Throwable t) { status.setRollbackOnly(); throw new SkipCallbackWrapperException(t); @@ -170,7 +172,7 @@ public static boolean commitFence(Method commitMethod, Object targetTCCBean, } /** - * tcc rollback method enhanced + * Common rollback method enhanced * * @param rollbackMethod rollback method * @param targetTCCBean target tcc bean @@ -180,34 +182,35 @@ public static boolean commitFence(Method commitMethod, Object targetTCCBean, * @param actionName the action name * @return the boolean */ - public static boolean rollbackFence(Method rollbackMethod, Object targetTCCBean, + @Override + public boolean rollbackFence(Method rollbackMethod, Object targetTCCBean, String xid, Long branchId, Object[] args, String actionName) { return transactionTemplate.execute(status -> { try { Connection conn = DataSourceUtils.getConnection(dataSource); - TCCFenceDO tccFenceDO = TCC_FENCE_DAO.queryTCCFenceDO(conn, xid, branchId); + CommonFenceDO commonFenceDO = COMMON_FENCE_DAO.queryCommonFenceDO(conn, xid, branchId); // non_rollback - if (tccFenceDO == null) { - boolean result = insertTCCFenceLog(conn, xid, branchId, actionName, TCCFenceConstant.STATUS_SUSPENDED); - LOGGER.info("Insert tcc fence record result: {}. xid: {}, branchId: {}", result, xid, branchId); + if (commonFenceDO == null) { + boolean result = insertCommonFenceLog(conn, xid, branchId, actionName, CommonFenceConstant.STATUS_SUSPENDED); + LOGGER.info("Insert common fence record result: {}. xid: {}, branchId: {}", result, xid, branchId); if (!result) { - throw new TCCFenceException(String.format("Insert tcc fence record error, rollback fence method failed. xid= %s, branchId= %s", xid, branchId), + throw new CommonFenceException(String.format("Insert common fence record error, rollback fence method failed. xid= %s, branchId= %s", xid, branchId), FrameworkErrorCode.InsertRecordError); } return true; } else { - if (TCCFenceConstant.STATUS_ROLLBACKED == tccFenceDO.getStatus() || TCCFenceConstant.STATUS_SUSPENDED == tccFenceDO.getStatus()) { - LOGGER.info("Branch transaction had already rollbacked before, idempotency rejected. xid: {}, branchId: {}, status: {}", xid, branchId, tccFenceDO.getStatus()); + if (CommonFenceConstant.STATUS_ROLLBACKED == commonFenceDO.getStatus() || CommonFenceConstant.STATUS_SUSPENDED == commonFenceDO.getStatus()) { + LOGGER.info("Branch transaction had already rollbacked before, idempotency rejected. xid: {}, branchId: {}, status: {}", xid, branchId, commonFenceDO.getStatus()); return true; } - if (TCCFenceConstant.STATUS_COMMITTED == tccFenceDO.getStatus()) { + if (CommonFenceConstant.STATUS_COMMITTED == commonFenceDO.getStatus()) { if (LOGGER.isWarnEnabled()) { - LOGGER.warn("Branch transaction status is unexpected. xid: {}, branchId: {}, status: {}", xid, branchId, tccFenceDO.getStatus()); + LOGGER.warn("Branch transaction status is unexpected. xid: {}, branchId: {}, status: {}", xid, branchId, commonFenceDO.getStatus()); } return false; } } - return updateStatusAndInvokeTargetMethod(conn, rollbackMethod, targetTCCBean, xid, branchId, TCCFenceConstant.STATUS_ROLLBACKED, status, args); + return updateStatusAndInvokeTargetMethod(conn, rollbackMethod, targetTCCBean, xid, branchId, CommonFenceConstant.STATUS_ROLLBACKED, status, args); } catch (Throwable t) { status.setRollbackOnly(); throw new SkipCallbackWrapperException(t); @@ -216,7 +219,7 @@ public static boolean rollbackFence(Method rollbackMethod, Object targetTCCBean, } /** - * Insert TCC fence log + * Insert Common fence log * * @param conn the db connection * @param xid the xid @@ -224,13 +227,13 @@ public static boolean rollbackFence(Method rollbackMethod, Object targetTCCBean, * @param status the status * @return the boolean */ - private static boolean insertTCCFenceLog(Connection conn, String xid, Long branchId, String actionName, Integer status) { - TCCFenceDO tccFenceDO = new TCCFenceDO(); - tccFenceDO.setXid(xid); - tccFenceDO.setBranchId(branchId); - tccFenceDO.setActionName(actionName); - tccFenceDO.setStatus(status); - return TCC_FENCE_DAO.insertTCCFenceDO(conn, tccFenceDO); + private static boolean insertCommonFenceLog(Connection conn, String xid, Long branchId, String actionName, Integer status) { + CommonFenceDO commonFenceDO = new CommonFenceDO(); + commonFenceDO.setXid(xid); + commonFenceDO.setBranchId(branchId); + commonFenceDO.setActionName(actionName); + commonFenceDO.setStatus(status); + return COMMON_FENCE_DAO.insertCommonFenceDO(conn, commonFenceDO); } /** @@ -247,7 +250,7 @@ private static boolean updateStatusAndInvokeTargetMethod(Connection conn, Method String xid, Long branchId, int status, TransactionStatus transactionStatus, Object[] args) throws Exception { - boolean result = TCC_FENCE_DAO.updateTCCFenceDO(conn, xid, branchId, status, TCCFenceConstant.STATUS_TRIED); + boolean result = COMMON_FENCE_DAO.updateCommonFenceDO(conn, xid, branchId, status, CommonFenceConstant.STATUS_TRIED); if (result) { // invoke two phase method Object ret = method.invoke(targetTCCBean, args); @@ -267,7 +270,7 @@ private static boolean updateStatusAndInvokeTargetMethod(Connection conn, Method } /** - * Delete TCC Fence + * Delete Common Fence * * @param xid the global transaction id * @param branchId the branch transaction id @@ -278,7 +281,7 @@ public static boolean deleteFence(String xid, Long branchId) { boolean ret = false; try { Connection conn = DataSourceUtils.getConnection(dataSource); - ret = TCC_FENCE_DAO.deleteTCCFenceDO(conn, xid, branchId); + ret = COMMON_FENCE_DAO.deleteCommonFenceDO(conn, xid, branchId); } catch (RuntimeException e) { status.setRollbackOnly(); LOGGER.error("delete fence log failed, xid: {}, branchId: {}", xid, branchId, e); @@ -287,26 +290,31 @@ public static boolean deleteFence(String xid, Long branchId) { }); } - - - public static int deleteFenceByDate(Date datetime) { - DataSource dataSource = TCCFenceHandler.getDataSource(); + /** + * Delete Common Fence By Datetime + * + * @param datetime datetime + * @return the deleted row count + */ + @Override + public int deleteFenceByDate(Date datetime) { + DataSource dataSource = SpringFenceHandler.getDataSource(); Connection connection = null; int total = 0; try { connection = DataSourceUtils.getConnection(dataSource); if (isOracle(connection)) { // delete by date if DB is oracle - return TCC_FENCE_DAO.deleteTCCFenceDOByDate(connection, datetime); + return COMMON_FENCE_DAO.deleteCommonFenceDOByDate(connection, datetime); } //delete by id if DB is not oracle while (true) { - Set xidSet = TCC_FENCE_DAO.queryEndStatusXidsByDate(connection, datetime, LIMIT_DELETE); + Set xidSet = COMMON_FENCE_DAO.queryEndStatusXidsByDate(connection, datetime, LIMIT_DELETE); if (xidSet.isEmpty()) { break; } - total += TCC_FENCE_DAO.deleteTCCFenceDO(connection, new ArrayList<>(xidSet)); + total += COMMON_FENCE_DAO.deleteTCCFenceDO(connection, new ArrayList<>(xidSet)); } } catch (RuntimeException e) { LOGGER.error("delete fence log failed ", e); @@ -352,7 +360,7 @@ private static void addToLogCleanQueue(final String xid, final long branchId) { /** * clean fence log that has the final status runnable. * - * @see TCCFenceConstant + * @see CommonFenceConstant */ private static class FenceLogCleanRunnable implements Runnable { @Override @@ -361,7 +369,7 @@ public void run() { try { FenceLogIdentity logIdentity = LOG_QUEUE.take(); - boolean ret = TCCFenceHandler.deleteFence(logIdentity.getXid(), logIdentity.getBranchId()); + boolean ret = SpringFenceHandler.deleteFence(logIdentity.getXid(), logIdentity.getBranchId()); if (!ret) { LOGGER.error("delete fence log failed, xid: {}, branchId: {}", logIdentity.getXid(), logIdentity.getBranchId()); } @@ -401,4 +409,4 @@ public void setBranchId(Long branchId) { this.branchId = branchId; } } -} +} \ No newline at end of file diff --git a/spring/src/main/java/io/seata/spring/SpringTargetClassParser.java b/spring/src/main/java/io/seata/spring/SpringTargetClassParser.java new file mode 100644 index 00000000000..47dbb9969da --- /dev/null +++ b/spring/src/main/java/io/seata/spring/SpringTargetClassParser.java @@ -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.spring; + +import io.seata.integration.tx.api.interceptor.parser.TargetClassParser; +import io.seata.spring.util.SpringProxyUtils; + +/** + * @author leezongjie + * @date 2022/12/17 + */ +public class SpringTargetClassParser implements TargetClassParser { + @Override + public Class findTargetClass(Object target) throws Exception { + return SpringProxyUtils.findTargetClass(target); + } + + @Override + public Class[] findInterfaces(Object target) throws Exception { + return SpringProxyUtils.findInterfaces(target); + } +} diff --git a/spring/src/main/java/io/seata/spring/annotation/AspectTransactionalInterceptor.java b/spring/src/main/java/io/seata/spring/annotation/AspectTransactionalInterceptor.java index df61dc15aea..15ea43cd82f 100644 --- a/spring/src/main/java/io/seata/spring/annotation/AspectTransactionalInterceptor.java +++ b/spring/src/main/java/io/seata/spring/annotation/AspectTransactionalInterceptor.java @@ -15,12 +15,28 @@ */ package io.seata.spring.annotation; +import java.lang.reflect.Method; + +import io.seata.integration.tx.api.annotation.AspectTransactional; +import io.seata.integration.tx.api.interceptor.DefaultInvocationWrapper; +import io.seata.integration.tx.api.interceptor.InvocationWrapper; +import io.seata.integration.tx.api.interceptor.handler.GlobalTransactionalInterceptorHandler; import io.seata.tm.api.FailureHandler; +import org.aopalliance.intercept.MethodInterceptor; +import org.aopalliance.intercept.MethodInvocation; +import org.springframework.aop.support.AopUtils; +import org.springframework.util.ClassUtils; /** - * @author funkye + * Aspect transactional interceptor. + * + * @author ruishansun */ -public class AspectTransactionalInterceptor extends GlobalTransactionalInterceptor { +public class AspectTransactionalInterceptor implements MethodInterceptor { + + private final FailureHandler failureHandler; + private final AspectTransactional aspectTransactional; + private final GlobalTransactionalInterceptorHandler globalTransactionalInterceptorHandler; private static final AspectTransactional DEFAULT_ASPECT_TRANSACTIONAL = new AspectTransactional(); @@ -37,8 +53,16 @@ public AspectTransactionalInterceptor(FailureHandler failureHandler) { } public AspectTransactionalInterceptor(FailureHandler failureHandler, AspectTransactional aspectTransactional) { - super(failureHandler); + this.failureHandler = failureHandler; this.aspectTransactional = aspectTransactional; + this.globalTransactionalInterceptorHandler = new GlobalTransactionalInterceptorHandler(this.failureHandler, null, this.aspectTransactional); } + @Override + public Object invoke(MethodInvocation invocation) throws Throwable { + Class targetClass = invocation.getThis() != null ? AopUtils.getTargetClass(invocation.getThis()) : null; + Method specificMethod = ClassUtils.getMostSpecificMethod(invocation.getMethod(), targetClass); + InvocationWrapper invocationWrapper = new DefaultInvocationWrapper(null, invocation.getThis(), specificMethod, invocation.getArguments()); + return this.globalTransactionalInterceptorHandler.invoke(invocationWrapper); + } } diff --git a/spring/src/main/java/io/seata/spring/annotation/GlobalTransactionScanner.java b/spring/src/main/java/io/seata/spring/annotation/GlobalTransactionScanner.java index ef455ea6e54..423dc9490d7 100644 --- a/spring/src/main/java/io/seata/spring/annotation/GlobalTransactionScanner.java +++ b/spring/src/main/java/io/seata/spring/annotation/GlobalTransactionScanner.java @@ -23,8 +23,6 @@ import java.util.Set; import java.util.concurrent.atomic.AtomicBoolean; -import javax.annotation.Nullable; - import io.seata.common.util.CollectionUtils; import io.seata.common.util.StringUtils; import io.seata.config.ConfigurationCache; @@ -35,25 +33,18 @@ import io.seata.core.rpc.ShutdownHook; import io.seata.core.rpc.netty.RmNettyRemotingClient; import io.seata.core.rpc.netty.TmNettyRemotingClient; +import io.seata.integration.tx.api.util.ProxyUtil; import io.seata.rm.RMClient; import io.seata.spring.annotation.scannercheckers.PackageScannerChecker; -import io.seata.spring.tcc.TccActionInterceptor; -import io.seata.spring.util.OrderUtil; -import io.seata.spring.util.SpringProxyUtils; -import io.seata.spring.util.TCCBeanParserUtils; import io.seata.tm.TMClient; import io.seata.tm.api.FailureHandler; -import org.aopalliance.aop.Advice; +import io.seata.tm.api.FailureHandlerHolder; import org.aopalliance.intercept.MethodInterceptor; -import org.aopalliance.intercept.MethodInvocation; import org.apache.commons.lang.ArrayUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.springframework.aop.Advisor; import org.springframework.aop.TargetSource; -import org.springframework.aop.framework.AdvisedSupport; import org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator; -import org.springframework.aop.support.AopUtils; import org.springframework.beans.BeansException; import org.springframework.beans.factory.DisposableBean; import org.springframework.beans.factory.FactoryBean; @@ -62,7 +53,6 @@ import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import org.springframework.context.ConfigurableApplicationContext; -import org.springframework.core.Ordered; import static io.seata.common.DefaultValues.DEFAULT_DISABLE_GLOBAL_TRANSACTION; import static io.seata.common.DefaultValues.DEFAULT_TX_GROUP; @@ -86,8 +76,6 @@ public class GlobalTransactionScanner extends AbstractAutoProxyCreator private static final int ORDER_NUM = 1024; private static final int DEFAULT_MODE = AT_MODE + MT_MODE; - private static final String SPRING_TRANSACTION_INTERCEPTOR_CLASS_NAME = "org.springframework.transaction.interceptor.TransactionInterceptor"; - private static final Set PROXYED_SET = new HashSet<>(); private static final Set EXCLUDE_BEAN_NAME_SET = new HashSet<>(); private static final Set SCANNER_CHECKER_SET = new LinkedHashSet<>(); @@ -178,6 +166,7 @@ public GlobalTransactionScanner(String applicationId, String txServiceGroup, int this.txServiceGroup = txServiceGroup; this.mode = mode; this.failureHandlerHook = failureHandlerHook; + FailureHandlerHolder.setFailureHandler(this.failureHandlerHook); } /** @@ -209,8 +198,8 @@ private void initClient() { } if (DEFAULT_TX_GROUP_OLD.equals(txServiceGroup)) { LOGGER.warn("the default value of seata.tx-service-group: {} has already changed to {} since Seata 1.5, " + - "please change your default configuration as soon as possible " + - "and we don't recommend you to use default tx-service-group's value provided by seata", + "please change your default configuration as soon as possible " + + "and we don't recommend you to use default tx-service-group's value provided by seata", DEFAULT_TX_GROUP_OLD, DEFAULT_TX_GROUP); } if (StringUtils.isNullOrEmpty(applicationId) || StringUtils.isNullOrEmpty(txServiceGroup)) { @@ -245,23 +234,23 @@ private void registerSpringShutdownHook() { /** * The following will be scanned, and added corresponding interceptor: - * + *

* TM: * @see io.seata.spring.annotation.GlobalTransactional // TM annotation * Corresponding interceptor: - * @see io.seata.spring.annotation.GlobalTransactionalInterceptor#handleGlobalTransaction(MethodInvocation, AspectTransactional) // TM handler - * + * @see io.seata.integration.tx.api.interceptor.handler.GlobalTransactionalInterceptorHandler#handleGlobalTransaction(io.seata.integration.tx.api.interceptor.InvocationWrapper, io.seata.integration.tx.api.annotation.AspectTransactional) // TM handler + *

* GlobalLock: * @see io.seata.spring.annotation.GlobalLock // GlobalLock annotation * Corresponding interceptor: - * @see io.seata.spring.annotation.GlobalTransactionalInterceptor#handleGlobalLock(MethodInvocation, GlobalLock) // GlobalLock handler - * + * @see io.seata.integration.tx.api.interceptor.handler.GlobalTransactionalInterceptorHandler#handleGlobalLock(io.seata.integration.tx.api.interceptor.InvocationWrapper, io.seata.spring.annotation.GlobalLock) // GlobalLock handler + *

* TCC mode: * @see io.seata.rm.tcc.api.LocalTCC // TCC annotation on interface * @see io.seata.rm.tcc.api.TwoPhaseBusinessAction // TCC annotation on try method - * @see io.seata.rm.tcc.remoting.RemotingParser // Remote TCC service parser + * @see io.seata.integration.tx.api.remoting.RemotingParser // Remote TCC service parser * Corresponding interceptor: - * @see io.seata.spring.tcc.TccActionInterceptor // the interceptor of TCC mode + * @see io.seata.rm.tcc.interceptor.TccActionInterceptorHandler // the interceptor of TCC mode */ @Override protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) { @@ -275,48 +264,11 @@ protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) if (PROXYED_SET.contains(beanName)) { return bean; } - interceptor = null; - //check TCC proxy - if (TCCBeanParserUtils.isTccAutoProxy(bean, beanName, applicationContext)) { - // init tcc fence clean task if enable useTccFence - TCCBeanParserUtils.initTccFenceCleanTask(TCCBeanParserUtils.getRemotingDesc(beanName), applicationContext); - //TCC interceptor, proxy bean of sofa:reference/dubbo:reference, and LocalTCC - interceptor = new TccActionInterceptor(TCCBeanParserUtils.getRemotingDesc(beanName)); - ConfigurationCache.addConfigListener(ConfigurationKeys.DISABLE_GLOBAL_TRANSACTION, - (ConfigurationChangeListener)interceptor); - } else { - Class serviceInterface = SpringProxyUtils.findTargetClass(bean); - Class[] interfacesIfJdk = SpringProxyUtils.findInterfaces(bean); - - if (!existsAnnotation(serviceInterface) - && !existsAnnotation(interfacesIfJdk)) { - return bean; - } - - if (globalTransactionalInterceptor == null) { - globalTransactionalInterceptor = new GlobalTransactionalInterceptor(failureHandlerHook); - ConfigurationCache.addConfigListener( - ConfigurationKeys.DISABLE_GLOBAL_TRANSACTION, - (ConfigurationChangeListener)globalTransactionalInterceptor); - } - interceptor = globalTransactionalInterceptor; - } - - LOGGER.info("Bean [{}] with name [{}] would use interceptor [{}]", bean.getClass().getName(), beanName, interceptor.getClass().getName()); - if (!AopUtils.isAopProxy(bean)) { - bean = super.wrapIfNecessary(bean, beanName, cacheKey); - } else { - AdvisedSupport advised = SpringProxyUtils.getAdvisedSupport(bean); - Advisor[] advisor = buildAdvisors(beanName, getAdvicesAndAdvisorsForBean(null, null, null)); - int pos; - for (Advisor avr : advisor) { - // Find the position based on the advisor's order, and add to advisors by pos - pos = findAddSeataAdvisorPosition(advised, avr); - advised.addAdvisor(pos, avr); - } + Object resultBean = ProxyUtil.createProxy(bean); + if (bean != resultBean) { + PROXYED_SET.add(beanName); } - PROXYED_SET.add(beanName); - return bean; + return resultBean; } } catch (Exception exx) { throw new RuntimeException(exx); @@ -346,148 +298,6 @@ private boolean doCheckers(Object bean, String beanName) { return true; } - - //region the methods about findAddSeataAdvisorPosition START - - /** - * Find pos for `advised.addAdvisor(pos, avr);` - * - * @param advised the advised - * @param seataAdvisor the seata advisor - * @return the pos - */ - private int findAddSeataAdvisorPosition(AdvisedSupport advised, Advisor seataAdvisor) { - // Get seataAdvisor's order and interceptorPosition - int seataOrder = OrderUtil.getOrder(seataAdvisor); - SeataInterceptorPosition seataInterceptorPosition = getSeataInterceptorPosition(seataAdvisor); - - // If the interceptorPosition is any, check lowest or highest. - if (SeataInterceptorPosition.Any == seataInterceptorPosition) { - if (seataOrder == Ordered.LOWEST_PRECEDENCE) { - // the last position - return advised.getAdvisors().length; - } else if (seataOrder == Ordered.HIGHEST_PRECEDENCE) { - // the first position - return 0; - } - } else { - // If the interceptorPosition is not any, compute position if has TransactionInterceptor. - Integer position = computePositionIfHasTransactionInterceptor(advised, seataAdvisor, seataInterceptorPosition, seataOrder); - if (position != null) { - // the position before or after TransactionInterceptor - return position; - } - } - - // Find position - return this.findPositionInAdvisors(advised.getAdvisors(), seataAdvisor); - } - - @Nullable - private Integer computePositionIfHasTransactionInterceptor(AdvisedSupport advised, Advisor seataAdvisor, SeataInterceptorPosition seataInterceptorPosition, int seataOrder) { - // Find the TransactionInterceptor's advisor, order and position - Advisor otherAdvisor = null; - Integer transactionInterceptorPosition = null; - Integer transactionInterceptorOrder = null; - for (int i = 0, l = advised.getAdvisors().length; i < l; ++i) { - otherAdvisor = advised.getAdvisors()[i]; - if (isTransactionInterceptor(otherAdvisor)) { - transactionInterceptorPosition = i; - transactionInterceptorOrder = OrderUtil.getOrder(otherAdvisor); - break; - } - } - // If the TransactionInterceptor does not exist, return null - if (transactionInterceptorPosition == null) { - return null; - } - - // Reset seataOrder if the seataOrder is not match the position - Advice seataAdvice = seataAdvisor.getAdvice(); - if (SeataInterceptorPosition.AfterTransaction == seataInterceptorPosition && OrderUtil.higherThan(seataOrder, transactionInterceptorOrder)) { - int newSeataOrder = OrderUtil.lower(transactionInterceptorOrder, 1); - ((SeataInterceptor)seataAdvice).setOrder(newSeataOrder); - if (LOGGER.isWarnEnabled()) { - LOGGER.warn("The {}'s order '{}' is higher or equals than {}'s order '{}' , reset {}'s order to lower order '{}'.", - seataAdvice.getClass().getSimpleName(), seataOrder, - otherAdvisor.getAdvice().getClass().getSimpleName(), transactionInterceptorOrder, - seataAdvice.getClass().getSimpleName(), newSeataOrder); - } - // the position after the TransactionInterceptor's advisor - return transactionInterceptorPosition + 1; - } else if (SeataInterceptorPosition.BeforeTransaction == seataInterceptorPosition && OrderUtil.lowerThan(seataOrder, transactionInterceptorOrder)) { - int newSeataOrder = OrderUtil.higher(transactionInterceptorOrder, 1); - ((SeataInterceptor)seataAdvice).setOrder(newSeataOrder); - if (LOGGER.isWarnEnabled()) { - LOGGER.warn("The {}'s order '{}' is lower or equals than {}'s order '{}' , reset {}'s order to higher order '{}'.", - seataAdvice.getClass().getSimpleName(), seataOrder, - otherAdvisor.getAdvice().getClass().getSimpleName(), transactionInterceptorOrder, - seataAdvice.getClass().getSimpleName(), newSeataOrder); - } - // the position before the TransactionInterceptor's advisor - return transactionInterceptorPosition; - } - - return null; - } - - private int findPositionInAdvisors(Advisor[] advisors, Advisor seataAdvisor) { - Advisor advisor; - for (int i = 0, l = advisors.length; i < l; ++i) { - advisor = advisors[i]; - if (OrderUtil.higherOrEquals(seataAdvisor, advisor)) { - // the position before the current advisor - return i; - } - } - - // the last position, after all the advisors - return advisors.length; - } - - private SeataInterceptorPosition getSeataInterceptorPosition(Advisor seataAdvisor) { - Advice seataAdvice = seataAdvisor.getAdvice(); - if (seataAdvice instanceof SeataInterceptor) { - return ((SeataInterceptor)seataAdvice).getPosition(); - } else { - return SeataInterceptorPosition.Any; - } - } - - private boolean isTransactionInterceptor(Advisor advisor) { - return SPRING_TRANSACTION_INTERCEPTOR_CLASS_NAME.equals(advisor.getAdvice().getClass().getName()); - } - - //endregion the methods about findAddSeataAdvisorPosition END - - - private boolean existsAnnotation(Class... classes) { - if (CollectionUtils.isNotEmpty(classes)) { - for (Class clazz : classes) { - if (clazz == null) { - continue; - } - GlobalTransactional trxAnno = clazz.getAnnotation(GlobalTransactional.class); - if (trxAnno != null) { - return true; - } - Method[] methods = clazz.getMethods(); - for (Method method : methods) { - trxAnno = method.getAnnotation(GlobalTransactional.class); - if (trxAnno != null) { - return true; - } - - GlobalLock lockAnno = method.getAnnotation(GlobalLock.class); - if (lockAnno != null) { - return true; - } - } - } - } - return false; - } - private MethodDesc makeMethodDesc(GlobalTransactional anno, Method method) { return new MethodDesc(anno, method); } @@ -505,7 +315,7 @@ public void afterPropertiesSet() { LOGGER.info("Global transaction is disabled."); } ConfigurationCache.addConfigListener(ConfigurationKeys.DISABLE_GLOBAL_TRANSACTION, - (ConfigurationChangeListener)this); + (ConfigurationChangeListener) this); return; } if (initialized.compareAndSet(false, true)) { diff --git a/spring/src/main/java/io/seata/spring/remoting/parser/RemotingFactoryBeanParser.java b/spring/src/main/java/io/seata/spring/remoting/parser/RemotingFactoryBeanParser.java new file mode 100644 index 00000000000..457e4006efe --- /dev/null +++ b/spring/src/main/java/io/seata/spring/remoting/parser/RemotingFactoryBeanParser.java @@ -0,0 +1,85 @@ +/* + * 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.spring.remoting.parser; + +import io.seata.common.exception.FrameworkException; +import io.seata.integration.tx.api.remoting.RemotingDesc; +import io.seata.integration.tx.api.remoting.parser.AbstractedRemotingParser; +import io.seata.integration.tx.api.remoting.parser.DefaultRemotingParser; +import io.seata.spring.util.SpringProxyUtils; +import org.springframework.context.ApplicationContext; + +/** + * @author leezongjie + * @date 2022/11/26 + */ +public class RemotingFactoryBeanParser extends AbstractedRemotingParser { + + public static ApplicationContext applicationContext; + + /** + * if it is proxy bean, check if the FactoryBean is Remoting bean + * + * @param bean the bean + * @param beanName the bean name + * @return boolean boolean + */ + protected static Object getRemotingFactoryBean(Object bean, String beanName) { + if (!SpringProxyUtils.isProxy(bean)) { + return null; + } + //the FactoryBean of proxy bean + String factoryBeanName = "&" + beanName; + Object factoryBean = null; + if (applicationContext != null && applicationContext.containsBean(factoryBeanName)) { + factoryBean = applicationContext.getBean(factoryBeanName); + } + return factoryBean; + } + + @Override + public boolean isReference(Object bean, String beanName) { + Object factoryBean = getRemotingFactoryBean(bean, beanName); + if (factoryBean == null) { + return false; + } + return DefaultRemotingParser.get().isReference(bean, beanName); + } + + @Override + public boolean isService(Object bean, String beanName) { + Object factoryBean = getRemotingFactoryBean(bean, beanName); + if (factoryBean == null) { + return false; + } + return DefaultRemotingParser.get().isReference(bean, beanName); + } + + @Override + public RemotingDesc getServiceDesc(Object bean, String beanName) throws FrameworkException { + Object factoryBean = getRemotingFactoryBean(bean, beanName); + if (factoryBean == null) { + return null; + } + return DefaultRemotingParser.get().getServiceDesc(bean, beanName); + } + + + @Override + public short getProtocol() { + return 0; + } +} \ No newline at end of file diff --git a/spring/src/main/java/io/seata/spring/tcc/TccActionInterceptor.java b/spring/src/main/java/io/seata/spring/tcc/TccActionInterceptor.java deleted file mode 100644 index b09f282ec72..00000000000 --- a/spring/src/main/java/io/seata/spring/tcc/TccActionInterceptor.java +++ /dev/null @@ -1,181 +0,0 @@ -/* - * 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.spring.tcc; - -import java.lang.reflect.Method; -import javax.annotation.Nullable; - -import io.seata.common.DefaultValues; -import io.seata.config.ConfigurationChangeEvent; -import io.seata.config.ConfigurationChangeListener; -import io.seata.config.ConfigurationFactory; -import io.seata.core.constants.ConfigurationKeys; -import io.seata.core.context.RootContext; -import io.seata.core.model.BranchType; -import io.seata.rm.tcc.api.TwoPhaseBusinessAction; -import io.seata.rm.tcc.interceptor.ActionInterceptorHandler; -import io.seata.rm.tcc.remoting.RemotingDesc; -import io.seata.rm.tcc.remoting.parser.DubboUtil; -import io.seata.spring.util.SpringProxyUtils; -import org.aopalliance.intercept.MethodInterceptor; -import org.aopalliance.intercept.MethodInvocation; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.slf4j.MDC; -import org.springframework.core.Ordered; - -import static io.seata.common.DefaultValues.DEFAULT_DISABLE_GLOBAL_TRANSACTION; -import static io.seata.core.constants.ConfigurationKeys.TCC_ACTION_INTERCEPTOR_ORDER; - -/** - * TCC Interceptor - * - * @author zhangsen - * @author wang.liang - */ -public class TccActionInterceptor implements MethodInterceptor, ConfigurationChangeListener, Ordered { - - private static final Logger LOGGER = LoggerFactory.getLogger(TccActionInterceptor.class); - private static final int ORDER_NUM = ConfigurationFactory.getInstance().getInt(TCC_ACTION_INTERCEPTOR_ORDER, - DefaultValues.TCC_ACTION_INTERCEPTOR_ORDER); - - private ActionInterceptorHandler actionInterceptorHandler = new ActionInterceptorHandler(); - - private volatile boolean disable = ConfigurationFactory.getInstance().getBoolean( - ConfigurationKeys.DISABLE_GLOBAL_TRANSACTION, DEFAULT_DISABLE_GLOBAL_TRANSACTION); - - /** - * remoting bean info - */ - protected RemotingDesc remotingDesc; - - /** - * Instantiates a new Tcc action interceptor. - */ - public TccActionInterceptor() { - } - - /** - * Instantiates a new Tcc action interceptor. - * - * @param remotingDesc the remoting desc - */ - public TccActionInterceptor(RemotingDesc remotingDesc) { - this.remotingDesc = remotingDesc; - } - - @Override - public Object invoke(final MethodInvocation invocation) throws Throwable { - if (!RootContext.inGlobalTransaction() || disable || RootContext.inSagaBranch()) { - //not in transaction, or this interceptor is disabled - return invocation.proceed(); - } - Method method = getActionInterfaceMethod(invocation); - TwoPhaseBusinessAction businessAction = method.getAnnotation(TwoPhaseBusinessAction.class); - //try method - if (businessAction != null) { - //save the xid - String xid = RootContext.getXID(); - //save the previous branchType - BranchType previousBranchType = RootContext.getBranchType(); - //if not TCC, bind TCC branchType - if (BranchType.TCC != previousBranchType) { - RootContext.bindBranchType(BranchType.TCC); - } - try { - //Handler the TCC Aspect, and return the business result - return actionInterceptorHandler.proceed(method, invocation.getArguments(), xid, businessAction, - invocation::proceed); - } finally { - //if not TCC, unbind branchType - if (BranchType.TCC != previousBranchType) { - RootContext.unbindBranchType(); - } - //MDC remove branchId - MDC.remove(RootContext.MDC_KEY_BRANCH_ID); - } - } - - //not TCC try method - return invocation.proceed(); - } - - /** - * get the method from interface - * - * @param invocation the invocation - * @return the action interface method - */ - protected Method getActionInterfaceMethod(MethodInvocation invocation) { - Class serviceType = null; - try { - if (remotingDesc == null) { - serviceType = getProxyInterface(invocation.getThis()); - } else { - serviceType = remotingDesc.getServiceClass(); - } - if (serviceType == null && remotingDesc != null && remotingDesc.getServiceClassName() != null) { - serviceType = Class.forName(remotingDesc.getServiceClassName(), true, - Thread.currentThread().getContextClassLoader()); - } - if (serviceType == null) { - return invocation.getMethod(); - } - return serviceType.getMethod(invocation.getMethod().getName(), - invocation.getMethod().getParameterTypes()); - } catch (NoSuchMethodException e) { - if (serviceType != null && !"toString".equals(invocation.getMethod().getName())) { - LOGGER.warn("no such method '{}' from interface {}", invocation.getMethod().getName(), serviceType.getName()); - } - return invocation.getMethod(); - } catch (Exception e) { - LOGGER.warn("get Method from interface failed", e); - return invocation.getMethod(); - } - } - - /** - * get the interface of proxy - * - * @param proxyBean the proxy bean - * @return proxy interface - * @throws Exception the exception - */ - @Nullable - protected Class getProxyInterface(Object proxyBean) throws Exception { - if (DubboUtil.isDubboProxyName(proxyBean.getClass().getName())) { - //dubbo javaassist proxy - return DubboUtil.getAssistInterface(proxyBean); - } else { - //jdk/cglib proxy - return SpringProxyUtils.getTargetInterface(proxyBean); - } - } - - @Override - public void onChangeEvent(ConfigurationChangeEvent event) { - if (ConfigurationKeys.DISABLE_GLOBAL_TRANSACTION.equals(event.getDataId())) { - LOGGER.info("{} config changed, old value:{}, new value:{}", ConfigurationKeys.DISABLE_GLOBAL_TRANSACTION, - disable, event.getNewValue()); - disable = Boolean.parseBoolean(event.getNewValue().trim()); - } - } - - @Override - public int getOrder() { - return ORDER_NUM; - } -} diff --git a/spring/src/main/java/io/seata/spring/tcc/TccAnnotationProcessor.java b/spring/src/main/java/io/seata/spring/tcc/TccAnnotationProcessor.java index 9b496fbe533..130e3cad82e 100644 --- a/spring/src/main/java/io/seata/spring/tcc/TccAnnotationProcessor.java +++ b/spring/src/main/java/io/seata/spring/tcc/TccAnnotationProcessor.java @@ -15,9 +15,9 @@ */ package io.seata.spring.tcc; +import io.seata.integration.tx.api.remoting.RemotingDesc; +import io.seata.integration.tx.api.util.ProxyUtil; import io.seata.rm.tcc.api.TwoPhaseBusinessAction; -import io.seata.rm.tcc.remoting.RemotingDesc; -import io.seata.spring.util.TCCBeanParserUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.BeansException; @@ -103,11 +103,10 @@ public void addTccAdvise(Object bean, String beanName, Field field, Class servic RemotingDesc remotingDesc = new RemotingDesc(); remotingDesc.setServiceClass(serviceClass); - TccActionInterceptor actionInterceptor = new TccActionInterceptor(remotingDesc); - Object proxyBean = TCCBeanParserUtils.createProxy(serviceClass, fieldValue, actionInterceptor); + Object proxyBean = ProxyUtil.createProxy(bean); field.setAccessible(true); field.set(bean, proxyBean); - LOGGER.info("Bean[" + bean.getClass().getName() + "] with name [" + field.getName() + "] would use proxy [" + actionInterceptor.getClass().getName() + "]"); + LOGGER.info("Bean[" + bean.getClass().getName() + "] with name [" + field.getName() + "] would use proxy"); } } } diff --git a/spring/src/main/java/io/seata/spring/util/SpringProxyUtils.java b/spring/src/main/java/io/seata/spring/util/SpringProxyUtils.java index c07455b9b79..37bc4f16cb0 100644 --- a/spring/src/main/java/io/seata/spring/util/SpringProxyUtils.java +++ b/spring/src/main/java/io/seata/spring/util/SpringProxyUtils.java @@ -22,7 +22,7 @@ import java.util.Set; import io.seata.common.util.CollectionUtils; -import io.seata.rm.tcc.remoting.parser.DubboUtil; +import io.seata.integration.tx.api.util.DubboUtil; import org.springframework.aop.TargetSource; import org.springframework.aop.framework.Advised; import org.springframework.aop.framework.AdvisedSupport; @@ -122,7 +122,7 @@ public static boolean isProxy(Object bean) { */ public static Class getTargetInterface(Object proxy) throws Exception { if (proxy == null) { - throw new java.lang.IllegalArgumentException("proxy can not be null"); + throw new IllegalArgumentException("proxy can not be null"); } //jdk proxy @@ -143,7 +143,7 @@ public static Class getTargetInterface(Object proxy) throws Exception { */ protected static Class getTargetClass(Object proxy) throws Exception { if (proxy == null) { - throw new java.lang.IllegalArgumentException("proxy can not be null"); + throw new IllegalArgumentException("proxy can not be null"); } //not proxy if (!AopUtils.isAopProxy(proxy)) { diff --git a/spring/src/main/java/io/seata/spring/util/TCCBeanParserUtils.java b/spring/src/main/java/io/seata/spring/util/TCCBeanParserUtils.java deleted file mode 100644 index 5ccd339cc3b..00000000000 --- a/spring/src/main/java/io/seata/spring/util/TCCBeanParserUtils.java +++ /dev/null @@ -1,210 +0,0 @@ -/* - * 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.spring.util; - -import io.seata.common.DefaultValues; -import io.seata.rm.tcc.api.TwoPhaseBusinessAction; -import io.seata.rm.tcc.config.TCCFenceConfig; -import io.seata.rm.tcc.remoting.Protocols; -import io.seata.rm.tcc.remoting.RemotingDesc; -import io.seata.rm.tcc.remoting.RemotingParser; -import io.seata.rm.tcc.remoting.parser.DefaultRemotingParser; -import io.seata.spring.tcc.TccActionInterceptor; -import org.springframework.aop.framework.ProxyFactory; -import org.springframework.context.ApplicationContext; - -import java.lang.reflect.Method; - -/** - * parser TCC bean - * - * @author zhangsen - */ -public class TCCBeanParserUtils { - - private TCCBeanParserUtils() { - } - - /** - * is auto proxy TCC bean - * - * @param bean the bean - * @param beanName the bean name - * @param applicationContext the application context - * @return boolean boolean - */ - public static boolean isTccAutoProxy(Object bean, String beanName, ApplicationContext applicationContext) { - boolean isRemotingBean = parserRemotingServiceInfo(bean, beanName); - //get RemotingBean description - RemotingDesc remotingDesc = DefaultRemotingParser.get().getRemotingBeanDesc(beanName); - //is remoting bean - if (isRemotingBean) { - if (remotingDesc != null && remotingDesc.getProtocol() == Protocols.IN_JVM) { - //LocalTCC - return isTccProxyTargetBean(remotingDesc); - } else { - // sofa:reference / dubbo:reference, factory bean - return false; - } - } else { - if (remotingDesc == null) { - //check FactoryBean - if (isRemotingFactoryBean(bean, beanName, applicationContext)) { - remotingDesc = DefaultRemotingParser.get().getRemotingBeanDesc(beanName); - return isTccProxyTargetBean(remotingDesc); - } else { - return false; - } - } else { - return isTccProxyTargetBean(remotingDesc); - } - } - } - - /** - * if it is proxy bean, check if the FactoryBean is Remoting bean - * - * @param bean the bean - * @param beanName the bean name - * @param applicationContext the application context - * @return boolean boolean - */ - protected static boolean isRemotingFactoryBean(Object bean, String beanName, - ApplicationContext applicationContext) { - if (!SpringProxyUtils.isProxy(bean)) { - return false; - } - //the FactoryBean of proxy bean - String factoryBeanName = "&" + beanName; - Object factoryBean = null; - if (applicationContext != null && applicationContext.containsBean(factoryBeanName)) { - factoryBean = applicationContext.getBean(factoryBeanName); - } - //not factory bean, needn't proxy - if (factoryBean == null) { - return false; - } - //get FactoryBean info - return parserRemotingServiceInfo(factoryBean, beanName); - } - - /** - * is TCC proxy-bean/target-bean: LocalTCC , the proxy bean of sofa:reference/dubbo:reference - * - * @param remotingDesc the remoting desc - * @return boolean boolean - */ - public static boolean isTccProxyTargetBean(RemotingDesc remotingDesc) { - if (remotingDesc == null) { - return false; - } - //check if it is TCC bean - boolean isTccClazz = false; - Class tccServiceClazz = remotingDesc.getServiceClass(); - Method[] methods = tccServiceClazz.getMethods(); - TwoPhaseBusinessAction twoPhaseBusinessAction; - for (Method method : methods) { - twoPhaseBusinessAction = method.getAnnotation(TwoPhaseBusinessAction.class); - if (twoPhaseBusinessAction != null) { - isTccClazz = true; - break; - } - } - if (!isTccClazz) { - return false; - } - short protocols = remotingDesc.getProtocol(); - //LocalTCC - if (Protocols.IN_JVM == protocols) { - //in jvm TCC bean , AOP - return true; - } - // sofa:reference / dubbo:reference, AOP - return remotingDesc.isReference(); - } - - /** - * init tcc fence clean task if enable useTccFence - * - * @param remotingDesc the remoting desc - * @param applicationContext applicationContext - */ - public static void initTccFenceCleanTask(RemotingDesc remotingDesc, ApplicationContext applicationContext) { - if (remotingDesc == null) { - return; - } - if (applicationContext != null && applicationContext.containsBean(DefaultValues.TCC_FENCE_BEAN_NAME)) { - TCCFenceConfig tccFenceConfig = (TCCFenceConfig) applicationContext.getBean(DefaultValues.TCC_FENCE_BEAN_NAME); - if (tccFenceConfig == null || tccFenceConfig.getInitialized().get()) { - return; - } - Class tccServiceClazz = remotingDesc.getServiceClass(); - Method[] methods = tccServiceClazz.getMethods(); - for (Method method : methods) { - TwoPhaseBusinessAction twoPhaseBusinessAction = method.getAnnotation(TwoPhaseBusinessAction.class); - if (twoPhaseBusinessAction != null && twoPhaseBusinessAction.useTCCFence()) { - if (tccFenceConfig.getInitialized().compareAndSet(false, true)) { - // init tcc fence clean task if enable useTccFence - tccFenceConfig.initCleanTask(); - break; - } - } - } - } - } - - /** - * get remoting bean info: sofa:service, sofa:reference, dubbo:reference, dubbo:service - * - * @param bean the bean - * @param beanName the bean name - * @return if sofa:service, sofa:reference, dubbo:reference, dubbo:service return true, else return false - */ - protected static boolean parserRemotingServiceInfo(Object bean, String beanName) { - RemotingParser remotingParser = DefaultRemotingParser.get().isRemoting(bean, beanName); - if (remotingParser != null) { - return DefaultRemotingParser.get().parserRemotingServiceInfo(bean, beanName, remotingParser) != null; - } - return false; - } - - /** - * get the remoting description of TCC bean - * - * @param beanName the bean name - * @return remoting desc - */ - public static RemotingDesc getRemotingDesc(String beanName) { - return DefaultRemotingParser.get().getRemotingBeanDesc(beanName); - } - - /** - * Create a proxy bean for tcc service - * - * @param interfaceClass the interface class - * @param fieldValue the field value - * @param actionInterceptor the action interceptor - * @return the service proxy bean - */ - public static T createProxy(Class interfaceClass, Object fieldValue, TccActionInterceptor actionInterceptor) { - ProxyFactory factory = new ProxyFactory(); - factory.setTarget(fieldValue); - factory.setInterfaces(interfaceClass); - factory.addAdvice(actionInterceptor); - - return (T) factory.getProxy(); - } -} diff --git a/spring/src/main/resources/META-INF/services/io.seata.integration.tx.api.interceptor.parser.TargetClassParser b/spring/src/main/resources/META-INF/services/io.seata.integration.tx.api.interceptor.parser.TargetClassParser new file mode 100644 index 00000000000..d811d7b2c51 --- /dev/null +++ b/spring/src/main/resources/META-INF/services/io.seata.integration.tx.api.interceptor.parser.TargetClassParser @@ -0,0 +1 @@ +io.seata.spring.SpringTargetClassParser \ No newline at end of file diff --git a/spring/src/main/resources/META-INF/services/io.seata.integration.tx.api.remoting.RemotingParser b/spring/src/main/resources/META-INF/services/io.seata.integration.tx.api.remoting.RemotingParser new file mode 100644 index 00000000000..3de6f0e864d --- /dev/null +++ b/spring/src/main/resources/META-INF/services/io.seata.integration.tx.api.remoting.RemotingParser @@ -0,0 +1 @@ +io.seata.spring.remoting.parser.RemotingFactoryBeanParser \ No newline at end of file diff --git a/spring/src/test/java/io/seata/spring/annotation/GlobalTransactionScannerTest.java b/spring/src/test/java/io/seata/spring/annotation/GlobalTransactionScannerTest.java deleted file mode 100644 index 1ab5b9927be..00000000000 --- a/spring/src/test/java/io/seata/spring/annotation/GlobalTransactionScannerTest.java +++ /dev/null @@ -1,130 +0,0 @@ -/* - * 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.spring.annotation; - -import io.seata.spring.tcc.LocalTccAction; -import io.seata.spring.tcc.LocalTccActionImpl; -import io.seata.spring.tcc.TccAction; -import io.seata.spring.tcc.TccActionImpl; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.Arguments; -import org.junit.jupiter.params.provider.MethodSource; - -import java.util.stream.Stream; - -/** - * GlobalTransactionScanner Unit Test - */ -public class GlobalTransactionScannerTest { - /** - * The Global transaction scanner. - */ - protected GlobalTransactionScanner globalTransactionScanner = new GlobalTransactionScanner("global-trans-scanner-test"); - - /** - * Test wrap normal bean. - * - * @param bean the bean - * @param beanName the bean name - * @param cacheKey the cache key - */ - @ParameterizedTest - @MethodSource("normalBeanProvider") - public void testWrapNormalBean(Object bean, String beanName, Object cacheKey) { - Object result = globalTransactionScanner.wrapIfNecessary(bean, beanName, cacheKey); - Assertions.assertNotSame(result, bean); - } - - /** - * wrap nothing - * - * @param bean the bean - * @param beanName the bean name - * @param cacheKey the cache key - */ - @ParameterizedTest - @MethodSource("normalTccBeanProvider") - public void testWrapNormalTccBean(Object bean, String beanName, Object cacheKey) { - Object result = globalTransactionScanner.wrapIfNecessary(bean, beanName, cacheKey); - Assertions.assertSame(result, bean); - } - - /** - * wrapped - * - * @param bean the bean - * @param beanName the bean name - * @param cacheKey the cache key - */ - @ParameterizedTest - @MethodSource("localTccBeanProvider") - public void testWrapLocalTccBean(Object bean, String beanName, Object cacheKey) { - TccAction result = (LocalTccAction) globalTransactionScanner.wrapIfNecessary(bean, beanName, cacheKey); - Assertions.assertNotSame(result, bean); - } - - /** - * Test after properties set. - */ - @Test - public void testAfterPropertiesSet() { - globalTransactionScanner.afterPropertiesSet(); - } - - /** - * Normal bean provider object [ ] [ ]. - * - * @return the object [ ] [ ] - */ - static Stream normalBeanProvider() { - Business business = new BusinessImpl(); - String beanName = "business"; - String cacheKey = "business-key"; - return Stream.of( - Arguments.of(business, beanName, cacheKey) - ); - } - - /** - * Normal tcc bean provider object [ ] [ ]. - * - * @return the object [ ] [ ] - */ - static Stream normalTccBeanProvider() { - TccAction tccAction = new TccActionImpl(); - String beanName = "tccBean"; - String cacheKey = "tccBean-key"; - return Stream.of( - Arguments.of(tccAction, beanName, cacheKey) - ); - } - - /** - * Local tcc bean provider object [ ] [ ]. - * - * @return the object [ ] [ ] - */ - static Stream localTccBeanProvider() { - LocalTccAction localTccAction = new LocalTccActionImpl(); - String beanName = "lcoalTccBean"; - String cacheKey = "lcoalTccBean-key"; - return Stream.of( - Arguments.of(localTccAction, beanName, cacheKey) - ); - } -} diff --git a/spring/src/test/java/io/seata/spring/annotation/MethodDescTest.java b/spring/src/test/java/io/seata/spring/annotation/MethodDescTest.java index 68d6696adb9..1733e7149e0 100644 --- a/spring/src/test/java/io/seata/spring/annotation/MethodDescTest.java +++ b/spring/src/test/java/io/seata/spring/annotation/MethodDescTest.java @@ -17,15 +17,16 @@ import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; -import io.seata.common.exception.FrameworkException; + import io.seata.common.DefaultValues; +import io.seata.common.exception.FrameworkException; import io.seata.core.context.RootContext; +import io.seata.integration.tx.api.interceptor.handler.GlobalTransactionalInterceptorHandler; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.mockito.Mockito; import org.springframework.aop.framework.ProxyFactory; - import static org.assertj.core.api.Assertions.assertThat; /** @@ -46,7 +47,7 @@ public MethodDescTest() throws NoSuchMethodException { @Test public void testGetAnnotation() throws NoSuchMethodException { - GlobalTransactionalInterceptor globalTransactionalInterceptor = new GlobalTransactionalInterceptor(null); + GlobalTransactionalInterceptorHandler globalTransactionalInterceptor = new GlobalTransactionalInterceptorHandler(null, null, null); Method method = MockBusiness.class.getDeclaredMethod("doBiz", String.class); targetClass = Mockito.mock(MockBusiness.class).getClass(); transactional = globalTransactionalInterceptor.getAnnotation(method, targetClass, GlobalTransactional.class); @@ -74,7 +75,7 @@ public void testGlobalTransactional() throws NoSuchMethodException { MockClassAnnotation mockClassAnnotation = new MockClassAnnotation(); ProxyFactory proxyFactory = new ProxyFactory(); proxyFactory.setTarget(mockClassAnnotation); - proxyFactory.addAdvice(new GlobalTransactionalInterceptor(null)); + proxyFactory.addAdvice(new AspectTransactionalInterceptor()); Object proxy = proxyFactory.getProxy(); mockClassAnnotation = (MockClassAnnotation)proxy; mockClassAnnotation.toString(); diff --git a/tcc/pom.xml b/tcc/pom.xml index 70d8a152bdf..a9f9e847841 100644 --- a/tcc/pom.xml +++ b/tcc/pom.xml @@ -52,5 +52,10 @@ com.alibaba fastjson + + ${project.groupId} + seata-integration-tx-api + ${project.version} + - + \ No newline at end of file diff --git a/tcc/src/main/java/io/seata/rm/tcc/TCCResourceManager.java b/tcc/src/main/java/io/seata/rm/tcc/TCCResourceManager.java index f431861e7ce..d5d2cd2fa7b 100644 --- a/tcc/src/main/java/io/seata/rm/tcc/TCCResourceManager.java +++ b/tcc/src/main/java/io/seata/rm/tcc/TCCResourceManager.java @@ -17,21 +17,21 @@ import java.lang.reflect.Method; import java.lang.reflect.UndeclaredThrowableException; -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.exception.SkipCallbackWrapperException; -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.integration.tx.api.fence.DefaultCommonFenceHandler; +import io.seata.integration.tx.api.remoting.TwoPhaseResult; import io.seata.rm.AbstractResourceManager; import io.seata.rm.tcc.api.BusinessActionContext; +import io.seata.rm.tcc.api.BusinessActionContextUtil; /** * TCC resource manager @@ -95,15 +95,16 @@ public BranchStatus branchCommit(BranchType branchType, String xid, long branchI } try { //BusinessActionContext - BusinessActionContext businessActionContext = getBusinessActionContext(xid, branchId, resourceId, - applicationData); + BusinessActionContext businessActionContext = BusinessActionContextUtil.getBusinessActionContext(xid, branchId, resourceId, + applicationData); + Object[] args = this.getTwoPhaseCommitArgs(tccResource, businessActionContext); Object ret; boolean result; // add idempotent and anti hanging - if (Boolean.TRUE.equals(businessActionContext.getActionContext(Constants.USE_TCC_FENCE))) { + if (Boolean.TRUE.equals(businessActionContext.getActionContext(Constants.USE_COMMON_FENCE))) { try { - result = TCCFenceHandler.commitFence(commitMethod, targetTCCBean, xid, branchId, args); + result = DefaultCommonFenceHandler.get().commitFence(commitMethod, targetTCCBean, xid, branchId, args); } catch (SkipCallbackWrapperException | UndeclaredThrowableException e) { throw e.getCause(); } @@ -153,15 +154,15 @@ public BranchStatus branchRollback(BranchType branchType, String xid, long branc } try { //BusinessActionContext - BusinessActionContext businessActionContext = getBusinessActionContext(xid, branchId, resourceId, - applicationData); + BusinessActionContext businessActionContext = BusinessActionContextUtil.getBusinessActionContext(xid, branchId, resourceId, + applicationData); Object[] args = this.getTwoPhaseRollbackArgs(tccResource, businessActionContext); Object ret; boolean result; // add idempotent and anti hanging - if (Boolean.TRUE.equals(businessActionContext.getActionContext(Constants.USE_TCC_FENCE))) { + if (Boolean.TRUE.equals(businessActionContext.getActionContext(Constants.USE_COMMON_FENCE))) { try { - result = TCCFenceHandler.rollbackFence(rollbackMethod, targetTCCBean, xid, branchId, + result = DefaultCommonFenceHandler.get().rollbackFence(rollbackMethod, targetTCCBean, xid, branchId, args, tccResource.getActionName()); } catch (SkipCallbackWrapperException | UndeclaredThrowableException e) { throw e.getCause(); @@ -187,33 +188,6 @@ public BranchStatus branchRollback(BranchType branchType, String xid, long branc } } - /** - * 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) { - Map actionContextMap = null; - if (StringUtils.isNotBlank(applicationData)) { - Map tccContext = JSON.parseObject(applicationData, Map.class); - actionContextMap = (Map)tccContext.get(Constants.TCC_ACTION_CONTEXT); - } - if (actionContextMap == null) { - actionContextMap = new HashMap<>(2); - } - - //instance the action context - BusinessActionContext businessActionContext = new BusinessActionContext( - xid, String.valueOf(branchId), actionContextMap); - businessActionContext.setActionName(resourceId); - return businessActionContext; - } - /** * get phase two commit method's args * @param tccResource tccResource @@ -223,7 +197,7 @@ protected BusinessActionContext getBusinessActionContext(String xid, long branch private Object[] getTwoPhaseCommitArgs(TCCResource tccResource, BusinessActionContext businessActionContext) { String[] keys = tccResource.getPhaseTwoCommitKeys(); Class[] argsCommitClasses = tccResource.getCommitArgsClasses(); - return this.getTwoPhaseMethodParams(keys, argsCommitClasses, businessActionContext); + return BusinessActionContextUtil.getTwoPhaseMethodParams(keys, argsCommitClasses, businessActionContext); } /** @@ -235,23 +209,11 @@ private Object[] getTwoPhaseCommitArgs(TCCResource tccResource, BusinessActionCo private Object[] getTwoPhaseRollbackArgs(TCCResource tccResource, BusinessActionContext businessActionContext) { String[] keys = tccResource.getPhaseTwoRollbackKeys(); Class[] argsRollbackClasses = tccResource.getRollbackArgsClasses(); - return this.getTwoPhaseMethodParams(keys, argsRollbackClasses, businessActionContext); - } - - private Object[] getTwoPhaseMethodParams(String[] keys, Class[] argsClasses, BusinessActionContext businessActionContext) { - Object[] args = new Object[argsClasses.length]; - for (int i = 0; i < argsClasses.length; i++) { - if (argsClasses[i].equals(BusinessActionContext.class)) { - args[i] = businessActionContext; - } else { - args[i] = businessActionContext.getActionContext(keys[i], argsClasses[i]); - } - } - return args; + return BusinessActionContextUtil.getTwoPhaseMethodParams(keys, argsRollbackClasses, businessActionContext); } @Override public BranchType getBranchType() { return BranchType.TCC; } -} +} \ No newline at end of file diff --git a/tcc/src/main/java/io/seata/rm/tcc/api/LocalTCC.java b/tcc/src/main/java/io/seata/rm/tcc/api/LocalTCC.java index 4b3be4ea139..2a8ee88c40a 100644 --- a/tcc/src/main/java/io/seata/rm/tcc/api/LocalTCC.java +++ b/tcc/src/main/java/io/seata/rm/tcc/api/LocalTCC.java @@ -15,6 +15,8 @@ */ package io.seata.rm.tcc.api; +import io.seata.rm.tcc.remoting.parser.LocalTCCRemotingParser; + import java.lang.annotation.ElementType; import java.lang.annotation.Inherited; import java.lang.annotation.Retention; @@ -26,7 +28,7 @@ * * @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 + * @see LocalTCCRemotingParser // the RemotingParser impl for LocalTCC */ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) diff --git a/tcc/src/main/java/io/seata/rm/tcc/api/TwoPhaseBusinessAction.java b/tcc/src/main/java/io/seata/rm/tcc/api/TwoPhaseBusinessAction.java index 0bc714b7b6e..2b49c829578 100644 --- a/tcc/src/main/java/io/seata/rm/tcc/api/TwoPhaseBusinessAction.java +++ b/tcc/src/main/java/io/seata/rm/tcc/api/TwoPhaseBusinessAction.java @@ -15,6 +15,8 @@ */ package io.seata.rm.tcc.api; +import io.seata.rm.tcc.interceptor.TccActionInterceptorHandler; + import java.lang.annotation.ElementType; import java.lang.annotation.Inherited; import java.lang.annotation.Retention; @@ -29,7 +31,7 @@ * @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 + * @see TccActionInterceptorHandler // the interceptor of TCC mode * @see BusinessActionContext * @see BusinessActionContextUtil * @see BusinessActionContextParameter diff --git a/tcc/src/main/java/io/seata/rm/tcc/interceptor/TccActionInterceptorHandler.java b/tcc/src/main/java/io/seata/rm/tcc/interceptor/TccActionInterceptorHandler.java new file mode 100644 index 00000000000..43ce91b237f --- /dev/null +++ b/tcc/src/main/java/io/seata/rm/tcc/interceptor/TccActionInterceptorHandler.java @@ -0,0 +1,132 @@ +/* + * 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.common.Constants; +import io.seata.common.util.ReflectionUtil; +import io.seata.core.context.RootContext; +import io.seata.core.model.BranchType; +import io.seata.integration.tx.api.interceptor.ActionInterceptorHandler; +import io.seata.integration.tx.api.interceptor.InvocationWrapper; +import io.seata.integration.tx.api.interceptor.TwoPhaseBusinessActionParam; +import io.seata.integration.tx.api.interceptor.handler.AbstractProxyInvocationHandler; +import io.seata.integration.tx.api.remoting.RemotingDesc; +import io.seata.rm.tcc.api.TwoPhaseBusinessAction; +import org.slf4j.MDC; + +import java.lang.reflect.Method; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; + +/** + * @author leezongjie + * @date 2022/11/26 + */ +public class TccActionInterceptorHandler extends AbstractProxyInvocationHandler { + + private ActionInterceptorHandler actionInterceptorHandler = new ActionInterceptorHandler(); + + private Set methodsToProxy; + private RemotingDesc remotingDesc; + + private Map parseAnnotationCache = new ConcurrentHashMap<>(); + + public TccActionInterceptorHandler(RemotingDesc remotingDesc, Set methodsToProxy) { + this.remotingDesc = remotingDesc; + this.methodsToProxy = methodsToProxy; + } + + @Override + public Set getMethodsToProxy() { + return methodsToProxy; + } + + @Override + protected Object doInvoke(InvocationWrapper invocation) throws Throwable { + if (!RootContext.inGlobalTransaction() || RootContext.inSagaBranch()) { + //not in transaction, or this interceptor is disabled + return invocation.proceed(); + } + Method method = invocation.getMethod(); + TwoPhaseBusinessAction businessAction = parseAnnotation(method); + + //try method + if (businessAction != null) { + //save the xid + String xid = RootContext.getXID(); + //save the previous branchType + BranchType previousBranchType = RootContext.getBranchType(); + //if not TCC, bind TCC branchType + if (BranchType.TCC != previousBranchType) { + RootContext.bindBranchType(BranchType.TCC); + } + try { + TwoPhaseBusinessActionParam businessActionParam = new TwoPhaseBusinessActionParam(); + businessActionParam.setActionName(businessAction.name()); + businessActionParam.setDelayReport(businessAction.isDelayReport()); + businessActionParam.setUseCommonFence(businessAction.useTCCFence()); + businessActionParam.setBranchType(BranchType.TCC); + Map businessActionContextMap = new HashMap<>(4); + //the phase two method name + businessActionContextMap.put(Constants.COMMIT_METHOD, businessAction.commitMethod()); + businessActionContextMap.put(Constants.ROLLBACK_METHOD, businessAction.rollbackMethod()); + businessActionContextMap.put(Constants.ACTION_NAME, businessAction.name()); + businessActionContextMap.put(Constants.USE_COMMON_FENCE, businessAction.useTCCFence()); + businessActionParam.setBusinessActionContext(businessActionContextMap); + //Handler the TCC Aspect, and return the business result + return actionInterceptorHandler.proceed(method, invocation.getArguments(), xid, businessActionParam, + invocation::proceed); + } finally { + //if not TCC, unbind branchType + if (BranchType.TCC != previousBranchType) { + RootContext.unbindBranchType(); + } + //MDC remove branchId + MDC.remove(RootContext.MDC_KEY_BRANCH_ID); + } + } + + //not TCC try method + return invocation.proceed(); + } + + private TwoPhaseBusinessAction parseAnnotation(Method methodKey) throws NoSuchMethodException { + TwoPhaseBusinessAction result = parseAnnotationCache.computeIfAbsent(methodKey, method -> { + TwoPhaseBusinessAction businessAction = method.getAnnotation(TwoPhaseBusinessAction.class); + if (businessAction == null && remotingDesc.getServiceClass() != null) { + Set> interfaceClasses = ReflectionUtil.getInterfaces(remotingDesc.getServiceClass()); + if (interfaceClasses != null) { + for (Class interClass : interfaceClasses) { + try { + Method m = interClass.getMethod(method.getName(), method.getParameterTypes()); + businessAction = m.getAnnotation(TwoPhaseBusinessAction.class); + if (businessAction != null) { + break; + } + } catch (NoSuchMethodException e) { + throw new RuntimeException(e); + } + } + } + } + return businessAction; + }); + return result; + } + +} diff --git a/tcc/src/main/java/io/seata/rm/tcc/interceptor/parser/TccActionInterceptorParser.java b/tcc/src/main/java/io/seata/rm/tcc/interceptor/parser/TccActionInterceptorParser.java new file mode 100644 index 00000000000..3f3cb5483c8 --- /dev/null +++ b/tcc/src/main/java/io/seata/rm/tcc/interceptor/parser/TccActionInterceptorParser.java @@ -0,0 +1,97 @@ +/* + * 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.parser; + +import io.seata.common.util.ReflectionUtil; +import io.seata.integration.tx.api.interceptor.TxBeanParserUtils; +import io.seata.integration.tx.api.interceptor.handler.ProxyInvocationHandler; +import io.seata.integration.tx.api.interceptor.parser.DefaultResourceRegisterParser; +import io.seata.integration.tx.api.interceptor.parser.InterfaceParser; +import io.seata.integration.tx.api.remoting.RemotingDesc; +import io.seata.integration.tx.api.remoting.parser.DefaultRemotingParser; +import io.seata.rm.tcc.api.TwoPhaseBusinessAction; +import io.seata.rm.tcc.interceptor.TccActionInterceptorHandler; + +import java.lang.reflect.Method; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; + +/** + * @author leezongjie + * @date 2022/11/26 + */ +public class TccActionInterceptorParser implements InterfaceParser { + + @Override + public ProxyInvocationHandler parserInterfaceToProxy(Object target) { + boolean isTxRemotingBean = TxBeanParserUtils.isTxRemotingBean(target, target.toString()); + if (isTxRemotingBean) { + RemotingDesc remotingDesc = DefaultRemotingParser.get().getRemotingBeanDesc(target); + if (remotingDesc != null) { + if (remotingDesc.isService()) { + DefaultResourceRegisterParser.get().registerResource(target); + } + if (remotingDesc.isReference()) { + //if it is a tcc remote reference + Set methodsToProxy = tccProxyTargetMethod(remotingDesc); + if (remotingDesc != null && !methodsToProxy.isEmpty()) { + ProxyInvocationHandler proxyInvocationHandler = new TccActionInterceptorHandler(remotingDesc, methodsToProxy); + return proxyInvocationHandler; + } + } + } + } + return null; + } + + /** + * is TCC proxy-bean/target-bean: LocalTCC , the proxy bean of sofa:reference/dubbo:reference + * + * @param remotingDesc the remoting desc + * @return boolean boolean + */ + private Set tccProxyTargetMethod(RemotingDesc remotingDesc) { + if (!remotingDesc.isReference() || remotingDesc == null) { + return Collections.emptySet(); + } + Set methodsToProxy = new HashSet<>(); + //check if it is TCC bean + Class tccServiceClazz = remotingDesc.getServiceClass(); + Set methods = new HashSet<>(Arrays.asList(tccServiceClazz.getMethods())); + Set> interfaceClasses = ReflectionUtil.getInterfaces(tccServiceClazz); + if (interfaceClasses != null) { + for (Class interClass : interfaceClasses) { + methods.addAll(Arrays.asList(interClass.getMethods())); + } + } + + TwoPhaseBusinessAction twoPhaseBusinessAction; + for (Method method : methods) { + twoPhaseBusinessAction = method.getAnnotation(TwoPhaseBusinessAction.class); + if (twoPhaseBusinessAction != null) { + methodsToProxy.add(method.getName()); + } + } + + if (methodsToProxy.isEmpty()) { + return Collections.emptySet(); + } + // sofa:reference / dubbo:reference, AOP + return methodsToProxy; + } +} diff --git a/tcc/src/main/java/io/seata/rm/tcc/json/FastJsonParser.java b/tcc/src/main/java/io/seata/rm/tcc/json/FastJsonParser.java new file mode 100644 index 00000000000..ed50e15ea52 --- /dev/null +++ b/tcc/src/main/java/io/seata/rm/tcc/json/FastJsonParser.java @@ -0,0 +1,36 @@ +/* + * 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.json; + +import com.alibaba.fastjson.JSON; +import io.seata.integration.tx.api.json.JsonParser; + +/** + * @author leezongjie + * @date 2023/1/13 + */ +public class FastJsonParser implements JsonParser { + + @Override + public String toJSONString(Object object) { + return JSON.toJSONString(object); + } + + @Override + public T parseObject(String text, Class clazz) { + return JSON.parseObject(text, clazz); + } +} diff --git a/tcc/src/main/java/io/seata/rm/tcc/remoting/parser/LocalTCCRemotingParser.java b/tcc/src/main/java/io/seata/rm/tcc/remoting/parser/LocalTCCRemotingParser.java index 7a966df29b5..e3133ed19dc 100644 --- a/tcc/src/main/java/io/seata/rm/tcc/remoting/parser/LocalTCCRemotingParser.java +++ b/tcc/src/main/java/io/seata/rm/tcc/remoting/parser/LocalTCCRemotingParser.java @@ -15,15 +15,16 @@ */ 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.integration.tx.api.remoting.Protocols; +import io.seata.integration.tx.api.remoting.RemotingDesc; +import io.seata.integration.tx.api.remoting.parser.AbstractedRemotingParser; import io.seata.rm.tcc.api.LocalTCC; -import io.seata.rm.tcc.remoting.Protocols; -import io.seata.rm.tcc.remoting.RemotingDesc; import org.springframework.aop.framework.AopProxyUtils; +import java.util.Set; + /** * local tcc bean parsing * @@ -47,7 +48,8 @@ public RemotingDesc getServiceDesc(Object bean, String beanName) throws Framewor return null; } RemotingDesc remotingDesc = new RemotingDesc(); - remotingDesc.setReference(true); + remotingDesc.setReference(this.isReference(bean, beanName)); + remotingDesc.setService(this.isService(bean, beanName)); remotingDesc.setProtocol(Protocols.IN_JVM); Class classType = bean.getClass(); // check if LocalTCC annotation is marked on the implementation class @@ -90,4 +92,4 @@ private boolean isLocalTCC(Object bean) { } return classType.isAnnotationPresent(LocalTCC.class); } -} +} \ No newline at end of file diff --git a/tcc/src/main/java/io/seata/rm/tcc/resource/parser/TccRegisterResourceParser.java b/tcc/src/main/java/io/seata/rm/tcc/resource/parser/TccRegisterResourceParser.java new file mode 100644 index 00000000000..d310f80a599 --- /dev/null +++ b/tcc/src/main/java/io/seata/rm/tcc/resource/parser/TccRegisterResourceParser.java @@ -0,0 +1,120 @@ +/* + * 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.resource.parser; + +import io.seata.common.exception.FrameworkException; +import io.seata.common.util.ReflectionUtil; +import io.seata.integration.tx.api.interceptor.ActionContextUtil; +import io.seata.integration.tx.api.interceptor.TxBeanParserUtils; +import io.seata.integration.tx.api.interceptor.parser.RegisterResourceParser; +import io.seata.integration.tx.api.remoting.RemotingDesc; +import io.seata.integration.tx.api.remoting.parser.DefaultRemotingParser; +import io.seata.rm.DefaultResourceManager; +import io.seata.rm.tcc.TCCResource; +import io.seata.rm.tcc.api.BusinessActionContext; +import io.seata.rm.tcc.api.BusinessActionContextParameter; +import io.seata.rm.tcc.api.TwoPhaseBusinessAction; + +import java.lang.annotation.Annotation; +import java.lang.reflect.Method; +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; + +/** + * @author leezongjie + * @date 2022/12/17 + */ +public class TccRegisterResourceParser implements RegisterResourceParser { + + @Override + public void registerResource(Object target) { + boolean isTxRemotingBean = TxBeanParserUtils.isTxRemotingBean(target, target.toString()); + if (isTxRemotingBean) { + RemotingDesc remotingDesc = DefaultRemotingParser.get().getRemotingBeanDesc(target); + if (remotingDesc != null) { + if (remotingDesc.isService()) { + try { + //service bean, registry resource + Class serviceClass = remotingDesc.getServiceClass(); + Set methods = new HashSet<>(Arrays.asList(serviceClass.getMethods())); + Set> interfaceClasses = ReflectionUtil.getInterfaces(serviceClass); + if (interfaceClasses != null) { + for (Class interClass : interfaceClasses) { + methods.addAll(Arrays.asList(interClass.getMethods())); + } + } + Object targetBean = remotingDesc.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(serviceClass.getMethod(twoPhaseBusinessAction.commitMethod(), + twoPhaseBusinessAction.commitArgsClasses())); + tccResource.setRollbackMethodName(twoPhaseBusinessAction.rollbackMethod()); + tccResource.setRollbackMethod(serviceClass.getMethod(twoPhaseBusinessAction.rollbackMethod(), + twoPhaseBusinessAction.rollbackArgsClasses())); + // set argsClasses + tccResource.setCommitArgsClasses(twoPhaseBusinessAction.commitArgsClasses()); + tccResource.setRollbackArgsClasses(twoPhaseBusinessAction.rollbackArgsClasses()); + // set phase two method's keys + tccResource.setPhaseTwoCommitKeys(this.getTwoPhaseArgs(tccResource.getCommitMethod(), + twoPhaseBusinessAction.commitArgsClasses())); + tccResource.setPhaseTwoRollbackKeys(this.getTwoPhaseArgs(tccResource.getRollbackMethod(), + twoPhaseBusinessAction.rollbackArgsClasses())); + //registry tcc resource + DefaultResourceManager.get().registerResource(tccResource); + } + } + } catch (Throwable t) { + throw new FrameworkException(t, "parser remoting service error"); + } + } + } + } + } + + protected String[] getTwoPhaseArgs(Method method, Class[] argsClasses) { + Annotation[][] parameterAnnotations = method.getParameterAnnotations(); + String[] keys = new String[parameterAnnotations.length]; + /* + * get parameter's key + * if method's parameter list is like + * (BusinessActionContext, @BusinessActionContextParameter("a") A a, @BusinessActionContextParameter("b") B b) + * the keys will be [null, a, b] + */ + 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]; + String key = ActionContextUtil.getParamNameFromAnnotation(param); + keys[i] = key; + break; + } + } + if (keys[i] == null && !(argsClasses[i].equals(BusinessActionContext.class))) { + throw new IllegalArgumentException("non-BusinessActionContext parameter should use annotation " + + "BusinessActionContextParameter"); + } + } + return keys; + } + +} diff --git a/tcc/src/main/resources/META-INF/services/io.seata.integration.tx.api.interceptor.parser.InterfaceParser b/tcc/src/main/resources/META-INF/services/io.seata.integration.tx.api.interceptor.parser.InterfaceParser new file mode 100644 index 00000000000..99009fc2956 --- /dev/null +++ b/tcc/src/main/resources/META-INF/services/io.seata.integration.tx.api.interceptor.parser.InterfaceParser @@ -0,0 +1 @@ +io.seata.rm.tcc.interceptor.parser.TccActionInterceptorParser \ No newline at end of file diff --git a/tcc/src/main/resources/META-INF/services/io.seata.integration.tx.api.interceptor.parser.RegisterResourceParser b/tcc/src/main/resources/META-INF/services/io.seata.integration.tx.api.interceptor.parser.RegisterResourceParser new file mode 100644 index 00000000000..9501e9d87cb --- /dev/null +++ b/tcc/src/main/resources/META-INF/services/io.seata.integration.tx.api.interceptor.parser.RegisterResourceParser @@ -0,0 +1 @@ +io.seata.rm.tcc.resource.parser.TccRegisterResourceParser \ No newline at end of file diff --git a/tcc/src/main/resources/META-INF/services/io.seata.integration.tx.api.json.JsonParser b/tcc/src/main/resources/META-INF/services/io.seata.integration.tx.api.json.JsonParser new file mode 100644 index 00000000000..61bb4491af2 --- /dev/null +++ b/tcc/src/main/resources/META-INF/services/io.seata.integration.tx.api.json.JsonParser @@ -0,0 +1 @@ +io.seata.rm.tcc.json.FastJsonParser \ No newline at end of file diff --git a/tcc/src/main/resources/META-INF/services/io.seata.integration.tx.api.remoting.RemotingParser b/tcc/src/main/resources/META-INF/services/io.seata.integration.tx.api.remoting.RemotingParser new file mode 100644 index 00000000000..78ff6f2e846 --- /dev/null +++ b/tcc/src/main/resources/META-INF/services/io.seata.integration.tx.api.remoting.RemotingParser @@ -0,0 +1 @@ +io.seata.rm.tcc.remoting.parser.LocalTCCRemotingParser \ No newline at end of file diff --git a/tcc/src/main/resources/META-INF/services/io.seata.rm.tcc.remoting.RemotingParser b/tcc/src/main/resources/META-INF/services/io.seata.rm.tcc.remoting.RemotingParser deleted file mode 100644 index 30ef923192e..00000000000 --- a/tcc/src/main/resources/META-INF/services/io.seata.rm.tcc.remoting.RemotingParser +++ /dev/null @@ -1,4 +0,0 @@ -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 \ No newline at end of file diff --git a/tcc/src/test/java/io/seata/rm/tcc/NormalTccAction.java b/tcc/src/test/java/io/seata/rm/tcc/NormalTccAction.java new file mode 100644 index 00000000000..1e3a0dc068e --- /dev/null +++ b/tcc/src/test/java/io/seata/rm/tcc/NormalTccAction.java @@ -0,0 +1,64 @@ +/* + * 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 NormalTccAction { + + /** + * 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", commitArgsClasses = {BusinessActionContext.class, TccParam.class}, rollbackArgsClasses = {BusinessActionContext.class, TccParam.class}) + String prepare(BusinessActionContext actionContext, + @BusinessActionContextParameter("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 + */ + boolean commit(BusinessActionContext actionContext, + @BusinessActionContextParameter("tccParam") TccParam param); + + /** + * Rollback boolean. + * + * @param actionContext the action context + * @return the boolean + */ + boolean rollback(BusinessActionContext actionContext, @BusinessActionContextParameter("tccParam") TccParam param); +} diff --git a/tcc/src/test/java/io/seata/rm/tcc/NormalTccActionImpl.java b/tcc/src/test/java/io/seata/rm/tcc/NormalTccActionImpl.java new file mode 100644 index 00000000000..60cd9cf1334 --- /dev/null +++ b/tcc/src/test/java/io/seata/rm/tcc/NormalTccActionImpl.java @@ -0,0 +1,48 @@ +/* + * 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; + +/** + * @author leezongjie + * @date 2022/12/9 + */ +public class NormalTccActionImpl implements NormalTccAction { + + @Override + public String prepare(BusinessActionContext actionContext, int a, List b, TccParam tccParam) { + return "a"; + } + + @Override + public boolean commit(BusinessActionContext actionContext, TccParam param) { + return false; + } + + @Override + public boolean rollback(BusinessActionContext actionContext, TccParam param) { + return false; + } + + public boolean otherMethod(){ + return true; + } + +} diff --git a/tcc/src/test/java/io/seata/rm/tcc/TccAction.java b/tcc/src/test/java/io/seata/rm/tcc/TccAction.java index 94f22365fa2..1c181b36a0f 100644 --- a/tcc/src/test/java/io/seata/rm/tcc/TccAction.java +++ b/tcc/src/test/java/io/seata/rm/tcc/TccAction.java @@ -39,7 +39,7 @@ public interface TccAction { * @param tccParam the tcc param * @return the boolean */ - @TwoPhaseBusinessAction(name = "tccActionForTest", commitMethod = "commit", rollbackMethod = "rollback") + @TwoPhaseBusinessAction(name = "tccActionForTest", commitMethod = "commit", rollbackMethod = "rollback", commitArgsClasses = {BusinessActionContext.class, TccParam.class, Integer.class}, rollbackArgsClasses = {BusinessActionContext.class, TccParam.class}) boolean prepare(BusinessActionContext actionContext, @BusinessActionContextParameter("a") int a, @BusinessActionContextParameter(paramName = "b", index = 0) List b, diff --git a/tcc/src/test/java/io/seata/rm/tcc/interceptor/ProxyUtilsTccTest.java b/tcc/src/test/java/io/seata/rm/tcc/interceptor/ProxyUtilsTccTest.java new file mode 100644 index 00000000000..7187560d6d9 --- /dev/null +++ b/tcc/src/test/java/io/seata/rm/tcc/interceptor/ProxyUtilsTccTest.java @@ -0,0 +1,130 @@ +/* + * 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.integration.tx.api.util.ProxyUtil; +import io.seata.core.context.RootContext; +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.core.model.ResourceManager; +import io.seata.rm.DefaultResourceManager; +import io.seata.rm.tcc.NormalTccAction; +import io.seata.rm.tcc.NormalTccActionImpl; +import io.seata.rm.tcc.TccParam; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.concurrent.atomic.AtomicReference; + +/** + * @author leezongjie + * @date 2022/12/14 + */ +public class ProxyUtilsTccTest { + + private final String DEFAULT_XID = "default_xid"; + + @Test + public void testTcc() { + //given + NormalTccActionImpl tccAction = new NormalTccActionImpl(); + + NormalTccAction tccActionProxy = ProxyUtil.createProxy(tccAction); + + RootContext.bind(DEFAULT_XID); + + TccParam tccParam = new TccParam(1, "abc@163.com"); + List listB = Arrays.asList("b"); + + AtomicReference branchReference = new AtomicReference(); + + ResourceManager resourceManager = new ResourceManager() { + + @Override + public Long branchRegister(BranchType branchType, String resourceId, String clientId, String xid, String applicationData, String lockKeys) throws TransactionException { + branchReference.set(resourceId); + return System.currentTimeMillis(); + } + + @Override + public void branchReport(BranchType branchType, String xid, long branchId, BranchStatus status, String applicationData) throws TransactionException { + + } + + @Override + public boolean lockQuery(BranchType branchType, String resourceId, String xid, String lockKeys) throws TransactionException { + return false; + } + + @Override + public BranchStatus branchCommit(BranchType branchType, String xid, long branchId, String resourceId, String applicationData) throws TransactionException { + return null; + } + + @Override + public BranchStatus branchRollback(BranchType branchType, String xid, long branchId, String resourceId, String applicationData) throws TransactionException { + return null; + } + + @Override + public void registerResource(Resource resource) { + + } + + @Override + public void unregisterResource(Resource resource) { + + } + + @Override + public Map getManagedResources() { + return null; + } + + @Override + public BranchType getBranchType() { + return null; + } + }; + + DefaultResourceManager.mockResourceManager(BranchType.TCC, resourceManager); + + //when + String result = tccActionProxy.prepare(null, 0, listB, tccParam); + + //then + Assertions.assertEquals("a", result); + Assertions.assertNotNull(result); + Assertions.assertEquals("tccActionForTest", branchReference.get()); + + } + + @Test + public void testTccImplementOtherMethod(){ + NormalTccActionImpl tccAction = new NormalTccActionImpl(); + NormalTccActionImpl tccActionProxy = ProxyUtil.createProxy(tccAction); + + Assertions.assertTrue(tccActionProxy.otherMethod()); + + } + + +} diff --git a/tcc/src/test/java/io/seata/rm/tcc/interceptor/parser/TccActionInterceptorParserTest.java b/tcc/src/test/java/io/seata/rm/tcc/interceptor/parser/TccActionInterceptorParserTest.java new file mode 100644 index 00000000000..9fd1b8205e2 --- /dev/null +++ b/tcc/src/test/java/io/seata/rm/tcc/interceptor/parser/TccActionInterceptorParserTest.java @@ -0,0 +1,43 @@ +/* + * 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.parser; + +import io.seata.integration.tx.api.interceptor.handler.ProxyInvocationHandler; +import io.seata.rm.tcc.NormalTccActionImpl; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +/** + * @author leezongjie + * @date 2022/12/8 + */ +class TccActionInterceptorParserTest { + + @Test + void parserInterfaceToProxy() { + + //given + TccActionInterceptorParser tccActionInterceptorParser = new TccActionInterceptorParser(); + NormalTccActionImpl tccAction = new NormalTccActionImpl(); + + //when + ProxyInvocationHandler proxyInvocationHandler = tccActionInterceptorParser.parserInterfaceToProxy(tccAction); + + //then + Assertions.assertNotNull(proxyInvocationHandler); + + } +} \ No newline at end of file diff --git a/tcc/src/test/java/io/seata/rm/tcc/remoting/parser/LocalTCCRemotingParserTest.java b/tcc/src/test/java/io/seata/rm/tcc/remoting/parser/LocalTCCRemotingParserTest.java index 6618c3395c0..d9afb753531 100644 --- a/tcc/src/test/java/io/seata/rm/tcc/remoting/parser/LocalTCCRemotingParserTest.java +++ b/tcc/src/test/java/io/seata/rm/tcc/remoting/parser/LocalTCCRemotingParserTest.java @@ -15,9 +15,9 @@ */ package io.seata.rm.tcc.remoting.parser; +import io.seata.integration.tx.api.remoting.RemotingDesc; 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; diff --git a/tcc/src/test/java/io/seata/rm/tcc/remoting/parser/DefaultRemotingParserTest.java b/tcc/src/test/java/io/seata/rm/tcc/resource/parser/TccRegisterResourceParserTest.java similarity index 67% rename from tcc/src/test/java/io/seata/rm/tcc/remoting/parser/DefaultRemotingParserTest.java rename to tcc/src/test/java/io/seata/rm/tcc/resource/parser/TccRegisterResourceParserTest.java index 33e493af265..8b29a2e790b 100644 --- a/tcc/src/test/java/io/seata/rm/tcc/remoting/parser/DefaultRemotingParserTest.java +++ b/tcc/src/test/java/io/seata/rm/tcc/resource/parser/TccRegisterResourceParserTest.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.seata.rm.tcc.remoting.parser; +package io.seata.rm.tcc.resource.parser; import io.seata.rm.tcc.TccParam; import io.seata.rm.tcc.api.BusinessActionContext; @@ -22,23 +22,26 @@ import java.lang.reflect.Method; -public class DefaultRemotingParserTest { +/** + * @author leezongjie + * @date 2022/12/23 + */ +class TccRegisterResourceParserTest { - DefaultRemotingParser defaultRemotingParser = new DefaultRemotingParser(); + TccRegisterResourceParser tccRegisterResourceParser = new TccRegisterResourceParser(); @Test public void testGetTwoPhaseArgs() throws Exception { Class tccActionImpl = Class.forName("io.seata.rm.tcc.TccActionImpl"); - Class[] argsCommitClasses = new Class[] {BusinessActionContext.class, TccParam.class, Integer.class}; + Class[] argsCommitClasses = new Class[]{BusinessActionContext.class, TccParam.class, Integer.class}; Method commitMethod = tccActionImpl.getMethod("commit", argsCommitClasses); Assertions.assertThrows(IllegalArgumentException.class, () -> { - defaultRemotingParser.getTwoPhaseArgs(commitMethod, argsCommitClasses); + tccRegisterResourceParser.getTwoPhaseArgs(commitMethod, argsCommitClasses); }); - Class[] argsRollbackClasses = new Class[] {BusinessActionContext.class, TccParam.class}; + Class[] argsRollbackClasses = new Class[]{BusinessActionContext.class, TccParam.class}; Method rollbackMethod = tccActionImpl.getMethod("rollback", argsRollbackClasses); - String[] keys = defaultRemotingParser.getTwoPhaseArgs(rollbackMethod, argsRollbackClasses); + String[] keys = tccRegisterResourceParser.getTwoPhaseArgs(rollbackMethod, argsRollbackClasses); Assertions.assertNull(keys[0]); Assertions.assertEquals("tccParam", keys[1]); } - -} +} \ No newline at end of file diff --git a/spring/src/test/java/io/seata/spring/annotation/Business.java b/tcc/src/test/java/io/seata/rm/tcc/spring/Business.java similarity index 95% rename from spring/src/test/java/io/seata/spring/annotation/Business.java rename to tcc/src/test/java/io/seata/rm/tcc/spring/Business.java index aaa351d03a3..2ad498f4d9e 100644 --- a/spring/src/test/java/io/seata/spring/annotation/Business.java +++ b/tcc/src/test/java/io/seata/rm/tcc/spring/Business.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.seata.spring.annotation; +package io.seata.rm.tcc.spring; /** * The interface Business. diff --git a/spring/src/test/java/io/seata/spring/annotation/BusinessImpl.java b/tcc/src/test/java/io/seata/rm/tcc/spring/BusinessImpl.java similarity index 92% rename from spring/src/test/java/io/seata/spring/annotation/BusinessImpl.java rename to tcc/src/test/java/io/seata/rm/tcc/spring/BusinessImpl.java index 2ac53976727..15c8e5b27c1 100644 --- a/spring/src/test/java/io/seata/spring/annotation/BusinessImpl.java +++ b/tcc/src/test/java/io/seata/rm/tcc/spring/BusinessImpl.java @@ -13,8 +13,9 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.seata.spring.annotation; +package io.seata.rm.tcc.spring; +import io.seata.spring.annotation.GlobalTransactional; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/spring/src/test/java/io/seata/spring/annotation/BusinessProxy.java b/tcc/src/test/java/io/seata/rm/tcc/spring/BusinessProxy.java similarity index 97% rename from spring/src/test/java/io/seata/spring/annotation/BusinessProxy.java rename to tcc/src/test/java/io/seata/rm/tcc/spring/BusinessProxy.java index 14e0043e06a..6eb4cc798e8 100644 --- a/spring/src/test/java/io/seata/spring/annotation/BusinessProxy.java +++ b/tcc/src/test/java/io/seata/rm/tcc/spring/BusinessProxy.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.seata.spring.annotation; +package io.seata.rm.tcc.spring; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; diff --git a/tcc/src/test/java/io/seata/rm/tcc/spring/GlobalTransactionScannerTest.java b/tcc/src/test/java/io/seata/rm/tcc/spring/GlobalTransactionScannerTest.java new file mode 100644 index 00000000000..1a7a35b3c73 --- /dev/null +++ b/tcc/src/test/java/io/seata/rm/tcc/spring/GlobalTransactionScannerTest.java @@ -0,0 +1,130 @@ +/* + * 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.spring; + +import io.seata.rm.tcc.spring.tcc.LocalTccAction; +import io.seata.rm.tcc.spring.tcc.LocalTccActionImpl; +import io.seata.rm.tcc.spring.tcc.TccAction; +import io.seata.rm.tcc.spring.tcc.TccActionImpl; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +import java.util.stream.Stream; + +/** + * GlobalTransactionScanner Unit Test + */ +public class GlobalTransactionScannerTest { +// /** +// * The Global transaction scanner. +// */ +// protected GlobalTransactionScanner globalTransactionScanner = new GlobalTransactionScanner("global-trans-scanner-test"); +// +// /** +// * Test wrap normal bean. +// * +// * @param bean the bean +// * @param beanName the bean name +// * @param cacheKey the cache key +// */ +// @ParameterizedTest +// @MethodSource("normalBeanProvider") +// public void testWrapNormalBean(Object bean, String beanName, Object cacheKey) { +// Object result = globalTransactionScanner.wrapIfNecessary(bean, beanName, cacheKey); +// Assertions.assertNotSame(result, bean); +// } +// +// /** +// * wrap nothing +// * +// * @param bean the bean +// * @param beanName the bean name +// * @param cacheKey the cache key +// */ +// @ParameterizedTest +// @MethodSource("normalTccBeanProvider") +// public void testWrapNormalTccBean(Object bean, String beanName, Object cacheKey) { +// Object result = globalTransactionScanner.wrapIfNecessary(bean, beanName, cacheKey); +// Assertions.assertSame(result, bean); +// } +// +// /** +// * wrapped +// * +// * @param bean the bean +// * @param beanName the bean name +// * @param cacheKey the cache key +// */ +// @ParameterizedTest +// @MethodSource("localTccBeanProvider") +// public void testWrapLocalTccBean(Object bean, String beanName, Object cacheKey) { +// TccAction result = (LocalTccAction) globalTransactionScanner.wrapIfNecessary(bean, beanName, cacheKey); +// Assertions.assertNotSame(result, bean); +// } +// +// /** +// * Test after properties set. +// */ +// @Test +// public void testAfterPropertiesSet() { +// globalTransactionScanner.afterPropertiesSet(); +// } +// +// /** +// * Normal bean provider object [ ] [ ]. +// * +// * @return the object [ ] [ ] +// */ +// static Stream normalBeanProvider() { +// Business business = new BusinessImpl(); +// String beanName = "business"; +// String cacheKey = "business-key"; +// return Stream.of( +// Arguments.of(business, beanName, cacheKey) +// ); +// } +// +// /** +// * Normal tcc bean provider object [ ] [ ]. +// * +// * @return the object [ ] [ ] +// */ +// static Stream normalTccBeanProvider() { +// TccAction tccAction = new TccActionImpl(); +// String beanName = "tccBean"; +// String cacheKey = "tccBean-key"; +// return Stream.of( +// Arguments.of(tccAction, beanName, cacheKey) +// ); +// } +// +// /** +// * Local tcc bean provider object [ ] [ ]. +// * +// * @return the object [ ] [ ] +// */ +// static Stream localTccBeanProvider() { +// LocalTccAction localTccAction = new LocalTccActionImpl(); +// String beanName = "lcoalTccBean"; +// String cacheKey = "lcoalTccBean-key"; +// return Stream.of( +// Arguments.of(localTccAction, beanName, cacheKey) +// ); +// } +} diff --git a/spring/src/test/java/io/seata/spring/tcc/LocalTccAction.java b/tcc/src/test/java/io/seata/rm/tcc/spring/tcc/LocalTccAction.java similarity index 95% rename from spring/src/test/java/io/seata/spring/tcc/LocalTccAction.java rename to tcc/src/test/java/io/seata/rm/tcc/spring/tcc/LocalTccAction.java index 4fdeb378a18..a3a2fb33473 100644 --- a/spring/src/test/java/io/seata/spring/tcc/LocalTccAction.java +++ b/tcc/src/test/java/io/seata/rm/tcc/spring/tcc/LocalTccAction.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.seata.spring.tcc; +package io.seata.rm.tcc.spring.tcc; import io.seata.rm.tcc.api.LocalTCC; diff --git a/spring/src/test/java/io/seata/spring/tcc/LocalTccActionImpl.java b/tcc/src/test/java/io/seata/rm/tcc/spring/tcc/LocalTccActionImpl.java similarity index 95% rename from spring/src/test/java/io/seata/spring/tcc/LocalTccActionImpl.java rename to tcc/src/test/java/io/seata/rm/tcc/spring/tcc/LocalTccActionImpl.java index e693c1e92cc..2b9c4091b70 100644 --- a/spring/src/test/java/io/seata/spring/tcc/LocalTccActionImpl.java +++ b/tcc/src/test/java/io/seata/rm/tcc/spring/tcc/LocalTccActionImpl.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.seata.spring.tcc; +package io.seata.rm.tcc.spring.tcc; /** * The type Local tcc action. diff --git a/spring/src/test/java/io/seata/spring/tcc/TccAction.java b/tcc/src/test/java/io/seata/rm/tcc/spring/tcc/TccAction.java similarity index 97% rename from spring/src/test/java/io/seata/spring/tcc/TccAction.java rename to tcc/src/test/java/io/seata/rm/tcc/spring/tcc/TccAction.java index db39fb708f5..b89e6fcfaba 100644 --- a/spring/src/test/java/io/seata/spring/tcc/TccAction.java +++ b/tcc/src/test/java/io/seata/rm/tcc/spring/tcc/TccAction.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.seata.spring.tcc; +package io.seata.rm.tcc.spring.tcc; import io.seata.rm.tcc.api.BusinessActionContext; import io.seata.rm.tcc.api.TwoPhaseBusinessAction; diff --git a/spring/src/test/java/io/seata/spring/tcc/TccActionImpl.java b/tcc/src/test/java/io/seata/rm/tcc/spring/tcc/TccActionImpl.java similarity index 96% rename from spring/src/test/java/io/seata/spring/tcc/TccActionImpl.java rename to tcc/src/test/java/io/seata/rm/tcc/spring/tcc/TccActionImpl.java index c28a844c4ae..31b7f594b76 100644 --- a/spring/src/test/java/io/seata/spring/tcc/TccActionImpl.java +++ b/tcc/src/test/java/io/seata/rm/tcc/spring/tcc/TccActionImpl.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.seata.spring.tcc; +package io.seata.rm.tcc.spring.tcc; import io.seata.rm.tcc.api.BusinessActionContext; diff --git a/tm/src/main/java/io/seata/tm/api/DefaultFailureHandlerImpl.java b/tm/src/main/java/io/seata/tm/api/DefaultFailureHandlerImpl.java index af1d5156d52..232a49f4928 100644 --- a/tm/src/main/java/io/seata/tm/api/DefaultFailureHandlerImpl.java +++ b/tm/src/main/java/io/seata/tm/api/DefaultFailureHandlerImpl.java @@ -48,7 +48,7 @@ public class DefaultFailureHandlerImpl implements FailureHandler { private static final int TICKS_PER_WHEEL = 8; - private HashedWheelTimer timer = new HashedWheelTimer( + private static final HashedWheelTimer TIMER = new HashedWheelTimer( new NamedThreadFactory("failedTransactionRetry", 1), TICK_DURATION, TimeUnit.SECONDS, TICKS_PER_WHEEL); @@ -60,19 +60,19 @@ public void onBeginFailure(GlobalTransaction tx, Throwable cause) { @Override public void onCommitFailure(GlobalTransaction tx, Throwable cause) { LOGGER.warn("Failed to commit transaction[" + tx.getXid() + "]", cause); - timer.newTimeout(new CheckTimerTask(tx, GlobalStatus.Committed), SCHEDULE_INTERVAL_SECONDS, TimeUnit.SECONDS); + TIMER.newTimeout(new CheckTimerTask(tx, GlobalStatus.Committed), SCHEDULE_INTERVAL_SECONDS, TimeUnit.SECONDS); } @Override public void onRollbackFailure(GlobalTransaction tx, Throwable originalException) { LOGGER.warn("Failed to rollback transaction[" + tx.getXid() + "]", originalException); - timer.newTimeout(new CheckTimerTask(tx, GlobalStatus.Rollbacked), SCHEDULE_INTERVAL_SECONDS, TimeUnit.SECONDS); + TIMER.newTimeout(new CheckTimerTask(tx, GlobalStatus.Rollbacked), SCHEDULE_INTERVAL_SECONDS, TimeUnit.SECONDS); } @Override public void onRollbackRetrying(GlobalTransaction tx, Throwable originalException) { StackTraceLogger.warn(LOGGER, originalException, "Retrying to rollback transaction[{}]", new String[] {tx.getXid()}); - timer.newTimeout(new CheckTimerTask(tx, GlobalStatus.RollbackRetrying), SCHEDULE_INTERVAL_SECONDS, + TIMER.newTimeout(new CheckTimerTask(tx, GlobalStatus.RollbackRetrying), SCHEDULE_INTERVAL_SECONDS, TimeUnit.SECONDS); } @@ -104,7 +104,7 @@ public void run(Timeout timeout) throws Exception { return; } isStopped = shouldStop(tx, required); - timer.newTimeout(this, SCHEDULE_INTERVAL_SECONDS, TimeUnit.SECONDS); + TIMER.newTimeout(this, SCHEDULE_INTERVAL_SECONDS, TimeUnit.SECONDS); } } } diff --git a/tm/src/main/java/io/seata/tm/api/FailureHandlerHolder.java b/tm/src/main/java/io/seata/tm/api/FailureHandlerHolder.java new file mode 100644 index 00000000000..effbc93fec9 --- /dev/null +++ b/tm/src/main/java/io/seata/tm/api/FailureHandlerHolder.java @@ -0,0 +1,36 @@ +/* + * 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.tm.api; + +/** + * @author leezongjie + * @date 2023/1/13 + */ +public class FailureHandlerHolder { + + private static FailureHandler FAILURE_HANDLER_HOLDER = new DefaultFailureHandlerImpl(); + + public static void setFailureHandler(FailureHandler failureHandler) { + if (failureHandler != null) { + FAILURE_HANDLER_HOLDER = failureHandler; + } + } + + public static FailureHandler getFailureHandler() { + return FAILURE_HANDLER_HOLDER; + } + +} diff --git a/tm/src/test/java/io/seata/tm/api/DefaultFailureHandlerImplTest.java b/tm/src/test/java/io/seata/tm/api/DefaultFailureHandlerImplTest.java index 56e0d8f7cb2..cb387d5a7ab 100644 --- a/tm/src/test/java/io/seata/tm/api/DefaultFailureHandlerImplTest.java +++ b/tm/src/test/java/io/seata/tm/api/DefaultFailureHandlerImplTest.java @@ -90,7 +90,7 @@ void onCommitFailure() throws Exception{ // get timer Class c = Class.forName("io.seata.tm.api.DefaultFailureHandlerImpl"); - Field field = c.getDeclaredField("timer"); + Field field = c.getDeclaredField("TIMER"); field.setAccessible(true); HashedWheelTimer timer = (HashedWheelTimer) field.get(failureHandler); // assert timer pendingCount: first time is 1 @@ -116,7 +116,7 @@ void onRollbackFailure() throws Exception { // get timer Class c = Class.forName("io.seata.tm.api.DefaultFailureHandlerImpl"); - Field field = c.getDeclaredField("timer"); + Field field = c.getDeclaredField("TIMER"); field.setAccessible(true); HashedWheelTimer timer = (HashedWheelTimer) field.get(failureHandler); // assert timer pendingCount: first time is 1