diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index dd84681ba7c..24f0b4b59f5 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -7,7 +7,7 @@ on: branches: [ 2.x, develop, master ] jobs: - # job 1: Test based on java 8 and 17. Do not checkstyle. + # job 1: Test based on java 8, 17 and 21. Do not checkstyle. build: name: "build" services: @@ -28,7 +28,7 @@ jobs: strategy: fail-fast: false matrix: - java: [ 8, 17 ] + java: [ 8, 17, 21 ] steps: # step 1 - name: "Checkout" diff --git a/.licenserc.yaml b/.licenserc.yaml index ab610730010..426dffad0a8 100644 --- a/.licenserc.yaml +++ b/.licenserc.yaml @@ -61,6 +61,7 @@ header: - 'ext/apm-seata-skywalking-plugin/config/agent.config' - 'server/src/main/resources/lua/redislocker/redislock.lua' - 'server/src/main/resources/banner.txt' + - 'namingserver/src/main/resources/banner.txt' - '**/*.json' comment: on-failure dependency: diff --git a/README.md b/README.md index 59209dacde9..bb8be9dca08 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ [![Build Status](https://github.com/apache/incubator-seata/workflows/build/badge.svg?branch=develop)](https://github.com/apache/incubator-seata/actions) [![codecov](https://codecov.io/gh/apache/incubator-seata/graph/badge.svg?token=tbmHt2ZfxO)](https://codecov.io/gh/apache/incubator-seata) [![license](https://img.shields.io/github/license/apache/incubator-seata.svg)](https://www.apache.org/licenses/LICENSE-2.0.html) -[![maven](https://img.shields.io/maven-central/v/org.apache.seata/seata-all?versionSuffix=2.1.0)](https://central.sonatype.com/search?q=org.apache.seata%3Aseata-all) +[![maven](https://img.shields.io/maven-central/v/org.apache.seata/seata-all?versionSuffix=2.2.0)](https://central.sonatype.com/search?q=org.apache.seata%3Aseata-all) ## What is Seata? @@ -85,7 +85,7 @@ For more details about principle and design, please go to [Seata wiki page](http Depending on the scenario, choose one of the two dependencies: `org.apache.seata:seata-all` or `org.apache.seata:seata-spring-boot-starter`. ```xml - 2.1.0 + 2.2.0 diff --git a/all/pom.xml b/all/pom.xml index dfffb07feea..867ec15c552 100644 --- a/all/pom.xml +++ b/all/pom.xml @@ -262,6 +262,11 @@ seata-hsf ${project.version} + + org.apache.seata + seata-serializer-fury + ${project.version} + org.apache.seata seata-serializer-kryo @@ -339,6 +344,11 @@ seata-saga-spring ${project.version} + + org.apache.seata + seata-saga-annotation + ${project.version} + io.seata seata-all diff --git a/bom/pom.xml b/bom/pom.xml index 722c97660f9..8162f25b5ca 100644 --- a/bom/pom.xml +++ b/bom/pom.xml @@ -348,6 +348,11 @@ seata-saga-engine-store ${project.version} + + org.apache.seata + seata-saga-annotation + ${project.version} + org.apache.seata seata-sqlparser-core diff --git a/build/pom.xml b/build/pom.xml index d54230f995d..3d8709ee683 100644 --- a/build/pom.xml +++ b/build/pom.xml @@ -71,7 +71,7 @@ - 2.3.0-SNAPSHOT + 2.4.0-SNAPSHOT 1.8 @@ -147,6 +147,7 @@ latest true + true diff --git a/changes/en-us/2.3.0.md b/changes/en-us/2.3.0.md new file mode 100644 index 00000000000..26643b163d4 --- /dev/null +++ b/changes/en-us/2.3.0.md @@ -0,0 +1,128 @@ +### 2.3.0 + +
+ Release notes + +### Apache Seata(incubating) 2.3.0 + +Seata 2.3.0 Released. + +Seata is an easy-to-use, high-performance, open source distributed transaction solution. + +The version is updated as follows: + +### feature: + +- [[#6904](https://github.com/apache/incubator-seata/pull/6904)] add fastjson2 serializer support +- [[#6876](https://github.com/apache/incubator-seata/pull/6876)] support kingbase +- [[#6881](https://github.com/apache/incubator-seata/pull/6881)] support grpc +- [[#6864](https://github.com/apache/incubator-seata/pull/6864)] support shentong database +- [[#6974](https://github.com/apache/incubator-seata/pull/6974)] support fastjson2 undolog parser +- [[#6992](https://github.com/apache/incubator-seata/pull/6992)] support grpc serializer +- [[#6973](https://github.com/apache/incubator-seata/pull/6973)] support saga annotation +- [[#6926](https://github.com/apache/incubator-seata/pull/6926)] support ssl communication for raft nodes + +### bugfix: + +- [[#6899](https://github.com/apache/incubator-seata/pull/6899)] fix file.conf read failed after package +- [[#6890](https://github.com/apache/incubator-seata/pull/6890)] fix designerJson to standardJson: subStateMachine + compensateState cannot be recognized +- [[#6907](https://github.com/apache/incubator-seata/pull/6907)] fix the issue of Codecov not generating reports +- [[#6923](https://github.com/apache/incubator-seata/pull/6923)] Enhance 401 Error Handling by Refreshing Token +- [[#6925](https://github.com/apache/incubator-seata/pull/6925)] fix the issue in Raft model a follower's crash may lead + to the continued use of expired tokens +- [[#6932](https://github.com/apache/incubator-seata/pull/6932)] when enabling local transactions, the lock contention + failure in file & raft mode does not exit, leading to a lingering lock +- [[#6940](https://github.com/apache/incubator-seata/pull/6940)] Fix NacosRegistry lookup behavior + transactionServiceGroup is empty causing NPE error +- [[#6943](https://github.com/apache/incubator-seata/pull/6943)] fix the conversion error for `convertBranchSession` in + concurrent environment. +- [[#6948](https://github.com/apache/incubator-seata/pull/6948)] Fix the CI build issue on the ARM64 platform +- [[#6947](https://github.com/apache/incubator-seata/pull/6947)] fix npe for nacos registry when look up address +- [[#6984](https://github.com/apache/incubator-seata/pull/6984)] support building docker image on openjdk23 +- [[#6994](https://github.com/apache/incubator-seata/pull/6994)] fix the problem of building undoLog exception when + update join does not update data +- [[#7005](https://github.com/apache/incubator-seata/pull/7005)] fix the Raft NPE issue caused by two-phase concurrency +- [[#7010](https://github.com/apache/incubator-seata/pull/7010)] fix error while the "context" is key word in DM8 when + delete undolog +- [[#7022](https://github.com/apache/incubator-seata/pull/7022)] fix `store.mode` property + in `application.raft.example.yml` +- [[#7025](https://github.com/apache/incubator-seata/pull/7025)] fix vGroupMappingManager is NOT init +- [[#7044](https://github.com/apache/incubator-seata/pull/7044)] fix tableMeta refresh after closed +- + +### optimize: + +- [[#6826](https://github.com/apache/incubator-seata/pull/6826)] remove the branch registration operation of the XA + read-only transaction +- [[#6874](https://github.com/apache/incubator-seata/pull/6874)] modify the version to 2.3.0-SNAPSHOT +- [[#6892](https://github.com/apache/incubator-seata/pull/6892)] upgrade npmjs version in console module +- [[#6883](https://github.com/apache/incubator-seata/pull/6874)] remove write only object +- [[#6892](https://github.com/apache/incubator-seata/pull/6892)] upgrade npmjs version +- [[#6889](https://github.com/apache/incubator-seata/pull/6889)] Correct word spelling errors +- [[#6898](https://github.com/apache/incubator-seata/pull/6898)] upgrade npmjs version in saga module +- [[#6879](https://github.com/apache/incubator-seata/pull/6879)] fix log argument mismatch issue +- [[#6902](https://github.com/apache/incubator-seata/pull/6900)] optimize readme docs +- [[#6807](https://github.com/apache/incubator-seata/pull/6807)] splitting MergedWarpMessage enhances the server + parallel processing capability +- [[#6905](https://github.com/apache/incubator-seata/pull/6905)] remove incompatible licenses at build time +- [[#6906](https://github.com/apache/incubator-seata/pull/6906)] h2 dependency adds test scope +- [[#6911](https://github.com/apache/incubator-seata/pull/6911)] fix some typos in project +- [[#6918](https://github.com/apache/incubator-seata/pull/6918)] Use the openjdk image of eclipse-temurin as the base + image +- [[#6938](https://github.com/apache/incubator-seata/pull/6938)] Update online chat information in README.md +- [[#6950](https://github.com/apache/incubator-seata/pull/6950)] Remove JVM parameter app.id +- [[#6959](https://github.com/apache/incubator-seata/pull/6959)] update the naming and description for + the `seata-http-jakarta` module +- [[#6991](https://github.com/apache/incubator-seata/pull/6991)] gRPC serialization default to Protobuf +- [[#6993](https://github.com/apache/incubator-seata/pull/6993)] optimize transaction metrics +- [[#6995](https://github.com/apache/incubator-seata/pull/6995)] upgrade outdate npmjs dependencies +- [[#6996](https://github.com/apache/incubator-seata/pull/6996)] optimize lock release logic in AT transaction mode +- [[#7023](https://github.com/apache/incubator-seata/pull/7023)] optimize fail fast, when all server not available +- [[#7027](https://github.com/apache/incubator-seata/pull/7027)] raft mode maintains the reload logic consistent with + the file +- [[#6891](https://github.com/apache/incubator-seata/pull/6891)] add StateType Enum +- [[#7040](https://github.com/apache/incubator-seata/pull/7040)] optimize the print info in ConfigurationFactory +- [[#7046](https://github.com/apache/incubator-seata/pull/7046)] remove the dependency conflict for spring-webmvc +- [[#7043](https://github.com/apache/incubator-seata/pull/7043)] finish rollback if sendResult/msg not found +- [[#7051](https://github.com/apache/incubator-seata/pull/7051)] add namingserver jib +- [[#7054](https://github.com/apache/incubator-seata/pull/7054)] In file mode when the lock cannot be acquired output + the holder's xid + +### refactor: + +- [[#7017](https://github.com/apache/incubator-seata/pull/7017)] remove dependency on seata-server module + +### test: + +- [[#6869](https://github.com/apache/incubator-seata/pull/6869)] Add unit tests for the `seata-core` module +- [[#6927](https://github.com/apache/incubator-seata/pull/6927)] Add unit tests for the `seata-rocketmq` module +- [[#7018](https://github.com/apache/incubator-seata/pull/7018)] Add unit tests for the `seata-tm` module +- [[#7030](https://github.com/apache/incubator-seata/pull/7030)] Add unit tests for the `seata-common` module + +Thanks to these contributors for their code commits. Please report an unintended omission. + + + +- [slievrly](https://github.com/slievrly) +- [GoodBoyCoder](https://github.com/GoodBoyCoder) +- [funky-eyes](https://github.com/funky-eyes) +- [dk2k](https://github.com/dk2k) +- [MaoMaoandSnail](https://github.com/MaoMaoandSnail) +- [yougecn](https://github.com/yougecn) +- [arrrnold17](https://github.com/arrrnold17) +- [xjlgod](https://github.com/xjlgod) +- [PleaseGiveMeTheCoke](https://github.com/PleaseGiveMeTheCoke) +- [dsomehan](https://github.com/dsomehan) +- [psxjoy](https://github.com/psxjoy) +- [xingfudeshi](https://github.com/xingfudeshi) +- [o-jimin](https://github.com/o-jimin) +- [lixingjia77](https://github.com/lixingjia77) +- [whaon](https://github.com/whaon) +- [YvCeung](https://github.com/YvCeung) +- [jsbxyyx](https://github.com/jsbxyyx) +- [lightClouds917](https://github.com/lightClouds917) +- [Muluo-cyan](https://github.com/Muluo-cyan) +- [yixia](https://github.com/wt-better) + +Also, we receive many valuable issues, questions and advices from our community. Thanks for you all. diff --git a/changes/en-us/2.x.md b/changes/en-us/2.x.md index 5e7938a1981..a2ea0b0b4a7 100644 --- a/changes/en-us/2.x.md +++ b/changes/en-us/2.x.md @@ -3,71 +3,45 @@ Add changes here for all PR submitted to the 2.x branch. ### feature: -- [[#6904](https://github.com/apache/incubator-seata/pull/6904)] add fastjson2 serializer support -- [[#6876](https://github.com/apache/incubator-seata/pull/6876)] support kingbase -- [[#6881](https://github.com/apache/incubator-seata/pull/6881)] support grpc -- [[#6864](https://github.com/apache/incubator-seata/pull/6864)] support shentong database -- [[#6974](https://github.com/apache/incubator-seata/pull/6974)] support fastjson2 undolog parser +- [[#7037](https://github.com/apache/incubator-seata/pull/7037)] support fury undolog parser +- [[#7069](https://github.com/apache/incubator-seata/pull/7069)] Raft cluster mode supports address translation +- [[#7038](https://github.com/apache/incubator-seata/pull/7038)] support fury serializer ### bugfix: -- [[#6899](https://github.com/apache/incubator-seata/pull/6899)] fix file.conf read failed after package -- [[#6890](https://github.com/apache/incubator-seata/pull/6890)] fix designerJson to standardJson: subStateMachine compensateState cannot be recognized -- [[#6907](https://github.com/apache/incubator-seata/pull/6907)] fix the issue of Codecov not generating reports -- [[#6923](https://github.com/apache/incubator-seata/pull/6923)] Enhance 401 Error Handling by Refreshing Token -- [[#6925](https://github.com/apache/incubator-seata/pull/6925)] fix the issue in Raft model a follower's crash may lead to the continued use of expired tokens -- [[#6932](https://github.com/apache/incubator-seata/pull/6932)] when enabling local transactions, the lock contention failure in file & raft mode does not exit, leading to a lingering lock -- [[#6940](https://github.com/apache/incubator-seata/pull/6940)] Fix NacosRegistry lookup behavior transactionServiceGroup is empty causing NPE error -- [[#6943](https://github.com/apache/incubator-seata/pull/6943)] fix the conversion error for `convertBranchSession` in concurrent environment. -- [[#6948](https://github.com/apache/incubator-seata/pull/6948)] Fix the CI build issue on the ARM64 platform -- [[#6947](https://github.com/apache/incubator-seata/pull/6947)] fix npe for nacos registry when look up address -- [[#6984](https://github.com/apache/incubator-seata/pull/6984)] support building docker image on openjdk23 -### optimize: -- [[#6826](https://github.com/apache/incubator-seata/pull/6826)] remove the branch registration operation of the XA read-only transaction -- [[#6874](https://github.com/apache/incubator-seata/pull/6874)] modify the version to 2.3.0-SNAPSHOT -- [[#6892](https://github.com/apache/incubator-seata/pull/6892)] upgrade npmjs version in console module -- [[#6883](https://github.com/apache/incubator-seata/pull/6874)] remove write only object -- [[#6892](https://github.com/apache/incubator-seata/pull/6892)] upgrade npmjs version -- [[#6889](https://github.com/apache/incubator-seata/pull/6889)] Correct word spelling errors -- [[#6898](https://github.com/apache/incubator-seata/pull/6898)] upgrade npmjs version in saga module -- [[#6879](https://github.com/apache/incubator-seata/pull/6879)] fix log argument mismatch issue -- [[#6902](https://github.com/apache/incubator-seata/pull/6900)] optimize readme docs -- [[#6807](https://github.com/apache/incubator-seata/pull/6807)] splitting MergedWarpMessage enhances the server parallel processing capability -- [[#6905](https://github.com/apache/incubator-seata/pull/6905)] remove incompatible licenses at build time -- [[#6906](https://github.com/apache/incubator-seata/pull/6906)] h2 dependency adds test scope -- [[#6911](https://github.com/apache/incubator-seata/pull/6911)] fix some typos in project -- [[#6918](https://github.com/apache/incubator-seata/pull/6918)] Use the openjdk image of eclipse-temurin as the base image -- [[#6938](https://github.com/apache/incubator-seata/pull/6938)] Update online chat information in README.md -- [[#6950](https://github.com/apache/incubator-seata/pull/6950)] Remove JVM parameter app.id -- [[#6959](https://github.com/apache/incubator-seata/pull/6959)] update the naming and description for the `seata-http-jakarta` module +- [[#PR_NO](https://github.com/apache/incubator-seata/pull/#PR_NO)] fix XXX +### optimize: -### refactor: +- [[#6828](https://github.com/apache/incubator-seata/pull/6828)] spring boot compatible with file.conf and registry.conf +- [[#7012](https://github.com/apache/incubator-seata/pull/7012)] When the number of primary keys exceeds 1000, use union to concatenate the SQL +- [[#7075](https://github.com/apache/incubator-seata/pull/7075)] fast fail when channel is null +- [[#7089](https://github.com/apache/incubator-seata/pull/7089)] support instance registration to the registry center +- [[#7093](https://github.com/apache/incubator-seata/pull/7093)] add a test workflow for JDK 21 ### security: +- [[#PR_NO](https://github.com/apache/incubator-seata/pull/PR_NO)] upgrade XXX + ### test: -- [[#6927](https://github.com/apache/incubator-seata/pull/6927)] Add unit tests for the `seata-rocketmq` module + +- [[#7092](https://github.com/apache/incubator-seata/pull/7092)] fix the issue of NacosMockTest failing to run + +### refactor: + +- [[#PR_NO](https://github.com/apache/incubator-seata/pull/PR_NO)] refactor XXX Thanks to these contributors for their code commits. Please report an unintended omission. + - [slievrly](https://github.com/slievrly) +- [lyl2008dsg](https://github.com/lyl2008dsg) +- [remind](https://github.com/remind) - [GoodBoyCoder](https://github.com/GoodBoyCoder) +- [PeppaO](https://github.com/PeppaO) - [funky-eyes](https://github.com/funky-eyes) -- [dk2k](https://github.com/dk2k) -- [MaoMaoandSnail](https://github.com/MaoMaoandSnail) -- [yougecn](https://github.com/yougecn) -- [arrrnold17](https://github.com/arrrnold17) -- [xjlgod](https://github.com/xjlgod) -- [PleaseGiveMeTheCoke](https://github.com/PleaseGiveMeTheCoke) -- [dsomehan](https://github.com/dsomehan) -- [psxjoy](https://github.com/psxjoy) -- [xingfudeshi](https://github.com/xingfudeshi) -- [o-jimin](https://github.com/o-jimin) -- [lixingjia77](https://github.com/lixingjia77) - -Also, we receive many valuable issues, questions and advices from our community. Thanks for you all. +Also, we receive many valuable issues, questions and advices from our community. Thanks for you all. \ No newline at end of file diff --git a/changes/zh-cn/2.3.0.md b/changes/zh-cn/2.3.0.md new file mode 100644 index 00000000000..8b8f5054521 --- /dev/null +++ b/changes/zh-cn/2.3.0.md @@ -0,0 +1,117 @@ +### 2.3.0 + +
+ Release notes + +### Apache Seata(incubating) 2.3.0 + +Apache Seata(incubating) 2.3.0 发布。 + +Apache Seata(incubating) 是一款开源的分布式事务解决方案,提供高性能和简单易用的分布式事务服务。 +此版本更新如下: + +### feature: + +- [[#6904](https://github.com/apache/incubator-seata/pull/6904)] 增加Fastjson2序列化Rpc消息支持 +- [[#6876](https://github.com/apache/incubator-seata/pull/6876)] 支持人大金仓数据库(kingbase) +- [[#6881](https://github.com/apache/incubator-seata/pull/6881)] client和server支持grpc协议 +- [[#6864](https://github.com/apache/incubator-seata/pull/6864)] 支持神通数据库(oscar) +- [[#6974](https://github.com/apache/incubator-seata/pull/6974)] 支持UndoLog的fastjson2序列化方式 +- [[#6992](https://github.com/apache/incubator-seata/pull/6992)] 支持grpc序列化器 +- [[#6995](https://github.com/apache/incubator-seata/pull/6995)] 升级过时的 npmjs 依赖 +- [[#6973](https://github.com/apache/incubator-seata/pull/6973)] 支持saga注解化 +- [[#6926](https://github.com/apache/incubator-seata/pull/6926)] 支持Raft节点间的SSL通信 + +### bugfix: + +- [[#6899](https://github.com/apache/incubator-seata/pull/6899)] 修复file.conf打包后的读取 +- [[#6890](https://github.com/apache/incubator-seata/pull/6890)] 修复saga设计json转标准json过程中: 子状态机补偿节点无法被识别 +- [[#6907](https://github.com/apache/incubator-seata/pull/6907)] 修复Codecov未生成报告的问题 +- [[#6923](https://github.com/apache/incubator-seata/pull/6923)] 增强 401 错误处理,通过刷新令牌 +- [[#6925](https://github.com/apache/incubator-seata/pull/6925)] 修复Raft模式下,Follower崩溃可能导致Client继续使用过期令牌的问题 +- [[#6932](https://github.com/apache/incubator-seata/pull/6932)] 修复开启本地事务时file&raft模式下锁争抢失败未退出导致可能出现残留锁 +- [[#6940](https://github.com/apache/incubator-seata/pull/6940)] 修复NacosRegistry lookup 行为 transactionServiceGroup + 为空导致 NPE 错误 +- [[#6943](https://github.com/apache/incubator-seata/pull/6943)] 修复并发状态下 `convertBranchSession` 转换报错问题 +- [[#6948](https://github.com/apache/incubator-seata/pull/6948)] 修复在ARM64平台下CI构建出错的问题 +- [[#6947](https://github.com/apache/incubator-seata/pull/6947)] 修复nacos注册中心查询可用地址时的空指针问题 +- [[#6984](https://github.com/apache/incubator-seata/pull/6984)] 修复 openjdk23 版本下无法构建 docker 镜像的问题 +- [[#6994](https://github.com/apache/incubator-seata/pull/6994)] 修复updateJoin语句未更新到数据时prepareUndoLog异常 +- [[#7005](https://github.com/apache/incubator-seata/pull/7005)] 修复Raft模式下两阶段并发可能导致NPE的问题 +- [[#7010](https://github.com/apache/incubator-seata/pull/7010)] 修复使用达梦数据库时删除undolog发生SQL语法错误 +- [[#7022](https://github.com/apache/incubator-seata/pull/7022)] 修复 `application.raft.example.yml`的 `store.mode`属性 +- [[#7025](https://github.com/apache/incubator-seata/pull/7025)] 修复vGroupMappingManager未初始化的问题 +- [[#7044](https://github.com/apache/incubator-seata/pull/7044)] 修复TableMeta在数据源关闭后刷新错误问题 + +### optimize: + +- [[#6826](https://github.com/apache/incubator-seata/pull/6826)] 移除只读XA事务的分支注册操作 +- [[#6874](https://github.com/apache/incubator-seata/pull/6874)] modify the version to 2.3.0-SNAPSHOT +- [[#6892](https://github.com/apache/incubator-seata/pull/6892)] 升级 console 模块 npmjs 版本 +- [[#6874](https://github.com/apache/incubator-seata/pull/6874)] 修改版本为2.3.0-SNAPSHOT +- [[#6883](https://github.com/apache/incubator-seata/pull/6874)] 删除代码中无用对象的创建 +- [[#6892](https://github.com/apache/incubator-seata/pull/6892)] 升级 npmjs 版本 +- [[#6889](https://github.com/apache/incubator-seata/pull/6889)] 修正单词拼写错误 +- [[#6898](https://github.com/apache/incubator-seata/pull/6898)] 升级 saga 模块 npmjs 版本 +- [[#6879](https://github.com/apache/incubator-seata/pull/6879)] 修复日志参数不匹配问题 +- [[#6898](https://github.com/apache/incubator-seata/pull/6898)] 升级 saga 模块 npmjs 版本 +- [[#6902](https://github.com/apache/incubator-seata/pull/6900)] 优化 readme 文档 +- [[#6807](https://github.com/apache/incubator-seata/pull/6807)] 分离merge消息使其能完全并行处理 +- [[#6905](https://github.com/apache/incubator-seata/pull/6905)] 移除构建期不兼容的 license +- [[#6906](https://github.com/apache/incubator-seata/pull/6906)] h2依赖添加test scope +- [[#6911](https://github.com/apache/incubator-seata/pull/6911)] 修正项目中的部分拼写错误 +- [[#6918](https://github.com/apache/incubator-seata/pull/6918)] 使用eclipse-temurin的openjdk镜像作为基础镜像 +- [[#6938](https://github.com/apache/incubator-seata/pull/6938)] 更新 README.md 中的社区联系信息 +- [[#6950](https://github.com/apache/incubator-seata/pull/6950)] 移除JVM参数app.id +- [[#6959](https://github.com/apache/incubator-seata/pull/6959)] 修正 `seata-http-jakarta`的模块命名和描述 +- [[#6991](https://github.com/apache/incubator-seata/pull/6991)] gRPC协议序列化默认值为protobuf +- [[#6996](https://github.com/apache/incubator-seata/pull/6996)] 优化 AT 事务模式锁释放逻辑 +- [[#6993](https://github.com/apache/incubator-seata/pull/6993)] 优化 metrics 指标 +- [[#6995](https://github.com/apache/incubator-seata/pull/6995)] 升级过时的 npmjs 依赖 +- [[#6996](https://github.com/apache/incubator-seata/pull/6996)] 优化 AT 事务模式锁释放逻辑 +- [[#7023](https://github.com/apache/incubator-seata/pull/7023)] 优化快速失败 +- [[#7027](https://github.com/apache/incubator-seata/pull/7027)] raft模式下reload行为与file保持一致 +- [[#6891](https://github.com/apache/incubator-seata/pull/6891)] 增加 StateType 类型 +- [[#7040](https://github.com/apache/incubator-seata/pull/7040)] 优化ConfigurationFactory加载的打印信息 +- [[#7046](https://github.com/apache/incubator-seata/pull/7046)] 去除spring-webmvc的依赖冲突 +- [[#7043](https://github.com/apache/incubator-seata/pull/7043)] 在获取不到mq的sendResult时,直接完成回滚 +- [[#7051](https://github.com/apache/incubator-seata/pull/7051)] 为namingserver模块添加Jib支持以构建Docker镜像 +- [[#7054](https://github.com/apache/incubator-seata/pull/7054)] file模式中竞争不到锁时输出持有者的xid + +### refactor: + +- [[#7017](https://github.com/apache/incubator-seata/pull/7017)] 移除 seata-server 模块的依赖 + +### test: + +- [[#6869](https://github.com/apache/incubator-seata/pull/6869)] 增加`seata-core`测试用例覆盖率 +- [[#6927](https://github.com/apache/incubator-seata/pull/6927)] 增加`seata-rocketmq`模块的测试用例 +- [[#7018](https://github.com/apache/incubator-seata/pull/7018)] 增加 `seata-tm` 模块的测试用例 +- [[#7030](https://github.com/apache/incubator-seata/pull/7030)] 增加 `seata-common` 模块的测试用例 + +非常感谢以下 contributors 的代码贡献。若有无意遗漏,请报告。 + + + +- [slievrly](https://github.com/slievrly) +- [GoodBoyCoder](https://github.com/GoodBoyCoder) +- [funky-eyes](https://github.com/funky-eyes) +- [dk2k](https://github.com/dk2k) +- [MaoMaoandSnail](https://github.com/MaoMaoandSnail) +- [yougecn](https://github.com/yougecn) +- [arrrnold17](https://github.com/arrrnold17) +- [xjlgod](https://github.com/xjlgod) +- [PleaseGiveMeTheCoke](https://github.com/PleaseGiveMeTheCoke) +- [dsomehan](https://github.com/dsomehan) +- [psxjoy](https://github.com/psxjoy) +- [xingfudeshi](https://github.com/xingfudeshi) +- [o-jimin](https://github.com/o-jimin) +- [lixingjia77](https://github.com/lixingjia77) +- [whaon](https://github.com/whaon) +- [YvCeung](https://github.com/YvCeung) +- [jsbxyyx](https://github.com/jsbxyyx) +- [lightClouds917](https://github.com/lightClouds917) +- [Muluo-cyan](https://github.com/Muluo-cyan) +- [yixia](https://github.com/wt-better) + +同时,我们收到了社区反馈的很多有价值的issue和建议,非常感谢大家。 diff --git a/changes/zh-cn/2.x.md b/changes/zh-cn/2.x.md index 87e3c059ab4..0c4892049b1 100644 --- a/changes/zh-cn/2.x.md +++ b/changes/zh-cn/2.x.md @@ -3,73 +3,44 @@ ### feature: -- [[#6904](https://github.com/apache/incubator-seata/pull/6904)] 增加Fastjson2序列化Rpc消息支持 -- [[#6876](https://github.com/apache/incubator-seata/pull/6876)] 支持人大金仓数据库(kingbase) -- [[#6881](https://github.com/apache/incubator-seata/pull/6881)] client和server支持grpc协议 -- [[#6864](https://github.com/apache/incubator-seata/pull/6864)] 支持神通数据库(oscar) -- [[#6974](https://github.com/apache/incubator-seata/pull/6974)] 支持UndoLog的fastjson2序列化方式 +- [[#7037](https://github.com/apache/incubator-seata/pull/7037)] 支持UndoLog的fury序列化方式 +- [[#7069](https://github.com/apache/incubator-seata/pull/7069)] Raft集群模式支持地址转换 +- [[#7038](https://github.com/apache/incubator-seata/pull/7038)] 支持Fury序列化器 ### bugfix: -- [[#6899](https://github.com/apache/incubator-seata/pull/6899)] 修复file.conf打包后的读取 -- [[#6890](https://github.com/apache/incubator-seata/pull/6890)] 修复saga设计json转标准json过程中: 子状态机补偿节点无法被识别 -- [[#6907](https://github.com/apache/incubator-seata/pull/6907)] 修复Codecov未生成报告的问题 -- [[#6923](https://github.com/apache/incubator-seata/pull/6923)] 增强 401 错误处理,通过刷新令牌 -- [[#6925](https://github.com/apache/incubator-seata/pull/6925)] 修复Raft模式下,Follower崩溃可能导致Client继续使用过期令牌的问题 -- [[#6932](https://github.com/apache/incubator-seata/pull/6932)] 修复开启本地事务时file&raft模式下锁争抢失败未退出导致可能出现残留锁 -- [[#6940](https://github.com/apache/incubator-seata/pull/6940)] 修复NacosRegistry lookup 行为 transactionServiceGroup 为空导致 NPE 错误 -- [[#6943](https://github.com/apache/incubator-seata/pull/6943)] 修复并发状态下 `convertBranchSession` 转换报错问题 -- [[#6948](https://github.com/apache/incubator-seata/pull/6948)] 修复在ARM64平台下CI构建出错的问题 -- [[#6947](https://github.com/apache/incubator-seata/pull/6947)] 修复nacos注册中心查询可用地址时的空指针问题 -- [[#6984](https://github.com/apache/incubator-seata/pull/6984)] 修复 openjdk23 版本下无法构建 docker 镜像的问题 -### optimize: -- [[#6826](https://github.com/apache/incubator-seata/pull/6826)] 移除只读XA事务的分支注册操作 -- [[#6874](https://github.com/apache/incubator-seata/pull/6874)] modify the version to 2.3.0-SNAPSHOT -- [[#6892](https://github.com/apache/incubator-seata/pull/6892)] 升级 console 模块 npmjs 版本 -- [[#6874](https://github.com/apache/incubator-seata/pull/6874)] 修改版本为2.3.0-SNAPSHOT -- [[#6883](https://github.com/apache/incubator-seata/pull/6874)] 删除代码中无用对象的创建 -- [[#6892](https://github.com/apache/incubator-seata/pull/6892)] 升级 npmjs 版本 -- [[#6889](https://github.com/apache/incubator-seata/pull/6889)] 修正单词拼写错误 -- [[#6898](https://github.com/apache/incubator-seata/pull/6898)] 升级 saga 模块 npmjs 版本 -- [[#6879](https://github.com/apache/incubator-seata/pull/6879)] 修复日志参数不匹配问题 -- [[#6898](https://github.com/apache/incubator-seata/pull/6898)] 升级 saga 模块 npmjs 版本 -- [[#6902](https://github.com/apache/incubator-seata/pull/6900)] 优化 readme 文档 -- [[#6807](https://github.com/apache/incubator-seata/pull/6807)] 分离merge消息使其能完全并行处理 -- [[#6905](https://github.com/apache/incubator-seata/pull/6905)] 移除构建期不兼容的 license -- [[#6906](https://github.com/apache/incubator-seata/pull/6906)] h2依赖添加test scope -- [[#6911](https://github.com/apache/incubator-seata/pull/6911)] 修正项目中的部分拼写错误 -- [[#6918](https://github.com/apache/incubator-seata/pull/6918)] 使用eclipse-temurin的openjdk镜像作为基础镜像 -- [[#6938](https://github.com/apache/incubator-seata/pull/6938)] 更新 README.md 中的社区联系信息 -- [[#6950](https://github.com/apache/incubator-seata/pull/6950)] 移除JVM参数app.id -- [[#6959](https://github.com/apache/incubator-seata/pull/6959)] 修正 `seata-http-jakarta`的模块命名和描述 +- [[#PR_NO](https://github.com/apache/incubator-seata/pull/#PR_NO)] 修复XXX -### refactor: +### optimize: +- [[#6828](https://github.com/apache/incubator-seata/pull/6828)] seata-spring-boot-starter兼容file.conf和registry.conf +- [[#7012](https://github.com/apache/incubator-seata/pull/7012)] 当主键超过1000个时,使用union拼接sql,可以使用索引 +- [[#7075](https://github.com/apache/incubator-seata/pull/7075)] 当channel为空时,快速失败,以便于减少不必要的等待 +- [[#7089](https://github.com/apache/incubator-seata/pull/7089)] 新增instance注册到注册中心的接口 +- [[#7093](https://github.com/apache/incubator-seata/pull/7093)] 增加jdk21的工作流测试 ### security: +- [[#PR_NO](https://github.com/apache/incubator-seata/pull/PR_NO)] 升级XXX + ### test: -- [[#6927](https://github.com/apache/incubator-seata/pull/6927)] 增加`seata-rocketmq`模块的测试用例 + +- [[#7092](https://github.com/apache/incubator-seata/pull/7092)] 修复NacosMockTest测试方法并行导致测试结果被干扰失败的问题 + +### refactor: + +- [[#PR_NO](https://github.com/apache/incubator-seata/pull/PR_NO)] 重构XXX 非常感谢以下 contributors 的代码贡献。若有无意遗漏,请报告。 + - [slievrly](https://github.com/slievrly) +- [lyl2008dsg](https://github.com/lyl2008dsg) +- [remind](https://github.com/remind) - [GoodBoyCoder](https://github.com/GoodBoyCoder) +- [PeppaO](https://github.com/PeppaO) - [funky-eyes](https://github.com/funky-eyes) -- [dk2k](https://github.com/dk2k) -- [MaoMaoandSnail](https://github.com/MaoMaoandSnail) -- [yougecn](https://github.com/yougecn) -- [arrrnold17](https://github.com/arrrnold17) -- [xjlgod](https://github.com/xjlgod) -- [PleaseGiveMeTheCoke](https://github.com/PleaseGiveMeTheCoke) -- [dsomehan](https://github.com/dsomehan) -- [psxjoy](https://github.com/psxjoy) -- [xingfudeshi](https://github.com/xingfudeshi) -- [o-jimin](https://github.com/o-jimin) -- [lixingjia77](https://github.com/lixingjia77) - - -同时,我们收到了社区反馈的很多有价值的issue和建议,非常感谢大家。 +同时,我们收到了社区反馈的很多有价值的issue和建议,非常感谢大家。 \ No newline at end of file diff --git a/common/src/main/java/org/apache/seata/common/ConfigurationKeys.java b/common/src/main/java/org/apache/seata/common/ConfigurationKeys.java index ff8436b6dc9..3bb4c9873ff 100644 --- a/common/src/main/java/org/apache/seata/common/ConfigurationKeys.java +++ b/common/src/main/java/org/apache/seata/common/ConfigurationKeys.java @@ -504,9 +504,15 @@ public interface ConfigurationKeys { /** * The constant ROLLBACK_RETRY_TIMEOUT_UNLOCK_ENABLE. + * This configuration is deprecated, please use {@link #ROLLBACK_FAILED_UNLOCK_ENABLE} instead. */ + @Deprecated String ROLLBACK_RETRY_TIMEOUT_UNLOCK_ENABLE = SERVER_PREFIX + "rollbackRetryTimeoutUnlockEnable"; + /** + * The constant ROLLBACK_FAILED_UNLOCK_ENABLE. + */ + String ROLLBACK_FAILED_UNLOCK_ENABLE = SERVER_PREFIX + "rollbackFailedUnlockEnable"; /** * the constant RETRY_DEAD_THRESHOLD */ @@ -888,6 +894,21 @@ public interface ConfigurationKeys { */ String SERVER_RAFT = SERVER_PREFIX + "raft."; + /** + * The constant SERVER_RAFT_SSL. + */ + String SERVER_RAFT_SSL = SERVER_RAFT + "ssl."; + + /** + * The constant SERVER_RAFT_SSL_CLIENT. + */ + String SERVER_RAFT_SSL_CLIENT = SERVER_RAFT_SSL + "client."; + + /** + * The constant SERVER_RAFT_SSL_SERVER. + */ + String SERVER_RAFT_SSL_SERVER = SERVER_RAFT_SSL + "server."; + /** * The constant SERVER_RAFT_SERVER_ADDR. */ @@ -918,6 +939,42 @@ public interface ConfigurationKeys { */ String SERVER_RAFT_SYNC = SERVER_RAFT + "sync"; + /** + * The constant SERVER_RAFT_SSL_ENABLED. + */ + String SERVER_RAFT_SSL_ENABLED = SERVER_RAFT_SSL + "enabled"; + + /** + * The constant SERVER_RAFT_SSL_SERVER_KEYSTORE. + */ + String SERVER_RAFT_SSL_SERVER_KEYSTORE_PATH = SERVER_RAFT_SSL_SERVER + "keystore.path"; + + /** + * The constant SERVER_RAFT_SSL_CLIENT_KEYSTORE. + */ + String SERVER_RAFT_SSL_CLIENT_KEYSTORE_PATH = SERVER_RAFT_SSL_CLIENT + "keystore.path"; + + /** + * The constant SERVER_RAFT_SSL_SERVER_KEYSTORE_PASSWORD. + */ + String SERVER_RAFT_SSL_SERVER_KEYSTORE_PASSWORD = SERVER_RAFT_SSL_SERVER + "keystore.password"; + + /** + * The constant SERVER_RAFT_SSL_CLIENT_KEYSTORE_PASSWORD. + */ + String SERVER_RAFT_SSL_CLIENT_KEYSTORE_PASSWORD = SERVER_RAFT_SSL_CLIENT + "keystore.password"; + + + /** + * The constant SERVER_RAFT_SSL_KEYSTORE_TYPE. + */ + String SERVER_RAFT_SSL_KEYSTORE_TYPE = SERVER_RAFT_SSL + "keystore.type"; + + /** + * The constant SERVER_RAFT_SSL_KMF_ALGORITHM. + */ + String SERVER_RAFT_SSL_KMF_ALGORITHM = SERVER_RAFT_SSL + "kmf.algorithm"; + /** * The constant SERVER_RAFT_MAX_APPEND_BUFFER_SIZE. */ @@ -1053,4 +1110,15 @@ public interface ConfigurationKeys { * The constant META_PREFIX */ String META_PREFIX = SEATA_FILE_ROOT_CONFIG + FILE_CONFIG_SPLIT_CHAR + FILE_ROOT_REGISTRY + FILE_CONFIG_SPLIT_CHAR + "metadata."; + + /** + * The constant SERVER_REGISTRY_METADATA_PREFIX + */ + String SERVER_REGISTRY_METADATA_PREFIX = SERVER_PREFIX + FILE_ROOT_REGISTRY + ".metadata"; + + /** + * The constant SERVER_REGISTRY_METADATA_EXTERNAL + */ + String SERVER_REGISTRY_METADATA_EXTERNAL = SERVER_REGISTRY_METADATA_PREFIX + ".external"; + } diff --git a/common/src/main/java/org/apache/seata/common/DefaultValues.java b/common/src/main/java/org/apache/seata/common/DefaultValues.java index eb0d40bb308..85ed496430d 100644 --- a/common/src/main/java/org/apache/seata/common/DefaultValues.java +++ b/common/src/main/java/org/apache/seata/common/DefaultValues.java @@ -18,56 +18,160 @@ import java.time.Duration; +/** + * The interface Default values. + */ public interface DefaultValues { + /** + * The constant DEFAULT_CLIENT_LOCK_RETRY_INTERVAL. + */ int DEFAULT_CLIENT_LOCK_RETRY_INTERVAL = 10; + /** + * The constant DEFAULT_TM_DEGRADE_CHECK_ALLOW_TIMES. + */ int DEFAULT_TM_DEGRADE_CHECK_ALLOW_TIMES = 10; + /** + * The constant DEFAULT_CLIENT_LOCK_RETRY_TIMES. + */ int DEFAULT_CLIENT_LOCK_RETRY_TIMES = 30; + /** + * The constant DEFAULT_CLIENT_LOCK_RETRY_POLICY_BRANCH_ROLLBACK_ON_CONFLICT. + */ boolean DEFAULT_CLIENT_LOCK_RETRY_POLICY_BRANCH_ROLLBACK_ON_CONFLICT = true; + /** + * The constant DEFAULT_LOG_EXCEPTION_RATE. + */ int DEFAULT_LOG_EXCEPTION_RATE = 100; + /** + * The constant DEFAULT_CLIENT_ASYNC_COMMIT_BUFFER_LIMIT. + */ int DEFAULT_CLIENT_ASYNC_COMMIT_BUFFER_LIMIT = 10000; + /** + * The constant DEFAULT_TM_DEGRADE_CHECK_PERIOD. + */ int DEFAULT_TM_DEGRADE_CHECK_PERIOD = 2000; + /** + * The constant DEFAULT_CLIENT_REPORT_RETRY_COUNT. + */ int DEFAULT_CLIENT_REPORT_RETRY_COUNT = 5; + /** + * The constant DEFAULT_CLIENT_REPORT_SUCCESS_ENABLE. + */ boolean DEFAULT_CLIENT_REPORT_SUCCESS_ENABLE = false; + /** + * The constant DEFAULT_CLIENT_TABLE_META_CHECK_ENABLE. + */ boolean DEFAULT_CLIENT_TABLE_META_CHECK_ENABLE = true; + /** + * The constant DEFAULT_TABLE_META_CHECKER_INTERVAL. + */ long DEFAULT_TABLE_META_CHECKER_INTERVAL = 60000L; + /** + * The constant DEFAULT_TM_DEGRADE_CHECK. + */ boolean DEFAULT_TM_DEGRADE_CHECK = false; + /** + * The constant DEFAULT_CLIENT_SAGA_BRANCH_REGISTER_ENABLE. + */ boolean DEFAULT_CLIENT_SAGA_BRANCH_REGISTER_ENABLE = false; /** * The default session store dir */ String DEFAULT_SESSION_STORE_FILE_DIR = "sessionStore"; + /** + * The constant DEFAULT_CLIENT_SAGA_RETRY_PERSIST_MODE_UPDATE. + */ boolean DEFAULT_CLIENT_SAGA_RETRY_PERSIST_MODE_UPDATE = false; + /** + * The constant DEFAULT_CLIENT_SAGA_COMPENSATE_PERSIST_MODE_UPDATE. + */ boolean DEFAULT_CLIENT_SAGA_COMPENSATE_PERSIST_MODE_UPDATE = false; + /** + * The constant DEFAULT_RAFT_SERIALIZATION. + */ String DEFAULT_RAFT_SERIALIZATION = "jackson"; + /** + * The constant DEFAULT_RAFT_COMPRESSOR. + */ String DEFAULT_RAFT_COMPRESSOR = "none"; /** * Shutdown timeout default 3s */ int DEFAULT_SHUTDOWN_TIMEOUT_SEC = 13; + /** + * The constant DEFAULT_SELECTOR_THREAD_SIZE. + */ int DEFAULT_SELECTOR_THREAD_SIZE = 1; + /** + * The constant DEFAULT_BOSS_THREAD_SIZE. + */ int DEFAULT_BOSS_THREAD_SIZE = 1; - + /** + * The constant DEFAULT_SELECTOR_THREAD_PREFIX. + */ String DEFAULT_SELECTOR_THREAD_PREFIX = "NettyClientSelector"; + /** + * The constant DEFAULT_WORKER_THREAD_PREFIX. + */ String DEFAULT_WORKER_THREAD_PREFIX = "NettyClientWorkerThread"; + /** + * The constant DEFAULT_ENABLE_CLIENT_BATCH_SEND_REQUEST. + */ @Deprecated boolean DEFAULT_ENABLE_CLIENT_BATCH_SEND_REQUEST = true; + /** + * The constant DEFAULT_ENABLE_TM_CLIENT_BATCH_SEND_REQUEST. + */ boolean DEFAULT_ENABLE_TM_CLIENT_BATCH_SEND_REQUEST = false; + /** + * The constant DEFAULT_ENABLE_RM_CLIENT_BATCH_SEND_REQUEST. + */ boolean DEFAULT_ENABLE_RM_CLIENT_BATCH_SEND_REQUEST = true; + /** + * The constant DEFAULT_ENABLE_TC_SERVER_BATCH_SEND_RESPONSE. + */ boolean DEFAULT_ENABLE_TC_SERVER_BATCH_SEND_RESPONSE = false; + /** + * The constant DEFAULT_CLIENT_CHANNEL_CHECK_FAIL_FAST. + */ boolean DEFAULT_CLIENT_CHANNEL_CHECK_FAIL_FAST = true; + /** + * The constant DEFAULT_BOSS_THREAD_PREFIX. + */ String DEFAULT_BOSS_THREAD_PREFIX = "NettyBoss"; + /** + * The constant DEFAULT_NIO_WORKER_THREAD_PREFIX. + */ String DEFAULT_NIO_WORKER_THREAD_PREFIX = "NettyServerNIOWorker"; + /** + * The constant DEFAULT_EXECUTOR_THREAD_PREFIX. + */ String DEFAULT_EXECUTOR_THREAD_PREFIX = "NettyServerBizHandler"; + /** + * The constant DEFAULT_PROTOCOL. + */ String DEFAULT_PROTOCOL = "seata"; + /** + * The constant DEFAULT_TRANSPORT_HEARTBEAT. + */ boolean DEFAULT_TRANSPORT_HEARTBEAT = true; + /** + * The constant DEFAULT_TRANSACTION_UNDO_DATA_VALIDATION. + */ boolean DEFAULT_TRANSACTION_UNDO_DATA_VALIDATION = true; + /** + * The constant DEFAULT_TRANSACTION_UNDO_LOG_SERIALIZATION. + */ String DEFAULT_TRANSACTION_UNDO_LOG_SERIALIZATION = "jackson"; + /** + * The constant DEFAULT_ONLY_CARE_UPDATE_COLUMNS. + */ boolean DEFAULT_ONLY_CARE_UPDATE_COLUMNS = true; /** * The constant DEFAULT_TRANSACTION_UNDO_LOG_TABLE. @@ -93,40 +197,97 @@ public interface DefaultValues { */ String DEFAULT_DISTRIBUTED_LOCK_DB_TABLE = "distributed_lock"; + /** + * The constant DEFAULT_TM_COMMIT_RETRY_COUNT. + */ int DEFAULT_TM_COMMIT_RETRY_COUNT = 5; + /** + * The constant DEFAULT_TM_ROLLBACK_RETRY_COUNT. + */ int DEFAULT_TM_ROLLBACK_RETRY_COUNT = 5; + /** + * The constant DEFAULT_GLOBAL_TRANSACTION_TIMEOUT. + */ int DEFAULT_GLOBAL_TRANSACTION_TIMEOUT = 60000; + /** + * The constant DEFAULT_TX_GROUP. + */ String DEFAULT_TX_GROUP = "default_tx_group"; + /** + * The constant DEFAULT_TX_GROUP_OLD. + */ @Deprecated String DEFAULT_TX_GROUP_OLD = "my_test_tx_group"; + /** + * The constant DEFAULT_TC_CLUSTER. + */ String DEFAULT_TC_CLUSTER = "default"; + /** + * The constant DEFAULT_GROUPLIST. + */ String DEFAULT_GROUPLIST = "127.0.0.1:8091"; + /** + * The constant DEFAULT_DATA_SOURCE_PROXY_MODE. + */ String DEFAULT_DATA_SOURCE_PROXY_MODE = "AT"; + /** + * The constant DEFAULT_DISABLE_GLOBAL_TRANSACTION. + */ boolean DEFAULT_DISABLE_GLOBAL_TRANSACTION = false; + /** + * The constant SERVICE_DEFAULT_PORT. + */ //currently not use and will be delete in the next version @Deprecated int SERVICE_DEFAULT_PORT = 8091; + /** + * The constant SERVICE_OFFSET_SPRING_BOOT. + */ int SERVICE_OFFSET_SPRING_BOOT = 1000; + /** + * The constant SERVER_PORT. + */ String SERVER_PORT = "seata.server.port"; + /** + * The constant SERVER_DEFAULT_STORE_MODE. + */ String SERVER_DEFAULT_STORE_MODE = "file"; + /** + * The constant DEFAULT_SAGA_JSON_PARSER. + */ String DEFAULT_SAGA_JSON_PARSER = "fastjson"; + /** + * The constant DEFAULT_TCC_BUSINESS_ACTION_CONTEXT_JSON_PARSER. + */ // default tcc business action context json parser String DEFAULT_TCC_BUSINESS_ACTION_CONTEXT_JSON_PARSER = "fastjson"; + /** + * The constant DEFAULT_SERVER_ENABLE_CHECK_AUTH. + */ boolean DEFAULT_SERVER_ENABLE_CHECK_AUTH = true; + /** + * The constant DEFAULT_LOAD_BALANCE. + */ String DEFAULT_LOAD_BALANCE = "XID"; + /** + * The constant VIRTUAL_NODES_DEFAULT. + */ int VIRTUAL_NODES_DEFAULT = 10; + /** + * The constant DEFAULT_SEATA_GROUP. + */ String DEFAULT_SEATA_GROUP = "default"; /** @@ -144,7 +305,6 @@ public interface DefaultValues { */ String DEFAULT_CLIENT_UNDO_COMPRESS_THRESHOLD = "64k"; - /** * the constant DEFAULT_RETRY_DEAD_THRESHOLD */ @@ -283,9 +443,9 @@ public interface DefaultValues { long DEFAULT_MAX_ROLLBACK_RETRY_TIMEOUT = -1L; /** - * the const DEFAULT_ROLLBACK_RETRY_TIMEOUT_UNLOCK_ENABLE + * The constant DEFAULT_ROLLBACK_FAILED_UNLOCK_ENABLE. */ - boolean DEFAULT_ROLLBACK_RETRY_TIMEOUT_UNLOCK_ENABLE = false; + boolean DEFAULT_ROLLBACK_FAILED_UNLOCK_ENABLE = false; /** * DEFAULT_DISTRIBUTED_LOCK_EXPIRE_TIME @@ -297,16 +457,34 @@ public interface DefaultValues { */ boolean DEFAULT_ENABLE_BRANCH_ASYNC_REMOVE = false; + /** + * The constant DEFAULT_DB_MAX_CONN. + */ int DEFAULT_DB_MAX_CONN = 100; + /** + * The constant DEFAULT_DB_MIN_CONN. + */ int DEFAULT_DB_MIN_CONN = 10; + /** + * The constant DEFAULT_REDIS_MAX_IDLE. + */ int DEFAULT_REDIS_MAX_IDLE = 100; + /** + * The constant DEFAULT_REDIS_MAX_TOTAL. + */ int DEFAULT_REDIS_MAX_TOTAL = 100; + /** + * The constant DEFAULT_REDIS_MIN_IDLE. + */ int DEFAULT_REDIS_MIN_IDLE = 10; + /** + * The constant DEFAULT_QUERY_LIMIT. + */ int DEFAULT_QUERY_LIMIT = 1000; /** @@ -314,5 +492,13 @@ public interface DefaultValues { */ String DRUID_LOCATION = "lib/sqlparser/druid.jar"; + /** + * The constant DEFAULT_ROCKET_MQ_MSG_TIMEOUT. + */ int DEFAULT_ROCKET_MQ_MSG_TIMEOUT = 60 * 1000; + + /** + * The constant DEFAULT_RAFT_SSL_ENABLED. + */ + boolean DEFAULT_RAFT_SSL_ENABLED = false; } diff --git a/common/src/main/java/org/apache/seata/common/LockStrategyMode.java b/common/src/main/java/org/apache/seata/common/LockStrategyMode.java index d3f26b98ac5..f70e8883a74 100644 --- a/common/src/main/java/org/apache/seata/common/LockStrategyMode.java +++ b/common/src/main/java/org/apache/seata/common/LockStrategyMode.java @@ -17,7 +17,7 @@ package org.apache.seata.common; /** - * @funkye + * Lock Strategy Mode */ public enum LockStrategyMode { /** diff --git a/common/src/main/java/org/apache/seata/common/exception/ParseEndpointException.java b/common/src/main/java/org/apache/seata/common/exception/ParseEndpointException.java new file mode 100644 index 00000000000..e4f550d5480 --- /dev/null +++ b/common/src/main/java/org/apache/seata/common/exception/ParseEndpointException.java @@ -0,0 +1,38 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.seata.common.exception; + +public class ParseEndpointException extends RuntimeException { + public ParseEndpointException() { + } + + public ParseEndpointException(String message) { + super(message); + } + + public ParseEndpointException(String message, Throwable cause) { + super(message, cause); + } + + public ParseEndpointException(Throwable cause) { + super(cause); + } + + public ParseEndpointException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) { + super(message, cause, enableSuppression, writableStackTrace); + } +} diff --git a/common/src/main/java/org/apache/seata/common/metadata/namingserver/Instance.java b/common/src/main/java/org/apache/seata/common/metadata/Instance.java similarity index 79% rename from common/src/main/java/org/apache/seata/common/metadata/namingserver/Instance.java rename to common/src/main/java/org/apache/seata/common/metadata/Instance.java index 3159dc4a429..e588df0e98f 100644 --- a/common/src/main/java/org/apache/seata/common/metadata/namingserver/Instance.java +++ b/common/src/main/java/org/apache/seata/common/metadata/Instance.java @@ -14,27 +14,22 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.apache.seata.common.metadata.namingserver; +package org.apache.seata.common.metadata; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; -import org.apache.seata.common.metadata.ClusterRole; -import org.apache.seata.common.metadata.Node; import java.util.HashMap; import java.util.Map; import java.util.Objects; -import static org.apache.seata.common.util.CollectionUtils.mapToJsonString; - - public class Instance { private String namespace; private String clusterName; private String unit; - private Node.Endpoint control = new Node.Endpoint(); - private Node.Endpoint transaction = new Node.Endpoint(); + private Node.Endpoint control; + private Node.Endpoint transaction; private double weight = 1.0; private boolean healthy = true; private long term; @@ -169,25 +164,6 @@ public String toJsonString(ObjectMapper objectMapper) { } } - - public Map toMap() { - Map resultMap = new HashMap<>(); - - - resultMap.put("namespace", namespace); - resultMap.put("clusterName", clusterName); - resultMap.put("unit", unit); - resultMap.put("control", control.toString()); - resultMap.put("transaction", transaction.toString()); - resultMap.put("weight", String.valueOf(weight)); - resultMap.put("healthy", String.valueOf(healthy)); - resultMap.put("term", String.valueOf(term)); - resultMap.put("timestamp", String.valueOf(timestamp)); - resultMap.put("metadata", mapToJsonString(metadata)); - - return resultMap; - } - private static class SingletonHolder { private static final Instance SERVER_INSTANCE = new Instance(); } diff --git a/common/src/main/java/org/apache/seata/common/metadata/Node.java b/common/src/main/java/org/apache/seata/common/metadata/Node.java index 92d43c366f8..bcc85a96962 100644 --- a/common/src/main/java/org/apache/seata/common/metadata/Node.java +++ b/common/src/main/java/org/apache/seata/common/metadata/Node.java @@ -18,11 +18,12 @@ import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; - +import org.apache.seata.common.exception.ParseEndpointException; import java.util.HashMap; import java.util.Map; import java.util.Objects; - +import java.util.List; +import java.util.ArrayList; public class Node { @@ -195,4 +196,85 @@ public String toString() { } } + private Node.ExternalEndpoint createExternalEndpoint(String host, int controllerPort, int transactionPort) { + return new Node.ExternalEndpoint(host, controllerPort, transactionPort); + } + + public List createExternalEndpoints(String external) { + List externalEndpoints = new ArrayList<>(); + String[] split = external.split(","); + + for (String s : split) { + String[] item = s.split(":"); + if (item.length == 3) { + try { + String host = item[0]; + int controllerPort = Integer.parseInt(item[1]); + int transactionPort = Integer.parseInt(item[2]); + externalEndpoints.add(createExternalEndpoint(host, controllerPort, transactionPort)); + } catch (NumberFormatException e) { + throw new ParseEndpointException("Invalid port number in: " + s); + } + } else { + throw new ParseEndpointException("Invalid format for endpoint: " + s); + } + } + return externalEndpoints; + } + + public Map updateMetadataWithExternalEndpoints(Map metadata, List externalEndpoints) { + Object obj = metadata.get("external"); + if (obj == null) { + if (!externalEndpoints.isEmpty()) { + Map metadataMap = new HashMap<>(metadata); + metadataMap.put("external", externalEndpoints); + return metadataMap; + } + return metadata; + } + if (obj instanceof List) { + List oldList = (List) obj; + oldList.addAll(externalEndpoints); + return metadata; + } else { + throw new ParseEndpointException("Metadata 'external' is not a List."); + } + } + + public static class ExternalEndpoint { + + private String host; + private int controlPort; + private int transactionPort; + + public ExternalEndpoint(String host, int controlPort, int transactionPort) { + this.host = host; + this.controlPort = controlPort; + this.transactionPort = transactionPort; + } + + public String getHost() { + return host; + } + + public void setHost(String host) { + this.host = host; + } + + public int getControlPort() { + return controlPort; + } + + public void setControlPort(int controlPort) { + this.controlPort = controlPort; + } + + public int getTransactionPort() { + return transactionPort; + } + + public void setTransactionPort(int transactionPort) { + this.transactionPort = transactionPort; + } + } } diff --git a/common/src/main/java/org/apache/seata/common/store/LockMode.java b/common/src/main/java/org/apache/seata/common/store/LockMode.java new file mode 100644 index 00000000000..e36c6547f86 --- /dev/null +++ b/common/src/main/java/org/apache/seata/common/store/LockMode.java @@ -0,0 +1,69 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.seata.common.store; + +public enum LockMode { + /** + * The File store mode. + */ + FILE("file"), + /** + * The Db store mode. + */ + DB("db"), + /** + * The Redis store mode. + */ + REDIS("redis"), + /** + * raft store + */ + RAFT("raft"); + + private String name; + + LockMode(String name) { + this.name = name; + } + + public static LockMode get(String name) { + for (LockMode mode : LockMode.values()) { + if (mode.getName().equalsIgnoreCase(name)) { + return mode; + } + } + throw new IllegalArgumentException("unknown lock mode:" + name); + } + + /** + * whether contains value of store mode + * + * @param name the mode name + * @return the boolean + */ + public static boolean contains(String name) { + try { + return get(name) != null ? true : false; + } catch (IllegalArgumentException e) { + return false; + } + } + + public String getName() { + return name; + } +} \ No newline at end of file diff --git a/common/src/main/java/org/apache/seata/common/store/SessionMode.java b/common/src/main/java/org/apache/seata/common/store/SessionMode.java new file mode 100644 index 00000000000..3db1307d82b --- /dev/null +++ b/common/src/main/java/org/apache/seata/common/store/SessionMode.java @@ -0,0 +1,69 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.seata.common.store; + +public enum SessionMode { + /** + * The File store mode. + */ + FILE("file"), + /** + * The Db store mode. + */ + DB("db"), + /** + * The Redis store mode. + */ + REDIS("redis"), + /** + * raft store + */ + RAFT("raft"); + + private String name; + + SessionMode(String name) { + this.name = name; + } + + public static SessionMode get(String name) { + for (SessionMode mode : SessionMode.values()) { + if (mode.getName().equalsIgnoreCase(name)) { + return mode; + } + } + throw new IllegalArgumentException("unknown session mode:" + name); + } + + /** + * whether contains value of store mode + * + * @param name the mode name + * @return the boolean + */ + public static boolean contains(String name) { + try { + return get(name) != null ? true : false; + } catch (IllegalArgumentException e) { + return false; + } + } + + public String getName() { + return name; + } +} \ No newline at end of file diff --git a/common/src/main/java/org/apache/seata/common/util/NetUtil.java b/common/src/main/java/org/apache/seata/common/util/NetUtil.java index 008f0839bef..2043f85c79a 100644 --- a/common/src/main/java/org/apache/seata/common/util/NetUtil.java +++ b/common/src/main/java/org/apache/seata/common/util/NetUtil.java @@ -90,9 +90,25 @@ public static String toIpAddress(SocketAddress address) { * @return the string */ public static String toStringAddress(InetSocketAddress address) { + if (address.getAddress() == null) { + return address.getHostString() + ":" + address.getPort(); + } return address.getAddress().getHostAddress() + ":" + address.getPort(); } + /** + * To string host string. + * + * @param address the address + * @return the string + */ + public static String toStringHost(InetSocketAddress address) { + if (address.getAddress() == null) { + return address.getHostString(); + } + return address.getAddress().getHostAddress(); + } + /** * To inet socket address inet socket address. * diff --git a/common/src/main/java/org/apache/seata/common/util/ReflectionUtil.java b/common/src/main/java/org/apache/seata/common/util/ReflectionUtil.java index 5da00a4b611..51e33b03892 100644 --- a/common/src/main/java/org/apache/seata/common/util/ReflectionUtil.java +++ b/common/src/main/java/org/apache/seata/common/util/ReflectionUtil.java @@ -25,11 +25,13 @@ import java.lang.reflect.Proxy; import java.util.Arrays; import java.util.Collections; +import java.util.HashMap; import java.util.LinkedHashSet; import java.util.LinkedList; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; +import java.util.function.Predicate; /** * Reflection tools @@ -495,6 +497,34 @@ public static Method getMethod(final Class clazz, final String methodName) return getMethod(clazz, methodName, EMPTY_CLASS_ARRAY); } + /** + * Recursively get clazz and their interfaces match matchCondition method-class mapping + * + * @param clazz clazz + * @param matchCondition matchCondition + * @return Set + */ + public static Map> findMatchMethodClazzMap(Class clazz, Predicate matchCondition) { + Map> methodClassMap = new HashMap<>(); + + for (Method method : clazz.getMethods()) { + if (matchCondition.test(method)) { + methodClassMap.put(method, clazz); + } + } + + Set> interfaceClasses = getInterfaces(clazz); + for (Class interClass : interfaceClasses) { + for (Method method : interClass.getMethods()) { + if (matchCondition.test(method)) { + methodClassMap.put(method, interClass); + } + } + } + + return methodClassMap; + } + /** * invoke Method * diff --git a/common/src/test/java/org/apache/seata/common/exception/ExceptionUtilTest.java b/common/src/test/java/org/apache/seata/common/exception/ExceptionUtilTest.java new file mode 100644 index 00000000000..f8b152b1a2d --- /dev/null +++ b/common/src/test/java/org/apache/seata/common/exception/ExceptionUtilTest.java @@ -0,0 +1,41 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.seata.common.exception; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.UndeclaredThrowableException; + +/** + * + */ +class ExceptionUtilTest { + + @Test + public void unwrap() { + InvocationTargetException targetException = new InvocationTargetException(new RuntimeException("invocation")); + Assertions.assertInstanceOf(RuntimeException.class, ExceptionUtil.unwrap(targetException)); + + UndeclaredThrowableException exception = new UndeclaredThrowableException(new RuntimeException("undeclared")); + Assertions.assertInstanceOf(RuntimeException.class, ExceptionUtil.unwrap(exception)); + + RuntimeException runtimeException = new RuntimeException("runtime"); + Assertions.assertInstanceOf(RuntimeException.class, ExceptionUtil.unwrap(runtimeException)); + } +} \ No newline at end of file diff --git a/common/src/test/java/org/apache/seata/common/metadata/namingserver/InstanceTest.java b/common/src/test/java/org/apache/seata/common/metadata/namingserver/InstanceTest.java index 77ba5f4bd4c..55741a835f0 100644 --- a/common/src/test/java/org/apache/seata/common/metadata/namingserver/InstanceTest.java +++ b/common/src/test/java/org/apache/seata/common/metadata/namingserver/InstanceTest.java @@ -18,6 +18,8 @@ import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; +import org.apache.seata.common.metadata.ClusterRole; +import org.apache.seata.common.metadata.Instance; import org.apache.seata.common.metadata.Node; import org.junit.jupiter.api.Test; @@ -33,13 +35,21 @@ class InstanceTest { void toJsonString() throws JsonProcessingException { ObjectMapper objectMapper = new ObjectMapper(); Instance instance = Instance.getInstance(); - Map map = new HashMap<>(); - Map mmap = new HashMap<>(); - mmap.put("k","v"); - map.put("k",mmap); + Map map = new HashMap<>(); + Map mmap = new HashMap<>(); + mmap.put("k", "v"); + map.put("k", mmap); instance.setMetadata(map); - instance.setControl(new Node.Endpoint("1.1.1.1",888)); - instance.setTransaction(new Node.Endpoint("2.2.2.2",999)); - assertEquals(instance.toJsonString(objectMapper),objectMapper.writeValueAsString(instance)); + instance.setNamespace("namespace"); + instance.setClusterName("clustername"); + instance.setRole(ClusterRole.LEADER); + instance.setUnit("unit"); + instance.setWeight(100d); + instance.setHealthy(true); + instance.setTerm(100L); + instance.setTimestamp(System.currentTimeMillis()); + instance.setControl(new Node.Endpoint("1.1.1.1", 888)); + instance.setTransaction(new Node.Endpoint("2.2.2.2", 999)); + assertEquals(instance.toJsonString(objectMapper), objectMapper.writeValueAsString(instance)); } } \ No newline at end of file diff --git a/common/src/test/java/org/apache/seata/common/metadata/namingserver/NamingServerNodeTest.java b/common/src/test/java/org/apache/seata/common/metadata/namingserver/NamingServerNodeTest.java index 2b70cd26ba0..bd1316b6ddc 100644 --- a/common/src/test/java/org/apache/seata/common/metadata/namingserver/NamingServerNodeTest.java +++ b/common/src/test/java/org/apache/seata/common/metadata/namingserver/NamingServerNodeTest.java @@ -38,6 +38,9 @@ void toJsonString() throws JsonProcessingException { map.put("k","v"); node.setMetadata(map); node.setGroup("group"); + node.setUnit("unit"); + node.setHealthy(true); + node.setTerm(111L); node.setControl(new Node.Endpoint("1.1.1.1",888)); node.setTransaction(new Node.Endpoint("2.2.2.2",999)); assertEquals(node.toJsonString(objectMapper),objectMapper.writeValueAsString(node)); diff --git a/integration-tx-api/src/main/java/org/apache/seata/integration/tx/api/interceptor/parser/RegisterResourceParser.java b/common/src/test/java/org/apache/seata/common/util/UUIDGeneratorTest.java similarity index 75% rename from integration-tx-api/src/main/java/org/apache/seata/integration/tx/api/interceptor/parser/RegisterResourceParser.java rename to common/src/test/java/org/apache/seata/common/util/UUIDGeneratorTest.java index 1300be74454..b6b525cba76 100644 --- a/integration-tx-api/src/main/java/org/apache/seata/integration/tx/api/interceptor/parser/RegisterResourceParser.java +++ b/common/src/test/java/org/apache/seata/common/util/UUIDGeneratorTest.java @@ -14,11 +14,18 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.apache.seata.integration.tx.api.interceptor.parser; +package org.apache.seata.common.util; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; -public interface RegisterResourceParser { - - void registerResource(Object target, String beanName); +/** + * + */ +class UUIDGeneratorTest { -} + @Test + void generateUUID() { + Assertions.assertTrue(UUIDGenerator.generateUUID() > 0); + } +} \ No newline at end of file diff --git a/compatible/src/main/java/io/seata/rm/tcc/interceptor/parser/TccActionInterceptorParser.java b/compatible/src/main/java/io/seata/rm/tcc/interceptor/parser/TccActionInterceptorParser.java index 3d929757e83..7d7e24f661d 100644 --- a/compatible/src/main/java/io/seata/rm/tcc/interceptor/parser/TccActionInterceptorParser.java +++ b/compatible/src/main/java/io/seata/rm/tcc/interceptor/parser/TccActionInterceptorParser.java @@ -16,17 +16,22 @@ */ package io.seata.rm.tcc.interceptor.parser; -import java.lang.reflect.Method; -import java.util.Arrays; -import java.util.Collections; -import java.util.HashSet; -import java.util.Set; - +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.TccActionInterceptorHandler; +import org.apache.seata.common.exception.FrameworkException; import org.apache.seata.common.util.ReflectionUtil; +import org.apache.seata.common.util.StringUtils; +import org.apache.seata.core.model.Resource; import org.apache.seata.integration.tx.api.interceptor.handler.ProxyInvocationHandler; -import org.apache.seata.integration.tx.api.interceptor.parser.DefaultResourceRegisterParser; +import org.apache.seata.rm.tcc.TCCResource; + +import java.lang.annotation.Annotation; +import java.lang.reflect.Method; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; /** * The type Tcc action interceptor parser. @@ -36,40 +41,72 @@ public class TccActionInterceptorParser extends org.apache.seata.rm.tcc.intercep @Override public ProxyInvocationHandler parserInterfaceToProxy(Object target, String objectName) { - // eliminate the bean without two phase annotation. - Set methodsToProxy = this.tccProxyTargetMethod(target); + Map> methodClassMap = ReflectionUtil.findMatchMethodClazzMap(target.getClass(), method -> method.isAnnotationPresent(getAnnotationClass())); + Set methodsToProxy = methodClassMap.keySet(); if (methodsToProxy.isEmpty()) { return null; } + // register resource and enhance with interceptor - DefaultResourceRegisterParser.get().registerResource(target, objectName); - return new TccActionInterceptorHandler(target, methodsToProxy); + registerResource(target, methodClassMap); + + return new TccActionInterceptorHandler(target, methodsToProxy.stream().map(Method::getName).collect(Collectors.toSet())); } - private Set tccProxyTargetMethod(Object target) { - Set methodsToProxy = new HashSet<>(); - //check if it is TCC bean - Class tccServiceClazz = target.getClass(); - 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())); - } - } + @Override + protected Class getAnnotationClass() { + return TwoPhaseBusinessAction.class; + } - TwoPhaseBusinessAction twoPhaseBusinessAction; - for (Method method : methods) { - twoPhaseBusinessAction = method.getAnnotation(TwoPhaseBusinessAction.class); - if (twoPhaseBusinessAction != null) { - methodsToProxy.add(method.getName()); - } + protected Resource createResource(Object target, Class targetServiceClass, Method m, Annotation annotation) throws NoSuchMethodException { + TwoPhaseBusinessAction twoPhaseBusinessAction = (TwoPhaseBusinessAction) annotation; + TCCResource tccResource = new TCCResource(); + if (StringUtils.isBlank(twoPhaseBusinessAction.name())) { + throw new FrameworkException("TCC bean name cannot be null or empty"); } + tccResource.setActionName(twoPhaseBusinessAction.name()); + tccResource.setTargetBean(target); + tccResource.setPrepareMethod(m); + tccResource.setCommitMethodName(twoPhaseBusinessAction.commitMethod()); + tccResource.setCommitMethod(targetServiceClass.getMethod(twoPhaseBusinessAction.commitMethod(), + twoPhaseBusinessAction.commitArgsClasses())); + tccResource.setRollbackMethodName(twoPhaseBusinessAction.rollbackMethod()); + tccResource.setRollbackMethod(targetServiceClass.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())); + return tccResource; + } - if (methodsToProxy.isEmpty()) { - return Collections.emptySet(); + 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 = io.seata.integration.tx.api.interceptor.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"); + } } - // sofa:reference / dubbo:reference, AOP - return methodsToProxy; + return keys; } -} +} \ No newline at end of file diff --git a/compatible/src/main/java/io/seata/rm/tcc/resource/parser/TccRegisterResourceParser.java b/compatible/src/main/java/io/seata/rm/tcc/resource/parser/TccRegisterResourceParser.java deleted file mode 100644 index 53168ff6bc2..00000000000 --- a/compatible/src/main/java/io/seata/rm/tcc/resource/parser/TccRegisterResourceParser.java +++ /dev/null @@ -1,96 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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 java.lang.annotation.Annotation; -import java.lang.reflect.Method; -import java.util.Set; - -import io.seata.integration.tx.api.interceptor.ActionContextUtil; -import io.seata.rm.tcc.api.BusinessActionContext; -import io.seata.rm.tcc.api.BusinessActionContextParameter; -import io.seata.rm.tcc.api.TwoPhaseBusinessAction; -import org.apache.seata.common.exception.FrameworkException; -import org.apache.seata.common.util.StringUtils; -import org.apache.seata.rm.DefaultResourceManager; -import org.apache.seata.rm.tcc.TCCResource; - -/** - * The type Tcc register resource parser. - */ -@Deprecated -public class TccRegisterResourceParser extends org.apache.seata.rm.tcc.resource.parser.TccRegisterResourceParser { - - protected void executeRegisterResource(Object target, Set methods, Class targetServiceClass) throws NoSuchMethodException { - for (Method m : methods) { - TwoPhaseBusinessAction twoPhaseBusinessAction = m.getAnnotation(TwoPhaseBusinessAction.class); - if (twoPhaseBusinessAction != null) { - TCCResource tccResource = new TCCResource(); - if (StringUtils.isBlank(twoPhaseBusinessAction.name())) { - throw new FrameworkException("TCC bean name cannot be null or empty"); - } - tccResource.setActionName(twoPhaseBusinessAction.name()); - tccResource.setTargetBean(target); - tccResource.setPrepareMethod(m); - tccResource.setCommitMethodName(twoPhaseBusinessAction.commitMethod()); - tccResource.setCommitMethod(targetServiceClass.getMethod(twoPhaseBusinessAction.commitMethod(), - twoPhaseBusinessAction.commitArgsClasses())); - tccResource.setRollbackMethodName(twoPhaseBusinessAction.rollbackMethod()); - tccResource.setRollbackMethod(targetServiceClass.getMethod(twoPhaseBusinessAction.rollbackMethod(), - twoPhaseBusinessAction.rollbackArgsClasses())); - // set argsClasses - tccResource.setCommitArgsClasses(twoPhaseBusinessAction.commitArgsClasses()); - tccResource.setRollbackArgsClasses(twoPhaseBusinessAction.rollbackArgsClasses()); - // set phase two method's keys - tccResource.setPhaseTwoCommitKeys(getTwoPhaseArgs(tccResource.getCommitMethod(), - twoPhaseBusinessAction.commitArgsClasses())); - tccResource.setPhaseTwoRollbackKeys(getTwoPhaseArgs(tccResource.getRollbackMethod(), - twoPhaseBusinessAction.rollbackArgsClasses())); - //registry tcc resource - DefaultResourceManager.get().registerResource(tccResource); - } - } - } - - @Override - 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/compatible/src/main/java/io/seata/saga/statelang/domain/State.java b/compatible/src/main/java/io/seata/saga/statelang/domain/State.java index 348445389a5..ff1a7b5e960 100644 --- a/compatible/src/main/java/io/seata/saga/statelang/domain/State.java +++ b/compatible/src/main/java/io/seata/saga/statelang/domain/State.java @@ -43,7 +43,7 @@ public interface State { * * @return the state type */ - String getType(); + StateType getType(); /** * next state name diff --git a/compatible/src/main/java/io/seata/saga/statelang/domain/StateInstance.java b/compatible/src/main/java/io/seata/saga/statelang/domain/StateInstance.java index ad11bdf7877..cf8f8099b42 100644 --- a/compatible/src/main/java/io/seata/saga/statelang/domain/StateInstance.java +++ b/compatible/src/main/java/io/seata/saga/statelang/domain/StateInstance.java @@ -72,14 +72,14 @@ public interface StateInstance { * * @return state instance type */ - String getType(); + StateType getType(); /** * set type * * @param type state instance type */ - void setType(String type); + void setType(StateType type); /** * get service name diff --git a/compatible/src/main/java/io/seata/saga/statelang/domain/StateType.java b/compatible/src/main/java/io/seata/saga/statelang/domain/StateType.java new file mode 100644 index 00000000000..22a9856efb4 --- /dev/null +++ b/compatible/src/main/java/io/seata/saga/statelang/domain/StateType.java @@ -0,0 +1,145 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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.saga.statelang.domain; + +/** + * StateType + */ +@Deprecated +public enum StateType { + + /** + * ServiceTask State + */ + SERVICE_TASK("ServiceTask"), + + /** + * Choice State + */ + CHOICE("Choice"), + + /** + * Fail State + */ + FAIL("Fail"), + + /** + * Succeed State + */ + SUCCEED("Succeed"), + + /** + * CompensationTrigger State + */ + COMPENSATION_TRIGGER("CompensationTrigger"), + + /** + * SubStateMachine State + */ + SUB_STATE_MACHINE("SubStateMachine"), + + /** + * CompensateSubMachine State + */ + SUB_MACHINE_COMPENSATION("CompensateSubMachine"), + + /** + * ScriptTask State + */ + SCRIPT_TASK("ScriptTask"), + + /** + * LoopStart State + */ + LOOP_START("LoopStart"); + + + private String value; + + StateType(String value) { + this.value = value; + } + + public String getValue() { + return value; + } + + public static StateType getStateType(String value) { + for (StateType stateType : values()) { + if (stateType.getValue().equalsIgnoreCase(value)) { + return stateType; + } + } + + throw new IllegalArgumentException("Unknown StateType[" + value + "]"); + } + + + public static StateType wrap(org.apache.seata.saga.statelang.domain.StateType target) { + if (target == null) { + return null; + } + switch (target) { + case SERVICE_TASK: + return SERVICE_TASK; + case CHOICE: + return CHOICE; + case FAIL: + return FAIL; + case SUCCEED: + return SUCCEED; + case COMPENSATION_TRIGGER: + return COMPENSATION_TRIGGER; + case SUB_STATE_MACHINE: + return SUB_STATE_MACHINE; + case SUB_MACHINE_COMPENSATION: + return SUB_MACHINE_COMPENSATION; + case SCRIPT_TASK: + return SCRIPT_TASK; + case LOOP_START: + return LOOP_START; + default: + throw new IllegalArgumentException("Cannot convert " + target.name()); + } + } + + public org.apache.seata.saga.statelang.domain.StateType unwrap() { + switch (this) { + case SERVICE_TASK: + return org.apache.seata.saga.statelang.domain.StateType.SERVICE_TASK; + case CHOICE: + return org.apache.seata.saga.statelang.domain.StateType.CHOICE; + case FAIL: + return org.apache.seata.saga.statelang.domain.StateType.FAIL; + case SUCCEED: + return org.apache.seata.saga.statelang.domain.StateType.SUCCEED; + case COMPENSATION_TRIGGER: + return org.apache.seata.saga.statelang.domain.StateType.COMPENSATION_TRIGGER; + case SUB_STATE_MACHINE: + return org.apache.seata.saga.statelang.domain.StateType.SUB_STATE_MACHINE; + case SUB_MACHINE_COMPENSATION: + return org.apache.seata.saga.statelang.domain.StateType.SUB_MACHINE_COMPENSATION; + case SCRIPT_TASK: + return org.apache.seata.saga.statelang.domain.StateType.SCRIPT_TASK; + case LOOP_START: + return org.apache.seata.saga.statelang.domain.StateType.LOOP_START; + default: + throw new IllegalArgumentException("Cannot convert " + this.name()); + } + } + +} diff --git a/compatible/src/main/java/io/seata/saga/statelang/domain/impl/StateImpl.java b/compatible/src/main/java/io/seata/saga/statelang/domain/impl/StateImpl.java index 29f31b9f68b..3e31881f1d8 100644 --- a/compatible/src/main/java/io/seata/saga/statelang/domain/impl/StateImpl.java +++ b/compatible/src/main/java/io/seata/saga/statelang/domain/impl/StateImpl.java @@ -20,6 +20,7 @@ import io.seata.saga.statelang.domain.State; import io.seata.saga.statelang.domain.StateMachine; +import io.seata.saga.statelang.domain.StateType; /** * The type State. @@ -45,8 +46,8 @@ public String getComment() { } @Override - public String getType() { - return actual.getType(); + public StateType getType() { + return StateType.wrap(actual.getType()); } @Override diff --git a/compatible/src/main/java/io/seata/saga/statelang/domain/impl/StateInstanceImpl.java b/compatible/src/main/java/io/seata/saga/statelang/domain/impl/StateInstanceImpl.java index cfc0f49701e..f29e28ed5ea 100644 --- a/compatible/src/main/java/io/seata/saga/statelang/domain/impl/StateInstanceImpl.java +++ b/compatible/src/main/java/io/seata/saga/statelang/domain/impl/StateInstanceImpl.java @@ -21,6 +21,7 @@ import io.seata.saga.statelang.domain.ExecutionStatus; import io.seata.saga.statelang.domain.StateInstance; import io.seata.saga.statelang.domain.StateMachineInstance; +import io.seata.saga.statelang.domain.StateType; /** * state execution instance @@ -67,13 +68,17 @@ public void setName(String name) { } @Override - public String getType() { - return actual.getType(); + public StateType getType() { + return StateType.wrap(actual.getType()); } @Override - public void setType(String type) { - actual.setType(type); + public void setType(StateType type) { + if (type == null) { + actual.setType(null); + } else { + actual.setType(type.unwrap()); + } } @Override diff --git a/compatible/src/test/java/io/seata/rm/datasource/mock/MockExecuteHandlerImpl.java b/compatible/src/test/java/io/seata/rm/datasource/mock/MockExecuteHandlerImpl.java index 6739869493c..abbd81edce2 100644 --- a/compatible/src/test/java/io/seata/rm/datasource/mock/MockExecuteHandlerImpl.java +++ b/compatible/src/test/java/io/seata/rm/datasource/mock/MockExecuteHandlerImpl.java @@ -71,7 +71,7 @@ public ResultSet executeQuery(MockStatementBase statement, String sql) throws SQ List metas = new ArrayList<>(); if(asts.get(0) instanceof SQLSelectStatement) { SQLSelectStatement ast = (SQLSelectStatement) asts.get(0); - SQLSelectQueryBlock queryBlock = ast.getSelect().getQueryBlock(); + SQLSelectQueryBlock queryBlock = ast.getSelect().getFirstQueryBlock(); String tableName = ""; if (queryBlock.getFrom() instanceof SQLExprTableSource) { MySQLSelectForUpdateRecognizer recognizer = new MySQLSelectForUpdateRecognizer(sql, ast); diff --git a/compressor/seata-compressor-zstd/src/main/java/org/apache/seata/compressor/zstd/ZstdUtil.java b/compressor/seata-compressor-zstd/src/main/java/org/apache/seata/compressor/zstd/ZstdUtil.java index 9159b78db49..606a9f5cf4a 100644 --- a/compressor/seata-compressor-zstd/src/main/java/org/apache/seata/compressor/zstd/ZstdUtil.java +++ b/compressor/seata-compressor-zstd/src/main/java/org/apache/seata/compressor/zstd/ZstdUtil.java @@ -24,6 +24,8 @@ */ public class ZstdUtil { + public static final int MAX_COMPRESSED_SIZE = 4 * 1024 * 1024; + public static byte[] compress(byte[] bytes) { if (bytes == null) { throw new NullPointerException("bytes is null"); @@ -37,8 +39,12 @@ public static byte[] decompress(byte[] bytes) { throw new NullPointerException("bytes is null"); } - int size = (int) Zstd.decompressedSize(bytes); - byte[] decompressBytes = new byte[size]; + long size = Zstd.decompressedSize(bytes); + if (size < 0 || size > MAX_COMPRESSED_SIZE) { + throw new IllegalArgumentException( + "Invalid decompressed size: " + size + ", the value of size ranges from 0 to " + MAX_COMPRESSED_SIZE); + } + byte[] decompressBytes = new byte[(int)size]; Zstd.decompress(decompressBytes, bytes); return decompressBytes; } diff --git a/compressor/seata-compressor-zstd/src/test/java/org/apache/seata/compressor/zstd/ZstdUtilTest.java b/compressor/seata-compressor-zstd/src/test/java/org/apache/seata/compressor/zstd/ZstdUtilTest.java index 5bf0a31f0ad..90e0c19703a 100644 --- a/compressor/seata-compressor-zstd/src/test/java/org/apache/seata/compressor/zstd/ZstdUtilTest.java +++ b/compressor/seata-compressor-zstd/src/test/java/org/apache/seata/compressor/zstd/ZstdUtilTest.java @@ -16,13 +16,17 @@ */ package org.apache.seata.compressor.zstd; +import java.util.ArrayList; +import java.util.List; + +import com.github.luben.zstd.Zstd; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; +import static org.apache.seata.compressor.zstd.ZstdUtil.MAX_COMPRESSED_SIZE; /** * the Zstd Util test - * */ public class ZstdUtilTest { @@ -39,4 +43,43 @@ public void test_decompress() { ZstdUtil.decompress(null); }); } + + @Test + public void test_decompress_with_len_illegal() { + Assertions.assertThrows(IllegalArgumentException.class, () -> { + //https://github.com/facebook/zstd/blob/dev/doc/zstd_compression_format.md#zstandard-frames + List bytes = new ArrayList<>(); + byte[] magic = new byte[] {(byte)0x28, (byte)0xB5, (byte)0x2F, (byte)0xFD}; + byte[] frameHeaderDescriptor = new byte[magic.length + 1]; + System.arraycopy(magic, 0, frameHeaderDescriptor, 0, magic.length); + frameHeaderDescriptor[magic.length] = (byte)0xA0; + //4*1024*1024 + 1 + byte[] frameContentSize = new byte[] {(byte)0x00, (byte)0x40, (byte)0x00, (byte)0x01}; + byte[] frameContent = new byte[frameHeaderDescriptor.length + frameContentSize.length]; + System.arraycopy(frameHeaderDescriptor, 0, frameContent, 0, frameHeaderDescriptor.length); + System.arraycopy(frameContentSize, 0, frameContent, frameHeaderDescriptor.length, frameContentSize.length); + ZstdUtil.decompress(frameContent); + + }); + } + + @Test + public void test_decompress_with_len() { + Assertions.assertThrows(IllegalArgumentException.class, () -> { + byte[] data = new byte[MAX_COMPRESSED_SIZE + 1]; + for (int i = 0; i < data.length; i++) { + data[i] = (byte)('A' + i % 26); + } + byte[] compressedData = Zstd.compress(data); + ZstdUtil.decompress(compressedData); + }); + int len = MAX_COMPRESSED_SIZE / 2; + byte[] data = new byte[len]; + for (int i = 0; i < data.length; i++) { + data[i] = (byte)('A' + i % 26); + } + byte[] compressedData = Zstd.compress(data); + byte[] decompressedData = ZstdUtil.decompress(compressedData); + Assertions.assertEquals(len, decompressedData.length); + } } diff --git a/config/seata-config-core/src/main/java/org/apache/seata/config/ConfigurationFactory.java b/config/seata-config-core/src/main/java/org/apache/seata/config/ConfigurationFactory.java index e7ba659bc7b..0a11e1ec6f1 100644 --- a/config/seata-config-core/src/main/java/org/apache/seata/config/ConfigurationFactory.java +++ b/config/seata-config-core/src/main/java/org/apache/seata/config/ConfigurationFactory.java @@ -71,7 +71,7 @@ private static void load() { extConfiguration = EnhancedServiceLoader.load(ExtConfigurationProvider.class).provide(configuration); if (LOGGER.isInfoEnabled()) { LOGGER.info("load Configuration from :{}", - extConfiguration == null ? configuration.getClass().getSimpleName() : "Spring Configuration"); + extConfiguration == null ? configuration.getClass().getSimpleName() : extConfiguration.getClass().getSimpleName()); } } catch (EnhancedServiceNotFoundException e) { if (LOGGER.isDebugEnabled()) { diff --git a/config/seata-config-nacos/src/test/java/org/apache/seata/config/nacos/NacosMockTest.java b/config/seata-config-nacos/src/test/java/org/apache/seata/config/nacos/NacosMockTest.java index f246ba33ea8..cafdcfc6f57 100644 --- a/config/seata-config-nacos/src/test/java/org/apache/seata/config/nacos/NacosMockTest.java +++ b/config/seata-config-nacos/src/test/java/org/apache/seata/config/nacos/NacosMockTest.java @@ -36,11 +36,14 @@ import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.MethodOrderer; +import org.junit.jupiter.api.Order; import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.condition.EnabledOnJre; +import org.junit.jupiter.api.TestMethodOrder; import org.junit.jupiter.api.condition.EnabledOnOs; import org.junit.jupiter.api.condition.OS; +@TestMethodOrder(MethodOrderer.OrderAnnotation.class) public class NacosMockTest { private static ConfigService configService; private static final String NACOS_ENDPOINT = "127.0.0.1:8848"; @@ -68,6 +71,7 @@ public static void setup() throws NacosException { @Test @EnabledOnOs(OS.LINUX) + @Order(1) public void getInstance() { Assertions.assertNotNull(configService); Assertions.assertNotNull(NacosConfiguration.getInstance()); @@ -76,6 +80,7 @@ public void getInstance() { @Test @EnabledOnOs(OS.LINUX) + @Order(2) public void getConfig() { Configuration configuration = ConfigurationFactory.getInstance(); String configStrValue = configuration.getConfig(SUB_NACOS_DATAID); @@ -141,6 +146,7 @@ public void getConfig() { @Test @EnabledOnOs(OS.LINUX) + @Order(3) public void putConfigIfAbsent() { Configuration configuration = ConfigurationFactory.getInstance(); Assertions.assertThrows(UndeclaredThrowableException.class, () -> { @@ -150,6 +156,7 @@ public void putConfigIfAbsent() { @Test @EnabledOnOs(OS.LINUX) + @Order(4) public void removeConfig() { Configuration configuration = ConfigurationFactory.getInstance(); boolean removed = configuration.removeConfig(NACOS_DATAID); @@ -158,6 +165,7 @@ public void removeConfig() { @Test @EnabledOnOs(OS.LINUX) + @Order(5) public void putConfig() { Configuration configuration = ConfigurationFactory.getInstance(); boolean added = configuration.putConfig(SUB_NACOS_DATAID, "TEST"); @@ -168,6 +176,7 @@ public void putConfig() { @Test @EnabledOnOs(OS.LINUX) + @Order(6) public void testConfigListener() throws NacosException, InterruptedException { Configuration configuration = ConfigurationFactory.getInstance(); configuration.putConfig(NACOS_DATAID, "KEY=TEST"); diff --git a/console/src/main/resources/static/console-fe/package-lock.json b/console/src/main/resources/static/console-fe/package-lock.json index 1b86e082a97..45ca71e4923 100644 --- a/console/src/main/resources/static/console-fe/package-lock.json +++ b/console/src/main/resources/static/console-fe/package-lock.json @@ -5331,9 +5331,9 @@ "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==" }, "node_modules/cookie": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz", - "integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==", + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.1.tgz", + "integrity": "sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==", "dev": true, "engines": { "node": ">= 0.6" @@ -6672,9 +6672,9 @@ "integrity": "sha512-1PpuqJUFWoXZ1E54m8bsLPVYwIVCRzvaL+n5cjigGga4z854abDnFRc+cTa2th4S79kyGqya/1xoR7h+Y5G5lg==" }, "node_modules/elliptic": { - "version": "6.5.7", - "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.7.tgz", - "integrity": "sha512-ESVCtTwiA+XhY3wyh24QqRGBoP3rEdDUl3EDUUo9tft074fi19IrdpH7hLCMMP3CIj7jb3W96rn8lt/BqIlt5Q==", + "version": "6.6.0", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.6.0.tgz", + "integrity": "sha512-dpwoQcLc/2WLQvJvLRHKZ+f9FgOdjnq11rurqwekGQygGPsYSK29OMMD2WalatiqQ+XGFDglTNixpPfI+lpaAA==", "dependencies": { "bn.js": "^4.11.9", "brorand": "^1.1.0", @@ -8160,9 +8160,9 @@ "dev": true }, "node_modules/express": { - "version": "4.21.0", - "resolved": "https://registry.npmjs.org/express/-/express-4.21.0.tgz", - "integrity": "sha512-VqcNGcj/Id5ZT1LZ/cfihi3ttTn+NJmkli2eZADigjq29qTlWi/hAQ43t/VLPq8+UX06FCEx3ByOYet6ZFblng==", + "version": "4.21.1", + "resolved": "https://registry.npmjs.org/express/-/express-4.21.1.tgz", + "integrity": "sha512-YSFlK1Ee0/GC8QaO91tHcDxJiE/X4FbpAyQWkxAvG6AXCuR65YzK8ua6D9hvi/TzUfZMpc+BwuM1IPw8fmQBiQ==", "dev": true, "dependencies": { "accepts": "~1.3.8", @@ -8170,7 +8170,7 @@ "body-parser": "1.20.3", "content-disposition": "0.5.4", "content-type": "~1.0.4", - "cookie": "0.6.0", + "cookie": "0.7.1", "cookie-signature": "1.0.6", "debug": "2.6.9", "depd": "2.0.0", @@ -9582,9 +9582,9 @@ } }, "node_modules/http-proxy-middleware": { - "version": "2.0.6", - "resolved": "https://registry.npmmirror.com/http-proxy-middleware/-/http-proxy-middleware-2.0.6.tgz", - "integrity": "sha512-ya/UeJ6HVBYxrgYotAZo1KvPWlgB48kUJLDePFeneHsVujFaW5WNj2NgWCAE//B1Dl02BIfYlpNgBy8Kf8Rjmw==", + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-2.0.7.tgz", + "integrity": "sha512-fgVY8AV7qU7z/MmXJ/rxwbrtQH4jBQ9m7kp3llF0liB7glmFeVZFBepQb32T3y8n8k2+AEYuMPCpinYW+/CuRA==", "dev": true, "dependencies": { "@types/http-proxy": "^1.17.8", diff --git a/core/src/main/java/org/apache/seata/core/model/BranchType.java b/core/src/main/java/org/apache/seata/core/model/BranchType.java index f3a868f93d3..37ae5342b75 100644 --- a/core/src/main/java/org/apache/seata/core/model/BranchType.java +++ b/core/src/main/java/org/apache/seata/core/model/BranchType.java @@ -38,6 +38,11 @@ public enum BranchType { */ SAGA, + /** + * The SAGA_ANNOTATION. + */ + SAGA_ANNOTATION, + /** * The XA. */ diff --git a/core/src/main/java/org/apache/seata/core/rpc/netty/AbstractNettyRemotingClient.java b/core/src/main/java/org/apache/seata/core/rpc/netty/AbstractNettyRemotingClient.java index bbbab50faa5..8618c3030b4 100644 --- a/core/src/main/java/org/apache/seata/core/rpc/netty/AbstractNettyRemotingClient.java +++ b/core/src/main/java/org/apache/seata/core/rpc/netty/AbstractNettyRemotingClient.java @@ -200,7 +200,7 @@ public Object sendSyncRequest(Channel channel, Object msg) throws TimeoutExcepti public void sendAsyncRequest(Channel channel, Object msg) { if (channel == null) { LOGGER.warn("sendAsyncRequest nothing, caused by null channel."); - return; + throw new FrameworkException(new Throwable("throw"), "frameworkException", FrameworkErrorCode.ChannelIsNotWritable); } RpcMessage rpcMessage = buildRequestMessage(msg, msg instanceof HeartbeatMessage ? ProtocolConstants.MSGTYPE_HEARTBEAT_REQUEST diff --git a/core/src/main/java/org/apache/seata/core/rpc/netty/NettyClientChannelManager.java b/core/src/main/java/org/apache/seata/core/rpc/netty/NettyClientChannelManager.java index 7be0de2e729..1ebd36fe453 100644 --- a/core/src/main/java/org/apache/seata/core/rpc/netty/NettyClientChannelManager.java +++ b/core/src/main/java/org/apache/seata/core/rpc/netty/NettyClientChannelManager.java @@ -247,6 +247,8 @@ void doReconnect(List availList, String transactionServiceGroup) { FrameworkErrorCode.NetConnect.getErrCode(), key, value.getMessage(), value); }); } + } + if (availList.size() == failedMap.size()) { String invalidAddress = StringUtils.join(failedMap.keySet().iterator(), ", "); throw new FrameworkException("can not connect to [" + invalidAddress + "]"); } diff --git a/core/src/main/java/org/apache/seata/core/rpc/netty/NettyServerBootstrap.java b/core/src/main/java/org/apache/seata/core/rpc/netty/NettyServerBootstrap.java index b589396e5ab..ac4632ce753 100644 --- a/core/src/main/java/org/apache/seata/core/rpc/netty/NettyServerBootstrap.java +++ b/core/src/main/java/org/apache/seata/core/rpc/netty/NettyServerBootstrap.java @@ -34,6 +34,8 @@ import io.netty.handler.timeout.IdleStateHandler; import org.apache.seata.common.ConfigurationKeys; import org.apache.seata.common.XID; +import org.apache.seata.common.metadata.Instance; +import org.apache.seata.common.metadata.Node; import org.apache.seata.common.thread.NamedThreadFactory; import org.apache.seata.config.ConfigurationFactory; import org.apache.seata.core.rpc.RemotingBootstrap; @@ -170,9 +172,13 @@ public void initChannel(SocketChannel ch) { try { this.serverBootstrap.bind(port).sync(); LOGGER.info("Server started, service listen port: {}", getListenPort()); - InetSocketAddress address = new InetSocketAddress(XID.getIpAddress(), XID.getPort()); + Instance instance = Instance.getInstance(); + // Lines 177-180 are just for compatibility with test cases + if (instance.getTransaction() == null) { + Instance.getInstance().setTransaction(new Node.Endpoint(XID.getIpAddress(), XID.getPort(), "netty")); + } for (RegistryService registryService : MultiRegistryFactory.getInstances()) { - registryService.register(address); + registryService.register(Instance.getInstance()); } initialized.set(true); } catch (SocketException se) { @@ -189,9 +195,8 @@ public void shutdown() { LOGGER.info("Shutting server down, the listen port: {}", XID.getPort()); } if (initialized.get()) { - InetSocketAddress address = new InetSocketAddress(XID.getIpAddress(), XID.getPort()); for (RegistryService registryService : MultiRegistryFactory.getInstances()) { - registryService.unregister(address); + registryService.unregister(Instance.getInstance()); registryService.close(); } //wait a few seconds for server transport diff --git a/core/src/main/java/org/apache/seata/core/rpc/netty/grpc/GrpcDecoder.java b/core/src/main/java/org/apache/seata/core/rpc/netty/grpc/GrpcDecoder.java index 71c9caf8be9..5544e994a55 100644 --- a/core/src/main/java/org/apache/seata/core/rpc/netty/grpc/GrpcDecoder.java +++ b/core/src/main/java/org/apache/seata/core/rpc/netty/grpc/GrpcDecoder.java @@ -94,7 +94,8 @@ public void onDataRead(ChannelHandlerContext ctx, Http2DataFrame msg) throws Exc bodyBytes = compressor.decompress(bodyBytes); } String codecValue = headMap.get(GrpcHeaderEnum.CODEC_TYPE.header); - int codec = Integer.parseInt(codecValue); + int codec = StringUtils.isBlank(codecValue) ? SerializerType.GRPC.getCode() + : Integer.parseInt(codecValue); SerializerType serializerType = SerializerType.getByCode(codec); rpcMsg.setCodec(serializerType.getCode()); Serializer serializer = SerializerServiceLoader.load(serializerType); diff --git a/core/src/main/java/org/apache/seata/core/rpc/netty/grpc/GrpcEncoder.java b/core/src/main/java/org/apache/seata/core/rpc/netty/grpc/GrpcEncoder.java index dbbbfe1be48..2601a2f0a6e 100644 --- a/core/src/main/java/org/apache/seata/core/rpc/netty/grpc/GrpcEncoder.java +++ b/core/src/main/java/org/apache/seata/core/rpc/netty/grpc/GrpcEncoder.java @@ -64,14 +64,14 @@ public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) ByteString dataBytes; if (messageType != ProtocolConstants.MSGTYPE_HEARTBEAT_REQUEST && messageType != ProtocolConstants.MSGTYPE_HEARTBEAT_RESPONSE) { - Serializer serializer = SerializerServiceLoader.load(SerializerType.getByCode(SerializerType.PROTOBUF.getCode())); + Serializer serializer = SerializerServiceLoader.load(SerializerType.getByCode(SerializerType.GRPC.getCode())); byte[] serializedBytes = serializer.serialize(body); Compressor compressor = CompressorFactory.getCompressor(rpcMessage.getCompressor()); dataBytes = ByteString.copyFrom(compressor.compress(serializedBytes)); } else { dataBytes = ByteString.EMPTY; } - headMap.put(GrpcHeaderEnum.CODEC_TYPE.header, String.valueOf(SerializerType.PROTOBUF.getCode())); + headMap.put(GrpcHeaderEnum.CODEC_TYPE.header, String.valueOf(SerializerType.GRPC.getCode())); headMap.put(GrpcHeaderEnum.COMPRESS_TYPE.header, String.valueOf(rpcMessage.getCompressor())); GrpcMessageProto.Builder builder = GrpcMessageProto.newBuilder() .putAllHeadMap(headMap) diff --git a/core/src/main/java/org/apache/seata/core/serializer/SerializerServiceLoader.java b/core/src/main/java/org/apache/seata/core/serializer/SerializerServiceLoader.java index 359867b5104..ed3e7e3b8e4 100644 --- a/core/src/main/java/org/apache/seata/core/serializer/SerializerServiceLoader.java +++ b/core/src/main/java/org/apache/seata/core/serializer/SerializerServiceLoader.java @@ -31,6 +31,7 @@ import org.slf4j.LoggerFactory; import static org.apache.seata.core.serializer.SerializerType.FASTJSON2; +import static org.apache.seata.core.serializer.SerializerType.FURY; import static org.apache.seata.core.serializer.SerializerType.HESSIAN; import static org.apache.seata.core.serializer.SerializerType.KRYO; import static org.apache.seata.core.serializer.SerializerType.PROTOBUF; @@ -47,7 +48,7 @@ public final class SerializerServiceLoader { private static final Logger LOGGER = LoggerFactory.getLogger(SerializerServiceLoader.class); private static final Configuration CONFIG = ConfigurationFactory.getInstance(); - private static final SerializerType[] DEFAULT_SERIALIZER_TYPE = new SerializerType[]{SEATA, PROTOBUF, KRYO, HESSIAN, FASTJSON2}; + private static final SerializerType[] DEFAULT_SERIALIZER_TYPE = new SerializerType[]{SEATA, PROTOBUF, KRYO, HESSIAN, FASTJSON2, FURY}; private final static Map SERIALIZER_MAP = new HashMap<>(); diff --git a/core/src/main/java/org/apache/seata/core/serializer/SerializerType.java b/core/src/main/java/org/apache/seata/core/serializer/SerializerType.java index 56fd8136d17..7112f8a32a8 100644 --- a/core/src/main/java/org/apache/seata/core/serializer/SerializerType.java +++ b/core/src/main/java/org/apache/seata/core/serializer/SerializerType.java @@ -70,6 +70,21 @@ public enum SerializerType { * Math.pow(2, 6) */ FASTJSON2((byte)0x64), + + + /** + * The grpc + *

+ * Math.pow(2, 7) + */ + GRPC((byte) 0x128), + + /** + * The fury. + *

+ * Math.pow(2, 8) + */ + FURY((byte) 0x256) ; private final byte code; diff --git a/core/src/main/java/org/apache/seata/core/store/db/AbstractDataSourceProvider.java b/core/src/main/java/org/apache/seata/core/store/db/AbstractDataSourceProvider.java index c11a55f0979..bb8306b2b22 100644 --- a/core/src/main/java/org/apache/seata/core/store/db/AbstractDataSourceProvider.java +++ b/core/src/main/java/org/apache/seata/core/store/db/AbstractDataSourceProvider.java @@ -16,6 +16,15 @@ */ package org.apache.seata.core.store.db; +import java.io.File; +import java.net.MalformedURLException; +import java.net.URL; +import java.net.URLClassLoader; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; +import java.util.stream.Stream; + import javax.sql.DataSource; import org.apache.seata.common.exception.StoreException; @@ -47,8 +56,20 @@ public abstract class AbstractDataSourceProvider implements DataSourceProvider, */ protected static final Configuration CONFIG = ConfigurationFactory.getInstance(); + private final static String MYSQL_DRIVER_CLASS_NAME = "com.mysql.jdbc.Driver"; + + private final static String MYSQL8_DRIVER_CLASS_NAME = "com.mysql.cj.jdbc.Driver"; + + private final static String MYSQL_DRIVER_FILE_PREFIX = "mysql-connector-java-"; + + private final static Map MYSQL_DRIVER_LOADERS; + private static final long DEFAULT_DB_MAX_WAIT = 5000; + static { + MYSQL_DRIVER_LOADERS = createMysqlDriverClassLoaders(); + } + @Override public void init() { this.dataSource = generate(); @@ -67,7 +88,7 @@ public DataSource generate() { public void validate() { //valid driver class name String driverClassName = getDriverClassName(); - ClassLoader loader = Thread.currentThread().getContextClassLoader(); + ClassLoader loader = getDriverClassLoader(); if (null == loader) { throw new StoreException("class loader set error, you should not use the Bootstrap classloader"); } @@ -124,7 +145,50 @@ protected Long getMaxWait() { } protected ClassLoader getDriverClassLoader() { - return ClassLoader.getSystemClassLoader(); + return MYSQL_DRIVER_LOADERS.getOrDefault(getDriverClassName(), ClassLoader.getSystemClassLoader()); + } + + private static Map createMysqlDriverClassLoaders() { + Map loaders = new HashMap<>(); + String cp = System.getProperty("java.class.path"); + if (cp == null || cp.isEmpty()) { + return loaders; + } + Stream.of(cp.split(File.pathSeparator)) + .map(File::new) + .filter(File::exists) + .map(file -> file.isFile() ? file.getParentFile() : file) + .filter(Objects::nonNull) + .filter(File::isDirectory) + .map(file -> new File(file, "jdbc")) + .filter(File::exists) + .filter(File::isDirectory) + .distinct() + .flatMap(file -> { + File[] files = file.listFiles((f, name) -> name.startsWith(MYSQL_DRIVER_FILE_PREFIX)); + if (files != null) { + return Stream.of(files); + } else { + return Stream.of(); + } + }) + .forEach(file -> { + if (loaders.containsKey(MYSQL8_DRIVER_CLASS_NAME) && loaders.containsKey(MYSQL_DRIVER_CLASS_NAME)) { + return; + } + try { + URL url = file.toURI().toURL(); + ClassLoader loader = new URLClassLoader(new URL[]{url}, ClassLoader.getSystemClassLoader()); + try { + loader.loadClass(MYSQL8_DRIVER_CLASS_NAME); + loaders.putIfAbsent(MYSQL8_DRIVER_CLASS_NAME, loader); + } catch (ClassNotFoundException e) { + loaders.putIfAbsent(MYSQL_DRIVER_CLASS_NAME, loader); + } + } catch (MalformedURLException ignore) { + } + }); + return loaders; } /** diff --git a/core/src/test/java/org/apache/seata/core/compressor/CompressorFactoryTest.java b/core/src/test/java/org/apache/seata/core/compressor/CompressorFactoryTest.java new file mode 100644 index 00000000000..b32cae92776 --- /dev/null +++ b/core/src/test/java/org/apache/seata/core/compressor/CompressorFactoryTest.java @@ -0,0 +1,63 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.seata.core.compressor; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.EnumSource; + +import static org.junit.jupiter.api.Assertions.*; + +class CompressorFactoryTest { + + @Test + void testGetCompressorNone() { + Compressor compressor = CompressorFactory.getCompressor(CompressorType.NONE.getCode()); + assertNotNull(compressor); + assertTrue(compressor instanceof CompressorFactory.NoneCompressor); + } + + @Test + void testNoneCompressor() { + CompressorFactory.NoneCompressor noneCompressor = new CompressorFactory.NoneCompressor(); + byte[] testData = "Test data".getBytes(); + + byte[] compressed = noneCompressor.compress(testData); + assertArrayEquals(testData, compressed); + + byte[] decompressed = noneCompressor.decompress(compressed); + assertArrayEquals(testData, decompressed); + } + + @Test + void testCompressorCaching() { + Compressor compressor1 = CompressorFactory.getCompressor(CompressorType.NONE.getCode()); + Compressor compressor2 = CompressorFactory.getCompressor(CompressorType.NONE.getCode()); + assertSame(compressor1, compressor2); + } + + @Test + void testInvalidCompressorCode() { + assertThrows(IllegalArgumentException.class, () -> CompressorFactory.getCompressor((byte) -1)); + } + + @Test + void testCompressorMapInitialization() { + assertTrue(CompressorFactory.COMPRESSOR_MAP.containsKey(CompressorType.NONE)); + assertTrue(CompressorFactory.COMPRESSOR_MAP.get(CompressorType.NONE) instanceof CompressorFactory.NoneCompressor); + } +} \ No newline at end of file diff --git a/core/src/test/java/org/apache/seata/core/exception/BranchTransactionExceptionTest.java b/core/src/test/java/org/apache/seata/core/exception/BranchTransactionExceptionTest.java new file mode 100644 index 00000000000..73a29cde7c9 --- /dev/null +++ b/core/src/test/java/org/apache/seata/core/exception/BranchTransactionExceptionTest.java @@ -0,0 +1,75 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.seata.core.exception; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +public class BranchTransactionExceptionTest { + + @Test + public void testConstructorWithCode() { + BranchTransactionException exception = new BranchTransactionException(TransactionExceptionCode.BranchRollbackFailed_Retriable); + assertEquals(TransactionExceptionCode.BranchRollbackFailed_Retriable, exception.getCode()); + } + + @Test + public void testConstructorWithCodeAndCause() { + Throwable cause = new RuntimeException("test"); + BranchTransactionException exception = new BranchTransactionException(TransactionExceptionCode.BranchRollbackFailed_Retriable, cause); + assertEquals(TransactionExceptionCode.BranchRollbackFailed_Retriable, exception.getCode()); + assertEquals(cause, exception.getCause()); + } + + @Test + public void testConstructorWithMessage() { + BranchTransactionException exception = new BranchTransactionException("test message"); + assertEquals("test message", exception.getMessage()); + } + + @Test + public void testConstructorWithCodeAndMessage() { + BranchTransactionException exception = new BranchTransactionException(TransactionExceptionCode.BranchRollbackFailed_Retriable, "test message"); + assertEquals(TransactionExceptionCode.BranchRollbackFailed_Retriable, exception.getCode()); + assertEquals("test message", exception.getMessage()); + } + + @Test + public void testConstructorWithCause() { + Throwable cause = new RuntimeException("test"); + BranchTransactionException exception = new BranchTransactionException(cause); + assertEquals(cause, exception.getCause()); + } + + @Test + public void testConstructorWithMessageAndCause() { + Throwable cause = new RuntimeException("test"); + BranchTransactionException exception = new BranchTransactionException("test message", cause); + assertEquals("test message", exception.getMessage()); + assertEquals(cause, exception.getCause()); + } + + @Test + public void testConstructorWithCodeMessageAndCause() { + Throwable cause = new RuntimeException("test"); + BranchTransactionException exception = new BranchTransactionException(TransactionExceptionCode.BranchRollbackFailed_Retriable, "test message", cause); + assertEquals(TransactionExceptionCode.BranchRollbackFailed_Retriable, exception.getCode()); + assertEquals("test message", exception.getMessage()); + assertEquals(cause, exception.getCause()); + } +} \ No newline at end of file diff --git a/core/src/test/java/org/apache/seata/core/exception/DecodeExceptionTest.java b/core/src/test/java/org/apache/seata/core/exception/DecodeExceptionTest.java new file mode 100644 index 00000000000..3981c54b156 --- /dev/null +++ b/core/src/test/java/org/apache/seata/core/exception/DecodeExceptionTest.java @@ -0,0 +1,31 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.seata.core.exception; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +public class DecodeExceptionTest { + + @Test + public void testConstructorWithCause() { + Throwable cause = new RuntimeException("test"); + DecodeException exception = new DecodeException(cause); + assertEquals(cause, exception.getCause()); + } +} \ No newline at end of file diff --git a/core/src/test/java/org/apache/seata/core/exception/GlobalTransactionExceptionTest.java b/core/src/test/java/org/apache/seata/core/exception/GlobalTransactionExceptionTest.java new file mode 100644 index 00000000000..093e853b7b0 --- /dev/null +++ b/core/src/test/java/org/apache/seata/core/exception/GlobalTransactionExceptionTest.java @@ -0,0 +1,75 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.seata.core.exception; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +public class GlobalTransactionExceptionTest { + + @Test + public void testConstructorWithCode() { + GlobalTransactionException exception = new GlobalTransactionException(TransactionExceptionCode.GlobalTransactionNotExist); + assertEquals(TransactionExceptionCode.GlobalTransactionNotExist, exception.getCode()); + } + + @Test + public void testConstructorWithCodeAndCause() { + Throwable cause = new RuntimeException("test"); + GlobalTransactionException exception = new GlobalTransactionException(TransactionExceptionCode.GlobalTransactionNotExist, cause); + assertEquals(TransactionExceptionCode.GlobalTransactionNotExist, exception.getCode()); + assertEquals(cause, exception.getCause()); + } + + @Test + public void testConstructorWithMessage() { + GlobalTransactionException exception = new GlobalTransactionException("test message"); + assertEquals("test message", exception.getMessage()); + } + + @Test + public void testConstructorWithCodeAndMessage() { + GlobalTransactionException exception = new GlobalTransactionException(TransactionExceptionCode.GlobalTransactionNotExist, "test message"); + assertEquals(TransactionExceptionCode.GlobalTransactionNotExist, exception.getCode()); + assertEquals("test message", exception.getMessage()); + } + + @Test + public void testConstructorWithCause() { + Throwable cause = new RuntimeException("test"); + GlobalTransactionException exception = new GlobalTransactionException(cause); + assertEquals(cause, exception.getCause()); + } + + @Test + public void testConstructorWithMessageAndCause() { + Throwable cause = new RuntimeException("test"); + GlobalTransactionException exception = new GlobalTransactionException("test message", cause); + assertEquals("test message", exception.getMessage()); + assertEquals(cause, exception.getCause()); + } + + @Test + public void testConstructorWithCodeMessageAndCause() { + Throwable cause = new RuntimeException("test"); + GlobalTransactionException exception = new GlobalTransactionException(TransactionExceptionCode.GlobalTransactionNotExist, "test message", cause); + assertEquals(TransactionExceptionCode.GlobalTransactionNotExist, exception.getCode()); + assertEquals("test message", exception.getMessage()); + assertEquals(cause, exception.getCause()); + } +} \ No newline at end of file diff --git a/core/src/test/java/org/apache/seata/core/exception/RmTransactionExceptionTest.java b/core/src/test/java/org/apache/seata/core/exception/RmTransactionExceptionTest.java new file mode 100644 index 00000000000..a8b89386517 --- /dev/null +++ b/core/src/test/java/org/apache/seata/core/exception/RmTransactionExceptionTest.java @@ -0,0 +1,75 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.seata.core.exception; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +public class RmTransactionExceptionTest { + + @Test + public void testConstructorWithCode() { + RmTransactionException exception = new RmTransactionException(TransactionExceptionCode.BranchRollbackFailed_Retriable); + assertEquals(TransactionExceptionCode.BranchRollbackFailed_Retriable, exception.getCode()); + } + + @Test + public void testConstructorWithCodeAndCause() { + Throwable cause = new RuntimeException("test"); + RmTransactionException exception = new RmTransactionException(TransactionExceptionCode.BranchRollbackFailed_Retriable, cause); + assertEquals(TransactionExceptionCode.BranchRollbackFailed_Retriable, exception.getCode()); + assertEquals(cause, exception.getCause()); + } + + @Test + public void testConstructorWithMessage() { + RmTransactionException exception = new RmTransactionException("test message"); + assertEquals("test message", exception.getMessage()); + } + + @Test + public void testConstructorWithCodeAndMessage() { + RmTransactionException exception = new RmTransactionException(TransactionExceptionCode.BranchRollbackFailed_Retriable, "test message"); + assertEquals(TransactionExceptionCode.BranchRollbackFailed_Retriable, exception.getCode()); + assertEquals("test message", exception.getMessage()); + } + + @Test + public void testConstructorWithCause() { + Throwable cause = new RuntimeException("test"); + RmTransactionException exception = new RmTransactionException(cause); + assertEquals(cause, exception.getCause()); + } + + @Test + public void testConstructorWithMessageAndCause() { + Throwable cause = new RuntimeException("test"); + RmTransactionException exception = new RmTransactionException("test message", cause); + assertEquals("test message", exception.getMessage()); + assertEquals(cause, exception.getCause()); + } + + @Test + public void testConstructorWithCodeMessageAndCause() { + Throwable cause = new RuntimeException("test"); + RmTransactionException exception = new RmTransactionException(TransactionExceptionCode.BranchRollbackFailed_Retriable, "test message", cause); + assertEquals(TransactionExceptionCode.BranchRollbackFailed_Retriable, exception.getCode()); + assertEquals("test message", exception.getMessage()); + assertEquals(cause, exception.getCause()); + } +} \ No newline at end of file diff --git a/core/src/test/java/org/apache/seata/core/exception/TmTransactionExceptionTest.java b/core/src/test/java/org/apache/seata/core/exception/TmTransactionExceptionTest.java new file mode 100644 index 00000000000..a1b64e5bb95 --- /dev/null +++ b/core/src/test/java/org/apache/seata/core/exception/TmTransactionExceptionTest.java @@ -0,0 +1,75 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.seata.core.exception; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +public class TmTransactionExceptionTest { + + @Test + public void testConstructorWithCode() { + TmTransactionException exception = new TmTransactionException(TransactionExceptionCode.GlobalTransactionNotExist); + assertEquals(TransactionExceptionCode.GlobalTransactionNotExist, exception.getCode()); + } + + @Test + public void testConstructorWithCodeAndCause() { + Throwable cause = new RuntimeException("test"); + TmTransactionException exception = new TmTransactionException(TransactionExceptionCode.GlobalTransactionNotExist, cause); + assertEquals(TransactionExceptionCode.GlobalTransactionNotExist, exception.getCode()); + assertEquals(cause, exception.getCause()); + } + + @Test + public void testConstructorWithMessage() { + TmTransactionException exception = new TmTransactionException("test message"); + assertEquals("test message", exception.getMessage()); + } + + @Test + public void testConstructorWithCodeAndMessage() { + TmTransactionException exception = new TmTransactionException(TransactionExceptionCode.GlobalTransactionNotExist, "test message"); + assertEquals(TransactionExceptionCode.GlobalTransactionNotExist, exception.getCode()); + assertEquals("test message", exception.getMessage()); + } + + @Test + public void testConstructorWithCause() { + Throwable cause = new RuntimeException("test"); + TmTransactionException exception = new TmTransactionException(cause); + assertEquals(cause, exception.getCause()); + } + + @Test + public void testConstructorWithMessageAndCause() { + Throwable cause = new RuntimeException("test"); + TmTransactionException exception = new TmTransactionException("test message", cause); + assertEquals("test message", exception.getMessage()); + assertEquals(cause, exception.getCause()); + } + + @Test + public void testConstructorWithCodeMessageAndCause() { + Throwable cause = new RuntimeException("test"); + TmTransactionException exception = new TmTransactionException(TransactionExceptionCode.GlobalTransactionNotExist, "test message", cause); + assertEquals(TransactionExceptionCode.GlobalTransactionNotExist, exception.getCode()); + assertEquals("test message", exception.getMessage()); + assertEquals(cause, exception.getCause()); + } +} \ No newline at end of file diff --git a/core/src/test/java/org/apache/seata/core/exception/TransactionExceptionTest.java b/core/src/test/java/org/apache/seata/core/exception/TransactionExceptionTest.java new file mode 100644 index 00000000000..1b19a33480f --- /dev/null +++ b/core/src/test/java/org/apache/seata/core/exception/TransactionExceptionTest.java @@ -0,0 +1,75 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.seata.core.exception; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +public class TransactionExceptionTest { + + @Test + public void testConstructorWithCode() { + TransactionException exception = new TransactionException(TransactionExceptionCode.GlobalTransactionNotExist); + assertEquals(TransactionExceptionCode.GlobalTransactionNotExist, exception.getCode()); + } + + @Test + public void testConstructorWithCodeAndCause() { + Throwable cause = new RuntimeException("test"); + TransactionException exception = new TransactionException(TransactionExceptionCode.GlobalTransactionNotExist, cause); + assertEquals(TransactionExceptionCode.GlobalTransactionNotExist, exception.getCode()); + assertEquals(cause, exception.getCause()); + } + + @Test + public void testConstructorWithMessage() { + TransactionException exception = new TransactionException("test message"); + assertEquals("test message", exception.getMessage()); + } + + @Test + public void testConstructorWithCodeAndMessage() { + TransactionException exception = new TransactionException(TransactionExceptionCode.GlobalTransactionNotExist, "test message"); + assertEquals(TransactionExceptionCode.GlobalTransactionNotExist, exception.getCode()); + assertEquals("test message", exception.getMessage()); + } + + @Test + public void testConstructorWithCause() { + Throwable cause = new RuntimeException("test"); + TransactionException exception = new TransactionException(cause); + assertEquals(cause, exception.getCause()); + } + + @Test + public void testConstructorWithMessageAndCause() { + Throwable cause = new RuntimeException("test"); + TransactionException exception = new TransactionException("test message", cause); + assertEquals("test message", exception.getMessage()); + assertEquals(cause, exception.getCause()); + } + + @Test + public void testConstructorWithCodeMessageAndCause() { + Throwable cause = new RuntimeException("test"); + TransactionException exception = new TransactionException(TransactionExceptionCode.GlobalTransactionNotExist, "test message", cause); + assertEquals(TransactionExceptionCode.GlobalTransactionNotExist, exception.getCode()); + assertEquals("test message", exception.getMessage()); + assertEquals(cause, exception.getCause()); + } +} \ No newline at end of file diff --git a/core/src/test/java/org/apache/seata/core/rpc/netty/RmNettyClientTest.java b/core/src/test/java/org/apache/seata/core/rpc/netty/RmNettyClientTest.java index 1709246e884..08151d58219 100644 --- a/core/src/test/java/org/apache/seata/core/rpc/netty/RmNettyClientTest.java +++ b/core/src/test/java/org/apache/seata/core/rpc/netty/RmNettyClientTest.java @@ -26,6 +26,7 @@ import org.apache.seata.config.ConfigurationCache; import org.apache.seata.core.model.Resource; import org.apache.seata.core.model.ResourceManager; +import org.apache.seata.core.protocol.HeartbeatMessage; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeAll; @@ -37,6 +38,7 @@ import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.assertThrows; /** * Rm RPC client test. @@ -96,4 +98,13 @@ private AtomicBoolean getInitializeStatus(final RmNettyRemotingClient rmNettyRem throw new RuntimeException(ex.getMessage()); } } + + @Test + public void testSendAsyncRequestWithNullChannelLogsWarning() { + RmNettyRemotingClient remotingClient = RmNettyRemotingClient.getInstance(); + Object message = HeartbeatMessage.PING; + assertThrows(FrameworkException.class, () -> { + remotingClient.sendAsyncRequest(null, message); + }); + } } diff --git a/core/src/test/java/org/apache/seata/core/store/db/sql/distributed/lock/BaseDistributedLockSqlServerTest.java b/core/src/test/java/org/apache/seata/core/store/db/sql/distributed/lock/BaseDistributedLockSqlServerTest.java new file mode 100644 index 00000000000..4a51509c9e0 --- /dev/null +++ b/core/src/test/java/org/apache/seata/core/store/db/sql/distributed/lock/BaseDistributedLockSqlServerTest.java @@ -0,0 +1,61 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.seata.core.store.db.sql.distributed.lock; + +import org.apache.seata.core.constants.ServerTableColumnsName; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.*; + +class BaseDistributedLockSqlServerTest { + + private BaseDistributedLockSqlServer baseDistributedLockSqlServer; + private final String testTable = "test_lock_table"; + + @BeforeEach + void setUp() { + baseDistributedLockSqlServer = new BaseDistributedLockSqlServer(); + } + + @Test + void testGetSelectDistributeForUpdateSql() { + String expected = "SELECT " + ServerTableColumnsName.DISTRIBUTED_LOCK_KEY + "," + + ServerTableColumnsName.DISTRIBUTED_LOCK_VALUE + "," + ServerTableColumnsName.DISTRIBUTED_LOCK_EXPIRE + + " FROM " + testTable + " WITH (ROWLOCK, UPDLOCK, HOLDLOCK) WHERE " + ServerTableColumnsName.DISTRIBUTED_LOCK_KEY + " = ?"; + String actual = baseDistributedLockSqlServer.getSelectDistributeForUpdateSql(testTable); + assertEquals(expected, actual); + } + + @Test + void testGetInsertSql() { + String expected = "INSERT INTO " + testTable + "(" + + ServerTableColumnsName.DISTRIBUTED_LOCK_KEY + "," + + ServerTableColumnsName.DISTRIBUTED_LOCK_VALUE + "," + ServerTableColumnsName.DISTRIBUTED_LOCK_EXPIRE + + ") VALUES (?, ?, ?)"; + String actual = baseDistributedLockSqlServer.getInsertSql(testTable); + assertEquals(expected, actual); + } + + @Test + void testGetUpdateSql() { + String expected = "UPDATE " + testTable + " SET " + + ServerTableColumnsName.DISTRIBUTED_LOCK_VALUE + "=?, " + ServerTableColumnsName.DISTRIBUTED_LOCK_EXPIRE + "=?" + + " WHERE " + ServerTableColumnsName.DISTRIBUTED_LOCK_KEY + "=?"; + String actual = baseDistributedLockSqlServer.getUpdateSql(testTable); + assertEquals(expected, actual); + } +} \ No newline at end of file diff --git a/core/src/test/java/org/apache/seata/core/store/db/sql/distributed/lock/BaseDistributedLockSqlTest.java b/core/src/test/java/org/apache/seata/core/store/db/sql/distributed/lock/BaseDistributedLockSqlTest.java new file mode 100644 index 00000000000..2b98cb1f78b --- /dev/null +++ b/core/src/test/java/org/apache/seata/core/store/db/sql/distributed/lock/BaseDistributedLockSqlTest.java @@ -0,0 +1,61 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.seata.core.store.db.sql.distributed.lock; + +import org.apache.seata.core.constants.ServerTableColumnsName; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.*; + +class BaseDistributedLockSqlTest { + + private BaseDistributedLockSql baseDistributedLockSql; + private final String testTable = "test_lock_table"; + + @BeforeEach + void setUp() { + baseDistributedLockSql = new BaseDistributedLockSql(); + } + + @Test + void testGetSelectDistributeForUpdateSql() { + String expected = "SELECT " + ServerTableColumnsName.DISTRIBUTED_LOCK_KEY + "," + + ServerTableColumnsName.DISTRIBUTED_LOCK_VALUE + "," + ServerTableColumnsName.DISTRIBUTED_LOCK_EXPIRE + + " FROM " + testTable + " WHERE " + ServerTableColumnsName.DISTRIBUTED_LOCK_KEY + " = ? FOR UPDATE"; + String actual = baseDistributedLockSql.getSelectDistributeForUpdateSql(testTable); + assertEquals(expected, actual); + } + + @Test + void testGetInsertSql() { + String expected = "INSERT INTO " + testTable + "(" + + ServerTableColumnsName.DISTRIBUTED_LOCK_KEY + "," + + ServerTableColumnsName.DISTRIBUTED_LOCK_VALUE + "," + ServerTableColumnsName.DISTRIBUTED_LOCK_EXPIRE + + ") VALUES (?, ?, ?)"; + String actual = baseDistributedLockSql.getInsertSql(testTable); + assertEquals(expected, actual); + } + + @Test + void testGetUpdateSql() { + String expected = "UPDATE " + testTable + " SET " + + ServerTableColumnsName.DISTRIBUTED_LOCK_VALUE + "=?, " + ServerTableColumnsName.DISTRIBUTED_LOCK_EXPIRE + "=?" + + " WHERE " + ServerTableColumnsName.DISTRIBUTED_LOCK_KEY + "=?"; + String actual = baseDistributedLockSql.getUpdateSql(testTable); + assertEquals(expected, actual); + } +} \ No newline at end of file diff --git a/core/src/test/java/org/apache/seata/core/store/db/sql/distributed/lock/DistributedLockSqlFactoryTest.java b/core/src/test/java/org/apache/seata/core/store/db/sql/distributed/lock/DistributedLockSqlFactoryTest.java new file mode 100644 index 00000000000..9d1c892b2e6 --- /dev/null +++ b/core/src/test/java/org/apache/seata/core/store/db/sql/distributed/lock/DistributedLockSqlFactoryTest.java @@ -0,0 +1,51 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.seata.core.store.db.sql.distributed.lock; + +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.*; + +class DistributedLockSqlFactoryTest { + + @Test + void testGetDistributedLogStoreSqlForMysql() { + DistributedLockSql sql = DistributedLockSqlFactory.getDistributedLogStoreSql("mysql"); + assertNotNull(sql); + assertTrue(sql instanceof BaseDistributedLockSql); + } + + @Test + void testGetDistributedLogStoreSqlForSqlServer() { + DistributedLockSql sql = DistributedLockSqlFactory.getDistributedLogStoreSql("sqlserver"); + assertNotNull(sql); + assertTrue(sql instanceof BaseDistributedLockSqlServer); + } + + @Test + void testGetDistributedLogStoreSqlForUnsupportedDb() { + DistributedLockSql sql = DistributedLockSqlFactory.getDistributedLogStoreSql("unsupported"); + assertNotNull(sql); + assertTrue(sql instanceof BaseDistributedLockSql); + } + + @Test + void testCacheImplementation() { + DistributedLockSql sql1 = DistributedLockSqlFactory.getDistributedLogStoreSql("mysql"); + DistributedLockSql sql2 = DistributedLockSqlFactory.getDistributedLogStoreSql("mysql"); + assertSame(sql1, sql2); + } +} \ No newline at end of file diff --git a/dependencies/pom.xml b/dependencies/pom.xml index 6b93148e613..2b567d6927f 100644 --- a/dependencies/pom.xml +++ b/dependencies/pom.xml @@ -77,7 +77,7 @@ 4.0.3 1.6.7 3.25.4 - 1.66.0 + 1.55.1 5.4.0 0.45 4.0.63 @@ -127,7 +127,6 @@ 1.7.3 - 5.3.26 9.0.90 @@ -137,6 +136,9 @@ 3.1.10 4.12.0 2.4.0 + + + 0.8.0 @@ -529,6 +531,10 @@ com.google.guava guava + + io.grpc + grpc-core + @@ -876,11 +882,11 @@ ${rocketmq-version} - + - org.springframework - spring-webmvc - ${spring-webmvc.version} + org.apache.fury + fury-core + ${fury.version} diff --git a/discovery/seata-discovery-core/src/main/java/org/apache/seata/discovery/registry/RegistryService.java b/discovery/seata-discovery-core/src/main/java/org/apache/seata/discovery/registry/RegistryService.java index efc997b20bf..93a3cfd1b84 100644 --- a/discovery/seata-discovery-core/src/main/java/org/apache/seata/discovery/registry/RegistryService.java +++ b/discovery/seata-discovery-core/src/main/java/org/apache/seata/discovery/registry/RegistryService.java @@ -16,6 +16,7 @@ */ package org.apache.seata.discovery.registry; +import org.apache.seata.common.metadata.Instance; import org.apache.seata.common.util.CollectionUtils; import org.apache.seata.config.ConfigurationFactory; @@ -61,16 +62,42 @@ public interface RegistryService { * @param address the address * @throws Exception the exception */ + @Deprecated void register(InetSocketAddress address) throws Exception; + /** + * Register. + * + * @param instance the address + * @throws Exception the exception + */ + default void register(Instance instance) throws Exception { + InetSocketAddress inetSocketAddress = + new InetSocketAddress(instance.getTransaction().getHost(), instance.getTransaction().getPort()); + register(inetSocketAddress); + } + /** * Unregister. * * @param address the address * @throws Exception the exception */ + @Deprecated void unregister(InetSocketAddress address) throws Exception; + /** + * Unregister. + * + * @param instance the instance + * @throws Exception the exception + */ + default void unregister(Instance instance) throws Exception { + InetSocketAddress inetSocketAddress = + new InetSocketAddress(instance.getTransaction().getHost(), instance.getTransaction().getPort()); + unregister(inetSocketAddress); + } + /** * Subscribe. * diff --git a/discovery/seata-discovery-namingserver/src/main/java/org/apache/seata/discovery/registry/namingserver/NamingserverRegistryServiceImpl.java b/discovery/seata-discovery-namingserver/src/main/java/org/apache/seata/discovery/registry/namingserver/NamingserverRegistryServiceImpl.java index fa53bd45f19..88386253fa6 100644 --- a/discovery/seata-discovery-namingserver/src/main/java/org/apache/seata/discovery/registry/namingserver/NamingserverRegistryServiceImpl.java +++ b/discovery/seata-discovery-namingserver/src/main/java/org/apache/seata/discovery/registry/namingserver/NamingserverRegistryServiceImpl.java @@ -47,8 +47,7 @@ import org.apache.http.entity.ContentType; import org.apache.http.protocol.HTTP; import org.apache.http.util.EntityUtils; -import org.apache.seata.common.metadata.Node; -import org.apache.seata.common.metadata.namingserver.Instance; +import org.apache.seata.common.metadata.Instance; import org.apache.seata.common.metadata.namingserver.MetaResponse; import org.apache.seata.common.thread.NamedThreadFactory; import org.apache.seata.common.util.CollectionUtils; @@ -148,9 +147,11 @@ static NamingserverRegistryServiceImpl getInstance() { @Override public void register(InetSocketAddress address) throws Exception { - NetUtil.validAddress(address); - Instance instance = Instance.getInstance(); - instance.setTransaction(new Node.Endpoint(address.getAddress().getHostAddress(), address.getPort(), "netty")); + register(Instance.getInstance()); + } + + @Override + public void register(Instance instance) throws Exception { instance.setTimestamp(System.currentTimeMillis()); doRegister(instance, getNamingAddrs()); } @@ -198,11 +199,15 @@ public boolean doHealthCheck(String url) { } } + + + @Override + public void unregister(InetSocketAddress inetSocketAddress) { + unregister(Instance.getInstance()); + } + @Override - public void unregister(InetSocketAddress address) { - NetUtil.validAddress(address); - Instance instance = Instance.getInstance(); - instance.setTransaction(new Node.Endpoint(address.getAddress().getHostAddress(), address.getPort(), "netty")); + public void unregister(Instance instance) { for (String urlSuffix : getNamingAddrs()) { String url = HTTP_PREFIX + urlSuffix + "/naming/v1/unregister?"; String unit = instance.getUnit(); diff --git a/discovery/seata-discovery-raft/src/main/java/org/apache/seata/discovery/registry/raft/RaftRegistryServiceImpl.java b/discovery/seata-discovery-raft/src/main/java/org/apache/seata/discovery/registry/raft/RaftRegistryServiceImpl.java index 8ba0d2256ed..06bed7b639d 100644 --- a/discovery/seata-discovery-raft/src/main/java/org/apache/seata/discovery/registry/raft/RaftRegistryServiceImpl.java +++ b/discovery/seata-discovery-raft/src/main/java/org/apache/seata/discovery/registry/raft/RaftRegistryServiceImpl.java @@ -24,6 +24,9 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.LinkedHashMap; +import java.util.Optional; +import java.util.Arrays; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.ThreadLocalRandom; @@ -38,6 +41,8 @@ import com.fasterxml.jackson.databind.ObjectMapper; import org.apache.seata.common.ConfigurationKeys; import org.apache.seata.common.exception.AuthenticationFailedException; +import org.apache.seata.common.exception.NotSupportYetException; +import org.apache.seata.common.exception.ParseEndpointException; import org.apache.seata.common.exception.RetryableException; import org.apache.seata.common.metadata.Metadata; import org.apache.seata.common.metadata.MetadataResponse; @@ -45,6 +50,7 @@ import org.apache.seata.common.thread.NamedThreadFactory; import org.apache.seata.common.util.CollectionUtils; import org.apache.seata.common.util.HttpClientUtil; +import org.apache.seata.common.util.NetUtil; import org.apache.seata.common.util.StringUtils; import org.apache.seata.config.ConfigChangeListener; import org.apache.seata.config.Configuration; @@ -114,10 +120,13 @@ public class RaftRegistryServiceImpl implements RegistryService> ALIVE_NODES = new ConcurrentHashMap<>(); + private static final String PREFERRED_NETWORKS; + static { TOKEN_EXPIRE_TIME_IN_MILLISECONDS = CONFIG.getLong(getTokenExpireTimeInMillisecondsKey(), 29 * 60 * 1000L); USERNAME = CONFIG.getConfig(getRaftUserNameKey()); PASSWORD = CONFIG.getConfig(getRaftPassWordKey()); + PREFERRED_NETWORKS = CONFIG.getConfig(getPreferredNetworks()); } private RaftRegistryServiceImpl() { @@ -221,7 +230,7 @@ private static String queryHttpAddress(String clusterName, String group) { List inetSocketAddresses = ALIVE_NODES.get(CURRENT_TRANSACTION_SERVICE_GROUP); if (CollectionUtils.isEmpty(inetSocketAddresses)) { addressList = - nodeList.stream().map(node -> node.getControl().createAddress()).collect(Collectors.toList()); + nodeList.stream().map(RaftRegistryServiceImpl::selectControlEndpointStr).collect(Collectors.toList()); } else { stream = inetSocketAddresses.stream(); } @@ -234,15 +243,20 @@ private static String queryHttpAddress(String clusterName, String group) { Map map = new HashMap<>(); if (CollectionUtils.isNotEmpty(nodeList)) { for (Node node : nodeList) { - map.put(new InetSocketAddress(node.getTransaction().getHost(), node.getTransaction().getPort()).getAddress().getHostAddress() - + IP_PORT_SPLIT_CHAR + node.getTransaction().getPort(), node); + InetSocketAddress inetSocketAddress = selectTransactionEndpoint(node); + map.put(inetSocketAddress.getHostString() + + IP_PORT_SPLIT_CHAR + inetSocketAddress.getPort(), node); } } addressList = stream.map(inetSocketAddress -> { - String host = inetSocketAddress.getAddress().getHostAddress(); + String host = NetUtil.toStringHost(inetSocketAddress); Node node = map.get(host + IP_PORT_SPLIT_CHAR + inetSocketAddress.getPort()); + InetSocketAddress controlEndpoint = null; + if (node != null) { + controlEndpoint = selectControlEndpoint(node); + } return host + IP_PORT_SPLIT_CHAR - + (node != null ? node.getControl().getPort() : inetSocketAddress.getPort()); + + (controlEndpoint != null ? controlEndpoint.getPort() : inetSocketAddress.getPort()); }).collect(Collectors.toList()); return addressList.get(ThreadLocalRandom.current().nextInt(addressList.size())); } @@ -263,6 +277,11 @@ private static String getRaftPassWordKey() { REGISTRY_TYPE, PRO_PASSWORD_KEY); } + private static String getPreferredNetworks() { + return String.join(ConfigurationKeys.FILE_CONFIG_SPLIT_CHAR, ConfigurationKeys.FILE_ROOT_REGISTRY, + "preferredNetworks"); + } + private static String getTokenExpireTimeInMillisecondsKey() { return String.join(ConfigurationKeys.FILE_CONFIG_SPLIT_CHAR, ConfigurationKeys.FILE_ROOT_REGISTRY, REGISTRY_TYPE, TOKEN_VALID_TIME_MS_KEY); @@ -276,9 +295,85 @@ private static boolean isTokenExpired() { return System.currentTimeMillis() >= tokenExpiredTime; } - private InetSocketAddress convertInetSocketAddress(Node node) { - Node.Endpoint endpoint = node.getTransaction(); - return new InetSocketAddress(endpoint.getHost(), endpoint.getPort()); + private static String selectControlEndpointStr(Node node) { + InetSocketAddress control = selectControlEndpoint(node); + return NetUtil.toStringAddress(control); + } + + private static String selectTransactionEndpointStr(Node node) { + InetSocketAddress transaction = selectTransactionEndpoint(node); + return NetUtil.toStringAddress(transaction); + } + + private static InetSocketAddress selectControlEndpoint(Node node) { + return selectEndpoint("control", node); + } + + private static InetSocketAddress selectTransactionEndpoint(Node node) { + return selectEndpoint("transaction", node); + } + + private static InetSocketAddress selectEndpoint(String type, Node node) { + if (StringUtils.isBlank(PREFERRED_NETWORKS)) { + // Use the default method, directly using node.control and node.transaction + switch (type) { + case "control": + return new InetSocketAddress(node.getControl().getHost(), node.getControl().getPort()); + case "transaction": + return new InetSocketAddress(node.getTransaction().getHost(), node.getTransaction().getPort()); + default: + throw new NotSupportYetException("SelectEndpoint is not support type: " + type); + } + } + Node.ExternalEndpoint externalEndpoint = selectExternalEndpoint(node, PREFERRED_NETWORKS.split(";")); + switch (type) { + case "control": + return new InetSocketAddress(externalEndpoint.getHost(), externalEndpoint.getControlPort()); + case "transaction": + return new InetSocketAddress(externalEndpoint.getHost(), externalEndpoint.getTransactionPort()); + default: + throw new NotSupportYetException("SelectEndpoint is not support type: " + type); + } + } + + private static Node.ExternalEndpoint selectExternalEndpoint(Node node, String[] preferredNetworks) { + Map metadata = node.getMetadata(); + if (CollectionUtils.isEmpty(metadata)) { + throw new ParseEndpointException("Node metadata is empty."); + } + + Object external = metadata.get("external"); + + if (external instanceof List) { + List> externalEndpoints = (List>) external; + + if (CollectionUtils.isEmpty(externalEndpoints)) { + throw new ParseEndpointException("ExternalEndpoints should not be empty."); + } + + for (LinkedHashMap externalEndpoint : externalEndpoints) { + String ip = Optional.ofNullable(externalEndpoint.get("host")) + .map(Object::toString) + .orElse(""); + + if (isPreferredNetwork(ip, Arrays.asList(preferredNetworks))) { + return createExternalEndpoint(externalEndpoint, ip); + } + } + } + throw new ParseEndpointException("No ExternalEndpoints value matches."); + } + + private static boolean isPreferredNetwork(String ip, List preferredNetworks) { + return preferredNetworks.stream().anyMatch(regex -> + StringUtils.isNotBlank(regex) && (ip.matches(regex) || ip.startsWith(regex)) + ); + } + + private static Node.ExternalEndpoint createExternalEndpoint(LinkedHashMap externalEndpoint, String ip) { + int controlPort = Integer.parseInt(externalEndpoint.get("controlPort").toString()); + int transactionPort = Integer.parseInt(externalEndpoint.get("transactionPort").toString()); + return new Node.ExternalEndpoint(ip, controlPort, transactionPort); } @Override @@ -292,7 +387,7 @@ public List aliveLookup(String transactionServiceGroup) { String clusterName = getServiceGroup(transactionServiceGroup); Node leader = METADATA.getLeader(clusterName); if (leader != null) { - return Collections.singletonList(convertInetSocketAddress(leader)); + return Collections.singletonList(selectTransactionEndpoint(leader)); } } return RegistryService.super.aliveLookup(transactionServiceGroup); @@ -340,7 +435,7 @@ public List refreshAliveLookup(String transactionServiceGroup List aliveAddress) { if (METADATA.isRaftMode()) { Node leader = METADATA.getLeader(getServiceGroup(transactionServiceGroup)); - InetSocketAddress leaderAddress = convertInetSocketAddress(leader); + InetSocketAddress leaderAddress = selectTransactionEndpoint(leader); return ALIVE_NODES.put(transactionServiceGroup, aliveAddress.isEmpty() ? aliveAddress : aliveAddress.parallelStream().filter(inetSocketAddress -> { // Since only follower will turn into leader, only the follower node needs to be listened to @@ -478,7 +573,7 @@ public List lookup(String key) throws Exception { } List nodes = METADATA.getNodes(clusterName); if (CollectionUtils.isNotEmpty(nodes)) { - return nodes.parallelStream().map(this::convertInetSocketAddress).collect(Collectors.toList()); + return nodes.parallelStream().map(RaftRegistryServiceImpl::selectTransactionEndpoint).collect(Collectors.toList()); } return Collections.emptyList(); } diff --git a/discovery/seata-discovery-raft/src/test/java/org/apache/seata/discovery/registry/raft/RaftRegistryServiceImplTest.java b/discovery/seata-discovery-raft/src/test/java/org/apache/seata/discovery/registry/raft/RaftRegistryServiceImplTest.java index e6c88fcc5c0..45b675dbe56 100644 --- a/discovery/seata-discovery-raft/src/test/java/org/apache/seata/discovery/registry/raft/RaftRegistryServiceImplTest.java +++ b/discovery/seata-discovery-raft/src/test/java/org/apache/seata/discovery/registry/raft/RaftRegistryServiceImplTest.java @@ -17,13 +17,18 @@ package org.apache.seata.discovery.registry.raft; -import org.apache.seata.common.util.HttpClientUtil; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.apache.seata.common.metadata.MetadataResponse; +import org.apache.seata.common.metadata.Node; +import org.apache.seata.common.util.*; import org.apache.seata.config.ConfigurationFactory; import org.apache.http.HttpStatus; import org.apache.http.StatusLine; import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.entity.StringEntity; import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.mockito.MockedStatic; @@ -33,7 +38,7 @@ import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; -import java.util.Map; +import java.util.*; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -53,6 +58,7 @@ public static void beforeClass() { System.setProperty("registry.raft.password", "seata"); System.setProperty("registry.raft.serverAddr", "127.0.0.1:8092"); System.setProperty("registry.raft.tokenValidityInMilliseconds", "10000"); + System.setProperty("registry.preferredNetworks", "10.10.*"); ConfigurationFactory.getInstance(); } @@ -145,4 +151,28 @@ public void testSecureTTL() throws NoSuchMethodException, InvocationTargetExcept assertEquals(true, rst); } + /** + * RaftRegistryServiceImpl#controlEndpointStr() + * RaftRegistryServiceImpl#transactionEndpointStr() + */ + @Test + public void testSelectEndpoint() throws JsonProcessingException, NoSuchMethodException, InvocationTargetException, IllegalAccessException { + String jsonString = "{\"nodes\":[{\"control\":{\"host\":\"v-0.svc-l.default.svc.cluster.local\",\"port\":7091},\"transaction\":{\"host\":\"v-0.svc-l.default.svc.cluster.local\",\"port\":8091},\"internal\":{\"host\":\"v-0.svc-l.default.svc.cluster.local\",\"port\":9091},\"group\":\"default\",\"role\":\"LEADER\",\"version\":\"2.3.0-SNAPSHOT\",\"metadata\":{\"external\":[{\"host\":\"192.168.105.7\",\"controlPort\":30071,\"transactionPort\":30091},{\"host\":\"10.10.105.7\",\"controlPort\":30071,\"transactionPort\":30091}]}},{\"control\":{\"host\":\"v-2.svc-l.default.svc.cluster.local\",\"port\":7091},\"transaction\":{\"host\":\"v-2.svc-l.default.svc.cluster.local\",\"port\":8091},\"internal\":{\"host\":\"v-2.svc-l.default.svc.cluster.local\",\"port\":9091},\"group\":\"default\",\"role\":\"FOLLOWER\",\"version\":\"2.3.0-SNAPSHOT\",\"metadata\":{\"external\":[{\"host\":\"192.168.105.7\",\"controlPort\":30073,\"transactionPort\":30093},{\"host\":\"10.10.105.7\",\"controlPort\":30073,\"transactionPort\":30093}]}},{\"control\":{\"host\":\"v-1.svc-l.default.svc.cluster.local\",\"port\":7091},\"transaction\":{\"host\":\"v-1.svc-l.default.svc.cluster.local\",\"port\":8091},\"internal\":{\"host\":\"v-1.svc-l.default.svc.cluster.local\",\"port\":9091},\"group\":\"default\",\"role\":\"FOLLOWER\",\"version\":\"2.3.0-SNAPSHOT\",\"metadata\":{\"external\":[{\"host\":\"192.168.105.7\",\"controlPort\":30072,\"transactionPort\":30092},{\"host\":\"10.10.105.7\",\"controlPort\":30072,\"transactionPort\":30092}]}}],\"storeMode\":\"raft\",\"term\":1}"; + + Method selectControlEndpointStrMethod = RaftRegistryServiceImpl.class.getDeclaredMethod("selectControlEndpointStr", Node.class); + selectControlEndpointStrMethod.setAccessible(true); + Method selectTransactionEndpointStrMethod = RaftRegistryServiceImpl.class.getDeclaredMethod("selectTransactionEndpointStr", Node.class); + selectTransactionEndpointStrMethod.setAccessible(true); + + ObjectMapper objectMapper = new ObjectMapper(); + MetadataResponse metadataResponse = objectMapper.readValue(jsonString, MetadataResponse.class); + List nodes = metadataResponse.getNodes(); + + for (Node node : nodes) { + String controlEndpointStr = (String) selectControlEndpointStrMethod.invoke(null, node);; + String transactionEndpointStr = (String) selectTransactionEndpointStrMethod.invoke(null, node);; + Assertions.assertTrue(controlEndpointStr.contains("10.10.105.7:3007")); + Assertions.assertTrue(transactionEndpointStr.contains("10.10.105.7:3009")); + } + } } diff --git a/distribution/LICENSE b/distribution/LICENSE index 2c483b9217a..c2cf0240dc3 100644 --- a/distribution/LICENSE +++ b/distribution/LICENSE @@ -218,12 +218,12 @@ Apache-2.0 licenses ======================================================================== @ampproject/remapping 2.2.1 Apache-2.0 - com.101tec:zkclient 0.11 Apache-2.0 com.alibaba.nacos:nacos-api 1.4.6 Apache-2.0 com.alibaba.nacos:nacos-client 1.4.6 Apache-2.0 com.alibaba.nacos:nacos-common 1.4.6 Apache-2.0 - com.alibaba:druid 1.2.7 Apache-2.0 + com.alibaba:druid 1.2.20 Apache-2.0 com.alibaba:fastjson 1.2.83 Apache-2.0 + com.alibaba.fastjson2:fastjson2 2.0.52 Apache-2.0 com.alipay.sofa.common:sofa-common-tools 1.0.12 Apache-2.0 com.alipay.sofa:bolt 1.6.7 Apache-2.0 com.alipay.sofa:hessian 4.0.3 Apache-2.0 @@ -243,7 +243,7 @@ Apache-2.0 licenses com.github.danielwegener:logback-kafka-appender 0.2.0-RC2 Apache-2.0 com.github.vlsi.compactmap:compactmap 2.0 Apache-2.0 com.google.android:annotations 4.1.1.4 Apache-2.0 - com.google.api.grpc:proto-google-common-protos 1.17.0 Apache-2.0 + com.google.api.grpc:proto-google-common-protos 2.9.0 Apache-2.0 com.google.code.findbugs:jsr305 3.0.2 Apache-2.0 com.google.code.gson:gson 2.8.9 Apache-2.0 com.google.code.gson:gson 2.9.1 Apache-2.0 @@ -275,18 +275,17 @@ Apache-2.0 licenses commons-pool:commons-pool 1.6 Apache-2.0 commons-validator:commons-validator 1.7 Apache-2.0 de.javakaffee:kryo-serializers 0.45 Apache-2.0 - htm 3.1.1 Apache-2.0 io.etcd:jetcd-common 0.5.0 Apache-2.0 io.etcd:jetcd-core 0.5.0 Apache-2.0 io.etcd:jetcd-resolver 0.5.0 Apache-2.0 - io.grpc:grpc-api 1.27.1 Apache-2.0 - io.grpc:grpc-context 1.27.1 Apache-2.0 - io.grpc:grpc-core 1.27.1 Apache-2.0 + io.grpc:grpc-api 1.55.1 Apache-2.0 + io.grpc:grpc-context 1.55.1 Apache-2.0 + io.grpc:grpc-core 1.55.1 Apache-2.0 io.grpc:grpc-grpclb 1.27.1 Apache-2.0 - io.grpc:grpc-netty 1.27.1 Apache-2.0 - io.grpc:grpc-protobuf 1.27.1 Apache-2.0 - io.grpc:grpc-protobuf-lite 1.27.1 Apache-2.0 - io.grpc:grpc-stub 1.27.1 Apache-2.0 + io.grpc:grpc-netty 1.55.1 Apache-2.0 + io.grpc:grpc-protobuf 1.55.1 Apache-2.0 + io.grpc:grpc-protobuf-lite 1.55.1 Apache-2.0 + io.grpc:grpc-stub 1.55.1 Apache-2.0 io.jsonwebtoken:jjwt-api 0.10.5 Apache-2.0 io.jsonwebtoken:jjwt-impl 0.10.5 Apache-2.0 io.jsonwebtoken:jjwt-jackson 0.10.5 Apache-2.0 @@ -321,7 +320,7 @@ Apache-2.0 licenses io.netty:netty-transport-rxtx 4.1.101.Final Apache-2.0 io.netty:netty-transport-sctp 4.1.101.Final Apache-2.0 io.netty:netty-transport-udt 4.1.101.Final Apache-2.0 - io.perfmark:perfmark-api 0.19.0 Apache-2.0 + io.perfmark:perfmark-api 0.25.0 Apache-2.0 io.prometheus:simpleclient 0.10.0 Apache-2.0 io.prometheus:simpleclient 0.15.0 Apache-2.0 io.prometheus:simpleclient_common 0.10.0 Apache-2.0 @@ -341,6 +340,10 @@ Apache-2.0 licenses org.apache.commons:commons-lang3 3.12.0 Apache-2.0 org.apache.commons:commons-pool2 2.9.0 Apache-2.0 org.apache.commons:commons-pool2 2.11.1 Apache-2.0 + org.apache.curator:curator-recipes 5.1.0 Apache-2.0 + org.apache.curator:curator-framework 5.1.0 Apache-2.0 + org.apache.curator:curator-client 5.1.0 Apache-2.0 + org.apache.curator:curator-test 5.1.0 Apache-2.0 org.apache.dubbo.extensions:dubbo-filter-seata 1.0.2 Apache-2.0 org.apache.httpcomponents:httpasyncclient 4.1.5 Apache-2.0 org.apache.httpcomponents:httpclient 4.5.14 Apache-2.0 @@ -418,10 +421,6 @@ Apache-2.0 licenses org.springframework:spring-web 5.3.39 Apache-2.0 org.springframework:spring-webmvc 5.3.39 Apache-2.0 org.apache.commons:commons-math 2.2 Apache-2.0 - org.apache.curator:curator-recipes:jar 5.1.0 Apache-2.0 - org.apache.curator:curator-framework:jar 5.1.0 Apache-2.0 - org.apache.curator:curator-client:jar 5.1.0 Apache-2.0 - org.apache.curator:curator-test:jar 5.1.0 Apache-2.0 ======================================================================== BSD-2-Clause licenses @@ -726,7 +725,7 @@ MIT licenses dva/node_modules/react-router-redux 5.0.0-alpha.9 MIT dva/node_modules/redux 3.7.2 MIT dva/node_modules/regenerator-runtime 0.11.1 MIT - elliptic 6.5.7 MIT + elliptic 6.6.0 MIT elliptic/node_modules/bn.js 4.12.0 MIT encoding 0.1.13 MIT encoding/node_modules/iconv-lite 0.6.3 MIT diff --git a/distribution/NOTICE.md b/distribution/NOTICE.md index cc71344309e..80f4fa27770 100644 --- a/distribution/NOTICE.md +++ b/distribution/NOTICE.md @@ -73,7 +73,7 @@ Please copy database driver dependencies, such as `mysql-connector-java.jar`, to │   │   ├── netty-transport-rxtx-4.1.101.Final.jar │   │   ├── netty-transport-sctp-4.1.101.Final.jar │   │   ├── netty-transport-udt-4.1.101.Final.jar -│   │   ├── seata-common-2.2.0.jar +│   │   ├── seata-common-2.3.0.jar │   │   ├── slf4j-api-1.7.36.jar │   │   ├── snakeyaml-2.0.jar │   │   ├── spring-aop-5.3.39.jar @@ -122,7 +122,7 @@ Please copy database driver dependencies, such as `mysql-connector-java.jar`, to │   │   ├── apm-mysql-6.x-plugin-8.6.0.jar │   │   ├── apm-mysql-8.x-plugin-8.6.0.jar │   │   ├── apm-mysql-commons-8.6.0.jar - │   │   └── apm-seata-skywalking-plugin-2.2.0.jar + │   │   └── apm-seata-skywalking-plugin-2.3.0.jar │   └── skywalking-agent.jar ├── lib │   ├── DmJdbcDriver18-8.1.2.192.jar @@ -165,15 +165,16 @@ Please copy database driver dependencies, such as `mysql-connector-java.jar`, to │   ├── failsafe-2.3.3.jar │   ├── failureaccess-1.0.1.jar │   ├── fastjson-1.2.83.jar - │   ├── grpc-api-1.66.0.jar - │   ├── grpc-context-1.66.0.jar - │   ├── grpc-core-1.66.0.jar + │   ├── fastjson2-2.0.52.jar + │   ├── grpc-api-1.55.1.jar + │   ├── grpc-context-1.55.1.jar + │   ├── grpc-core-1.55.1.jar │   ├── grpc-grpclb-1.27.1.jar - │   ├── grpc-netty-1.66.0.jar - │   ├── grpc-protobuf-1.66.0.jar - │   ├── grpc-protobuf-lite-1.66.0.jar - │   ├── grpc-stub-1.66.0.jar - │   ├── grpc-util-1.66.0.jar + │   ├── grpc-netty-1.55.1.jar + │   ├── grpc-protobuf-1.55.1.jar + │   ├── grpc-protobuf-lite-1.55.1.jar + │   ├── grpc-stub-1.55.1.jar + │   ├── grpc-util-1.55.1.jar │   ├── gson-2.9.1.jar │   ├── guava-32.1.3-jre.jar │   ├── guice-5.0.1.jar @@ -272,55 +273,55 @@ Please copy database driver dependencies, such as `mysql-connector-java.jar`, to │   ├── netty-transport-sctp-4.1.101.Final.jar │   ├── netty-transport-udt-4.1.101.Final.jar │   ├── objenesis-3.2.jar - │   ├── perfmark-api-0.27.0.jar + │   ├── perfmark-api-0.25.0.jar │   ├── postgresql-42.3.8.jar - │   ├── proto-google-common-protos-2.41.0.jar + │   ├── proto-google-common-protos-2.9.0.jar │   ├── protobuf-java-3.25.4.jar │   ├── protobuf-java-util-3.11.0.jar │   ├── reflectasm-1.11.9.jar │   ├── registry-client-all-6.3.0.jar │   ├── rocksdbjni-8.8.1.jar - │   ├── seata-common-2.2.0.jar - │   ├── seata-compressor-all-2.2.0.jar - │   ├── seata-compressor-bzip2-2.2.0.jar - │   ├── seata-compressor-deflater-2.2.0.jar - │   ├── seata-compressor-gzip-2.2.0.jar - │   ├── seata-compressor-lz4-2.2.0.jar - │   ├── seata-compressor-zip-2.2.0.jar - │   ├── seata-compressor-zstd-2.2.0.jar - │   ├── seata-config-all-2.2.0.jar - │   ├── seata-config-apollo-2.2.0.jar - │   ├── seata-config-consul-2.2.0.jar - │   ├── seata-config-core-2.2.0.jar - │   ├── seata-config-etcd3-2.2.0.jar - │   ├── seata-config-nacos-2.2.0.jar - │   ├── seata-config-spring-cloud-2.2.0.jar - │   ├── seata-config-zk-2.2.0.jar - │   ├── seata-console-2.2.0.jar - │   ├── seata-core-2.2.0.jar - │   ├── seata-discovery-all-2.2.0.jar - │   ├── seata-discovery-consul-2.2.0.jar - │   ├── seata-discovery-core-2.2.0.jar - │   ├── seata-discovery-custom-2.2.0.jar - │   ├── seata-discovery-etcd3-2.2.0.jar - │   ├── seata-discovery-eureka-2.2.0.jar - │   ├── seata-discovery-nacos-2.2.0.jar - │   ├── seata-discovery-namingserver-2.2.0.jar - │   ├── seata-discovery-redis-2.2.0.jar - │   ├── seata-discovery-sofa-2.2.0.jar - │   ├── seata-discovery-zk-2.2.0.jar - │   ├── seata-metrics-all-2.2.0.jar - │   ├── seata-metrics-api-2.2.0.jar - │   ├── seata-metrics-core-2.2.0.jar - │   ├── seata-metrics-exporter-prometheus-2.2.0.jar - │   ├── seata-metrics-registry-compact-2.2.0.jar - │   ├── seata-serializer-all-2.2.0.jar - │   ├── seata-serializer-hessian-2.2.0.jar - │   ├── seata-serializer-kryo-2.2.0.jar - │   ├── seata-serializer-protobuf-2.2.0.jar - │   ├── seata-serializer-seata-2.2.0.jar - │   ├── seata-spring-autoconfigure-core-2.2.0.jar - │   ├── seata-spring-autoconfigure-server-2.2.0.jar + │   ├── seata-common-2.3.0.jar + │   ├── seata-compressor-all-2.3.0.jar + │   ├── seata-compressor-bzip2-2.3.0.jar + │   ├── seata-compressor-deflater-2.3.0.jar + │   ├── seata-compressor-gzip-2.3.0.jar + │   ├── seata-compressor-lz4-2.3.0.jar + │   ├── seata-compressor-zip-2.3.0.jar + │   ├── seata-compressor-zstd-2.3.0.jar + │   ├── seata-config-all-2.3.0.jar + │   ├── seata-config-apollo-2.3.0.jar + │   ├── seata-config-consul-2.3.0.jar + │   ├── seata-config-core-2.3.0.jar + │   ├── seata-config-etcd3-2.3.0.jar + │   ├── seata-config-nacos-2.3.0.jar + │   ├── seata-config-spring-cloud-2.3.0.jar + │   ├── seata-config-zk-2.3.0.jar + │   ├── seata-console-2.3.0.jar + │   ├── seata-core-2.3.0.jar + │   ├── seata-discovery-all-2.3.0.jar + │   ├── seata-discovery-consul-2.3.0.jar + │   ├── seata-discovery-core-2.3.0.jar + │   ├── seata-discovery-custom-2.3.0.jar + │   ├── seata-discovery-etcd3-2.3.0.jar + │   ├── seata-discovery-eureka-2.3.0.jar + │   ├── seata-discovery-nacos-2.3.0.jar + │   ├── seata-discovery-namingserver-2.3.0.jar + │   ├── seata-discovery-redis-2.3.0.jar + │   ├── seata-discovery-sofa-2.3.0.jar + │   ├── seata-discovery-zk-2.3.0.jar + │   ├── seata-metrics-all-2.3.0.jar + │   ├── seata-metrics-api-2.3.0.jar + │   ├── seata-metrics-core-2.3.0.jar + │   ├── seata-metrics-exporter-prometheus-2.3.0.jar + │   ├── seata-metrics-registry-compact-2.3.0.jar + │   ├── seata-serializer-all-2.3.0.jar + │   ├── seata-serializer-hessian-2.3.0.jar + │   ├── seata-serializer-kryo-2.3.0.jar + │   ├── seata-serializer-protobuf-2.3.0.jar + │   ├── seata-serializer-seata-2.3.0.jar + │   ├── seata-spring-autoconfigure-core-2.3.0.jar + │   ├── seata-spring-autoconfigure-server-2.3.0.jar │   ├── servo-core-0.12.21.jar │   ├── simpleclient-0.15.0.jar │   ├── simpleclient_common-0.15.0.jar @@ -453,6 +454,8 @@ Please copy database driver dependencies, such as `mysql-connector-java.jar`, to │   │   ├── jakarta.annotation-api-1.3.5.jar │   │   ├── janino-3.1.10.jar │   │   ├── jul-to-slf4j-1.7.36.jar +│   │   ├── log4j-api-2.17.2.jar +│   │   ├── log4j-to-slf4j-2.17.2.jar │   │   ├── logback-classic-1.2.12.jar │   │   ├── logback-core-1.2.12.jar │   │   ├── netty-all-4.1.101.Final.jar @@ -489,7 +492,7 @@ Please copy database driver dependencies, such as `mysql-connector-java.jar`, to │   │   ├── netty-transport-rxtx-4.1.101.Final.jar │   │   ├── netty-transport-sctp-4.1.101.Final.jar │   │   ├── netty-transport-udt-4.1.101.Final.jar -│   │   ├── seata-common-2.2.0.jar +│   │   ├── seata-common-2.3.0.jar │   │   ├── slf4j-api-1.7.36.jar │   │   ├── snakeyaml-2.0.jar │   │   ├── spring-aop-5.3.39.jar @@ -538,7 +541,7 @@ Please copy database driver dependencies, such as `mysql-connector-java.jar`, to │   │   ├── apm-mysql-6.x-plugin-8.6.0.jar │   │   ├── apm-mysql-8.x-plugin-8.6.0.jar │   │   ├── apm-mysql-commons-8.6.0.jar - │   │   └── apm-seata-skywalking-plugin-2.2.0.jar + │   │   └── apm-seata-skywalking-plugin-2.3.0.jar │   └── skywalking-agent.jar ├── lib │   ├── DmJdbcDriver18-8.1.2.192.jar @@ -581,15 +584,16 @@ Please copy database driver dependencies, such as `mysql-connector-java.jar`, to │   ├── failsafe-2.3.3.jar │   ├── failureaccess-1.0.1.jar │   ├── fastjson-1.2.83.jar - │   ├── grpc-api-1.66.0.jar - │   ├── grpc-context-1.66.0.jar - │   ├── grpc-core-1.66.0.jar + │   ├── fastjson2-2.0.52.jar + │   ├── grpc-api-1.55.1.jar + │   ├── grpc-context-1.55.1.jar + │   ├── grpc-core-1.55.1.jar │   ├── grpc-grpclb-1.27.1.jar - │   ├── grpc-netty-1.66.0.jar - │   ├── grpc-protobuf-1.66.0.jar - │   ├── grpc-protobuf-lite-1.66.0.jar - │   ├── grpc-stub-1.66.0.jar - │   ├── grpc-util-1.66.0.jar + │   ├── grpc-netty-1.55.1.jar + │   ├── grpc-protobuf-1.55.1.jar + │   ├── grpc-protobuf-lite-1.55.1.jar + │   ├── grpc-stub-1.55.1.jar + │   ├── grpc-util-1.55.1.jar │   ├── gson-2.9.1.jar │   ├── guava-32.1.3-jre.jar │   ├── guice-5.0.1.jar @@ -689,55 +693,55 @@ Please copy database driver dependencies, such as `mysql-connector-java.jar`, to │   ├── netty-transport-sctp-4.1.101.Final.jar │   ├── netty-transport-udt-4.1.101.Final.jar │   ├── objenesis-3.2.jar - │   ├── perfmark-api-0.27.0.jar + │   ├── perfmark-api-0.25.0.jar │   ├── postgresql-42.3.8.jar - │   ├── proto-google-common-protos-2.41.0.jar + │   ├── proto-google-common-protos-2.9.0.jar │   ├── protobuf-java-3.25.4.jar │   ├── protobuf-java-util-3.11.0.jar │   ├── reflectasm-1.11.9.jar │   ├── registry-client-all-6.3.0.jar │   ├── rocksdbjni-8.8.1.jar - │   ├── seata-common-2.2.0.jar - │   ├── seata-compressor-all-2.2.0.jar - │   ├── seata-compressor-bzip2-2.2.0.jar - │   ├── seata-compressor-deflater-2.2.0.jar - │   ├── seata-compressor-gzip-2.2.0.jar - │   ├── seata-compressor-lz4-2.2.0.jar - │   ├── seata-compressor-zip-2.2.0.jar - │   ├── seata-compressor-zstd-2.2.0.jar - │   ├── seata-config-all-2.2.0.jar - │   ├── seata-config-apollo-2.2.0.jar - │   ├── seata-config-consul-2.2.0.jar - │   ├── seata-config-core-2.2.0.jar - │   ├── seata-config-etcd3-2.2.0.jar - │   ├── seata-config-nacos-2.2.0.jar - │   ├── seata-config-spring-cloud-2.2.0.jar - │   ├── seata-config-zk-2.2.0.jar - │   ├── seata-console-2.2.0.jar - │   ├── seata-core-2.2.0.jar - │   ├── seata-discovery-all-2.2.0.jar - │   ├── seata-discovery-consul-2.2.0.jar - │   ├── seata-discovery-core-2.2.0.jar - │   ├── seata-discovery-custom-2.2.0.jar - │   ├── seata-discovery-etcd3-2.2.0.jar - │   ├── seata-discovery-eureka-2.2.0.jar - │   ├── seata-discovery-nacos-2.2.0.jar - │   ├── seata-discovery-namingserver-2.2.0.jar - │   ├── seata-discovery-redis-2.2.0.jar - │   ├── seata-discovery-sofa-2.2.0.jar - │   ├── seata-discovery-zk-2.2.0.jar - │   ├── seata-metrics-all-2.2.0.jar - │   ├── seata-metrics-api-2.2.0.jar - │   ├── seata-metrics-core-2.2.0.jar - │   ├── seata-metrics-exporter-prometheus-2.2.0.jar - │   ├── seata-metrics-registry-compact-2.2.0.jar - │   ├── seata-serializer-all-2.2.0.jar - │   ├── seata-serializer-hessian-2.2.0.jar - │   ├── seata-serializer-kryo-2.2.0.jar - │   ├── seata-serializer-protobuf-2.2.0.jar - │   ├── seata-serializer-seata-2.2.0.jar - │   ├── seata-spring-autoconfigure-core-2.2.0.jar - │   ├── seata-spring-autoconfigure-server-2.2.0.jar + │   ├── seata-common-2.3.0.jar + │   ├── seata-compressor-all-2.3.0.jar + │   ├── seata-compressor-bzip2-2.3.0.jar + │   ├── seata-compressor-deflater-2.3.0.jar + │   ├── seata-compressor-gzip-2.3.0.jar + │   ├── seata-compressor-lz4-2.3.0.jar + │   ├── seata-compressor-zip-2.3.0.jar + │   ├── seata-compressor-zstd-2.3.0.jar + │   ├── seata-config-all-2.3.0.jar + │   ├── seata-config-apollo-2.3.0.jar + │   ├── seata-config-consul-2.3.0.jar + │   ├── seata-config-core-2.3.0.jar + │   ├── seata-config-etcd3-2.3.0.jar + │   ├── seata-config-nacos-2.3.0.jar + │   ├── seata-config-spring-cloud-2.3.0.jar + │   ├── seata-config-zk-2.3.0.jar + │   ├── seata-console-2.3.0.jar + │   ├── seata-core-2.3.0.jar + │   ├── seata-discovery-all-2.3.0.jar + │   ├── seata-discovery-consul-2.3.0.jar + │   ├── seata-discovery-core-2.3.0.jar + │   ├── seata-discovery-custom-2.3.0.jar + │   ├── seata-discovery-etcd3-2.3.0.jar + │   ├── seata-discovery-eureka-2.3.0.jar + │   ├── seata-discovery-nacos-2.3.0.jar + │   ├── seata-discovery-namingserver-2.3.0.jar + │   ├── seata-discovery-redis-2.3.0.jar + │   ├── seata-discovery-sofa-2.3.0.jar + │   ├── seata-discovery-zk-2.3.0.jar + │   ├── seata-metrics-all-2.3.0.jar + │   ├── seata-metrics-api-2.3.0.jar + │   ├── seata-metrics-core-2.3.0.jar + │   ├── seata-metrics-exporter-prometheus-2.3.0.jar + │   ├── seata-metrics-registry-compact-2.3.0.jar + │   ├── seata-serializer-all-2.3.0.jar + │   ├── seata-serializer-hessian-2.3.0.jar + │   ├── seata-serializer-kryo-2.3.0.jar + │   ├── seata-serializer-protobuf-2.3.0.jar + │   ├── seata-serializer-seata-2.3.0.jar + │   ├── seata-spring-autoconfigure-core-2.3.0.jar + │   ├── seata-spring-autoconfigure-server-2.3.0.jar │   ├── servo-core-0.12.21.jar │   ├── simpleclient-0.15.0.jar │   ├── simpleclient_common-0.15.0.jar diff --git a/integration-tx-api/src/main/java/org/apache/seata/integration/tx/api/interceptor/ActionContextUtil.java b/integration-tx-api/src/main/java/org/apache/seata/integration/tx/api/interceptor/ActionContextUtil.java index ad6b7be469d..914ef9d7340 100644 --- a/integration-tx-api/src/main/java/org/apache/seata/integration/tx/api/interceptor/ActionContextUtil.java +++ b/integration-tx-api/src/main/java/org/apache/seata/integration/tx/api/interceptor/ActionContextUtil.java @@ -29,7 +29,9 @@ import javax.annotation.Nonnull; import javax.annotation.Nullable; +import java.lang.annotation.Annotation; import java.lang.reflect.Field; +import java.lang.reflect.Method; import java.util.Collections; import java.util.HashMap; import java.util.List; @@ -37,7 +39,6 @@ /** * Extracting TCC Context from Method - * */ public final class ActionContextUtil { @@ -97,7 +98,7 @@ public static Map fetchContextFromObject(@Nonnull Object targetP * @param actionContext the action context */ public static void loadParamByAnnotationAndPutToContext(@Nonnull final ParamType paramType, @Nonnull String paramName, Object paramValue, - @Nonnull final BusinessActionContextParameter annotation, @Nonnull final Map actionContext) { + @Nonnull final BusinessActionContextParameter annotation, @Nonnull final Map actionContext) { if (paramValue == null) { return; } @@ -131,7 +132,7 @@ public static void loadParamByAnnotationAndPutToContext(@Nonnull final ParamType public static Object getByIndex(@Nonnull ParamType paramType, @Nonnull String paramName, @Nonnull Object paramValue, int index) { if (paramValue instanceof List) { @SuppressWarnings("unchecked") - List list = (List)paramValue; + List list = (List) paramValue; if (list.isEmpty()) { return null; } @@ -145,7 +146,7 @@ public static Object getByIndex(@Nonnull ParamType paramType, @Nonnull String pa paramValue = list.get(index); } else { LOGGER.warn("the {} named '{}' is not a `List`, so the 'index' field of '@{}' cannot be used on it", - paramType.getCode(), paramName, BusinessActionContextParameter.class.getSimpleName()); + paramType.getCode(), paramName, BusinessActionContextParameter.class.getSimpleName()); } return paramValue; @@ -271,12 +272,12 @@ public static T convertActionContext(String key, @Nullable Object value, @No // Same class or super class, can cast directly if (targetClazz.isAssignableFrom(value.getClass())) { - return (T)value; + return (T) value; } // String class if (String.class.equals(targetClazz)) { - return (T)value.toString(); + return (T) value.toString(); } // JSON to Object @@ -292,4 +293,30 @@ public static T convertActionContext(String key, @Nullable Object value, @No throw new FrameworkException(e, errorMsg); } } + + public static 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/integration-tx-api/src/main/java/org/apache/seata/integration/tx/api/interceptor/InvocationHandlerType.java b/integration-tx-api/src/main/java/org/apache/seata/integration/tx/api/interceptor/InvocationHandlerType.java index 3825f45e4ce..d9ac494c4cd 100644 --- a/integration-tx-api/src/main/java/org/apache/seata/integration/tx/api/interceptor/InvocationHandlerType.java +++ b/integration-tx-api/src/main/java/org/apache/seata/integration/tx/api/interceptor/InvocationHandlerType.java @@ -29,6 +29,10 @@ public enum InvocationHandlerType { /** * TwoPhase InvocationHandler */ - TwoPhaseAnnotation + TwoPhaseAnnotation, + /** + * SagaAnnotation InvocationHandler + */ + SagaAnnotation } diff --git a/integration-tx-api/src/main/java/org/apache/seata/integration/tx/api/interceptor/parser/DefaultResourceRegisterParser.java b/integration-tx-api/src/main/java/org/apache/seata/integration/tx/api/interceptor/parser/DefaultResourceRegisterParser.java deleted file mode 100644 index deead0b6e20..00000000000 --- a/integration-tx-api/src/main/java/org/apache/seata/integration/tx/api/interceptor/parser/DefaultResourceRegisterParser.java +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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 org.apache.seata.integration.tx.api.interceptor.parser; - -import org.apache.seata.common.loader.EnhancedServiceLoader; -import org.apache.seata.common.util.CollectionUtils; - -import java.util.ArrayList; -import java.util.List; - - -public class DefaultResourceRegisterParser { - - protected static List allRegisterResourceParsers = new ArrayList<>(); - - public void registerResource(Object target, String beanName) { - for (RegisterResourceParser registerResourceParser : allRegisterResourceParsers) { - registerResourceParser.registerResource(target, beanName); - } - } - - 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); - } - } - - -} diff --git a/integration/grpc/pom.xml b/integration/grpc/pom.xml index 04665dcc956..f3b6101fc77 100644 --- a/integration/grpc/pom.xml +++ b/integration/grpc/pom.xml @@ -84,7 +84,7 @@ com.google.protobuf:protoc:3.25.4:exe:${os.detected.classifier} grpc-java - io.grpc:protoc-gen-grpc-java:1.66.0:exe:${os.detected.classifier} + io.grpc:protoc-gen-grpc-java:1.55.1:exe:${os.detected.classifier} diff --git a/test-mock-server/pom.xml b/mock-server/pom.xml similarity index 78% rename from test-mock-server/pom.xml rename to mock-server/pom.xml index 2c6eb9e4198..d23ba4e4f01 100644 --- a/test-mock-server/pom.xml +++ b/mock-server/pom.xml @@ -107,6 +107,32 @@ + + org.springframework.boot + spring-boot-starter-web + + + log4j-to-slf4j + org.apache.logging.log4j + + + org.apache.tomcat.embed + tomcat-embed-core + + + org.apache.tomcat.embed + tomcat-embed-websocket + + + org.apache.tomcat.embed + tomcat-embed-el + + + org.yaml + snakeyaml + + + org.apache.tomcat.embed tomcat-embed-core @@ -122,21 +148,20 @@ tomcat-embed-websocket ${tomcat-embed.version} - - org.apache.seata - seata-server + ${project.groupId} + seata-core + ${project.version} + + + ${project.groupId} + seata-discovery-core + ${project.version} + + + ${project.groupId} + seata-serializer-all ${project.version} - - - slf4j-log4j12 - org.slf4j - - - log4j - log4j - - diff --git a/test-mock-server/src/main/java/org/apache/seata/mockserver/MockCoordinator.java b/mock-server/src/main/java/org/apache/seata/mockserver/MockCoordinator.java similarity index 61% rename from test-mock-server/src/main/java/org/apache/seata/mockserver/MockCoordinator.java rename to mock-server/src/main/java/org/apache/seata/mockserver/MockCoordinator.java index 181e824b15e..180a15e70c5 100644 --- a/test-mock-server/src/main/java/org/apache/seata/mockserver/MockCoordinator.java +++ b/mock-server/src/main/java/org/apache/seata/mockserver/MockCoordinator.java @@ -23,6 +23,7 @@ import java.util.stream.IntStream; import org.apache.seata.common.util.CollectionUtils; +import org.apache.seata.common.util.UUIDGenerator; import org.apache.seata.core.exception.TransactionException; import org.apache.seata.core.exception.TransactionExceptionCode; import org.apache.seata.core.model.BranchStatus; @@ -30,7 +31,9 @@ import org.apache.seata.core.protocol.AbstractMessage; import org.apache.seata.core.protocol.AbstractResultMessage; import org.apache.seata.core.protocol.ResultCode; +import org.apache.seata.core.protocol.transaction.AbstractGlobalEndResponse; import org.apache.seata.core.protocol.transaction.AbstractTransactionRequestToTC; +import org.apache.seata.core.protocol.transaction.AbstractTransactionResponse; import org.apache.seata.core.protocol.transaction.BranchRegisterRequest; import org.apache.seata.core.protocol.transaction.BranchRegisterResponse; import org.apache.seata.core.protocol.transaction.BranchReportRequest; @@ -47,22 +50,21 @@ import org.apache.seata.core.protocol.transaction.GlobalRollbackResponse; import org.apache.seata.core.protocol.transaction.GlobalStatusRequest; import org.apache.seata.core.protocol.transaction.GlobalStatusResponse; +import org.apache.seata.core.protocol.transaction.TCInboundHandler; import org.apache.seata.core.rpc.Disposable; import org.apache.seata.core.rpc.RemotingServer; import org.apache.seata.core.rpc.RpcContext; import org.apache.seata.core.rpc.TransactionMessageHandler; import org.apache.seata.mockserver.call.CallRm; -import org.apache.seata.server.AbstractTCInboundHandler; -import org.apache.seata.common.util.UUIDGenerator; -import org.apache.seata.server.session.BranchSession; -import org.apache.seata.server.session.GlobalSession; +import org.apache.seata.mockserver.model.MockBranchSession; +import org.apache.seata.mockserver.model.MockGlobalSession; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Mock Coordinator **/ -public class MockCoordinator extends AbstractTCInboundHandler implements TransactionMessageHandler, Disposable { +public class MockCoordinator implements TCInboundHandler, TransactionMessageHandler, Disposable { protected static final Logger LOGGER = LoggerFactory.getLogger(MockCoordinator.class); @@ -75,12 +77,11 @@ public class MockCoordinator extends AbstractTCInboundHandler implements Transac private Map globalStatusMap; private Map expectedResultMap; private Map expectRetryTimesMap; - private Map> branchMap; + private Map> branchMap; private MockCoordinator() { } - public static MockCoordinator getInstance() { if (coordinator == null) { synchronized (MockCoordinator.class) { @@ -96,7 +97,6 @@ public static MockCoordinator getInstance() { return coordinator; } - @Override public void destroy() { @@ -107,7 +107,7 @@ public AbstractResultMessage onRequest(AbstractMessage request, RpcContext conte if (!(request instanceof AbstractTransactionRequestToTC)) { throw new IllegalArgumentException(); } - AbstractTransactionRequestToTC transactionRequest = (AbstractTransactionRequestToTC) request; + AbstractTransactionRequestToTC transactionRequest = (AbstractTransactionRequestToTC)request; transactionRequest.setTCInboundHandler(this); return transactionRequest.handle(context); @@ -118,68 +118,121 @@ public void onResponse(AbstractResultMessage response, RpcContext context) { response.setResultCode(ResultCode.Success); } + public void setRemotingServer(RemotingServer remotingServer) { + this.remotingServer = remotingServer; + } + + public void setExpectedResult(String xid, ResultCode expected) { + expectedResultMap.put(xid, expected); + } + + public void setExpectedRetry(String xid, int times) { + expectRetryTimesMap.put(xid, times); + } + + private void checkMockActionFail(String xid) throws TransactionException { + if (ResultCode.Failed == expectedResultMap.get(xid)) { + throw new TransactionException(TransactionExceptionCode.Broken, "mock action expect fail"); + } + } + + private T handleException(TransactionException e, T response, + ResultCode resultCode, String messagePrefix, + GlobalStatus... globalStatus) { + response.setTransactionExceptionCode(e.getCode()); + response.setResultCode(resultCode); + response.setMsg(messagePrefix + "[" + e.getMessage() + "]"); + if (response instanceof AbstractGlobalEndResponse) { + if (globalStatus != null && globalStatus.length > 0) { + ((AbstractGlobalEndResponse)response).setGlobalStatus(globalStatus[0]); + } + } + return response; + } + @Override - protected void doGlobalBegin(GlobalBeginRequest request, GlobalBeginResponse response, RpcContext rpcContext) throws TransactionException { - checkMockActionFail(AllBeginFailXid); - GlobalSession session = GlobalSession.createGlobalSession(rpcContext.getApplicationId(), - rpcContext.getTransactionServiceGroup(), request.getTransactionName(), request.getTimeout()); + public GlobalBeginResponse handle(GlobalBeginRequest request, RpcContext rpcContext) { + GlobalBeginResponse response = new GlobalBeginResponse(); + try { + checkMockActionFail(AllBeginFailXid); + } catch (TransactionException e) { + return handleException(e, response, ResultCode.Failed, "MockBeginException"); + } + MockGlobalSession session = new MockGlobalSession(rpcContext.getApplicationId(), + rpcContext.getTransactionServiceGroup(), request.getTransactionName(), request.getTimeout()); globalStatusMap.putIfAbsent(session.getXid(), GlobalStatus.Begin); response.setXid(session.getXid()); response.setResultCode(ResultCode.Success); + return response; } - @Override - protected void doGlobalCommit(GlobalCommitRequest request, GlobalCommitResponse response, RpcContext rpcContext) throws TransactionException { - checkMockActionFail(request.getXid()); + public GlobalCommitResponse handle(GlobalCommitRequest request, RpcContext rpcContext) { + GlobalCommitResponse response = new GlobalCommitResponse(); + try { + checkMockActionFail(request.getXid()); + } catch (TransactionException e) { + return handleException(e, response, ResultCode.Failed, "MockCommitException", GlobalStatus.CommitFailed); + } response.setGlobalStatus(GlobalStatus.Committed); response.setResultCode(ResultCode.Success); globalStatusMap.put(request.getXid(), GlobalStatus.Committed); int retry = expectRetryTimesMap.getOrDefault(request.getXid(), 0); - List branchSessions = branchMap.get(request.getXid()); + List branchSessions = branchMap.get(request.getXid()); if (CollectionUtils.isEmpty(branchSessions)) { LOGGER.warn("[doGlobalCommit]branchSessions is empty,XID=" + request.getXid()); - return; + return response; } branchSessions.forEach(branch -> { CallRm.branchCommit(remotingServer, branch); - IntStream.range(0, retry).forEach(i -> - CallRm.branchCommit(remotingServer, branch)); + IntStream.range(0, retry).forEach(i -> CallRm.branchCommit(remotingServer, branch)); }); branchMap.remove(request.getXid()); globalStatusMap.remove(request.getXid()); + return response; } @Override - protected void doGlobalRollback(GlobalRollbackRequest request, GlobalRollbackResponse response, RpcContext rpcContext) throws TransactionException { - checkMockActionFail(request.getXid()); + public GlobalRollbackResponse handle(GlobalRollbackRequest request, RpcContext rpcContext) { + GlobalRollbackResponse response = new GlobalRollbackResponse(); + try { + checkMockActionFail(request.getXid()); + } catch (TransactionException e) { + return handleException(e, response, ResultCode.Failed, "MockRollbackException", + GlobalStatus.RollbackFailed); + } response.setGlobalStatus(GlobalStatus.Rollbacked); response.setResultCode(ResultCode.Success); globalStatusMap.put(request.getXid(), GlobalStatus.Rollbacked); int retry = expectRetryTimesMap.getOrDefault(request.getXid(), 0); - List branchSessions = branchMap.get(request.getXid()); + List branchSessions = branchMap.get(request.getXid()); if (CollectionUtils.isEmpty(branchSessions)) { LOGGER.warn("[doGlobalRollback]branchSessions is empty,XID=" + request.getXid()); - return; + return response; } branchSessions.forEach(branch -> { CallRm.branchRollback(remotingServer, branch); - IntStream.range(0, retry).forEach(i -> - CallRm.branchRollback(remotingServer, branch)); + IntStream.range(0, retry).forEach(i -> CallRm.branchRollback(remotingServer, branch)); }); branchMap.remove(request.getXid()); globalStatusMap.remove(request.getXid()); + return response; } @Override - protected void doBranchRegister(BranchRegisterRequest request, BranchRegisterResponse response, RpcContext rpcContext) throws TransactionException { - checkMockActionFail(request.getXid()); - BranchSession branchSession = new BranchSession(request.getBranchType()); + public BranchRegisterResponse handle(BranchRegisterRequest request, RpcContext rpcContext) { + BranchRegisterResponse response = new BranchRegisterResponse(); + try { + checkMockActionFail(request.getXid()); + } catch (TransactionException e) { + return handleException(e, response, ResultCode.Failed, "MockBranchRegisterException"); + } + MockBranchSession branchSession = new MockBranchSession(request.getBranchType()); String xid = request.getXid(); branchSession.setXid(xid); -// branchSession.setTransactionId(request.getTransactionId()); + // branchSession.setTransactionId(request.getTransactionId()); branchSession.setBranchId(UUIDGenerator.generateUUID()); branchSession.setResourceId(request.getResourceId()); branchSession.setLockKey(request.getLockKey()); @@ -196,11 +249,17 @@ protected void doBranchRegister(BranchRegisterRequest request, BranchRegisterRes response.setBranchId(branchSession.getBranchId()); response.setResultCode(ResultCode.Success); + return response; } @Override - protected void doBranchReport(BranchReportRequest request, BranchReportResponse response, RpcContext rpcContext) throws TransactionException { - checkMockActionFail(request.getXid()); + public BranchReportResponse handle(BranchReportRequest request, RpcContext rpcContext) { + BranchReportResponse response = new BranchReportResponse(); + try { + checkMockActionFail(request.getXid()); + } catch (TransactionException e) { + return handleException(e, response, ResultCode.Failed, "MockBranchReportException"); + } String xid = request.getXid(); branchMap.compute(xid, (key, val) -> { if (val != null) { @@ -211,51 +270,51 @@ protected void doBranchReport(BranchReportRequest request, BranchReportResponse return val; }); response.setResultCode(ResultCode.Success); + return response; } @Override - protected void doLockCheck(GlobalLockQueryRequest request, GlobalLockQueryResponse response, RpcContext rpcContext) throws TransactionException { - checkMockActionFail(request.getXid()); + public GlobalLockQueryResponse handle(GlobalLockQueryRequest request, RpcContext rpcContext) { + GlobalLockQueryResponse response = new GlobalLockQueryResponse(); + try { + checkMockActionFail(request.getXid()); + } catch (TransactionException e) { + return handleException(e, response, ResultCode.Failed, "MockLockQueryException"); + } response.setLockable(true); response.setResultCode(ResultCode.Success); + return response; } @Override - protected void doGlobalStatus(GlobalStatusRequest request, GlobalStatusResponse response, RpcContext rpcContext) throws TransactionException { - checkMockActionFail(request.getXid()); + public GlobalStatusResponse handle(GlobalStatusRequest request, RpcContext rpcContext) { + GlobalStatusResponse response = new GlobalStatusResponse(); + try { + checkMockActionFail(request.getXid()); + } catch (TransactionException e) { + return handleException(e, response, ResultCode.Failed, "MockStatusException", GlobalStatus.Finished); + } GlobalStatus globalStatus = globalStatusMap.get(request.getXid()); if (globalStatus == null) { globalStatus = GlobalStatus.Finished; } response.setGlobalStatus(globalStatus); response.setResultCode(ResultCode.Success); + return response; } @Override - protected void doGlobalReport(GlobalReportRequest request, GlobalReportResponse response, RpcContext rpcContext) throws TransactionException { - checkMockActionFail(request.getXid()); + public GlobalReportResponse handle(GlobalReportRequest request, RpcContext rpcContext) { + GlobalReportResponse response = new GlobalReportResponse(); + try { + checkMockActionFail(request.getXid()); + } catch (TransactionException e) { + return handleException(e, response, ResultCode.Failed, "MockReportException", GlobalStatus.Finished); + } GlobalStatus globalStatus = request.getGlobalStatus(); globalStatusMap.put(request.getXid(), globalStatus); response.setGlobalStatus(globalStatus); response.setResultCode(ResultCode.Success); - } - - public void setRemotingServer(RemotingServer remotingServer) { - this.remotingServer = remotingServer; - } - - - public void setExpectedResult(String xid, ResultCode expected) { - expectedResultMap.put(xid, expected); - } - - public void setExpectedRetry(String xid, int times) { - expectRetryTimesMap.put(xid, times); - } - - private void checkMockActionFail(String xid) throws TransactionException { - if (ResultCode.Failed == expectedResultMap.get(xid)) { - throw new TransactionException(TransactionExceptionCode.Broken, "mock action expect fail"); - } + return response; } } diff --git a/test-mock-server/src/main/java/org/apache/seata/mockserver/MockNettyRemotingServer.java b/mock-server/src/main/java/org/apache/seata/mockserver/MockNettyRemotingServer.java similarity index 90% rename from test-mock-server/src/main/java/org/apache/seata/mockserver/MockNettyRemotingServer.java rename to mock-server/src/main/java/org/apache/seata/mockserver/MockNettyRemotingServer.java index 3a871cb93e0..1ecc5bbe2d8 100644 --- a/test-mock-server/src/main/java/org/apache/seata/mockserver/MockNettyRemotingServer.java +++ b/mock-server/src/main/java/org/apache/seata/mockserver/MockNettyRemotingServer.java @@ -39,6 +39,11 @@ public class MockNettyRemotingServer extends AbstractNettyRemotingServer { private TransactionMessageHandler handler; + /** + * Sets handler. + * + * @param transactionMessageHandler the transaction message handler + */ public void setHandler(TransactionMessageHandler transactionMessageHandler) { this.handler = transactionMessageHandler; } @@ -56,7 +61,17 @@ public void init() { * @param messageExecutor the message executor */ public MockNettyRemotingServer(ThreadPoolExecutor messageExecutor) { - super(messageExecutor, new NettyServerConfig()); + this(messageExecutor, new NettyServerConfig()); + } + + /** + * Instantiates a new Mock netty remoting server. + * + * @param messageExecutor the message executor + * @param nettyServerConfig the netty server config + */ + public MockNettyRemotingServer(ThreadPoolExecutor messageExecutor, NettyServerConfig nettyServerConfig) { + super(messageExecutor, nettyServerConfig); } @Override diff --git a/test-mock-server/src/main/java/org/apache/seata/mockserver/MockServer.java b/mock-server/src/main/java/org/apache/seata/mockserver/MockServer.java similarity index 85% rename from test-mock-server/src/main/java/org/apache/seata/mockserver/MockServer.java rename to mock-server/src/main/java/org/apache/seata/mockserver/MockServer.java index 535d6d3dd06..b04f1c7b6ed 100644 --- a/test-mock-server/src/main/java/org/apache/seata/mockserver/MockServer.java +++ b/mock-server/src/main/java/org/apache/seata/mockserver/MockServer.java @@ -22,15 +22,20 @@ import java.util.concurrent.TimeUnit; import org.apache.seata.common.XID; +import org.apache.seata.common.metadata.Instance; +import org.apache.seata.common.metadata.Node; import org.apache.seata.common.thread.NamedThreadFactory; import org.apache.seata.common.util.NetUtil; -import org.apache.seata.server.ParameterParser; +import org.apache.seata.common.util.NumberUtils; import org.apache.seata.common.util.UUIDGenerator; +import org.apache.seata.core.rpc.netty.NettyServerConfig; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; +import static org.apache.seata.common.ConfigurationKeys.ENV_SEATA_PORT_KEY; + /** * The type Mock Server. */ @@ -54,8 +59,7 @@ public class MockServer { public static void main(String[] args) { SpringApplication.run(MockServer.class, args); - ParameterParser parameterParser = new ParameterParser(args); - int port = parameterParser.getPort() > 0 ? parameterParser.getPort() : DEFAULT_PORT; + int port = NumberUtils.toInt(System.getenv(ENV_SEATA_PORT_KEY), DEFAULT_PORT); start(port); } @@ -68,12 +72,15 @@ public static void start(int port) { 50, 500, TimeUnit.SECONDS, new LinkedBlockingQueue<>(20000), new NamedThreadFactory("ServerHandlerThread", 500), new ThreadPoolExecutor.CallerRunsPolicy()); - nettyRemotingServer = new MockNettyRemotingServer(workingThreads); + NettyServerConfig config = new NettyServerConfig(); + config.setServerListenPort(port); + nettyRemotingServer = new MockNettyRemotingServer(workingThreads, config); // set registry XID.setIpAddress(NetUtil.getLocalIp()); XID.setPort(port); // init snowflake for transactionId, branchId + Instance.getInstance().setTransaction(new Node.Endpoint(XID.getIpAddress(),XID.getPort(),"netty")); UUIDGenerator.init(1L); MockCoordinator coordinator = MockCoordinator.getInstance(); diff --git a/test-mock-server/src/main/java/org/apache/seata/mockserver/call/CallRm.java b/mock-server/src/main/java/org/apache/seata/mockserver/call/CallRm.java similarity index 85% rename from test-mock-server/src/main/java/org/apache/seata/mockserver/call/CallRm.java rename to mock-server/src/main/java/org/apache/seata/mockserver/call/CallRm.java index ffee3c7f969..8a347246f33 100644 --- a/test-mock-server/src/main/java/org/apache/seata/mockserver/call/CallRm.java +++ b/mock-server/src/main/java/org/apache/seata/mockserver/call/CallRm.java @@ -29,7 +29,7 @@ import org.apache.seata.core.protocol.transaction.UndoLogDeleteRequest; import org.apache.seata.core.rpc.RemotingServer; import org.apache.seata.core.rpc.netty.ChannelManager; -import org.apache.seata.server.session.BranchSession; +import org.apache.seata.mockserver.model.MockBranchSession; /** * call rm @@ -42,32 +42,31 @@ public class CallRm { * @param remotingServer * @return */ - public static BranchStatus branchCommit(RemotingServer remotingServer, BranchSession branchSession) { + public static BranchStatus branchCommit(RemotingServer remotingServer, MockBranchSession branchSession) { BranchCommitRequest request = new BranchCommitRequest(); setReq(request, branchSession); try { - BranchCommitResponse response = (BranchCommitResponse) remotingServer.sendSyncRequest( - branchSession.getResourceId(), branchSession.getClientId(), request, false); + BranchCommitResponse response = (BranchCommitResponse)remotingServer.sendSyncRequest( + branchSession.getResourceId(), branchSession.getClientId(), request, false); return response.getBranchStatus(); } catch (TimeoutException e) { throw new RuntimeException(e); } } - /** * call branchRollback :TYPE_BRANCH_ROLLBACK = 5 , TYPE_BRANCH_ROLLBACK_RESULT = 6 * * @param remotingServer * @return */ - public static BranchStatus branchRollback(RemotingServer remotingServer, BranchSession branchSession) { + public static BranchStatus branchRollback(RemotingServer remotingServer, MockBranchSession branchSession) { BranchRollbackRequest request = new BranchRollbackRequest(); setReq(request, branchSession); try { - BranchRollbackResponse response = (BranchRollbackResponse) remotingServer.sendSyncRequest( - branchSession.getResourceId(), branchSession.getClientId(), request, false); + BranchRollbackResponse response = (BranchRollbackResponse)remotingServer.sendSyncRequest( + branchSession.getResourceId(), branchSession.getClientId(), request, false); return response.getBranchStatus(); } catch (TimeoutException e) { throw new RuntimeException(e); @@ -80,20 +79,21 @@ public static BranchStatus branchRollback(RemotingServer remotingServer, Branch * @param remotingServer * @return */ - public static void deleteUndoLog(RemotingServer remotingServer, BranchSession branchSession) { + public static void deleteUndoLog(RemotingServer remotingServer, MockBranchSession branchSession) { UndoLogDeleteRequest request = new UndoLogDeleteRequest(); request.setResourceId(branchSession.getResourceId()); - request.setSaveDays((short) 1); + request.setSaveDays((short)1); request.setBranchType(BranchType.TCC); try { - Channel channel = ChannelManager.getChannel(branchSession.getResourceId(), branchSession.getClientId(), false); + Channel channel = ChannelManager.getChannel(branchSession.getResourceId(), branchSession.getClientId(), + false); remotingServer.sendAsyncRequest(channel, request); } catch (Exception e) { throw new RuntimeException(e); } } - private static void setReq(AbstractBranchEndRequest request, BranchSession branchSession) { + private static void setReq(AbstractBranchEndRequest request, MockBranchSession branchSession) { request.setXid(branchSession.getXid()); request.setBranchId(branchSession.getBranchId()); request.setResourceId(branchSession.getResourceId()); diff --git a/test-mock-server/src/main/java/org/apache/seata/mockserver/controller/MockHelpController.java b/mock-server/src/main/java/org/apache/seata/mockserver/controller/MockHelpController.java similarity index 100% rename from test-mock-server/src/main/java/org/apache/seata/mockserver/controller/MockHelpController.java rename to mock-server/src/main/java/org/apache/seata/mockserver/controller/MockHelpController.java diff --git a/mock-server/src/main/java/org/apache/seata/mockserver/model/MockBranchSession.java b/mock-server/src/main/java/org/apache/seata/mockserver/model/MockBranchSession.java new file mode 100644 index 00000000000..0b5fcf1e828 --- /dev/null +++ b/mock-server/src/main/java/org/apache/seata/mockserver/model/MockBranchSession.java @@ -0,0 +1,234 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.seata.mockserver.model; + +import org.apache.seata.core.model.BranchStatus; +import org.apache.seata.core.model.BranchType; + +/** + * The type Mock branch session. + */ +public class MockBranchSession { + + private String xid; + + private long transactionId; + + private long branchId; + + private String resourceGroupId; + private String resourceId; + + private String lockKey; + + private BranchType branchType; + + private BranchStatus status = BranchStatus.Unknown; + + private String clientId; + + private String applicationData; + + /** + * Gets resource id. + * + * @return the resource id + */ + public String getResourceId() { + return resourceId; + } + + /** + * Sets resource id. + * + * @param resourceId the resource id + */ + public void setResourceId(String resourceId) { + this.resourceId = resourceId; + } + + /** + * Gets lock key. + * + * @return the lock key + */ + public String getLockKey() { + return lockKey; + } + + /** + * Sets lock key. + * + * @param lockKey the lock key + */ + public void setLockKey(String lockKey) { + this.lockKey = lockKey; + } + + /** + * Gets branch type. + * + * @return the branch type + */ + public BranchType getBranchType() { + return branchType; + } + + /** + * Sets branch type. + * + * @param branchType the branch type + */ + public void setBranchType(BranchType branchType) { + this.branchType = branchType; + } + + /** + * Gets status. + * + * @return the status + */ + public BranchStatus getStatus() { + return status; + } + + /** + * Sets status. + * + * @param status the status + */ + public void setStatus(BranchStatus status) { + this.status = status; + } + + /** + * Gets client id. + * + * @return the client id + */ + public String getClientId() { + return clientId; + } + + /** + * Sets client id. + * + * @param clientId the client id + */ + public void setClientId(String clientId) { + this.clientId = clientId; + } + + /** + * Gets application data. + * + * @return the application data + */ + public String getApplicationData() { + return applicationData; + } + + /** + * Sets application data. + * + * @param applicationData the application data + */ + public void setApplicationData(String applicationData) { + this.applicationData = applicationData; + } + + /** + * Gets xid. + * + * @return the xid + */ + public String getXid() { + return xid; + } + + /** + * Sets xid. + * + * @param xid the xid + */ + public void setXid(String xid) { + this.xid = xid; + } + + /** + * Gets transaction id. + * + * @return the transaction id + */ + public long getTransactionId() { + return transactionId; + } + + /** + * Sets transaction id. + * + * @param transactionId the transaction id + */ + public void setTransactionId(long transactionId) { + this.transactionId = transactionId; + } + + /** + * Gets branch id. + * + * @return the branch id + */ + public long getBranchId() { + return branchId; + } + + /** + * Sets branch id. + * + * @param branchId the branch id + */ + public void setBranchId(long branchId) { + this.branchId = branchId; + } + + /** + * Gets resource group id. + * + * @return the resource group id + */ + public String getResourceGroupId() { + return resourceGroupId; + } + + /** + * Sets resource group id. + * + * @param resourceGroupId the resource group id + */ + public void setResourceGroupId(String resourceGroupId) { + this.resourceGroupId = resourceGroupId; + } + + /** + * Instantiates a new Mock branch session. + * + * @param branchType the branch type + */ + public MockBranchSession(BranchType branchType) { + this.branchType = branchType; + } +} diff --git a/mock-server/src/main/java/org/apache/seata/mockserver/model/MockGlobalSession.java b/mock-server/src/main/java/org/apache/seata/mockserver/model/MockGlobalSession.java new file mode 100644 index 00000000000..1dd4e8f2121 --- /dev/null +++ b/mock-server/src/main/java/org/apache/seata/mockserver/model/MockGlobalSession.java @@ -0,0 +1,266 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.seata.mockserver.model; + +import java.util.List; + +import org.apache.seata.common.XID; +import org.apache.seata.common.util.UUIDGenerator; +import org.apache.seata.core.model.GlobalStatus; + +/** + * The type Mock global session. + */ +public class MockGlobalSession { + private String xid; + + private long transactionId; + + private volatile GlobalStatus status; + + private String applicationId; + + private String transactionServiceGroup; + + private String transactionName; + + private int timeout; + + private long beginTime; + + private String applicationData; + + private volatile boolean active = true; + + private List branchSessions; + + /** + * Instantiates a new Mock global session. + * + * @param applicationId the application id + * @param transactionServiceGroup the transaction service group + * @param transactionName the transaction name + * @param timeout the timeout + */ + public MockGlobalSession(String applicationId, String transactionServiceGroup, String transactionName, int timeout) { + this.transactionId = UUIDGenerator.generateUUID(); + this.status = GlobalStatus.Begin; + this.applicationId = applicationId; + this.transactionServiceGroup = transactionServiceGroup; + this.transactionName = transactionName; + this.timeout = timeout; + this.xid = XID.generateXID(transactionId); + } + + /** + * Gets xid. + * + * @return the xid + */ + public String getXid() { + return xid; + } + + /** + * Sets xid. + * + * @param xid the xid + */ + public void setXid(String xid) { + this.xid = xid; + } + + /** + * Gets transaction id. + * + * @return the transaction id + */ + public long getTransactionId() { + return transactionId; + } + + /** + * Sets transaction id. + * + * @param transactionId the transaction id + */ + public void setTransactionId(long transactionId) { + this.transactionId = transactionId; + } + + /** + * Gets status. + * + * @return the status + */ + public GlobalStatus getStatus() { + return status; + } + + /** + * Sets status. + * + * @param status the status + */ + public void setStatus(GlobalStatus status) { + this.status = status; + } + + /** + * Gets application id. + * + * @return the application id + */ + public String getApplicationId() { + return applicationId; + } + + /** + * Sets application id. + * + * @param applicationId the application id + */ + public void setApplicationId(String applicationId) { + this.applicationId = applicationId; + } + + /** + * Gets transaction service group. + * + * @return the transaction service group + */ + public String getTransactionServiceGroup() { + return transactionServiceGroup; + } + + /** + * Sets transaction service group. + * + * @param transactionServiceGroup the transaction service group + */ + public void setTransactionServiceGroup(String transactionServiceGroup) { + this.transactionServiceGroup = transactionServiceGroup; + } + + /** + * Gets transaction name. + * + * @return the transaction name + */ + public String getTransactionName() { + return transactionName; + } + + /** + * Sets transaction name. + * + * @param transactionName the transaction name + */ + public void setTransactionName(String transactionName) { + this.transactionName = transactionName; + } + + /** + * Gets timeout. + * + * @return the timeout + */ + public int getTimeout() { + return timeout; + } + + /** + * Sets timeout. + * + * @param timeout the timeout + */ + public void setTimeout(int timeout) { + this.timeout = timeout; + } + + /** + * Gets begin time. + * + * @return the begin time + */ + public long getBeginTime() { + return beginTime; + } + + /** + * Sets begin time. + * + * @param beginTime the begin time + */ + public void setBeginTime(long beginTime) { + this.beginTime = beginTime; + } + + /** + * Gets application data. + * + * @return the application data + */ + public String getApplicationData() { + return applicationData; + } + + /** + * Sets application data. + * + * @param applicationData the application data + */ + public void setApplicationData(String applicationData) { + this.applicationData = applicationData; + } + + /** + * Is active boolean. + * + * @return the boolean + */ + public boolean isActive() { + return active; + } + + /** + * Sets active. + * + * @param active the active + */ + public void setActive(boolean active) { + this.active = active; + } + + /** + * Gets branch sessions. + * + * @return the branch sessions + */ + public List getBranchSessions() { + return branchSessions; + } + + /** + * Sets branch sessions. + * + * @param branchSessions the branch sessions + */ + public void setBranchSessions(List branchSessions) { + this.branchSessions = branchSessions; + } +} diff --git a/test-mock-server/src/main/java/org/apache/seata/mockserver/processor/MockHeartbeatProcessor.java b/mock-server/src/main/java/org/apache/seata/mockserver/processor/MockHeartbeatProcessor.java similarity index 100% rename from test-mock-server/src/main/java/org/apache/seata/mockserver/processor/MockHeartbeatProcessor.java rename to mock-server/src/main/java/org/apache/seata/mockserver/processor/MockHeartbeatProcessor.java diff --git a/test-mock-server/src/main/java/org/apache/seata/mockserver/processor/MockOnReqProcessor.java b/mock-server/src/main/java/org/apache/seata/mockserver/processor/MockOnReqProcessor.java similarity index 100% rename from test-mock-server/src/main/java/org/apache/seata/mockserver/processor/MockOnReqProcessor.java rename to mock-server/src/main/java/org/apache/seata/mockserver/processor/MockOnReqProcessor.java diff --git a/test-mock-server/src/main/java/org/apache/seata/mockserver/processor/MockOnRespProcessor.java b/mock-server/src/main/java/org/apache/seata/mockserver/processor/MockOnRespProcessor.java similarity index 100% rename from test-mock-server/src/main/java/org/apache/seata/mockserver/processor/MockOnRespProcessor.java rename to mock-server/src/main/java/org/apache/seata/mockserver/processor/MockOnRespProcessor.java diff --git a/test-mock-server/src/main/java/org/apache/seata/mockserver/processor/MockRegisterProcessor.java b/mock-server/src/main/java/org/apache/seata/mockserver/processor/MockRegisterProcessor.java similarity index 100% rename from test-mock-server/src/main/java/org/apache/seata/mockserver/processor/MockRegisterProcessor.java rename to mock-server/src/main/java/org/apache/seata/mockserver/processor/MockRegisterProcessor.java diff --git a/test-mock-server/src/main/java/org/apache/seata/mockserver/processor/MockRemotingProcessor.java b/mock-server/src/main/java/org/apache/seata/mockserver/processor/MockRemotingProcessor.java similarity index 99% rename from test-mock-server/src/main/java/org/apache/seata/mockserver/processor/MockRemotingProcessor.java rename to mock-server/src/main/java/org/apache/seata/mockserver/processor/MockRemotingProcessor.java index c24bf11b46e..00920bd555d 100644 --- a/test-mock-server/src/main/java/org/apache/seata/mockserver/processor/MockRemotingProcessor.java +++ b/mock-server/src/main/java/org/apache/seata/mockserver/processor/MockRemotingProcessor.java @@ -43,7 +43,6 @@ public MockRemotingProcessor(RemotingServer remotingServer, TransactionMessageHa public void process(ChannelHandlerContext ctx, RpcMessage rpcMessage) throws Exception { Object message = rpcMessage.getBody(); LOGGER.info("process message : " + message); - } diff --git a/namingserver/pom.xml b/namingserver/pom.xml index fba22f8f9a1..a34b0027b96 100644 --- a/namingserver/pom.xml +++ b/namingserver/pom.xml @@ -195,6 +195,85 @@ + + + com.google.cloud.tools + jib-maven-plugin + ${jib-maven-plugin.version} + + + ${image.name} + + + linux + amd64 + + + linux + arm64 + + + + + docker.io/apache/seata-naming-server + ${image.tags} + + ${REGISTRY_USERNAME} + ${REGISTRY_PASSWORD} + + + + /seata-naming-server + /seata-naming-server + + 8080 + + + seata-naming-server + ${git.commit.message.full} + ${git.remote.origin.url} + ${git.commit.id} + ${git.commit.time} + ${git.branch} + ${git.build.time} + ${git.build.version} + ${git.dirty} + ${project.version} + + USE_CURRENT_TIMESTAMP + + /bin/bash + /seata-naming-server-entrypoint.sh + + + Asia/Shanghai + + + + + + src/main/resources/docker + seata-naming-server-entrypoint.sh + + + ../distribution/bin + seata-namingserver-setup.sh + + + + ${image.publish.skip} + true + + + + package + + build + + + + + diff --git a/namingserver/src/main/resources/banner.txt b/namingserver/src/main/resources/banner.txt new file mode 100644 index 00000000000..2fe3d99326f --- /dev/null +++ b/namingserver/src/main/resources/banner.txt @@ -0,0 +1,8 @@ + _ + (_) + _ __ __ _ _ __ ___ _ _ __ __ _ ___ ___ _ ____ _____ _ __ + | '_ \ / _` | '_ ` _ \| | '_ \ / _` / __|/ _ \ '__\ \ / / _ \ '__| + | | | | (_| | | | | | | | | | | (_| \__ \ __/ | \ V / __/ | + |_| |_|\__,_|_| |_| |_|_|_| |_|\__, |___/\___|_| \_/ \___|_| + __/ | + |___/ \ No newline at end of file diff --git a/namingserver/src/main/resources/docker/seata-naming-server-entrypoint.sh b/namingserver/src/main/resources/docker/seata-naming-server-entrypoint.sh new file mode 100644 index 00000000000..f32ae4c3389 --- /dev/null +++ b/namingserver/src/main/resources/docker/seata-naming-server-entrypoint.sh @@ -0,0 +1,26 @@ +#!/bin/bash +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You 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. +# + +# entrypoint for namingserver + +. /seata-namingserver-setup.sh +JAVA_OPT=${JAVA_OPT//"//"/"/"} +echo "Affected JVM parameters:$JAVA_OPT" +exec java $JAVA_OPT \ + -cp $( cat /seata-naming-server/jib-classpath-file ) \ + $( cat /seata-naming-server/jib-main-class-file ) diff --git a/pom.xml b/pom.xml index 958e9d497b1..b905728f7b5 100644 --- a/pom.xml +++ b/pom.xml @@ -61,7 +61,7 @@ spring tcc test - test-mock-server + mock-server test-old-version tm metrics @@ -181,6 +181,7 @@ false false + false 5.1.42 8.0.27 false diff --git a/rm-datasource/pom.xml b/rm-datasource/pom.xml index af2c0d09f59..8dc7afd62d8 100644 --- a/rm-datasource/pom.xml +++ b/rm-datasource/pom.xml @@ -143,5 +143,11 @@ DmJdbcDriver18 test + + org.apache.fury + fury-core + provided + true + diff --git a/rm-datasource/src/main/java/org/apache/seata/rm/datasource/SqlGenerateUtils.java b/rm-datasource/src/main/java/org/apache/seata/rm/datasource/SqlGenerateUtils.java index 90d61226f30..04b7b5b89c8 100644 --- a/rm-datasource/src/main/java/org/apache/seata/rm/datasource/SqlGenerateUtils.java +++ b/rm-datasource/src/main/java/org/apache/seata/rm/datasource/SqlGenerateUtils.java @@ -18,8 +18,10 @@ import java.sql.PreparedStatement; import java.sql.SQLException; +import java.util.ArrayList; import java.util.List; import java.util.Map; +import java.util.StringJoiner; import org.apache.seata.rm.datasource.sql.struct.Field; import org.apache.seata.sqlparser.util.ColumnUtils; @@ -36,32 +38,51 @@ private SqlGenerateUtils() { } - public static String buildWhereConditionByPKs(List pkNameList, int rowSize, String dbType) - throws SQLException { - return buildWhereConditionByPKs(pkNameList, rowSize, dbType, MAX_IN_SIZE); - + /** + * build full sql by pks. + * @param sqlPrefix sql prefix + * @param suffix sql suffix + * @param pkNameList pk column name list + * @param rowSize the row size of records + * @param dbType the type of database + * @return full sql + */ + public static String buildSQLByPKs(String sqlPrefix, String suffix, List pkNameList, int rowSize, String dbType) { + List whereList = buildWhereConditionListByPKs(pkNameList, rowSize, dbType, MAX_IN_SIZE); + StringJoiner sqlJoiner = new StringJoiner(" UNION "); + whereList.forEach(whereSql -> sqlJoiner.add(sqlPrefix + " " + whereSql.getSql() + " " + suffix)); + return sqlJoiner.toString(); } /** - * each pk is a condition.the result will like :" (id,userCode) in ((?,?),(?,?)) or (id,userCode) in ((?,?),(?,?) - * ) or (id,userCode) in ((?,?))" + * each pk is a condition.the result will like :" [(id,userCode) in ((?,?),(?,?)), (id,userCode) in ((?,?),(?,?) + * ), (id,userCode) in ((?,?))]" + * Build where condition by pks string. size default MAX_IN_SIZE + * + * @param pkNameList pk column name list + * @param rowSize the row size of records + * @param dbType the type of database + * @return return where condition sql list.the sql can search all related records not just one. + */ + public static List buildWhereConditionListByPKs(List pkNameList, int rowSize, String dbType) { + return buildWhereConditionListByPKs(pkNameList, rowSize, dbType, MAX_IN_SIZE); + } + /** + * each pk is a condition.the result will like :" [(id,userCode) in ((?,?),(?,?)), (id,userCode) in ((?,?),(?,?) + * ), (id,userCode) in ((?,?))]" * Build where condition by pks string. * * @param pkNameList pk column name list * @param rowSize the row size of records * @param dbType the type of database * @param maxInSize the max in size - * @return return where condition sql string.the sql can search all related records not just one. - * @throws SQLException the sql exception + * @return return where condition sql list.the sql can search all related records not just one. */ - public static String buildWhereConditionByPKs(List pkNameList, int rowSize, String dbType, int maxInSize) - throws SQLException { - StringBuilder whereStr = new StringBuilder(); + public static List buildWhereConditionListByPKs(List pkNameList, int rowSize, String dbType, int maxInSize) { + List whereSqls = new ArrayList<>(); //we must consider the situation of composite primary key int batchSize = rowSize % maxInSize == 0 ? rowSize / maxInSize : (rowSize / maxInSize) + 1; for (int batch = 0; batch < batchSize; batch++) { - if (batch > 0) { - whereStr.append(" or "); - } + StringBuilder whereStr = new StringBuilder(); whereStr.append("("); for (int i = 0; i < pkNameList.size(); i++) { if (i > 0) { @@ -88,9 +109,10 @@ public static String buildWhereConditionByPKs(List pkNameList, int rowSi whereStr.append(")"); } whereStr.append(" )"); + whereSqls.add(new WhereSql(whereStr.toString(), eachSize, pkNameList.size())); } - return whereStr.toString(); + return whereSqls; } /** @@ -135,4 +157,38 @@ public static String buildWhereConditionByPKs(List pkNameList, String db return whereStr.toString(); } + public static class WhereSql { + /** + * sql + */ + private final String sql; + + /** + * row size + */ + private final int rowSize; + + /** + * pk size + */ + private final int pkSize; + + public WhereSql(String sql, int rowSize, int pkSize) { + this.sql = sql; + this.rowSize = rowSize; + this.pkSize = pkSize; + } + + public String getSql() { + return sql; + } + + public int getRowSize() { + return rowSize; + } + + public int getPkSize() { + return pkSize; + } + } } diff --git a/rm-datasource/src/main/java/org/apache/seata/rm/datasource/exec/BaseTransactionalExecutor.java b/rm-datasource/src/main/java/org/apache/seata/rm/datasource/exec/BaseTransactionalExecutor.java index 742f3894101..de2ab8883c8 100644 --- a/rm-datasource/src/main/java/org/apache/seata/rm/datasource/exec/BaseTransactionalExecutor.java +++ b/rm-datasource/src/main/java/org/apache/seata/rm/datasource/exec/BaseTransactionalExecutor.java @@ -520,16 +520,17 @@ protected TableRecords buildTableRecords(Map> pkValuesMap) // build check sql String firstKey = pkValuesMap.keySet().stream().findFirst().get(); int rowSize = pkValuesMap.get(firstKey).size(); - suffix.append(WHERE).append(SqlGenerateUtils.buildWhereConditionByPKs(pkColumnNameList, rowSize, getDbType())); + suffix.append(WHERE); StringJoiner selectSQLJoin = new StringJoiner(", ", prefix, suffix.toString()); List insertColumnsUnEscape = recognizer.getInsertColumnsUnEscape(); List needColumns = getNeedColumns(tableMeta.getTableName(), sqlRecognizer.getTableAlias(), insertColumnsUnEscape); needColumns.forEach(selectSQLJoin::add); PreparedStatement ps = null; + String sqlStr = SqlGenerateUtils.buildSQLByPKs(selectSQLJoin.toString(), "", pkColumnNameList, rowSize, getDbType()); ResultSet rs = null; try { - ps = statementProxy.getConnection().prepareStatement(selectSQLJoin.toString()); + ps = statementProxy.getConnection().prepareStatement(sqlStr); int paramIndex = 1; for (int r = 0; r < rowSize; r++) { for (int c = 0; c < pkColumnNameList.size(); c++) { diff --git a/rm-datasource/src/main/java/org/apache/seata/rm/datasource/exec/MultiUpdateExecutor.java b/rm-datasource/src/main/java/org/apache/seata/rm/datasource/exec/MultiUpdateExecutor.java index 886e2d2d090..242be23b52a 100644 --- a/rm-datasource/src/main/java/org/apache/seata/rm/datasource/exec/MultiUpdateExecutor.java +++ b/rm-datasource/src/main/java/org/apache/seata/rm/datasource/exec/MultiUpdateExecutor.java @@ -147,9 +147,8 @@ private String buildAfterImageSQL(TableMeta tableMeta, TableRecords beforeImage) SQLUpdateRecognizer sqlUpdateRecognizer = (SQLUpdateRecognizer) sqlRecognizer; updateColumnsSet.addAll(sqlUpdateRecognizer.getUpdateColumnsUnEscape()); } - StringBuilder prefix = new StringBuilder("SELECT "); - String suffix = " FROM " + getFromTableInSQL() + " WHERE " + SqlGenerateUtils.buildWhereConditionByPKs(tableMeta.getPrimaryKeyOnlyName(), beforeImage.pkRows().size(), getDbType()); - StringJoiner selectSQLJoiner = new StringJoiner(", ", prefix.toString(), suffix); + StringJoiner selectSQLJoiner = new StringJoiner(", ", "SELECT ", + " FROM " + getFromTableInSQL() + " WHERE "); if (ONLY_CARE_UPDATE_COLUMNS) { if (!containsPK(new ArrayList<>(updateColumnsSet))) { selectSQLJoiner.add(getColumnNamesInSQL(tableMeta.getEscapePkNameList(getDbType()))); @@ -162,7 +161,7 @@ private String buildAfterImageSQL(TableMeta tableMeta, TableRecords beforeImage) selectSQLJoiner.add(ColumnUtils.addEscape(columnName, getDbType())); } } - return selectSQLJoiner.toString(); + return SqlGenerateUtils.buildSQLByPKs(selectSQLJoiner.toString(), "", tableMeta.getPrimaryKeyOnlyName(), beforeImage.pkRows().size(), getDbType()); } protected String buildSuffixSql(String whereCondition) { diff --git a/rm-datasource/src/main/java/org/apache/seata/rm/datasource/exec/UpdateExecutor.java b/rm-datasource/src/main/java/org/apache/seata/rm/datasource/exec/UpdateExecutor.java index 5459f13abbd..2f752454634 100644 --- a/rm-datasource/src/main/java/org/apache/seata/rm/datasource/exec/UpdateExecutor.java +++ b/rm-datasource/src/main/java/org/apache/seata/rm/datasource/exec/UpdateExecutor.java @@ -113,14 +113,12 @@ protected TableRecords afterImage(TableRecords beforeImage) throws SQLException } private String buildAfterImageSQL(TableMeta tableMeta, TableRecords beforeImage) throws SQLException { - String prefix = "SELECT "; - String whereSql = SqlGenerateUtils.buildWhereConditionByPKs(tableMeta.getPrimaryKeyOnlyName(), beforeImage.pkRows().size(), getDbType()); - String suffix = " FROM " + getFromTableInSQL() + " WHERE " + whereSql; - StringJoiner selectSQLJoiner = new StringJoiner(", ", prefix, suffix); + StringJoiner selectSQLJoiner = new StringJoiner(", ", "SELECT " + , " FROM " + getFromTableInSQL() + " WHERE "); SQLUpdateRecognizer recognizer = (SQLUpdateRecognizer) sqlRecognizer; List needUpdateColumns = getNeedColumns(tableMeta.getTableName(), sqlRecognizer.getTableAlias(), recognizer.getUpdateColumnsUnEscape()); needUpdateColumns.forEach(selectSQLJoiner::add); - return selectSQLJoiner.toString(); + return SqlGenerateUtils.buildSQLByPKs(selectSQLJoiner.toString(), "", tableMeta.getPrimaryKeyOnlyName(), beforeImage.pkRows().size(), getDbType()); } } diff --git a/rm-datasource/src/main/java/org/apache/seata/rm/datasource/exec/mysql/MySQLUpdateJoinExecutor.java b/rm-datasource/src/main/java/org/apache/seata/rm/datasource/exec/mysql/MySQLUpdateJoinExecutor.java index b682105fa0a..95144967e2c 100644 --- a/rm-datasource/src/main/java/org/apache/seata/rm/datasource/exec/mysql/MySQLUpdateJoinExecutor.java +++ b/rm-datasource/src/main/java/org/apache/seata/rm/datasource/exec/mysql/MySQLUpdateJoinExecutor.java @@ -91,7 +91,11 @@ protected TableRecords beforeImage() throws SQLException { } String selectSQL = buildBeforeImageSQL(joinTable, tableItems[i], suffixCommonCondition, itemTableUpdateColumns); TableRecords tableRecords = buildTableRecords(getTableMeta(tableItems[i]), selectSQL, paramAppenderList); - beforeImagesMap.put(tableItems[i], tableRecords); + if (CollectionUtils.isNotEmpty(tableRecords.getRows())) { + //when building the after image, the table with empty records in before image is skipped + //link issue https://github.com/apache/incubator-seata/issues/6976 + beforeImagesMap.put(tableItems[i], tableRecords); + } } return null; } @@ -195,18 +199,12 @@ private String buildAfterImageSQL(String joinTable, String itemTable, TableRecords beforeImage) throws SQLException { SQLUpdateRecognizer recognizer = (SQLUpdateRecognizer) sqlRecognizer; TableMeta itemTableMeta = getTableMeta(itemTable); - StringBuilder prefix = new StringBuilder("SELECT "); List pkColumns = getColumnNamesWithTablePrefixList(itemTable, recognizer.getTableAlias(itemTable), itemTableMeta.getPrimaryKeyOnlyName()); - String whereSql = SqlGenerateUtils.buildWhereConditionByPKs(pkColumns, beforeImage.pkRows().size(), getDbType()); - String suffix = " FROM " + joinTable + " WHERE " + whereSql; - //maybe duplicate row for select join sql.remove duplicate row by 'group by' condition - suffix += GROUP_BY; List itemTableUpdateColumns = getItemUpdateColumns(itemTableMeta, recognizer.getUpdateColumns()); List needUpdateColumns = getNeedColumns(itemTable, recognizer.getTableAlias(itemTable), itemTableUpdateColumns); - suffix += buildGroupBy(pkColumns, needUpdateColumns); - StringJoiner selectSQLJoiner = new StringJoiner(", ", prefix.toString(), suffix); + StringJoiner selectSQLJoiner = new StringJoiner(", ", "SELECT ", " FROM " + joinTable + " WHERE "); needUpdateColumns.forEach(selectSQLJoiner::add); - return selectSQLJoiner.toString(); + return SqlGenerateUtils.buildSQLByPKs(selectSQLJoiner.toString(), GROUP_BY + buildGroupBy(pkColumns, needUpdateColumns), pkColumns, beforeImage.pkRows().size(), getDbType()); } private List getItemUpdateColumns(TableMeta itemTableMeta, List updateColumns) { @@ -231,6 +229,9 @@ private List getItemUpdateColumns(TableMeta itemTableMeta, List @Override protected void prepareUndoLog(TableRecords beforeImage, TableRecords afterImage) throws SQLException { + if (CollectionUtils.isEmpty(beforeImagesMap) && CollectionUtils.isEmpty(afterImagesMap)) { + return; + } if (CollectionUtils.isEmpty(beforeImagesMap) || CollectionUtils.isEmpty(afterImagesMap)) { throw new IllegalStateException("images can not be null"); } diff --git a/rm-datasource/src/main/java/org/apache/seata/rm/datasource/sql/struct/TableMetaCacheFactory.java b/rm-datasource/src/main/java/org/apache/seata/rm/datasource/sql/struct/TableMetaCacheFactory.java index b0048b32596..c956796ea12 100644 --- a/rm-datasource/src/main/java/org/apache/seata/rm/datasource/sql/struct/TableMetaCacheFactory.java +++ b/rm-datasource/src/main/java/org/apache/seata/rm/datasource/sql/struct/TableMetaCacheFactory.java @@ -17,6 +17,7 @@ package org.apache.seata.rm.datasource.sql.struct; import java.sql.Connection; +import java.sql.SQLException; import java.util.Map; import java.util.concurrent.BlockingQueue; import java.util.concurrent.ConcurrentHashMap; @@ -29,6 +30,7 @@ import org.apache.seata.common.loader.EnhancedServiceLoader; import org.apache.seata.common.thread.NamedThreadFactory; import org.apache.seata.common.util.CollectionUtils; +import org.apache.seata.common.util.StringUtils; import org.apache.seata.config.ConfigurationFactory; import org.apache.seata.rm.datasource.DataSourceProxy; import org.apache.seata.sqlparser.struct.TableMetaCache; @@ -99,6 +101,14 @@ public static void tableMetaRefreshEvent(String resourceId) { } } + /** + * Remove the TableMetaRefreshHolder from the map. + */ + private static void removeHolderFromMap(String resourceId) { + TABLE_META_REFRESH_HOLDER_MAP.remove(resourceId); + LOGGER.info("Removed TableMetaRefreshHolder for resourceId: {}", resourceId); + } + static class TableMetaRefreshHolder { private long lastRefreshFinishTime; private DataSourceProxy dataSource; @@ -133,6 +143,16 @@ static class TableMetaRefreshHolder { } lastRefreshFinishTime = System.nanoTime(); } + } catch (SQLException ex) { + if (isDataSourceClosedException(ex)) { + LOGGER.info("DataSource is closed, exiting refresh task for resourceId: {}", dataSource.getResourceId()); + removeHolderFromMap(dataSource.getResourceId()); + return; + } else { + // other error, avoid high CPU usage due to infinite loops caused by database exceptions + LOGGER.error("Table refresh SQL error: {}", ex.getMessage(), ex); + lastRefreshFinishTime = System.nanoTime(); + } } catch (Exception exx) { LOGGER.error("table refresh error:{}", exx.getMessage(), exx); // Avoid high CPU usage due to infinite loops caused by database exceptions @@ -142,7 +162,20 @@ static class TableMetaRefreshHolder { }); } - - + /** + * Helper method to determine if the exception is caused by the data source being closed. + * + * @param ex the SQLException to check + * @return true if the exception indicates the data source is closed; false otherwise + */ + private boolean isDataSourceClosedException(SQLException ex) { + String message = ex.getMessage().toLowerCase(); + String sqlState = ex.getSQLState(); + // Most jdbc drivers use '08006' as the datasource close code. + if ("08006".equals(sqlState)) { + return true; + } + return StringUtils.isNotBlank(message) && message.contains("datasource") && message.contains("close"); + } } } diff --git a/rm-datasource/src/main/java/org/apache/seata/rm/datasource/undo/AbstractUndoExecutor.java b/rm-datasource/src/main/java/org/apache/seata/rm/datasource/undo/AbstractUndoExecutor.java index d964c5469c5..538c811478d 100644 --- a/rm-datasource/src/main/java/org/apache/seata/rm/datasource/undo/AbstractUndoExecutor.java +++ b/rm-datasource/src/main/java/org/apache/seata/rm/datasource/undo/AbstractUndoExecutor.java @@ -309,30 +309,32 @@ protected TableRecords queryCurrentRecords(ConnectionProxy connectionProxy) thro // build check sql String firstKey = pkRowValues.keySet().stream().findFirst().get(); int pkRowSize = pkRowValues.get(firstKey).size(); - String checkSQL = buildCheckSql(sqlUndoLog.getTableName(), - SqlGenerateUtils.buildWhereConditionByPKs(pkNameList, pkRowSize, connectionProxy.getDbType())); - - PreparedStatement statement = null; - ResultSet checkSet = null; - TableRecords currentRecords; - try { - statement = conn.prepareStatement(checkSQL); - int paramIndex = 1; - int rowSize = pkRowValues.get(pkNameList.get(0)).size(); - for (int r = 0; r < rowSize; r++) { - for (int c = 0; c < pkNameList.size(); c++) { - List pkColumnValueList = pkRowValues.get(pkNameList.get(c)); - Field field = pkColumnValueList.get(r); - int dataType = tableMeta.getColumnMeta(field.getName()).getDataType(); - statement.setObject(paramIndex, field.getValue(), dataType); - paramIndex++; + List sqlConditions = SqlGenerateUtils.buildWhereConditionListByPKs(pkNameList, pkRowSize, connectionProxy.getDbType()); + TableRecords currentRecords = new TableRecords(tableMeta); + int totalRowIndex = 0; + for (SqlGenerateUtils.WhereSql sqlCondition : sqlConditions) { + String checkSQL = buildCheckSql(sqlUndoLog.getTableName(), sqlCondition.getSql()); + PreparedStatement statement = null; + ResultSet checkSet = null; + try { + statement = conn.prepareStatement(checkSQL); + int paramIndex = 1; + for (int r = 0; r < sqlCondition.getRowSize(); r++) { + for (int c = 0; c < sqlCondition.getPkSize(); c++) { + List pkColumnValueList = pkRowValues.get(pkNameList.get(c)); + Field field = pkColumnValueList.get(totalRowIndex + r); + int dataType = tableMeta.getColumnMeta(field.getName()).getDataType(); + statement.setObject(paramIndex, field.getValue(), dataType); + paramIndex++; + } } - } + totalRowIndex += sqlCondition.getRowSize(); - checkSet = statement.executeQuery(); - currentRecords = TableRecords.buildRecords(tableMeta, checkSet); - } finally { - IOUtil.close(checkSet, statement); + checkSet = statement.executeQuery(); + currentRecords.getRows().addAll(TableRecords.buildRecords(tableMeta, checkSet).getRows()); + } finally { + IOUtil.close(checkSet, statement); + } } return currentRecords; } diff --git a/rm-datasource/src/main/java/org/apache/seata/rm/datasource/undo/dm/DmUndoLogManager.java b/rm-datasource/src/main/java/org/apache/seata/rm/datasource/undo/dm/DmUndoLogManager.java index e267e832131..0ff038fa5a7 100644 --- a/rm-datasource/src/main/java/org/apache/seata/rm/datasource/undo/dm/DmUndoLogManager.java +++ b/rm-datasource/src/main/java/org/apache/seata/rm/datasource/undo/dm/DmUndoLogManager.java @@ -18,9 +18,11 @@ import org.apache.seata.common.loader.LoadLevel; +import org.apache.seata.common.util.CollectionUtils; import org.apache.seata.core.compressor.CompressorType; import org.apache.seata.core.constants.ClientTableColumnsName; import org.apache.seata.rm.datasource.undo.AbstractUndoLogManager; +import org.apache.seata.rm.datasource.undo.UndoLogConstants; import org.apache.seata.rm.datasource.undo.UndoLogParser; import org.apache.seata.sqlparser.util.JdbcConstants; import org.slf4j.Logger; @@ -30,6 +32,7 @@ import java.sql.PreparedStatement; import java.sql.SQLException; import java.util.Date; +import java.util.Set; @LoadLevel(name = JdbcConstants.DM) @@ -37,6 +40,8 @@ public class DmUndoLogManager extends AbstractUndoLogManager { private static final Logger LOGGER = LoggerFactory.getLogger(DmUndoLogManager.class); + protected static final String DELETE_SUB_UNDO_LOG_SQL = "DELETE FROM " + UNDO_LOG_TABLE_NAME + " WHERE \"" + + ClientTableColumnsName.UNDO_LOG_CONTEXT.toUpperCase() + "\" = ? AND " + ClientTableColumnsName.UNDO_LOG_XID + " = ?"; private static final String INSERT_UNDO_LOG_SQL = "INSERT INTO " + UNDO_LOG_TABLE_NAME + " (" + ClientTableColumnsName.UNDO_LOG_BRANCH_XID + ", " @@ -48,6 +53,88 @@ public class DmUndoLogManager extends AbstractUndoLogManager { private static final String DELETE_UNDO_LOG_BY_CREATE_SQL = "DELETE FROM " + UNDO_LOG_TABLE_NAME + " WHERE " + ClientTableColumnsName.UNDO_LOG_LOG_CREATED + " <= ? and ROWNUM <= ?"; + /** + * Delete undo log. + * + * @param xid the xid + * @param branchId the branch id + * @param conn the conn + * @throws SQLException the sql exception + */ + @Override + public void deleteUndoLog(String xid, long branchId, Connection conn) throws SQLException { + try (PreparedStatement deletePST = conn.prepareStatement(DELETE_UNDO_LOG_SQL); + PreparedStatement deleteSubPST = conn.prepareStatement(DELETE_SUB_UNDO_LOG_SQL)) { + deletePST.setLong(1, branchId); + deletePST.setString(2, xid); + deletePST.executeUpdate(); + + deleteSubPST.setString(1, UndoLogConstants.BRANCH_ID_KEY + CollectionUtils.KV_SPLIT + branchId); + deleteSubPST.setString(2, xid); + deleteSubPST.executeUpdate(); + } catch (Exception e) { + if (!(e instanceof SQLException)) { + e = new SQLException(e); + } + throw (SQLException) e; + } + } + + /** + * batch Delete undo log. + * + * @param xids xid + * @param branchIds branch Id + * @param conn connection + */ + @Override + public void batchDeleteUndoLog(Set xids, Set branchIds, Connection conn) throws SQLException { + if (CollectionUtils.isEmpty(xids) || CollectionUtils.isEmpty(branchIds)) { + return; + } + int xidSize = xids.size(); + int branchIdSize = branchIds.size(); + String batchDeleteSql = toBatchDeleteUndoLogSql(xidSize, branchIdSize); + String batchDeleteSubSql = toBatchDeleteSubUndoLogSql(xidSize, branchIdSize); + try (PreparedStatement deletePST = conn.prepareStatement(batchDeleteSql); + PreparedStatement deleteSubPST = conn.prepareStatement(batchDeleteSubSql)) { + int paramsIndex = 1; + for (Long branchId : branchIds) { + deletePST.setLong(paramsIndex, branchId); + deleteSubPST.setString(paramsIndex, UndoLogConstants.BRANCH_ID_KEY + CollectionUtils.KV_SPLIT + branchId); + paramsIndex++; + } + for (String xid : xids) { + deletePST.setString(paramsIndex, xid); + deleteSubPST.setString(paramsIndex, xid); + paramsIndex++; + } + int deleteRows = deletePST.executeUpdate(); + if (LOGGER.isDebugEnabled()) { + LOGGER.debug("batch delete undo log size {}", deleteRows); + } + int deleteSubRows = deleteSubPST.executeUpdate(); + if (LOGGER.isDebugEnabled()) { + LOGGER.debug("batch delete sub undo log size {}", deleteSubRows); + } + } catch (Exception e) { + if (!(e instanceof SQLException)) { + e = new SQLException(e); + } + throw (SQLException) e; + } + } + + protected static String toBatchDeleteSubUndoLogSql(int xidSize, int branchIdSize) { + StringBuilder sqlBuilder = new StringBuilder(64); + sqlBuilder.append("DELETE FROM ").append(UNDO_LOG_TABLE_NAME).append(" WHERE \"").append( + ClientTableColumnsName.UNDO_LOG_CONTEXT.toUpperCase()).append("\" IN "); + appendInParam(branchIdSize, sqlBuilder); + sqlBuilder.append(" AND ").append(ClientTableColumnsName.UNDO_LOG_XID).append(" IN "); + appendInParam(xidSize, sqlBuilder); + return sqlBuilder.toString(); + } + @Override public int deleteUndoLogByLogCreated(Date logCreated, int limitRows, Connection conn) throws SQLException { try (PreparedStatement deletePST = conn.prepareStatement(DELETE_UNDO_LOG_BY_CREATE_SQL)) { diff --git a/rm-datasource/src/main/java/org/apache/seata/rm/datasource/undo/parser/FuryUndoLogParser.java b/rm-datasource/src/main/java/org/apache/seata/rm/datasource/undo/parser/FuryUndoLogParser.java new file mode 100644 index 00000000000..0d64a2b6e04 --- /dev/null +++ b/rm-datasource/src/main/java/org/apache/seata/rm/datasource/undo/parser/FuryUndoLogParser.java @@ -0,0 +1,67 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.seata.rm.datasource.undo.parser; + +import org.apache.fury.Fury; +import org.apache.fury.ThreadLocalFury; +import org.apache.fury.ThreadSafeFury; +import org.apache.fury.config.CompatibleMode; +import org.apache.fury.config.Language; +import org.apache.seata.common.executor.Initialize; +import org.apache.seata.common.loader.LoadLevel; +import org.apache.seata.rm.datasource.undo.BranchUndoLog; +import org.apache.seata.rm.datasource.undo.UndoLogParser; + +@LoadLevel(name = FuryUndoLogParser.NAME) +public class FuryUndoLogParser implements UndoLogParser, Initialize { + public static final String NAME = "fury"; + + private static final ThreadSafeFury FURY = new ThreadLocalFury(classLoader -> Fury.builder() + .withLanguage(Language.JAVA) + // In JAVA mode, classes cannot be registered by tag, and the different registration order between the server and the client will cause deserialization failure + // In XLANG cross-language mode has problems with Java class serialization, such as enum classes [https://github.com/apache/fury/issues/1644]. + .requireClassRegistration(false) + //enable reference tracking for shared/circular reference. + .withRefTracking(true) + .withClassLoader(classLoader) + .withCompatibleMode(CompatibleMode.COMPATIBLE) + .build()); + @Override + public void init() { + } + + @Override + public String getName() { + return NAME; + } + + @Override + public byte[] getDefaultContent() { + return encode(new BranchUndoLog()); + } + + @Override + public byte[] encode(BranchUndoLog branchUndoLog) { + return FURY.serializeJavaObject(branchUndoLog); + } + + @Override + public BranchUndoLog decode(byte[] bytes) { + return FURY.deserializeJavaObject(bytes, BranchUndoLog.class); + } + +} diff --git a/rm-datasource/src/main/resources/META-INF/services/org.apache.seata.rm.datasource.undo.UndoLogParser b/rm-datasource/src/main/resources/META-INF/services/org.apache.seata.rm.datasource.undo.UndoLogParser index 9a32cda00b5..41596cc4c6a 100644 --- a/rm-datasource/src/main/resources/META-INF/services/org.apache.seata.rm.datasource.undo.UndoLogParser +++ b/rm-datasource/src/main/resources/META-INF/services/org.apache.seata.rm.datasource.undo.UndoLogParser @@ -18,4 +18,5 @@ org.apache.seata.rm.datasource.undo.parser.FastjsonUndoLogParser org.apache.seata.rm.datasource.undo.parser.JacksonUndoLogParser org.apache.seata.rm.datasource.undo.parser.ProtostuffUndoLogParser org.apache.seata.rm.datasource.undo.parser.KryoUndoLogParser -org.apache.seata.rm.datasource.undo.parser.Fastjson2UndoLogParser \ No newline at end of file +org.apache.seata.rm.datasource.undo.parser.Fastjson2UndoLogParser +org.apache.seata.rm.datasource.undo.parser.FuryUndoLogParser \ No newline at end of file diff --git a/rm-datasource/src/test/java/org/apache/seata/rm/datasource/SqlGenerateUtilsTest.java b/rm-datasource/src/test/java/org/apache/seata/rm/datasource/SqlGenerateUtilsTest.java index 7ac5b0b4675..45353c2bfc6 100644 --- a/rm-datasource/src/test/java/org/apache/seata/rm/datasource/SqlGenerateUtilsTest.java +++ b/rm-datasource/src/test/java/org/apache/seata/rm/datasource/SqlGenerateUtilsTest.java @@ -16,28 +16,49 @@ */ package org.apache.seata.rm.datasource; -import java.sql.SQLException; -import java.util.ArrayList; -import java.util.List; - -import org.apache.seata.rm.datasource.SqlGenerateUtils; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; +import java.util.ArrayList; +import java.util.List; +import java.util.StringJoiner; + class SqlGenerateUtilsTest { @Test - void testBuildWhereConditionByPKs() throws SQLException { - List pkNameList=new ArrayList<>(); + void testBuildWhereConditionListByPKs() { + List pkNameList = new ArrayList<>(); + pkNameList.add("id"); + pkNameList.add("name"); + List results1 = SqlGenerateUtils.buildWhereConditionListByPKs(pkNameList, 4, "mysql", 2); + Assertions.assertEquals(2, results1.size()); + results1.forEach(result -> { + Assertions.assertEquals("(id,name) in ( (?,?),(?,?) )", result.getSql()); + Assertions.assertEquals(2, result.getRowSize()); + Assertions.assertEquals(2, result.getPkSize()); + }); + List results2 = SqlGenerateUtils.buildWhereConditionListByPKs(pkNameList, 5, "mysql", 2); + Assertions.assertEquals(3, results2.size()); + Assertions.assertEquals("(id,name) in ( (?,?),(?,?) )", results2.get(0).getSql()); + Assertions.assertEquals(2, results2.get(0).getRowSize()); + Assertions.assertEquals(2, results2.get(0).getPkSize()); + Assertions.assertEquals("(id,name) in ( (?,?),(?,?) )", results2.get(1).getSql()); + Assertions.assertEquals("(id,name) in ( (?,?) )", results2.get(2).getSql()); + Assertions.assertEquals(1, results2.get(2).getRowSize()); + Assertions.assertEquals(2, results2.get(2).getPkSize()); + } + + @Test + void testBuildSQLByPKs() { + String sqlPrefix = "select id,name from t_order where "; + List pkNameList = new ArrayList<>(); pkNameList.add("id"); pkNameList.add("name"); - String result = SqlGenerateUtils.buildWhereConditionByPKs(pkNameList,4,"mysql",2); - Assertions.assertEquals("(id,name) in ( (?,?),(?,?) ) or (id,name) in ( (?,?),(?,?) )", result); - result = SqlGenerateUtils.buildWhereConditionByPKs(pkNameList,5,"mysql",2); - Assertions.assertEquals("(id,name) in ( (?,?),(?,?) ) or (id,name) in ( (?,?),(?,?) ) or (id,name) in ( (?,?)" - + " )", - result); + List whereList = SqlGenerateUtils.buildWhereConditionListByPKs(pkNameList, 4, "mysql", 2); + StringJoiner sqlJoiner = new StringJoiner(" union "); + whereList.forEach(whereSql -> sqlJoiner.add(sqlPrefix + " " + whereSql.getSql())); + Assertions.assertEquals("select id,name from t_order where (id,name) in ( (?,?),(?,?) ) union select id,name from t_order where (id,name) in ( (?,?),(?,?) )", sqlJoiner.toString()); } } diff --git a/rm-datasource/src/test/java/org/apache/seata/rm/datasource/exec/UpdateJoinExecutorTest.java b/rm-datasource/src/test/java/org/apache/seata/rm/datasource/exec/UpdateJoinExecutorTest.java index 3a8ce32ce72..67404ec763a 100644 --- a/rm-datasource/src/test/java/org/apache/seata/rm/datasource/exec/UpdateJoinExecutorTest.java +++ b/rm-datasource/src/test/java/org/apache/seata/rm/datasource/exec/UpdateJoinExecutorTest.java @@ -32,7 +32,6 @@ import org.apache.seata.rm.datasource.DataSourceProxy; import org.apache.seata.rm.datasource.DataSourceProxyTest; import org.apache.seata.rm.datasource.StatementProxy; -import org.apache.seata.rm.datasource.exec.UpdateExecutor; import org.apache.seata.rm.datasource.exec.mysql.MySQLUpdateJoinExecutor; import org.apache.seata.rm.datasource.mock.MockDriver; import org.apache.seata.rm.datasource.sql.struct.TableRecords; @@ -58,6 +57,7 @@ public void testUpdateJoinUndoLog() throws SQLException { }; Object[][] beforeReturnValue = new Object[][]{ new Object[]{1, "Tom"}, + new Object[]{2, "Tony"}, }; StatementProxy beforeMockStatementProxy = mockStatementProxy(returnValueColumnLabels, beforeReturnValue, columnMetas, indexMetas); String sql = "update t1 inner join t2 on t1.id = t2.id set t1.name = 'WILL',t2.name = 'WILL'"; @@ -69,6 +69,7 @@ public void testUpdateJoinUndoLog() throws SQLException { TableRecords beforeImage = mySQLUpdateJoinExecutor.beforeImage(); Object[][] afterReturnValue = new Object[][]{ new Object[]{1, "WILL"}, + new Object[]{2, "Tony"}, }; StatementProxy afterMockStatementProxy = mockStatementProxy(returnValueColumnLabels, afterReturnValue, columnMetas, indexMetas); mySQLUpdateJoinExecutor.statementProxy = afterMockStatementProxy; @@ -76,6 +77,36 @@ public void testUpdateJoinUndoLog() throws SQLException { Assertions.assertDoesNotThrow(()->mySQLUpdateJoinExecutor.prepareUndoLog(beforeImage, afterImage)); } + @Test + public void testEmptyUpdateJoinUndoLog() throws SQLException { + List returnValueColumnLabels = Lists.newArrayList("id", "name"); + Object[][] columnMetas = new Object[][]{ + new Object[]{"", "", "t1", "id", Types.INTEGER, "INTEGER", 64, 0, 10, 1, "", "", 0, 0, 64, 1, "NO", "YES"}, + new Object[]{"", "", "t1", "name", Types.VARCHAR, "VARCHAR", 64, 0, 10, 0, "", "", 0, 0, 64, 2, "YES", "NO"}, + new Object[]{"", "", "t2", "id", Types.INTEGER, "INTEGER", 64, 0, 10, 1, "", "", 0, 0, 64, 1, "NO", "YES"}, + new Object[]{"", "", "t2", "name", Types.VARCHAR, "VARCHAR", 64, 0, 10, 0, "", "", 0, 0, 64, 2, "YES", "NO"}, + new Object[]{"", "", "t1 inner join t2 on t1.id = t2.id", "id", Types.VARCHAR, "VARCHAR", 64, 0, 10, 0, "", "", 0, 0, 64, 2, "YES", "NO"}, + new Object[]{"", "", "t1 inner join t2 on t1.id = t2.id", "name", Types.VARCHAR, "VARCHAR", 64, 0, 10, 0, "", "", 0, 0, 64, 2, "YES", "NO"}, + }; + Object[][] indexMetas = new Object[][]{ + new Object[]{"PRIMARY", "id", false, "", 3, 1, "A", 34}, + }; + Object[][] beforeReturnValue = new Object[][]{}; + StatementProxy beforeMockStatementProxy = mockStatementProxy(returnValueColumnLabels, beforeReturnValue, columnMetas, indexMetas); + String sql = "update t1 inner join t2 on t1.id = t2.id set t1.name = 'WILL',t2.name = 'WILL'"; + List asts = SQLUtils.parseStatements(sql, JdbcConstants.MYSQL); + MySQLUpdateRecognizer recognizer = new MySQLUpdateRecognizer(sql, asts.get(0)); + UpdateExecutor mySQLUpdateJoinExecutor = new MySQLUpdateJoinExecutor(beforeMockStatementProxy, (statement, args) -> { + return null; + }, recognizer); + TableRecords beforeImage = mySQLUpdateJoinExecutor.beforeImage(); + Object[][] afterReturnValue = new Object[][]{}; + StatementProxy afterMockStatementProxy = mockStatementProxy(returnValueColumnLabels, afterReturnValue, columnMetas, indexMetas); + mySQLUpdateJoinExecutor.statementProxy = afterMockStatementProxy; + TableRecords afterImage = mySQLUpdateJoinExecutor.afterImage(beforeImage); + Assertions.assertDoesNotThrow(()->mySQLUpdateJoinExecutor.prepareUndoLog(beforeImage, afterImage)); + } + private StatementProxy mockStatementProxy(List returnValueColumnLabels, Object[][] returnValue, Object[][] columnMetas, Object[][] indexMetas) { MockDriver mockDriver = new MockDriver(returnValueColumnLabels, returnValue, columnMetas, indexMetas); DruidDataSource dataSource = new DruidDataSource(); diff --git a/rm-datasource/src/test/java/org/apache/seata/rm/datasource/mock/MockExecuteHandlerImpl.java b/rm-datasource/src/test/java/org/apache/seata/rm/datasource/mock/MockExecuteHandlerImpl.java index 17c0cf6607c..22cb3fe8a4a 100644 --- a/rm-datasource/src/test/java/org/apache/seata/rm/datasource/mock/MockExecuteHandlerImpl.java +++ b/rm-datasource/src/test/java/org/apache/seata/rm/datasource/mock/MockExecuteHandlerImpl.java @@ -70,7 +70,7 @@ public ResultSet executeQuery(MockStatementBase statement, String sql) throws SQ List metas = new ArrayList<>(); if(asts.get(0) instanceof SQLSelectStatement) { SQLSelectStatement ast = (SQLSelectStatement) asts.get(0); - SQLSelectQueryBlock queryBlock = ast.getSelect().getQueryBlock(); + SQLSelectQueryBlock queryBlock = ast.getSelect().getFirstQueryBlock(); String tableName = ""; if (queryBlock.getFrom() instanceof SQLExprTableSource) { MySQLSelectForUpdateRecognizer recognizer = new MySQLSelectForUpdateRecognizer(sql, ast); diff --git a/rm-datasource/src/test/java/org/apache/seata/rm/datasource/undo/AbstractUndoExecutorTest.java b/rm-datasource/src/test/java/org/apache/seata/rm/datasource/undo/AbstractUndoExecutorTest.java index 8f6522617af..b50eb6761b9 100644 --- a/rm-datasource/src/test/java/org/apache/seata/rm/datasource/undo/AbstractUndoExecutorTest.java +++ b/rm-datasource/src/test/java/org/apache/seata/rm/datasource/undo/AbstractUndoExecutorTest.java @@ -209,12 +209,12 @@ public void testBuildWhereConditionByPKs() throws SQLException { pkRowValues.put("id1", pkId1Values); pkRowValues.put("id2", pkId2Values); - String sql = SqlGenerateUtils.buildWhereConditionByPKs(pkNameList, pkRowValues.get("id1").size(), JdbcConstants.MYSQL); - Assertions.assertEquals("(id1,id2) in ( (?,?),(?,?),(?,?) )", sql); - sql = SqlGenerateUtils.buildWhereConditionByPKs(pkNameList, pkRowValues.get("id1").size(), JdbcConstants.MARIADB); - Assertions.assertEquals("(id1,id2) in ( (?,?),(?,?),(?,?) )", sql); - sql = SqlGenerateUtils.buildWhereConditionByPKs(pkNameList, pkRowValues.get("id1").size(), JdbcConstants.POLARDBX); - Assertions.assertEquals("(id1,id2) in ( (?,?),(?,?),(?,?) )", sql); + List sql = SqlGenerateUtils.buildWhereConditionListByPKs(pkNameList, pkRowValues.get("id1").size(), JdbcConstants.MYSQL, 1000); + Assertions.assertEquals("(id1,id2) in ( (?,?),(?,?),(?,?) )", sql.get(0).getSql()); + sql = SqlGenerateUtils.buildWhereConditionListByPKs(pkNameList, pkRowValues.get("id1").size(), JdbcConstants.MARIADB, 1000); + Assertions.assertEquals("(id1,id2) in ( (?,?),(?,?),(?,?) )", sql.get(0).getSql()); + sql = SqlGenerateUtils.buildWhereConditionListByPKs(pkNameList, pkRowValues.get("id1").size(), JdbcConstants.POLARDBX, 1000); + Assertions.assertEquals("(id1,id2) in ( (?,?),(?,?),(?,?) )", sql.get(0).getSql()); } @Test @@ -227,12 +227,12 @@ public void testBuildWhereConditionByPK() throws SQLException { pkId1Values.add(new Field()); pkRowValues.put("id1", pkId1Values); - String sql = SqlGenerateUtils.buildWhereConditionByPKs(pkNameList, pkRowValues.get("id1").size(), JdbcConstants.MYSQL); - Assertions.assertEquals("(id1) in ( (?) )", sql); - sql = SqlGenerateUtils.buildWhereConditionByPKs(pkNameList, pkRowValues.get("id1").size(), JdbcConstants.MARIADB); - Assertions.assertEquals("(id1) in ( (?) )", sql); - sql = SqlGenerateUtils.buildWhereConditionByPKs(pkNameList, pkRowValues.get("id1").size(), JdbcConstants.POLARDBX); - Assertions.assertEquals("(id1) in ( (?) )", sql); + List sql = SqlGenerateUtils.buildWhereConditionListByPKs(pkNameList, pkRowValues.get("id1").size(), JdbcConstants.MYSQL); + Assertions.assertEquals("(id1) in ( (?) )", sql.get(0).getSql()); + sql = SqlGenerateUtils.buildWhereConditionListByPKs(pkNameList, pkRowValues.get("id1").size(), JdbcConstants.MARIADB); + Assertions.assertEquals("(id1) in ( (?) )", sql.get(0).getSql()); + sql = SqlGenerateUtils.buildWhereConditionListByPKs(pkNameList, pkRowValues.get("id1").size(), JdbcConstants.POLARDBX); + Assertions.assertEquals("(id1) in ( (?) )", sql.get(0).getSql()); } } diff --git a/rm-datasource/src/test/java/org/apache/seata/rm/datasource/undo/parser/FuryUndoLogParserTest.java b/rm-datasource/src/test/java/org/apache/seata/rm/datasource/undo/parser/FuryUndoLogParserTest.java new file mode 100644 index 00000000000..926339f42ea --- /dev/null +++ b/rm-datasource/src/test/java/org/apache/seata/rm/datasource/undo/parser/FuryUndoLogParserTest.java @@ -0,0 +1,36 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.seata.rm.datasource.undo.parser; + +import org.apache.seata.common.loader.EnhancedServiceLoader; +import org.apache.seata.rm.datasource.undo.BaseUndoLogParserTest; +import org.apache.seata.rm.datasource.undo.UndoLogParser; + + +public class FuryUndoLogParserTest extends BaseUndoLogParserTest { + + FuryUndoLogParser parser = (FuryUndoLogParser) EnhancedServiceLoader.load(UndoLogParser.class, FuryUndoLogParser.NAME); + + @Override + public UndoLogParser getParser() { + return parser; + } + + @Override + public void testTimestampEncodeAndDecode() { + } +} diff --git a/rocketmq/src/main/java/org/apache/seata/integration/rocketmq/TCCRocketMQ.java b/rocketmq/src/main/java/org/apache/seata/integration/rocketmq/TCCRocketMQ.java index 89abf945543..b7390ea4dc4 100644 --- a/rocketmq/src/main/java/org/apache/seata/integration/rocketmq/TCCRocketMQ.java +++ b/rocketmq/src/main/java/org/apache/seata/integration/rocketmq/TCCRocketMQ.java @@ -20,7 +20,6 @@ import org.apache.seata.rm.tcc.api.BusinessActionContext; import java.net.UnknownHostException; -import java.util.concurrent.TimeoutException; import org.apache.rocketmq.client.exception.MQBrokerException; import org.apache.rocketmq.client.exception.MQClientException; @@ -60,7 +59,7 @@ public interface TCCRocketMQ { * @throws InterruptedException */ boolean commit(BusinessActionContext context) - throws UnknownHostException, MQBrokerException, RemotingException, InterruptedException, TransactionException, TimeoutException; + throws UnknownHostException, MQBrokerException, RemotingException, InterruptedException, TransactionException; /** * RocketMQ half send rollback diff --git a/rocketmq/src/main/java/org/apache/seata/integration/rocketmq/TCCRocketMQImpl.java b/rocketmq/src/main/java/org/apache/seata/integration/rocketmq/TCCRocketMQImpl.java index beeb6db944c..faab2628b01 100644 --- a/rocketmq/src/main/java/org/apache/seata/integration/rocketmq/TCCRocketMQImpl.java +++ b/rocketmq/src/main/java/org/apache/seata/integration/rocketmq/TCCRocketMQImpl.java @@ -17,6 +17,7 @@ package org.apache.seata.integration.rocketmq; import org.apache.rocketmq.client.impl.producer.DefaultMQProducerImpl; +import org.apache.seata.common.util.StringUtils; import org.apache.seata.core.exception.TransactionException; import org.apache.seata.rm.tcc.api.BusinessActionContext; import org.apache.seata.rm.tcc.api.BusinessActionContextUtil; @@ -34,7 +35,6 @@ import java.net.UnknownHostException; import java.util.HashMap; import java.util.Map; -import java.util.concurrent.TimeoutException; /** * the type TCCRocketMQImpl @@ -70,11 +70,11 @@ public SendResult prepare(Message message, long timeout) throws MQClientExceptio @Override public boolean commit(BusinessActionContext context) - throws UnknownHostException, MQBrokerException, RemotingException, InterruptedException, TimeoutException, TransactionException { + throws UnknownHostException, MQBrokerException, RemotingException, InterruptedException, TransactionException { Message message = context.getActionContext(ROCKET_MSG_KEY, Message.class); SendResult sendResult = context.getActionContext(ROCKET_SEND_RESULT_KEY, SendResult.class); - if (message == null || sendResult == null) { - throw new TransactionException("TCCRocketMQ commit but cannot find message and sendResult"); + if (checkMqStatus(message, sendResult)) { + throw new TransactionException("TCCRocketMQ commit but cannot find message or sendResult"); } this.producerImpl.endTransaction(message, sendResult, LocalTransactionState.COMMIT_MESSAGE, null); LOGGER.info("RocketMQ message send commit, xid = {}, branchId = {}", context.getXid(), context.getBranchId()); @@ -86,11 +86,21 @@ public boolean rollback(BusinessActionContext context) throws UnknownHostException, MQBrokerException, RemotingException, InterruptedException, TransactionException { Message message = context.getActionContext(ROCKET_MSG_KEY, Message.class); SendResult sendResult = context.getActionContext(ROCKET_SEND_RESULT_KEY, SendResult.class); - if (message == null || sendResult == null) { - LOGGER.error("TCCRocketMQ rollback but cannot find message and sendResult"); + if (checkMqStatus(message, sendResult)) { + LOGGER.error("TCCRocketMQ rollback but cannot find message or sendResult"); + return true; } this.producerImpl.endTransaction(message, sendResult, LocalTransactionState.ROLLBACK_MESSAGE, null); LOGGER.info("RocketMQ message send rollback, xid = {}, branchId = {}", context.getXid(), context.getBranchId()); return true; } + + private static boolean checkMqStatus(Message message, SendResult sendResult) { + boolean empty = message == null || sendResult == null || + (StringUtils.isBlank(sendResult.getOffsetMsgId()) && StringUtils.isBlank(sendResult.getMsgId())); + if (empty) { + LOGGER.info("checkMqStatus message = {}, sendResult = {}", message, sendResult); + } + return empty; + } } diff --git a/rocketmq/src/test/java/org/apache/seata/integration/rocketmq/TCCRocketMQImplTest.java b/rocketmq/src/test/java/org/apache/seata/integration/rocketmq/TCCRocketMQImplTest.java index af9ef218f2b..5d83d2faee0 100644 --- a/rocketmq/src/test/java/org/apache/seata/integration/rocketmq/TCCRocketMQImplTest.java +++ b/rocketmq/src/test/java/org/apache/seata/integration/rocketmq/TCCRocketMQImplTest.java @@ -66,6 +66,8 @@ public class TCCRocketMQImplTest { private TCCRocketMQImpl tccRocketMQ; private TCCRocketMQImpl prepareTccRocketMQ; + private static final String TEST_TOPIC = "testTopic"; + @BeforeEach void setUp() throws Exception { MockitoAnnotations.openMocks(this); @@ -83,7 +85,7 @@ void testPrepare() throws MQClientException { MockedStatic mockedStatic = mockStatic(BusinessActionContextUtil.class); try { - Message message = new Message("testTopic", "testBody".getBytes()); + Message message = new Message(TEST_TOPIC, "testBody".getBytes()); long timeout = 3000L; String xid = "testXid"; long branchId = 123L; @@ -92,7 +94,7 @@ void testPrepare() throws MQClientException { when(businessActionContext.getXid()).thenReturn(xid); when(businessActionContext.getBranchId()).thenReturn(branchId); - SendResult mockSendResult = mock(SendResult.class); + SendResult mockSendResult = mockSendResultWithId(); when(mockSendResult.getSendStatus()).thenReturn(SendStatus.SEND_OK); when(producer.doSendMessageInTransaction(message, timeout, xid, branchId)).thenReturn(mockSendResult); @@ -114,7 +116,7 @@ void testPrepareWithException() throws MQClientException { MockedStatic mockedStatic = mockStatic(BusinessActionContextUtil.class); try { - Message message = new Message("testTopic", "testBody".getBytes()); + Message message = new Message(TEST_TOPIC, "testBody".getBytes()); long timeout = 3000L; String xid = "testXid"; long branchId = 123L; @@ -141,8 +143,8 @@ void testCommitSuccess() throws UnknownHostException, MQBrokerException, RemotingException, InterruptedException, TimeoutException, TransactionException { - Message message = new Message("testTopic", "testBody".getBytes()); - SendResult sendResult = mock(SendResult.class); + Message message = new Message(TEST_TOPIC, "testBody".getBytes()); + SendResult sendResult = mockSendResultWithId(); when(businessActionContext.getActionContext("ROCKET_MSG", Message.class)).thenReturn(message); when(businessActionContext.getActionContext("ROCKET_SEND_RESULT", SendResult.class)).thenReturn(sendResult); @@ -156,22 +158,18 @@ void testCommitSuccess() isNull()); } + + @Test - void testCommitWithNullMessage() { + void testCommitWithNullMessageOrResult() { when(businessActionContext.getActionContext("ROCKET_MSG", Message.class)).thenReturn(null); when(businessActionContext.getActionContext("ROCKET_SEND_RESULT", SendResult.class)).thenReturn( - mock(SendResult.class)); - + mock(SendResult.class)); assertThrows(TransactionException.class, () -> tccRocketMQ.commit(businessActionContext)); - } - - @Test - void testCommitWithNullSendResult() { when(businessActionContext.getActionContext("ROCKET_MSG", Message.class)).thenReturn(new Message()); when(businessActionContext.getActionContext("ROCKET_SEND_RESULT", SendResult.class)).thenReturn(null); - assertThrows(TransactionException.class, () -> tccRocketMQ.commit(businessActionContext)); } @@ -179,8 +177,8 @@ void testCommitWithNullSendResult() { void testCommitWithException() throws UnknownHostException, MQBrokerException, RemotingException, InterruptedException, TimeoutException { - Message message = new Message("testTopic", "testBody".getBytes()); - SendResult sendResult = mock(SendResult.class); + Message message = new Message(TEST_TOPIC, "testBody".getBytes()); + SendResult sendResult = mockSendResultWithId(); when(businessActionContext.getActionContext("ROCKET_MSG", Message.class)).thenReturn(message); when(businessActionContext.getActionContext("ROCKET_SEND_RESULT", SendResult.class)).thenReturn(sendResult); @@ -195,8 +193,8 @@ void testCommitWithException() void testRollbackSuccess() throws UnknownHostException, MQBrokerException, RemotingException, InterruptedException, TransactionException { - Message message = new Message("testTopic", "testBody".getBytes()); - SendResult sendResult = mock(SendResult.class); + Message message = new Message(TEST_TOPIC, "testBody".getBytes()); + SendResult sendResult = mockSendResultWithId(); when(businessActionContext.getActionContext("ROCKET_MSG", Message.class)).thenReturn(message); when(businessActionContext.getActionContext("ROCKET_SEND_RESULT", SendResult.class)).thenReturn(sendResult); @@ -211,47 +209,31 @@ void testRollbackSuccess() } @Test - void testRollbackWithNullMessage() + void testRollbackWithNullMessageOrResult() throws UnknownHostException, MQBrokerException, RemotingException, InterruptedException, TransactionException { - SendResult sendResult = mock(SendResult.class); - - when(businessActionContext.getActionContext("ROCKET_MSG", Message.class)).thenReturn(null); - when(businessActionContext.getActionContext("ROCKET_SEND_RESULT", SendResult.class)).thenReturn(sendResult); when(businessActionContext.getXid()).thenReturn("testXid"); when(businessActionContext.getBranchId()).thenReturn(123L); + SendResult sendResult = mock(SendResult.class); + when(businessActionContext.getActionContext("ROCKET_MSG", Message.class)).thenReturn(null); + when(businessActionContext.getActionContext("ROCKET_SEND_RESULT", SendResult.class)).thenReturn(sendResult); boolean result = tccRocketMQ.rollback(businessActionContext); - assertTrue(result); - verify(producerImpl).endTransaction(isNull(), eq(sendResult), eq(LocalTransactionState.ROLLBACK_MESSAGE), - isNull()); - } - - @Test - void testRollbackWithNullSendResult() - throws UnknownHostException, MQBrokerException, RemotingException, InterruptedException, TransactionException { - - Message message = new Message("testTopic", "testBody".getBytes()); + Message message = new Message(TEST_TOPIC, "testBody".getBytes()); when(businessActionContext.getActionContext("ROCKET_MSG", Message.class)).thenReturn(message); when(businessActionContext.getActionContext("ROCKET_SEND_RESULT", SendResult.class)).thenReturn(null); - when(businessActionContext.getXid()).thenReturn("testXid"); - when(businessActionContext.getBranchId()).thenReturn(123L); - - boolean result = tccRocketMQ.rollback(businessActionContext); - - assertTrue(result); - verify(producerImpl).endTransaction(eq(message), isNull(), eq(LocalTransactionState.ROLLBACK_MESSAGE), - isNull()); + boolean result2 = tccRocketMQ.rollback(businessActionContext); + assertTrue(result2); } @Test void testRollbackWithException() throws UnknownHostException, MQBrokerException, RemotingException, InterruptedException { - Message message = new Message("testTopic", "testBody".getBytes()); - SendResult sendResult = mock(SendResult.class); + Message message = new Message(TEST_TOPIC, "testBody".getBytes()); + SendResult sendResult = mockSendResultWithId(); when(businessActionContext.getActionContext("ROCKET_MSG", Message.class)).thenReturn(message); when(businessActionContext.getActionContext("ROCKET_SEND_RESULT", SendResult.class)).thenReturn(sendResult); @@ -264,4 +246,11 @@ void testRollbackWithException() assertThrows(MQBrokerException.class, () -> tccRocketMQ.rollback(businessActionContext)); } + + private static SendResult mockSendResultWithId() { + SendResult mock = mock(SendResult.class); + when(mock.getMsgId()).thenReturn("testMsgId"); + when(mock.getOffsetMsgId()).thenReturn("testOffsetMsgId"); + return mock; + } } diff --git a/saga/pom.xml b/saga/pom.xml index 68185fed444..0d05dbb5fa3 100644 --- a/saga/pom.xml +++ b/saga/pom.xml @@ -38,6 +38,7 @@ seata-saga-rm seata-saga-engine-store seata-saga-spring + seata-saga-annotation diff --git a/saga/seata-saga-annotation/pom.xml b/saga/seata-saga-annotation/pom.xml new file mode 100644 index 00000000000..ee9881c6825 --- /dev/null +++ b/saga/seata-saga-annotation/pom.xml @@ -0,0 +1,51 @@ + + + + + seata-saga + org.apache.seata + ${revision} + + 4.0.0 + seata-saga-annotation + seata-saga-annotation ${project.version} + saga annotation module for Seata built with Maven + + + + ${project.groupId} + seata-core + ${project.version} + + + ${project.groupId} + seata-rm + ${project.version} + + + ${project.groupId} + seata-integration-tx-api + ${project.version} + + + + diff --git a/saga/seata-saga-annotation/src/main/java/org/apache/seata/saga/rm/RMHandlerSagaAnnotation.java b/saga/seata-saga-annotation/src/main/java/org/apache/seata/saga/rm/RMHandlerSagaAnnotation.java new file mode 100644 index 00000000000..21dad9e5b47 --- /dev/null +++ b/saga/seata-saga-annotation/src/main/java/org/apache/seata/saga/rm/RMHandlerSagaAnnotation.java @@ -0,0 +1,39 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.seata.saga.rm; + +import org.apache.seata.core.model.BranchType; +import org.apache.seata.core.model.ResourceManager; +import org.apache.seata.rm.AbstractRMHandler; +import org.apache.seata.rm.DefaultResourceManager; + +/** + * The type Rm handler SagaAnnotation. + */ +public class RMHandlerSagaAnnotation extends AbstractRMHandler { + + @Override + protected ResourceManager getResourceManager() { + return DefaultResourceManager.get().getResourceManager(BranchType.SAGA_ANNOTATION); + } + + @Override + public BranchType getBranchType() { + return BranchType.SAGA_ANNOTATION; + } + +} diff --git a/saga/seata-saga-annotation/src/main/java/org/apache/seata/saga/rm/SagaAnnotationResource.java b/saga/seata-saga-annotation/src/main/java/org/apache/seata/saga/rm/SagaAnnotationResource.java new file mode 100644 index 00000000000..f5d1ba04355 --- /dev/null +++ b/saga/seata-saga-annotation/src/main/java/org/apache/seata/saga/rm/SagaAnnotationResource.java @@ -0,0 +1,208 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.seata.saga.rm; + +import java.lang.reflect.Method; + +import org.apache.seata.core.model.BranchType; +import org.apache.seata.core.model.Resource; + +/** + * The type Saga annotation resource. + */ +public class SagaAnnotationResource implements Resource { + + private String resourceGroupId = "DEFAULT"; + + private String appName; + + private String actionName; + + private Object targetBean; + + private String compensationMethodName; + + private Method compensationMethod; + + private Class[] compensationArgsClasses; + + private String[] phaseTwoCompensationKeys; + + @Override + public String getResourceGroupId() { + return resourceGroupId; + } + + /** + * Sets resource group id. + * + * @param resourceGroupId the resource group id + */ + public void setResourceGroupId(String resourceGroupId) { + this.resourceGroupId = resourceGroupId; + } + + @Override + public String getResourceId() { + return actionName; + } + + @Override + public BranchType getBranchType() { + return BranchType.SAGA_ANNOTATION; + } + + /** + * Gets app name. + * + * @return the app name + */ + public String getAppName() { + return appName; + } + + /** + * Sets app name. + * + * @param appName the app name + */ + public void setAppName(String appName) { + this.appName = appName; + } + + /** + * Gets action name. + * + * @return the action name + */ + public String getActionName() { + return actionName; + } + + /** + * Sets action name. + * + * @param actionName the action name + */ + public void setActionName(String actionName) { + this.actionName = actionName; + } + + /** + * Gets target bean. + * + * @return the target bean + */ + public Object getTargetBean() { + return targetBean; + } + + /** + * Sets target bean. + * + * @param targetBean the target bean + */ + public void setTargetBean(Object targetBean) { + this.targetBean = targetBean; + } + + /** + * Gets compensation method. + * + * @return the rollback method + */ + public Method getCompensationMethod() { + return compensationMethod; + } + + /** + * Sets compensation method. + * + * @param compensationMethod the rollback method + */ + public void setCompensationMethod(Method compensationMethod) { + this.compensationMethod = compensationMethod; + } + + /** + * Gets compensation method name. + * + * @return the rollback method name + */ + public String getCompensationMethodName() { + return compensationMethodName; + } + + /** + * Sets compensation method name. + * + * @param compensationMethodName the rollback method name + */ + public void setCompensationMethodName(String compensationMethodName) { + this.compensationMethodName = compensationMethodName; + } + + /** + * get compensation method args + * + * @return class array + */ + public Class[] getCompensationArgsClasses() { + return compensationArgsClasses; + } + + /** + * set compensation method args + * + * @param compensationArgsClasses rollbackArgsClasses + */ + public void setCompensationArgsClasses(Class[] compensationArgsClasses) { + this.compensationArgsClasses = compensationArgsClasses; + } + + /** + * get compensation method args keys + * + * @return keys array + */ + public String[] getPhaseTwoCompensationKeys() { + return phaseTwoCompensationKeys; + } + + /** + * set compensation method args key + * + * @param phaseTwoCompensationKeys phaseTwoCompensationKeys + */ + public void setPhaseTwoCompensationKeys(String[] phaseTwoCompensationKeys) { + this.phaseTwoCompensationKeys = phaseTwoCompensationKeys; + } + + @Override + public int hashCode() { + return actionName.hashCode(); + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof SagaAnnotationResource)) { + return false; + } + return this.actionName.equals(((SagaAnnotationResource) obj).actionName); + } + +} \ No newline at end of file diff --git a/saga/seata-saga-annotation/src/main/java/org/apache/seata/saga/rm/SagaAnnotationResourceManager.java b/saga/seata-saga-annotation/src/main/java/org/apache/seata/saga/rm/SagaAnnotationResourceManager.java new file mode 100644 index 00000000000..ea802b16781 --- /dev/null +++ b/saga/seata-saga-annotation/src/main/java/org/apache/seata/saga/rm/SagaAnnotationResourceManager.java @@ -0,0 +1,153 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.seata.saga.rm; + +import org.apache.seata.common.exception.ExceptionUtil; +import org.apache.seata.common.exception.RepeatRegistrationException; +import org.apache.seata.common.exception.ShouldNeverHappenException; +import org.apache.seata.core.exception.TransactionException; +import org.apache.seata.core.model.BranchStatus; +import org.apache.seata.core.model.BranchType; +import org.apache.seata.core.model.Resource; +import org.apache.seata.integration.tx.api.remoting.TwoPhaseResult; +import org.apache.seata.rm.AbstractResourceManager; +import org.apache.seata.rm.tcc.api.BusinessActionContext; +import org.apache.seata.rm.tcc.api.BusinessActionContextUtil; + +import java.lang.reflect.Method; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +/** + * Saga annotation resource manager + */ +public class SagaAnnotationResourceManager extends AbstractResourceManager { + + /** + * TCC resource cache + */ + private final Map resourceCache = new ConcurrentHashMap<>(); + + @Override + public void registerResource(Resource resource) { + String resourceId = resource.getResourceId(); + SagaAnnotationResource newResource = (SagaAnnotationResource) resource; + SagaAnnotationResource oldResource = (SagaAnnotationResource) resourceCache.get(resourceId); + + if (oldResource != null) { + Object newResourceBean = newResource.getTargetBean(); + Object oldResourceBean = oldResource.getTargetBean(); + if (newResourceBean != oldResourceBean) { + throw new RepeatRegistrationException(String.format("Same SagaAnnotation resource name <%s> between method1 <%s> of class1 <%s> and method2 <%s> of class2 <%s>, should be unique", + resourceId, + newResource.getActionName(), + newResourceBean.getClass().getName(), + oldResource.getActionName(), + oldResourceBean.getClass().getName())); + } + } + + resourceCache.put(resourceId, newResource); + super.registerResource(newResource); + } + + /** + * saga branch commit + * + * @param branchType + * @param xid Transaction id. + * @param branchId Branch id. + * @param resourceId Resource id. + * @param applicationData Application data bind with this branch. + * @return BranchStatus + */ + @Override + public BranchStatus branchCommit(BranchType branchType, String xid, long branchId, String resourceId, String applicationData) { + //impossible to reach here + return BranchStatus.PhaseTwo_Committed; + } + + @Override + public BranchStatus branchRollback(BranchType branchType, String xid, long branchId, String resourceId, String applicationData) throws TransactionException { + SagaAnnotationResource resource = (SagaAnnotationResource) resourceCache.get(resourceId); + if (resource == null) { + throw new ShouldNeverHappenException(String.format("SagaAnnotation resource is not exist, resourceId: %s", resourceId)); + } + + Object targetBean = resource.getTargetBean(); + Method compensationMethod = resource.getCompensationMethod(); + if (targetBean == null || compensationMethod == null) { + throw new ShouldNeverHappenException(String.format("SagaAnnotation resource is not available, resourceId: %s", resourceId)); + } + + try { + BusinessActionContext businessActionContext = BusinessActionContextUtil.getBusinessActionContext(xid, branchId, resourceId, + applicationData); + Object[] args = this.getTwoPhaseRollbackArgs(resource, businessActionContext); + BusinessActionContextUtil.setContext(businessActionContext); + + boolean result; + Object ret = compensationMethod.invoke(targetBean, args); + if (ret != null) { + if (ret instanceof TwoPhaseResult) { + result = ((TwoPhaseResult) ret).isSuccess(); + } else { + result = (boolean) ret; + } + } else { + result = true; + } + + LOGGER.info("SagaAnnotation resource rollback result : {}, xid: {}, branchId: {}, resourceId: {}", result, xid, branchId, resourceId); + return result ? BranchStatus.PhaseTwo_Rollbacked : BranchStatus.PhaseTwo_RollbackFailed_Retryable; + } catch (Throwable t) { + String msg = String.format("rollback SagaAnnotation resource error, resourceId: %s, xid: %s.", resourceId, xid); + LOGGER.error(msg, ExceptionUtil.unwrap(t)); + return BranchStatus.PhaseTwo_RollbackFailed_Retryable; + } finally { + BusinessActionContextUtil.clear(); + } + } + + @Override + public Map getManagedResources() { + return resourceCache; + } + + @Override + public BranchType getBranchType() { + return BranchType.SAGA_ANNOTATION; + } + + private Object[] getTwoPhaseRollbackArgs(SagaAnnotationResource resource, BusinessActionContext businessActionContext) { + String[] keys = resource.getPhaseTwoCompensationKeys(); + Class[] argsRollbackClasses = resource.getCompensationArgsClasses(); + return getTwoPhaseMethodParams(keys, argsRollbackClasses, businessActionContext); + } + + protected 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/saga/seata-saga-annotation/src/main/java/org/apache/seata/saga/rm/api/CompensationBusinessAction.java b/saga/seata-saga-annotation/src/main/java/org/apache/seata/saga/rm/api/CompensationBusinessAction.java new file mode 100644 index 00000000000..fe47e18fe73 --- /dev/null +++ b/saga/seata-saga-annotation/src/main/java/org/apache/seata/saga/rm/api/CompensationBusinessAction.java @@ -0,0 +1,71 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.seata.saga.rm.api; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Inherited; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import org.apache.seata.rm.tcc.api.BusinessActionContext; + + +/** + * Saga annotation. + * Define a saga interface, which added on the commit method, if occurs rollback, compensation will be called. + */ +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.METHOD}) +@Inherited +public @interface CompensationBusinessAction { + + /** + * saga bean name, must be unique + * + * @return the string + */ + String name(); + + /** + * compensation method name + * + * @return the string + */ + String compensationMethod() default "compensation"; + + /** + * delay branch report while sharing params to phase 2 to enhance performance + * + * @return isDelayReport + */ + boolean isDelayReport() default false; + + /** + * whether use fence (idempotent,non_rollback,suspend) + * + * @return the boolean + */ + boolean useFence() default false; + + /** + * compensation method's args + * + * @return the Class[] + */ + Class[] compensationArgsClasses() default {BusinessActionContext.class}; +} \ No newline at end of file diff --git a/saga/seata-saga-annotation/src/main/java/org/apache/seata/saga/rm/interceptor/SagaAnnotationActionInterceptorHandler.java b/saga/seata-saga-annotation/src/main/java/org/apache/seata/saga/rm/interceptor/SagaAnnotationActionInterceptorHandler.java new file mode 100644 index 00000000000..3685f249b51 --- /dev/null +++ b/saga/seata-saga-annotation/src/main/java/org/apache/seata/saga/rm/interceptor/SagaAnnotationActionInterceptorHandler.java @@ -0,0 +1,160 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.seata.saga.rm.interceptor; + +import org.apache.seata.common.Constants; +import org.apache.seata.common.util.ReflectionUtil; +import org.apache.seata.core.context.RootContext; +import org.apache.seata.core.model.BranchType; +import org.apache.seata.integration.tx.api.interceptor.ActionInterceptorHandler; +import org.apache.seata.integration.tx.api.interceptor.InvocationHandlerType; +import org.apache.seata.integration.tx.api.interceptor.InvocationWrapper; +import org.apache.seata.integration.tx.api.interceptor.SeataInterceptorPosition; +import org.apache.seata.integration.tx.api.interceptor.TwoPhaseBusinessActionParam; +import org.apache.seata.integration.tx.api.interceptor.handler.AbstractProxyInvocationHandler; +import org.apache.seata.saga.rm.api.CompensationBusinessAction; +import org.slf4j.MDC; + +import java.lang.annotation.Annotation; +import java.lang.reflect.Method; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; + +/** + * saga-annotation invocationHandler + */ +public class SagaAnnotationActionInterceptorHandler extends AbstractProxyInvocationHandler { + + private Set methodsToProxy; + + protected Object targetBean; + + protected ActionInterceptorHandler actionInterceptorHandler = new ActionInterceptorHandler(); + + protected Map parseAnnotationCache = new ConcurrentHashMap<>(); + + public SagaAnnotationActionInterceptorHandler(Object targetBean, Set methodsToProxy) { + this.targetBean = targetBean; + this.methodsToProxy = 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(); + Annotation 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 (getBranchType() != previousBranchType) { + RootContext.bindBranchType(getBranchType()); + } + try { + TwoPhaseBusinessActionParam businessActionParam = createTwoPhaseBusinessActionParam(businessAction); + return actionInterceptorHandler.proceed(method, invocation.getArguments(), xid, businessActionParam, + invocation::proceed); + } finally { + //if not TCC, unbind branchType + if (getBranchType() != previousBranchType) { + RootContext.unbindBranchType(); + } + //MDC remove branchId + MDC.remove(RootContext.MDC_KEY_BRANCH_ID); + } + } + + //not TCC try method + return invocation.proceed(); + } + + + @Override + public Set getMethodsToProxy() { + return methodsToProxy; + } + + @Override + public SeataInterceptorPosition getPosition() { + return SeataInterceptorPosition.Any; + } + + @Override + public String type() { + return InvocationHandlerType.SagaAnnotation.name(); + } + + @Override + public int order() { + return 2; + } + + private TwoPhaseBusinessActionParam createTwoPhaseBusinessActionParam(Annotation annotation) { + CompensationBusinessAction businessAction = (CompensationBusinessAction) annotation; + + TwoPhaseBusinessActionParam businessActionParam = new TwoPhaseBusinessActionParam(); + businessActionParam.setActionName(businessAction.name()); + businessActionParam.setDelayReport(businessAction.isDelayReport()); + businessActionParam.setUseCommonFence(businessAction.useFence()); + businessActionParam.setBranchType(BranchType.SAGA_ANNOTATION); + + Map businessActionContextMap = new HashMap<>(4); + businessActionContextMap.put(Constants.ROLLBACK_METHOD, businessAction.compensationMethod()); + businessActionContextMap.put(Constants.ACTION_NAME, businessAction.name()); + businessActionContextMap.put(Constants.USE_COMMON_FENCE, businessAction.useFence()); + businessActionParam.setBusinessActionContext(businessActionContextMap); + + return businessActionParam; + } + + private Annotation parseAnnotation(Method methodKey) { + return parseAnnotationCache.computeIfAbsent(methodKey, method -> { + Annotation twoPhaseBusinessAction = method.getAnnotation(getAnnotationClass()); + if (twoPhaseBusinessAction == null) { + Set> interfaceClasses = ReflectionUtil.getInterfaces(targetBean.getClass()); + for (Class interClass : interfaceClasses) { + try { + Method m = interClass.getMethod(method.getName(), method.getParameterTypes()); + twoPhaseBusinessAction = m.getAnnotation(getAnnotationClass()); + } catch (NoSuchMethodException e) { + throw new RuntimeException(e); + } + } + } + return twoPhaseBusinessAction; + }); + } + + private BranchType getBranchType() { + return BranchType.SAGA_ANNOTATION; + } + + private Class getAnnotationClass() { + return CompensationBusinessAction.class; + } + +} \ No newline at end of file diff --git a/saga/seata-saga-annotation/src/main/java/org/apache/seata/saga/rm/interceptor/parser/SagaAnnotationActionInterceptorParser.java b/saga/seata-saga-annotation/src/main/java/org/apache/seata/saga/rm/interceptor/parser/SagaAnnotationActionInterceptorParser.java new file mode 100644 index 00000000000..84ba16c9c58 --- /dev/null +++ b/saga/seata-saga-annotation/src/main/java/org/apache/seata/saga/rm/interceptor/parser/SagaAnnotationActionInterceptorParser.java @@ -0,0 +1,104 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.seata.saga.rm.interceptor.parser; + +import org.apache.seata.common.exception.FrameworkException; +import org.apache.seata.common.util.ReflectionUtil; +import org.apache.seata.core.model.Resource; +import org.apache.seata.integration.tx.api.interceptor.ActionContextUtil; +import org.apache.seata.integration.tx.api.interceptor.handler.ProxyInvocationHandler; +import org.apache.seata.integration.tx.api.interceptor.parser.IfNeedEnhanceBean; +import org.apache.seata.integration.tx.api.interceptor.parser.InterfaceParser; +import org.apache.seata.integration.tx.api.interceptor.parser.NeedEnhanceEnum; +import org.apache.seata.integration.tx.api.remoting.parser.DefaultRemotingParser; +import org.apache.seata.rm.DefaultResourceManager; +import org.apache.seata.saga.rm.SagaAnnotationResource; +import org.apache.seata.saga.rm.api.CompensationBusinessAction; +import org.apache.seata.saga.rm.interceptor.SagaAnnotationActionInterceptorHandler; + +import java.lang.annotation.Annotation; +import java.lang.reflect.Method; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; + +/** + * saga-annotation proxyInvocationHandler parser + * used to identify the saga annotation @CompensationBusinessAction and return the proxy handler. + */ +public class SagaAnnotationActionInterceptorParser implements InterfaceParser { + + @Override + public ProxyInvocationHandler parserInterfaceToProxy(Object target, String objectName) { + Map> methodClassMap = ReflectionUtil.findMatchMethodClazzMap(target.getClass(), method -> method.isAnnotationPresent(getAnnotationClass())); + Set methodsToProxy = methodClassMap.keySet(); + if (methodsToProxy.isEmpty()) { + return null; + } + + // register resource and enhance with interceptor + registerResource(target, methodClassMap); + + return new SagaAnnotationActionInterceptorHandler(target, methodsToProxy.stream().map(Method::getName).collect(Collectors.toSet())); + } + + private void registerResource(Object target, Map> methodClassMap) { + try { + for (Map.Entry> methodClassEntry : methodClassMap.entrySet()) { + Method method = methodClassEntry.getKey(); + Annotation annotation = method.getAnnotation(getAnnotationClass()); + if (annotation != null) { + Resource resource = createResource(target, methodClassEntry.getValue(), annotation); + //registry resource + DefaultResourceManager.get().registerResource(resource); + } + } + } catch (Throwable t) { + throw new FrameworkException(t, "register SagaAnnotation resource error"); + } + } + + + @Override + public IfNeedEnhanceBean parseIfNeedEnhancement(Class beanClass) { + IfNeedEnhanceBean ifNeedEnhanceBean = new IfNeedEnhanceBean(); + //current support remote service enhance + if (DefaultRemotingParser.get().isService(beanClass)) { + ifNeedEnhanceBean.setIfNeed(true); + ifNeedEnhanceBean.setNeedEnhanceEnum(NeedEnhanceEnum.SERVICE_BEAN); + } + return ifNeedEnhanceBean; + } + + protected Class getAnnotationClass() { + return CompensationBusinessAction.class; + } + + protected Resource createResource(Object targetBean, Class serviceClass, Annotation annotation) throws NoSuchMethodException { + CompensationBusinessAction compensationBusinessAction = (CompensationBusinessAction) annotation; + SagaAnnotationResource sagaAnnotationResource = new SagaAnnotationResource(); + sagaAnnotationResource.setActionName(compensationBusinessAction.name()); + sagaAnnotationResource.setTargetBean(targetBean); + sagaAnnotationResource.setCompensationMethodName(compensationBusinessAction.compensationMethod()); + Method compensationMethod = serviceClass.getMethod(compensationBusinessAction.compensationMethod(), compensationBusinessAction.compensationArgsClasses()); + sagaAnnotationResource.setCompensationMethod(compensationMethod); + sagaAnnotationResource.setCompensationArgsClasses(compensationBusinessAction.compensationArgsClasses()); + sagaAnnotationResource.setPhaseTwoCompensationKeys(ActionContextUtil.getTwoPhaseArgs(sagaAnnotationResource.getCompensationMethod(), compensationBusinessAction.compensationArgsClasses())); + + return sagaAnnotationResource; + } +} diff --git a/compatible/src/main/resources/META-INF/services/org.apache.seata.integration.tx.api.interceptor.parser.RegisterResourceParser b/saga/seata-saga-annotation/src/main/resources/META-INF/services/org.apache.seata.core.model.ResourceManager similarity index 93% rename from compatible/src/main/resources/META-INF/services/org.apache.seata.integration.tx.api.interceptor.parser.RegisterResourceParser rename to saga/seata-saga-annotation/src/main/resources/META-INF/services/org.apache.seata.core.model.ResourceManager index 82c08236e87..fb0ebe6018a 100644 --- a/compatible/src/main/resources/META-INF/services/org.apache.seata.integration.tx.api.interceptor.parser.RegisterResourceParser +++ b/saga/seata-saga-annotation/src/main/resources/META-INF/services/org.apache.seata.core.model.ResourceManager @@ -14,4 +14,4 @@ # See the License for the specific language governing permissions and # limitations under the License. # -io.seata.rm.tcc.resource.parser.TccRegisterResourceParser \ No newline at end of file +org.apache.seata.saga.rm.SagaAnnotationResourceManager diff --git a/saga/seata-saga-annotation/src/main/resources/META-INF/services/org.apache.seata.integration.tx.api.interceptor.parser.InterfaceParser b/saga/seata-saga-annotation/src/main/resources/META-INF/services/org.apache.seata.integration.tx.api.interceptor.parser.InterfaceParser new file mode 100644 index 00000000000..099adeee7a7 --- /dev/null +++ b/saga/seata-saga-annotation/src/main/resources/META-INF/services/org.apache.seata.integration.tx.api.interceptor.parser.InterfaceParser @@ -0,0 +1,17 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You 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. +# +org.apache.seata.saga.rm.interceptor.parser.SagaAnnotationActionInterceptorParser \ No newline at end of file diff --git a/tcc/src/main/resources/META-INF/services/org.apache.seata.integration.tx.api.interceptor.parser.RegisterResourceParser b/saga/seata-saga-annotation/src/main/resources/META-INF/services/org.apache.seata.rm.AbstractRMHandler similarity index 92% rename from tcc/src/main/resources/META-INF/services/org.apache.seata.integration.tx.api.interceptor.parser.RegisterResourceParser rename to saga/seata-saga-annotation/src/main/resources/META-INF/services/org.apache.seata.rm.AbstractRMHandler index 0df6a52001a..042138ccffd 100644 --- a/tcc/src/main/resources/META-INF/services/org.apache.seata.integration.tx.api.interceptor.parser.RegisterResourceParser +++ b/saga/seata-saga-annotation/src/main/resources/META-INF/services/org.apache.seata.rm.AbstractRMHandler @@ -14,4 +14,4 @@ # See the License for the specific language governing permissions and # limitations under the License. # -org.apache.seata.rm.tcc.resource.parser.TccRegisterResourceParser \ No newline at end of file +org.apache.seata.saga.rm.RMHandlerSagaAnnotation \ No newline at end of file diff --git a/saga/seata-saga-engine/src/main/java/org/apache/seata/saga/engine/config/AbstractStateMachineConfig.java b/saga/seata-saga-engine/src/main/java/org/apache/seata/saga/engine/config/AbstractStateMachineConfig.java index f61c3777554..96446594775 100644 --- a/saga/seata-saga-engine/src/main/java/org/apache/seata/saga/engine/config/AbstractStateMachineConfig.java +++ b/saga/seata-saga-engine/src/main/java/org/apache/seata/saga/engine/config/AbstractStateMachineConfig.java @@ -54,6 +54,7 @@ import org.apache.seata.saga.proctrl.impl.ProcessControllerImpl; import org.apache.seata.saga.proctrl.process.impl.CustomizeBusinessProcessor; import org.apache.seata.saga.statelang.domain.DomainConstants; +import org.apache.seata.saga.statelang.domain.StateType; import javax.script.ScriptEngineManager; import java.io.InputStream; @@ -214,20 +215,8 @@ public void init() throws Exception { } public ProcessControllerImpl createProcessorController(ProcessCtrlEventPublisher eventPublisher) throws Exception { - StateMachineProcessRouter stateMachineProcessRouter = new StateMachineProcessRouter(); - stateMachineProcessRouter.initDefaultStateRouters(); - loadStateRouterInterceptors(stateMachineProcessRouter.getStateRouters()); - - StateMachineProcessHandler stateMachineProcessHandler = new StateMachineProcessHandler(); - stateMachineProcessHandler.initDefaultHandlers(); - loadStateHandlerInterceptors(stateMachineProcessHandler.getStateHandlers()); - - DefaultRouterHandler defaultRouterHandler = new DefaultRouterHandler(); - defaultRouterHandler.setEventPublisher(eventPublisher); - - Map processRouterMap = new HashMap<>(1); - processRouterMap.put(ProcessType.STATE_LANG.getCode(), stateMachineProcessRouter); - defaultRouterHandler.setProcessRouters(processRouterMap); + StateMachineProcessHandler stateMachineProcessHandler = buildStateMachineProcessHandler(); + DefaultRouterHandler defaultRouterHandler = buildDefaultRouterHandler(eventPublisher); CustomizeBusinessProcessor customizeBusinessProcessor = new CustomizeBusinessProcessor(); @@ -245,7 +234,28 @@ public ProcessControllerImpl createProcessorController(ProcessCtrlEventPublisher return processorController; } - public void loadStateHandlerInterceptors(Map stateHandlerMap) { + private StateMachineProcessHandler buildStateMachineProcessHandler() { + StateMachineProcessHandler stateMachineProcessHandler = new StateMachineProcessHandler(); + stateMachineProcessHandler.initDefaultHandlers(); + loadStateHandlerInterceptors(stateMachineProcessHandler.getStateHandlers()); + return stateMachineProcessHandler; + } + + private DefaultRouterHandler buildDefaultRouterHandler(ProcessCtrlEventPublisher eventPublisher) { + DefaultRouterHandler defaultRouterHandler = new DefaultRouterHandler(); + defaultRouterHandler.setEventPublisher(eventPublisher); + + StateMachineProcessRouter stateMachineProcessRouter = new StateMachineProcessRouter(); + stateMachineProcessRouter.initDefaultStateRouters(); + loadStateRouterInterceptors(stateMachineProcessRouter.getStateRouters()); + + Map processRouterMap = new HashMap<>(2); + processRouterMap.put(ProcessType.STATE_LANG.getCode(), stateMachineProcessRouter); + defaultRouterHandler.setProcessRouters(processRouterMap); + return defaultRouterHandler; + } + + public void loadStateHandlerInterceptors(Map stateHandlerMap) { for (StateHandler stateHandler : stateHandlerMap.values()) { if (stateHandler instanceof InterceptableStateHandler) { InterceptableStateHandler interceptableStateHandler = (InterceptableStateHandler) stateHandler; @@ -259,7 +269,7 @@ public void loadStateHandlerInterceptors(Map stateHandlerM } } - public void loadStateRouterInterceptors(Map stateRouterMap) { + public void loadStateRouterInterceptors(Map stateRouterMap) { for (StateRouter stateRouter : stateRouterMap.values()) { if (stateRouter instanceof InterceptableStateRouter) { InterceptableStateRouter interceptableStateRouter = (InterceptableStateRouter) stateRouter; diff --git a/saga/seata-saga-engine/src/main/java/org/apache/seata/saga/engine/impl/ProcessCtrlStateMachineEngine.java b/saga/seata-saga-engine/src/main/java/org/apache/seata/saga/engine/impl/ProcessCtrlStateMachineEngine.java index d18e72f8be8..bc4405abe96 100644 --- a/saga/seata-saga-engine/src/main/java/org/apache/seata/saga/engine/impl/ProcessCtrlStateMachineEngine.java +++ b/saga/seata-saga-engine/src/main/java/org/apache/seata/saga/engine/impl/ProcessCtrlStateMachineEngine.java @@ -38,6 +38,7 @@ import org.apache.seata.saga.proctrl.ProcessContext; import org.apache.seata.saga.proctrl.ProcessType; import org.apache.seata.saga.statelang.domain.DomainConstants; +import org.apache.seata.saga.statelang.domain.StateType; import org.apache.seata.saga.statelang.domain.ExecutionStatus; import org.apache.seata.saga.statelang.domain.State; import org.apache.seata.saga.statelang.domain.StateInstance; @@ -237,7 +238,7 @@ protected StateMachineInstance forwardInternal(String stateMachineInstId, Map actList = stateMachineInstance.getStateList(); if (CollectionUtils.isEmpty(actList)) { throw new ForwardInvalidException("StateMachineInstance[id:" + stateMachineInstId - + "] has no stateInstance, pls start a new StateMachine execution instead", + + "] has no stateInstance, please start a new StateMachine execution instead", FrameworkErrorCode.OperationDenied); } @@ -280,7 +281,7 @@ protected StateMachineInstance forwardInternal(String stateMachineInstId, Map stateIn continue; } - if (DomainConstants.STATE_TYPE_SUB_STATE_MACHINE.equals(stateInstance.getType())) { + if (StateType.SUB_STATE_MACHINE.equals(stateInstance.getType())) { StateInstance finalState = stateInstance; diff --git a/saga/seata-saga-engine/src/main/java/org/apache/seata/saga/engine/pcext/StateMachineProcessHandler.java b/saga/seata-saga-engine/src/main/java/org/apache/seata/saga/engine/pcext/StateMachineProcessHandler.java index 30cdbf22c9d..462d9b1a055 100644 --- a/saga/seata-saga-engine/src/main/java/org/apache/seata/saga/engine/pcext/StateMachineProcessHandler.java +++ b/saga/seata-saga-engine/src/main/java/org/apache/seata/saga/engine/pcext/StateMachineProcessHandler.java @@ -33,7 +33,7 @@ import org.apache.seata.saga.engine.pcext.handlers.SucceedEndStateHandler; import org.apache.seata.saga.proctrl.ProcessContext; import org.apache.seata.saga.proctrl.handler.ProcessHandler; -import org.apache.seata.saga.statelang.domain.DomainConstants; +import org.apache.seata.saga.statelang.domain.StateType; import org.apache.seata.saga.statelang.domain.State; /** @@ -43,13 +43,13 @@ */ public class StateMachineProcessHandler implements ProcessHandler { - private final Map stateHandlers = new ConcurrentHashMap<>(); + private final Map stateHandlers = new ConcurrentHashMap<>(); @Override public void process(ProcessContext context) throws FrameworkException { StateInstruction instruction = context.getInstruction(StateInstruction.class); State state = instruction.getState(context); - String stateType = state.getType(); + StateType stateType = state.getType(); StateHandler stateHandler = stateHandlers.get(stateType); List interceptors = null; @@ -84,28 +84,27 @@ public void process(ProcessContext context) throws FrameworkException { } public void initDefaultHandlers() { - if (stateHandlers.isEmpty()) { - stateHandlers.put(DomainConstants.STATE_TYPE_SERVICE_TASK, new ServiceTaskStateHandler()); - - stateHandlers.put(DomainConstants.STATE_TYPE_SCRIPT_TASK, new ScriptTaskStateHandler()); - - stateHandlers.put(DomainConstants.STATE_TYPE_SUB_MACHINE_COMPENSATION, new ServiceTaskStateHandler()); + if (!stateHandlers.isEmpty()) { + return; + } - stateHandlers.put(DomainConstants.STATE_TYPE_SUB_STATE_MACHINE, new SubStateMachineHandler()); + stateHandlers.put(StateType.SERVICE_TASK, new ServiceTaskStateHandler()); + stateHandlers.put(StateType.SCRIPT_TASK, new ScriptTaskStateHandler()); + stateHandlers.put(StateType.SUB_MACHINE_COMPENSATION, new ServiceTaskStateHandler()); + stateHandlers.put(StateType.SUB_STATE_MACHINE, new SubStateMachineHandler()); + stateHandlers.put(StateType.CHOICE, new ChoiceStateHandler()); + stateHandlers.put(StateType.SUCCEED, new SucceedEndStateHandler()); + stateHandlers.put(StateType.FAIL, new FailEndStateHandler()); + stateHandlers.put(StateType.COMPENSATION_TRIGGER, new CompensationTriggerStateHandler()); + stateHandlers.put(StateType.LOOP_START, new LoopStartStateHandler()); - stateHandlers.put(DomainConstants.STATE_TYPE_CHOICE, new ChoiceStateHandler()); - stateHandlers.put(DomainConstants.STATE_TYPE_SUCCEED, new SucceedEndStateHandler()); - stateHandlers.put(DomainConstants.STATE_TYPE_FAIL, new FailEndStateHandler()); - stateHandlers.put(DomainConstants.STATE_TYPE_COMPENSATION_TRIGGER, new CompensationTriggerStateHandler()); - stateHandlers.put(DomainConstants.STATE_TYPE_LOOP_START, new LoopStartStateHandler()); - } } - public Map getStateHandlers() { + public Map getStateHandlers() { return stateHandlers; } - public void setStateHandlers(Map stateHandlers) { + public void setStateHandlers(Map stateHandlers) { this.stateHandlers.putAll(stateHandlers); } } diff --git a/saga/seata-saga-engine/src/main/java/org/apache/seata/saga/engine/pcext/StateMachineProcessRouter.java b/saga/seata-saga-engine/src/main/java/org/apache/seata/saga/engine/pcext/StateMachineProcessRouter.java index e3a4569fa9e..bcb7c238858 100644 --- a/saga/seata-saga-engine/src/main/java/org/apache/seata/saga/engine/pcext/StateMachineProcessRouter.java +++ b/saga/seata-saga-engine/src/main/java/org/apache/seata/saga/engine/pcext/StateMachineProcessRouter.java @@ -33,6 +33,7 @@ import org.apache.seata.saga.statelang.domain.DomainConstants; import org.apache.seata.saga.statelang.domain.State; import org.apache.seata.saga.statelang.domain.StateMachine; +import org.apache.seata.saga.statelang.domain.StateType; /** * StateMachine ProcessRouter @@ -41,7 +42,7 @@ */ public class StateMachineProcessRouter implements ProcessRouter { - private final Map stateRouters = new ConcurrentHashMap<>(); + private final Map stateRouters = new ConcurrentHashMap<>(); @Override public Instruction route(ProcessContext context) throws FrameworkException { @@ -60,7 +61,7 @@ public Instruction route(ProcessContext context) throws FrameworkException { state = stateMachine.getStates().get(stateInstruction.getStateName()); } - String stateType = state.getType(); + StateType stateType = state.getType(); StateRouter router = stateRouters.get(stateType); @@ -105,26 +106,28 @@ public Instruction route(ProcessContext context) throws FrameworkException { } public void initDefaultStateRouters() { - if (this.stateRouters.isEmpty()) { - TaskStateRouter taskStateRouter = new TaskStateRouter(); - this.stateRouters.put(DomainConstants.STATE_TYPE_SERVICE_TASK, taskStateRouter); - this.stateRouters.put(DomainConstants.STATE_TYPE_SCRIPT_TASK, taskStateRouter); - this.stateRouters.put(DomainConstants.STATE_TYPE_CHOICE, taskStateRouter); - this.stateRouters.put(DomainConstants.STATE_TYPE_COMPENSATION_TRIGGER, taskStateRouter); - this.stateRouters.put(DomainConstants.STATE_TYPE_SUB_STATE_MACHINE, taskStateRouter); - this.stateRouters.put(DomainConstants.STATE_TYPE_SUB_MACHINE_COMPENSATION, taskStateRouter); - this.stateRouters.put(DomainConstants.STATE_TYPE_LOOP_START, taskStateRouter); - - this.stateRouters.put(DomainConstants.STATE_TYPE_SUCCEED, new EndStateRouter()); - this.stateRouters.put(DomainConstants.STATE_TYPE_FAIL, new EndStateRouter()); + if (!stateRouters.isEmpty()) { + return; } + + TaskStateRouter taskStateRouter = new TaskStateRouter(); + stateRouters.put(StateType.SERVICE_TASK, taskStateRouter); + stateRouters.put(StateType.SCRIPT_TASK, taskStateRouter); + stateRouters.put(StateType.CHOICE, taskStateRouter); + stateRouters.put(StateType.COMPENSATION_TRIGGER, taskStateRouter); + stateRouters.put(StateType.SUB_STATE_MACHINE, taskStateRouter); + stateRouters.put(StateType.SUB_MACHINE_COMPENSATION, taskStateRouter); + stateRouters.put(StateType.LOOP_START, taskStateRouter); + + stateRouters.put(StateType.SUCCEED, new EndStateRouter()); + stateRouters.put(StateType.FAIL, new EndStateRouter()); } - public Map getStateRouters() { + public Map getStateRouters() { return stateRouters; } - public void setStateRouters(Map stateRouters) { + public void setStateRouters(Map stateRouters) { this.stateRouters.putAll(stateRouters); } } diff --git a/saga/seata-saga-engine/src/main/java/org/apache/seata/saga/engine/pcext/interceptors/ServiceTaskHandlerInterceptor.java b/saga/seata-saga-engine/src/main/java/org/apache/seata/saga/engine/pcext/interceptors/ServiceTaskHandlerInterceptor.java index 1cee6d2ccf4..5091fc584d6 100644 --- a/saga/seata-saga-engine/src/main/java/org/apache/seata/saga/engine/pcext/interceptors/ServiceTaskHandlerInterceptor.java +++ b/saga/seata-saga-engine/src/main/java/org/apache/seata/saga/engine/pcext/interceptors/ServiceTaskHandlerInterceptor.java @@ -369,7 +369,7 @@ private void decideExecutionStatus(ProcessContext context, StateInstance stateIn } EngineExecutionException exception = new EngineExecutionException("State [" + state.getName() - + "] execute finished, but cannot matching status, pls check its status manually", + + "] execute finished, but cannot matching status, please check its status manually", FrameworkErrorCode.NoMatchedStatus); if (LOGGER.isDebugEnabled()) { LOGGER.debug("State[{}] execute finish with status[{}]", state.getName(), diff --git a/saga/seata-saga-engine/src/main/java/org/apache/seata/saga/engine/pcext/utils/CompensationHolder.java b/saga/seata-saga-engine/src/main/java/org/apache/seata/saga/engine/pcext/utils/CompensationHolder.java index d7d19d42a4f..b4d748646ae 100644 --- a/saga/seata-saga-engine/src/main/java/org/apache/seata/saga/engine/pcext/utils/CompensationHolder.java +++ b/saga/seata-saga-engine/src/main/java/org/apache/seata/saga/engine/pcext/utils/CompensationHolder.java @@ -26,6 +26,7 @@ import org.apache.seata.common.util.CollectionUtils; import org.apache.seata.common.util.StringUtils; import org.apache.seata.saga.engine.exception.EngineExecutionException; +import org.apache.seata.saga.statelang.domain.StateType; import org.apache.seata.saga.engine.utils.ExceptionUtils; import org.apache.seata.saga.proctrl.ProcessContext; import org.apache.seata.saga.statelang.domain.DomainConstants; @@ -125,13 +126,13 @@ private static boolean stateNeedToCompensate(StateInstance stateInstance) { if (stateInstance.isIgnoreStatus()) { return false; } - if (DomainConstants.STATE_TYPE_SUB_STATE_MACHINE.equals(stateInstance.getType())) { + if (StateType.SUB_STATE_MACHINE.equals(stateInstance.getType())) { return (!ExecutionStatus.FA.equals(stateInstance.getStatus())) && (!ExecutionStatus.SU.equals( stateInstance.getCompensationStatus())); } else { - return DomainConstants.STATE_TYPE_SERVICE_TASK.equals(stateInstance.getType()) && !stateInstance + return StateType.SERVICE_TASK.equals(stateInstance.getType()) && !stateInstance .isForCompensation() && (!ExecutionStatus.FA.equals(stateInstance.getStatus())) && (!ExecutionStatus.SU .equals(stateInstance.getCompensationStatus())); } diff --git a/saga/seata-saga-engine/src/main/java/org/apache/seata/saga/engine/pcext/utils/LoopTaskUtils.java b/saga/seata-saga-engine/src/main/java/org/apache/seata/saga/engine/pcext/utils/LoopTaskUtils.java index e71818f1f1a..f9500eb3337 100644 --- a/saga/seata-saga-engine/src/main/java/org/apache/seata/saga/engine/pcext/utils/LoopTaskUtils.java +++ b/saga/seata-saga-engine/src/main/java/org/apache/seata/saga/engine/pcext/utils/LoopTaskUtils.java @@ -32,6 +32,7 @@ import org.apache.seata.saga.engine.exception.ForwardInvalidException; import org.apache.seata.saga.engine.expression.ExpressionResolver; import org.apache.seata.saga.engine.pcext.StateInstruction; +import org.apache.seata.saga.statelang.domain.StateType; import org.apache.seata.saga.proctrl.ProcessContext; import org.apache.seata.saga.proctrl.impl.ProcessContextImpl; import org.apache.seata.saga.statelang.domain.DomainConstants; @@ -97,9 +98,9 @@ public static Loop getLoopConfig(ProcessContext context, State currentState) { * @return the boolean */ public static boolean matchLoop(State state) { - return state != null && (DomainConstants.STATE_TYPE_SERVICE_TASK.equals(state.getType()) - || DomainConstants.STATE_TYPE_SCRIPT_TASK.equals(state.getType()) - || DomainConstants.STATE_TYPE_SUB_STATE_MACHINE.equals(state.getType())); + return state != null && (StateType.SERVICE_TASK.equals(state.getType()) + || StateType.SCRIPT_TASK.equals(state.getType()) + || StateType.SUB_STATE_MACHINE.equals(state.getType())); } /** @@ -334,7 +335,7 @@ public static boolean isForSubStateMachineForward(ProcessContext context) { StateInstance lastRetriedStateInstance = LoopTaskUtils.findOutLastRetriedStateInstance( stateMachineInstance, LoopTaskUtils.generateLoopStateName(context, instruction.getStateName())); - if (null != lastRetriedStateInstance && DomainConstants.STATE_TYPE_SUB_STATE_MACHINE.equals( + if (null != lastRetriedStateInstance && StateType.SUB_STATE_MACHINE.getValue().equals( lastRetriedStateInstance.getType()) && !ExecutionStatus.SU.equals( lastRetriedStateInstance.getCompensationStatus())) { @@ -381,7 +382,7 @@ public static String decideCurrentExceptionRoute(List subContext // compensate must be execute State state = stateMachine.getState(next); - if (DomainConstants.STATE_TYPE_COMPENSATION_TRIGGER.equals(state.getType())) { + if (StateType.COMPENSATION_TRIGGER.equals(state.getType())) { route = next; break; } else if (null == route) { diff --git a/saga/seata-saga-engine/src/main/java/org/apache/seata/saga/engine/strategy/impl/DefaultStatusDecisionStrategy.java b/saga/seata-saga-engine/src/main/java/org/apache/seata/saga/engine/strategy/impl/DefaultStatusDecisionStrategy.java index 54b75a3815c..5e8ddd23283 100644 --- a/saga/seata-saga-engine/src/main/java/org/apache/seata/saga/engine/strategy/impl/DefaultStatusDecisionStrategy.java +++ b/saga/seata-saga-engine/src/main/java/org/apache/seata/saga/engine/strategy/impl/DefaultStatusDecisionStrategy.java @@ -21,6 +21,7 @@ import org.apache.seata.common.exception.FrameworkErrorCode; import org.apache.seata.common.util.CollectionUtils; import org.apache.seata.saga.engine.exception.EngineExecutionException; +import org.apache.seata.saga.statelang.domain.StateType; import org.apache.seata.saga.engine.pcext.utils.CompensationHolder; import org.apache.seata.saga.engine.strategy.StatusDecisionStrategy; import org.apache.seata.saga.engine.utils.ExceptionUtils; @@ -110,7 +111,7 @@ public static void setMachineStatusBasedOnStateListAndException(StateMachineInst stateMachineInstance.setStatus(ExecutionStatus.UN); hasSetStatus = true; } else if (ExecutionStatus.SU.equals(stateInstance.getStatus())) { - if (DomainConstants.STATE_TYPE_SERVICE_TASK.equals(stateInstance.getType())) { + if (StateType.SERVICE_TASK.equals(stateInstance.getType())) { if (stateInstance.isForUpdate() && !stateInstance.isForCompensation()) { hasSuccessUpdateService = true; } diff --git a/saga/seata-saga-spring/src/main/java/org/apache/seata/saga/engine/store/db/DbAndReportTcStateLogStore.java b/saga/seata-saga-spring/src/main/java/org/apache/seata/saga/engine/store/db/DbAndReportTcStateLogStore.java index d70515ca288..6c0a2f18fc8 100644 --- a/saga/seata-saga-spring/src/main/java/org/apache/seata/saga/engine/store/db/DbAndReportTcStateLogStore.java +++ b/saga/seata-saga-spring/src/main/java/org/apache/seata/saga/engine/store/db/DbAndReportTcStateLogStore.java @@ -53,6 +53,7 @@ import org.apache.seata.saga.statelang.domain.StateInstance; import org.apache.seata.saga.statelang.domain.StateMachine; import org.apache.seata.saga.statelang.domain.StateMachineInstance; +import org.apache.seata.saga.statelang.domain.StateType; import org.apache.seata.saga.statelang.domain.impl.ServiceTaskStateImpl; import org.apache.seata.saga.statelang.domain.impl.StateInstanceImpl; import org.apache.seata.saga.statelang.domain.impl.StateMachineInstanceImpl; @@ -843,7 +844,7 @@ public void toStatement(StateInstance stateInstance, PreparedStatement statement statement.setString(1, stateInstance.getId()); statement.setString(2, stateInstance.getMachineInstanceId()); statement.setString(3, stateInstance.getName()); - statement.setString(4, stateInstance.getType()); + statement.setString(4, stateInstance.getType().getValue()); statement.setTimestamp(5, new Timestamp(stateInstance.getGmtStarted().getTime())); statement.setString(6, stateInstance.getServiceName()); statement.setString(7, stateInstance.getServiceMethod()); @@ -908,7 +909,7 @@ public StateInstance toObject(ResultSet resultSet) throws SQLException { stateInstance.setId(resultSet.getString("id")); stateInstance.setMachineInstanceId(resultSet.getString("machine_inst_id")); stateInstance.setName(resultSet.getString("name")); - stateInstance.setType(resultSet.getString("type")); + stateInstance.setType(StateType.getStateType(resultSet.getString("type"))); stateInstance.setBusinessKey(resultSet.getString("business_key")); stateInstance.setStatus(ExecutionStatus.valueOf(resultSet.getString("status"))); stateInstance.setGmtStarted(resultSet.getTimestamp("gmt_started")); diff --git a/saga/seata-saga-statelang/src/main/java/org/apache/seata/saga/statelang/domain/DomainConstants.java b/saga/seata-saga-statelang/src/main/java/org/apache/seata/saga/statelang/domain/DomainConstants.java index 09ce9cbb017..50267e00e45 100644 --- a/saga/seata-saga-statelang/src/main/java/org/apache/seata/saga/statelang/domain/DomainConstants.java +++ b/saga/seata-saga-statelang/src/main/java/org/apache/seata/saga/statelang/domain/DomainConstants.java @@ -21,19 +21,6 @@ * */ public interface DomainConstants { - - //region State Types - String STATE_TYPE_SERVICE_TASK = "ServiceTask"; - String STATE_TYPE_CHOICE = "Choice"; - String STATE_TYPE_FAIL = "Fail"; - String STATE_TYPE_SUCCEED = "Succeed"; - String STATE_TYPE_COMPENSATION_TRIGGER = "CompensationTrigger"; - String STATE_TYPE_SUB_STATE_MACHINE = "SubStateMachine"; - String STATE_TYPE_SUB_MACHINE_COMPENSATION = "CompensateSubMachine"; - String STATE_TYPE_SCRIPT_TASK = "ScriptTask"; - String STATE_TYPE_LOOP_START = "LoopStart"; - //endregion - String COMPENSATE_SUB_MACHINE_STATE_NAME_PREFIX = "_compensate_sub_machine_state_"; //region Service Types diff --git a/saga/seata-saga-statelang/src/main/java/org/apache/seata/saga/statelang/domain/State.java b/saga/seata-saga-statelang/src/main/java/org/apache/seata/saga/statelang/domain/State.java index 1eb02dbe958..985bfb3bfbc 100644 --- a/saga/seata-saga-statelang/src/main/java/org/apache/seata/saga/statelang/domain/State.java +++ b/saga/seata-saga-statelang/src/main/java/org/apache/seata/saga/statelang/domain/State.java @@ -43,7 +43,7 @@ public interface State { * * @return the state type */ - String getType(); + StateType getType(); /** * next state name diff --git a/saga/seata-saga-statelang/src/main/java/org/apache/seata/saga/statelang/domain/StateInstance.java b/saga/seata-saga-statelang/src/main/java/org/apache/seata/saga/statelang/domain/StateInstance.java index a7b4dc66fd5..fcbcebc9c73 100644 --- a/saga/seata-saga-statelang/src/main/java/org/apache/seata/saga/statelang/domain/StateInstance.java +++ b/saga/seata-saga-statelang/src/main/java/org/apache/seata/saga/statelang/domain/StateInstance.java @@ -71,14 +71,14 @@ public interface StateInstance { * * @return state instance type */ - String getType(); + StateType getType(); /** * set type * * @param type state instance type */ - void setType(String type); + void setType(StateType type); /** * get service name diff --git a/saga/seata-saga-statelang/src/main/java/org/apache/seata/saga/statelang/domain/StateType.java b/saga/seata-saga-statelang/src/main/java/org/apache/seata/saga/statelang/domain/StateType.java new file mode 100644 index 00000000000..38cd69e9881 --- /dev/null +++ b/saga/seata-saga-statelang/src/main/java/org/apache/seata/saga/statelang/domain/StateType.java @@ -0,0 +1,91 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.seata.saga.statelang.domain; + +/** + * StateType + * + */ +public enum StateType { + + /** + * ServiceTask State + */ + SERVICE_TASK("ServiceTask"), + + /** + * Choice State + */ + CHOICE("Choice"), + + /** + * Fail State + */ + FAIL("Fail"), + + /** + * Succeed State + */ + SUCCEED("Succeed"), + + /** + * CompensationTrigger State + */ + COMPENSATION_TRIGGER("CompensationTrigger"), + + /** + * SubStateMachine State + */ + SUB_STATE_MACHINE("SubStateMachine"), + + /** + * CompensateSubMachine State + */ + SUB_MACHINE_COMPENSATION("CompensateSubMachine"), + + /** + * ScriptTask State + */ + SCRIPT_TASK("ScriptTask"), + + /** + * LoopStart State + */ + LOOP_START("LoopStart"); + + + private String value; + + StateType(String value) { + this.value = value; + } + + public String getValue() { + return value; + } + + public static StateType getStateType(String value) { + for (StateType stateType : values()) { + if (stateType.getValue().equalsIgnoreCase(value)) { + return stateType; + } + } + + throw new IllegalArgumentException("Unknown StateType[" + value + "]"); + } + +} diff --git a/saga/seata-saga-statelang/src/main/java/org/apache/seata/saga/statelang/domain/impl/BaseState.java b/saga/seata-saga-statelang/src/main/java/org/apache/seata/saga/statelang/domain/impl/BaseState.java index 50ae952f9b9..001083de024 100644 --- a/saga/seata-saga-statelang/src/main/java/org/apache/seata/saga/statelang/domain/impl/BaseState.java +++ b/saga/seata-saga-statelang/src/main/java/org/apache/seata/saga/statelang/domain/impl/BaseState.java @@ -20,6 +20,7 @@ import org.apache.seata.saga.statelang.domain.State; import org.apache.seata.saga.statelang.domain.StateMachine; +import org.apache.seata.saga.statelang.domain.StateType; /** * BaseState @@ -28,7 +29,7 @@ public abstract class BaseState implements State { private transient String name; - private String type; + private StateType type; private String comment; private String next; private Map extensions; @@ -80,11 +81,11 @@ public void setStateMachine(StateMachine stateMachine) { } @Override - public String getType() { + public StateType getType() { return type; } - protected void setType(String type) { + protected void setType(StateType type) { this.type = type; } } diff --git a/saga/seata-saga-statelang/src/main/java/org/apache/seata/saga/statelang/domain/impl/ChoiceStateImpl.java b/saga/seata-saga-statelang/src/main/java/org/apache/seata/saga/statelang/domain/impl/ChoiceStateImpl.java index c20b6be2935..da087f545e0 100644 --- a/saga/seata-saga-statelang/src/main/java/org/apache/seata/saga/statelang/domain/impl/ChoiceStateImpl.java +++ b/saga/seata-saga-statelang/src/main/java/org/apache/seata/saga/statelang/domain/impl/ChoiceStateImpl.java @@ -20,7 +20,7 @@ import java.util.Map; import org.apache.seata.saga.statelang.domain.ChoiceState; -import org.apache.seata.saga.statelang.domain.DomainConstants; +import org.apache.seata.saga.statelang.domain.StateType; /** * Single selection status @@ -36,7 +36,7 @@ public class ChoiceStateImpl extends BaseState implements ChoiceState { private Map choiceEvaluators; public ChoiceStateImpl() { - setType(DomainConstants.STATE_TYPE_CHOICE); + setType(StateType.CHOICE); } @Override diff --git a/saga/seata-saga-statelang/src/main/java/org/apache/seata/saga/statelang/domain/impl/CompensateSubStateMachineStateImpl.java b/saga/seata-saga-statelang/src/main/java/org/apache/seata/saga/statelang/domain/impl/CompensateSubStateMachineStateImpl.java index c68fe4e47b2..61cddbb6029 100644 --- a/saga/seata-saga-statelang/src/main/java/org/apache/seata/saga/statelang/domain/impl/CompensateSubStateMachineStateImpl.java +++ b/saga/seata-saga-statelang/src/main/java/org/apache/seata/saga/statelang/domain/impl/CompensateSubStateMachineStateImpl.java @@ -17,7 +17,7 @@ package org.apache.seata.saga.statelang.domain.impl; import org.apache.seata.saga.statelang.domain.CompensateSubStateMachineState; -import org.apache.seata.saga.statelang.domain.DomainConstants; +import org.apache.seata.saga.statelang.domain.StateType; /** * Used to compensate the state of the sub state machine, inherited from ServiceTaskState @@ -25,6 +25,6 @@ */ public class CompensateSubStateMachineStateImpl extends ServiceTaskStateImpl implements CompensateSubStateMachineState { public CompensateSubStateMachineStateImpl() { - setType(DomainConstants.STATE_TYPE_SUB_MACHINE_COMPENSATION); + setType(StateType.SUB_MACHINE_COMPENSATION); } } diff --git a/saga/seata-saga-statelang/src/main/java/org/apache/seata/saga/statelang/domain/impl/CompensationTriggerStateImpl.java b/saga/seata-saga-statelang/src/main/java/org/apache/seata/saga/statelang/domain/impl/CompensationTriggerStateImpl.java index 8e638e48905..7aa422cd555 100644 --- a/saga/seata-saga-statelang/src/main/java/org/apache/seata/saga/statelang/domain/impl/CompensationTriggerStateImpl.java +++ b/saga/seata-saga-statelang/src/main/java/org/apache/seata/saga/statelang/domain/impl/CompensationTriggerStateImpl.java @@ -17,7 +17,7 @@ package org.apache.seata.saga.statelang.domain.impl; import org.apache.seata.saga.statelang.domain.CompensationTriggerState; -import org.apache.seata.saga.statelang.domain.DomainConstants; +import org.apache.seata.saga.statelang.domain.StateType; /** * Triggering the "compensation" process for the state machine @@ -26,6 +26,6 @@ public class CompensationTriggerStateImpl extends BaseState implements CompensationTriggerState { public CompensationTriggerStateImpl() { - setType(DomainConstants.STATE_TYPE_COMPENSATION_TRIGGER); + setType(StateType.COMPENSATION_TRIGGER); } } diff --git a/saga/seata-saga-statelang/src/main/java/org/apache/seata/saga/statelang/domain/impl/FailEndStateImpl.java b/saga/seata-saga-statelang/src/main/java/org/apache/seata/saga/statelang/domain/impl/FailEndStateImpl.java index 4893c4daadd..8c5b3d7a57c 100644 --- a/saga/seata-saga-statelang/src/main/java/org/apache/seata/saga/statelang/domain/impl/FailEndStateImpl.java +++ b/saga/seata-saga-statelang/src/main/java/org/apache/seata/saga/statelang/domain/impl/FailEndStateImpl.java @@ -16,7 +16,7 @@ */ package org.apache.seata.saga.statelang.domain.impl; -import org.apache.seata.saga.statelang.domain.DomainConstants; +import org.apache.seata.saga.statelang.domain.StateType; import org.apache.seata.saga.statelang.domain.FailEndState; /** @@ -29,7 +29,7 @@ public class FailEndStateImpl extends BaseState implements FailEndState { private String message; public FailEndStateImpl() { - setType(DomainConstants.STATE_TYPE_FAIL); + setType(StateType.FAIL); } @Override diff --git a/saga/seata-saga-statelang/src/main/java/org/apache/seata/saga/statelang/domain/impl/LoopStartStateImpl.java b/saga/seata-saga-statelang/src/main/java/org/apache/seata/saga/statelang/domain/impl/LoopStartStateImpl.java index 433ce91a2c1..72a3a1f3f04 100644 --- a/saga/seata-saga-statelang/src/main/java/org/apache/seata/saga/statelang/domain/impl/LoopStartStateImpl.java +++ b/saga/seata-saga-statelang/src/main/java/org/apache/seata/saga/statelang/domain/impl/LoopStartStateImpl.java @@ -16,7 +16,7 @@ */ package org.apache.seata.saga.statelang.domain.impl; -import org.apache.seata.saga.statelang.domain.DomainConstants; +import org.apache.seata.saga.statelang.domain.StateType; import org.apache.seata.saga.statelang.domain.LoopStartState; /** @@ -26,7 +26,7 @@ public class LoopStartStateImpl extends BaseState implements LoopStartState { public LoopStartStateImpl() { - setType(DomainConstants.STATE_TYPE_LOOP_START); + setType(StateType.LOOP_START); } } diff --git a/saga/seata-saga-statelang/src/main/java/org/apache/seata/saga/statelang/domain/impl/ScriptTaskStateImpl.java b/saga/seata-saga-statelang/src/main/java/org/apache/seata/saga/statelang/domain/impl/ScriptTaskStateImpl.java index ed291b06b00..0634c0fbdeb 100644 --- a/saga/seata-saga-statelang/src/main/java/org/apache/seata/saga/statelang/domain/impl/ScriptTaskStateImpl.java +++ b/saga/seata-saga-statelang/src/main/java/org/apache/seata/saga/statelang/domain/impl/ScriptTaskStateImpl.java @@ -16,7 +16,7 @@ */ package org.apache.seata.saga.statelang.domain.impl; -import org.apache.seata.saga.statelang.domain.DomainConstants; +import org.apache.seata.saga.statelang.domain.StateType; import org.apache.seata.saga.statelang.domain.ScriptTaskState; /** @@ -32,7 +32,7 @@ public class ScriptTaskStateImpl extends AbstractTaskState implements ScriptTask private String scriptContent; public ScriptTaskStateImpl() { - setType(DomainConstants.STATE_TYPE_SCRIPT_TASK); + setType(StateType.SCRIPT_TASK); } @Override diff --git a/saga/seata-saga-statelang/src/main/java/org/apache/seata/saga/statelang/domain/impl/ServiceTaskStateImpl.java b/saga/seata-saga-statelang/src/main/java/org/apache/seata/saga/statelang/domain/impl/ServiceTaskStateImpl.java index fdb1a075e36..aa16d60b83b 100644 --- a/saga/seata-saga-statelang/src/main/java/org/apache/seata/saga/statelang/domain/impl/ServiceTaskStateImpl.java +++ b/saga/seata-saga-statelang/src/main/java/org/apache/seata/saga/statelang/domain/impl/ServiceTaskStateImpl.java @@ -20,7 +20,7 @@ import java.util.List; import java.util.Map; -import org.apache.seata.saga.statelang.domain.DomainConstants; +import org.apache.seata.saga.statelang.domain.StateType; import org.apache.seata.saga.statelang.domain.ServiceTaskState; /** @@ -38,7 +38,7 @@ public class ServiceTaskStateImpl extends AbstractTaskState implements ServiceTa private boolean isAsync; public ServiceTaskStateImpl() { - setType(DomainConstants.STATE_TYPE_SERVICE_TASK); + setType(StateType.SERVICE_TASK); } @Override diff --git a/saga/seata-saga-statelang/src/main/java/org/apache/seata/saga/statelang/domain/impl/StateInstanceImpl.java b/saga/seata-saga-statelang/src/main/java/org/apache/seata/saga/statelang/domain/impl/StateInstanceImpl.java index dd6ec21de59..0838f6ea15e 100644 --- a/saga/seata-saga-statelang/src/main/java/org/apache/seata/saga/statelang/domain/impl/StateInstanceImpl.java +++ b/saga/seata-saga-statelang/src/main/java/org/apache/seata/saga/statelang/domain/impl/StateInstanceImpl.java @@ -22,6 +22,7 @@ import org.apache.seata.saga.statelang.domain.ExecutionStatus; import org.apache.seata.saga.statelang.domain.StateInstance; import org.apache.seata.saga.statelang.domain.StateMachineInstance; +import org.apache.seata.saga.statelang.domain.StateType; /** * state execution instance @@ -32,7 +33,7 @@ public class StateInstanceImpl implements StateInstance { private String id; private String machineInstanceId; private String name; - private String type; + private StateType type; private String serviceName; private String serviceMethod; private String serviceType; @@ -85,12 +86,12 @@ public void setName(String name) { } @Override - public String getType() { + public StateType getType() { return type; } @Override - public void setType(String type) { + public void setType(StateType type) { this.type = type; } diff --git a/saga/seata-saga-statelang/src/main/java/org/apache/seata/saga/statelang/domain/impl/SubStateMachineImpl.java b/saga/seata-saga-statelang/src/main/java/org/apache/seata/saga/statelang/domain/impl/SubStateMachineImpl.java index bf6d1e78b1c..534d95644b0 100644 --- a/saga/seata-saga-statelang/src/main/java/org/apache/seata/saga/statelang/domain/impl/SubStateMachineImpl.java +++ b/saga/seata-saga-statelang/src/main/java/org/apache/seata/saga/statelang/domain/impl/SubStateMachineImpl.java @@ -16,7 +16,7 @@ */ package org.apache.seata.saga.statelang.domain.impl; -import org.apache.seata.saga.statelang.domain.DomainConstants; +import org.apache.seata.saga.statelang.domain.StateType; import org.apache.seata.saga.statelang.domain.SubStateMachine; import org.apache.seata.saga.statelang.domain.TaskState; @@ -31,7 +31,7 @@ public class SubStateMachineImpl extends ServiceTaskStateImpl implements SubStat private TaskState compensateStateObject; public SubStateMachineImpl() { - setType(DomainConstants.STATE_TYPE_SUB_STATE_MACHINE); + setType(StateType.SUB_STATE_MACHINE); } @Override diff --git a/saga/seata-saga-statelang/src/main/java/org/apache/seata/saga/statelang/domain/impl/SucceedEndStateImpl.java b/saga/seata-saga-statelang/src/main/java/org/apache/seata/saga/statelang/domain/impl/SucceedEndStateImpl.java index eb5ab712d37..1e6bf1414e2 100644 --- a/saga/seata-saga-statelang/src/main/java/org/apache/seata/saga/statelang/domain/impl/SucceedEndStateImpl.java +++ b/saga/seata-saga-statelang/src/main/java/org/apache/seata/saga/statelang/domain/impl/SucceedEndStateImpl.java @@ -16,7 +16,7 @@ */ package org.apache.seata.saga.statelang.domain.impl; -import org.apache.seata.saga.statelang.domain.DomainConstants; +import org.apache.seata.saga.statelang.domain.StateType; import org.apache.seata.saga.statelang.domain.SucceedEndState; /** @@ -26,6 +26,6 @@ public class SucceedEndStateImpl extends BaseState implements SucceedEndState { public SucceedEndStateImpl() { - setType(DomainConstants.STATE_TYPE_SUCCEED); + setType(StateType.SUCCEED); } } diff --git a/saga/seata-saga-statelang/src/main/java/org/apache/seata/saga/statelang/parser/StateParserFactory.java b/saga/seata-saga-statelang/src/main/java/org/apache/seata/saga/statelang/parser/StateParserFactory.java index 3ef0d1e7a86..b2e70fa3498 100644 --- a/saga/seata-saga-statelang/src/main/java/org/apache/seata/saga/statelang/parser/StateParserFactory.java +++ b/saga/seata-saga-statelang/src/main/java/org/apache/seata/saga/statelang/parser/StateParserFactory.java @@ -19,7 +19,7 @@ import java.util.Map; import java.util.concurrent.ConcurrentHashMap; -import org.apache.seata.saga.statelang.domain.DomainConstants; +import org.apache.seata.saga.statelang.domain.StateType; import org.apache.seata.saga.statelang.parser.impl.ChoiceStateParser; import org.apache.seata.saga.statelang.parser.impl.CompensateSubStateMachineStateParser; import org.apache.seata.saga.statelang.parser.impl.CompensationTriggerStateParser; @@ -35,21 +35,21 @@ */ public class StateParserFactory { - protected static Map stateParserMap = new ConcurrentHashMap<>(); + protected static Map stateParserMap = new ConcurrentHashMap<>(); static { - stateParserMap.put(DomainConstants.STATE_TYPE_SERVICE_TASK, new ServiceTaskStateParser()); - stateParserMap.put(DomainConstants.STATE_TYPE_CHOICE, new ChoiceStateParser()); - stateParserMap.put(DomainConstants.STATE_TYPE_COMPENSATION_TRIGGER, new CompensationTriggerStateParser()); - stateParserMap.put(DomainConstants.STATE_TYPE_FAIL, new FailEndStateParser()); - stateParserMap.put(DomainConstants.STATE_TYPE_SUCCEED, new SucceedEndStateParser()); - stateParserMap.put(DomainConstants.STATE_TYPE_SUB_STATE_MACHINE, new SubStateMachineParser()); - stateParserMap.put(DomainConstants.STATE_TYPE_SUB_MACHINE_COMPENSATION, + stateParserMap.put(StateType.SERVICE_TASK, new ServiceTaskStateParser()); + stateParserMap.put(StateType.CHOICE, new ChoiceStateParser()); + stateParserMap.put(StateType.COMPENSATION_TRIGGER, new CompensationTriggerStateParser()); + stateParserMap.put(StateType.FAIL, new FailEndStateParser()); + stateParserMap.put(StateType.SUCCEED, new SucceedEndStateParser()); + stateParserMap.put(StateType.SUB_STATE_MACHINE, new SubStateMachineParser()); + stateParserMap.put(StateType.SUB_MACHINE_COMPENSATION, new CompensateSubStateMachineStateParser()); - stateParserMap.put(DomainConstants.STATE_TYPE_SCRIPT_TASK, new ScriptTaskStateParser()); + stateParserMap.put(StateType.SCRIPT_TASK, new ScriptTaskStateParser()); } - public static StateParser getStateParser(String stateType) { + public static StateParser getStateParser(StateType stateType) { return stateParserMap.get(stateType); } } diff --git a/saga/seata-saga-statelang/src/main/java/org/apache/seata/saga/statelang/parser/impl/StateMachineParserImpl.java b/saga/seata-saga-statelang/src/main/java/org/apache/seata/saga/statelang/parser/impl/StateMachineParserImpl.java index 46ec2f12df4..da4b5ba1971 100644 --- a/saga/seata-saga-statelang/src/main/java/org/apache/seata/saga/statelang/parser/impl/StateMachineParserImpl.java +++ b/saga/seata-saga-statelang/src/main/java/org/apache/seata/saga/statelang/parser/impl/StateMachineParserImpl.java @@ -21,6 +21,7 @@ import org.apache.seata.saga.statelang.domain.DomainConstants; import org.apache.seata.saga.statelang.domain.RecoverStrategy; import org.apache.seata.saga.statelang.domain.State; +import org.apache.seata.saga.statelang.domain.StateType; import org.apache.seata.saga.statelang.domain.StateMachine; import org.apache.seata.saga.statelang.domain.impl.AbstractTaskState; import org.apache.seata.saga.statelang.domain.impl.BaseState; @@ -97,7 +98,7 @@ public StateMachine parse(String json) { Map statesNode = (Map) node.get("States"); statesNode.forEach((stateName, value) -> { Map stateNode = (Map) value; - String stateType = (String) stateNode.get("Type"); + StateType stateType = StateType.getStateType((String) stateNode.get("Type")); StateParser stateParser = StateParserFactory.getStateParser(stateType); if (stateParser == null) { throw new IllegalArgumentException("State Type [" + stateType + "] is not support"); diff --git a/saga/seata-saga-statelang/src/main/java/org/apache/seata/saga/statelang/parser/utils/DesignerJsonTransformer.java b/saga/seata-saga-statelang/src/main/java/org/apache/seata/saga/statelang/parser/utils/DesignerJsonTransformer.java index 7a69c8b5c25..930c24241fd 100644 --- a/saga/seata-saga-statelang/src/main/java/org/apache/seata/saga/statelang/parser/utils/DesignerJsonTransformer.java +++ b/saga/seata-saga-statelang/src/main/java/org/apache/seata/saga/statelang/parser/utils/DesignerJsonTransformer.java @@ -82,7 +82,7 @@ private static void transformNode(Map machineJsonObject, Map getAllPossibleSubsequentStates(State state) { // Next state subsequentStates.add(state.getNext()); switch (state.getType()) { - case DomainConstants.STATE_TYPE_SCRIPT_TASK: - case DomainConstants.STATE_TYPE_SERVICE_TASK: - case DomainConstants.STATE_TYPE_SUB_STATE_MACHINE: - case DomainConstants.STATE_TYPE_SUB_MACHINE_COMPENSATION: + case SCRIPT_TASK: + case SERVICE_TASK: + case SUB_STATE_MACHINE: + case SUB_MACHINE_COMPENSATION: // Next state in catches Optional.ofNullable(((TaskState) state).getCatches()) .ifPresent(c -> c.forEach(e -> subsequentStates.add(e.getNext()))); break; - case DomainConstants.STATE_TYPE_CHOICE: + case CHOICE: // Choice state Optional.ofNullable(((ChoiceState) state).getChoices()) .ifPresent(c -> c.forEach(e -> subsequentStates.add(e.getNext()))); diff --git a/saga/seata-saga-statelang/src/main/java/org/apache/seata/saga/statelang/validator/impl/NoRecursiveSubStateMachineRule.java b/saga/seata-saga-statelang/src/main/java/org/apache/seata/saga/statelang/validator/impl/NoRecursiveSubStateMachineRule.java index e8266f7d453..a355cb3705c 100644 --- a/saga/seata-saga-statelang/src/main/java/org/apache/seata/saga/statelang/validator/impl/NoRecursiveSubStateMachineRule.java +++ b/saga/seata-saga-statelang/src/main/java/org/apache/seata/saga/statelang/validator/impl/NoRecursiveSubStateMachineRule.java @@ -16,7 +16,7 @@ */ package org.apache.seata.saga.statelang.validator.impl; -import org.apache.seata.saga.statelang.domain.DomainConstants; +import org.apache.seata.saga.statelang.domain.StateType; import org.apache.seata.saga.statelang.domain.State; import org.apache.seata.saga.statelang.domain.StateMachine; import org.apache.seata.saga.statelang.domain.SubStateMachine; @@ -30,7 +30,7 @@ public class NoRecursiveSubStateMachineRule extends AbstractRule { @Override public boolean validate(StateMachine stateMachine) { for (State state: stateMachine.getStates().values()) { - if (!DomainConstants.STATE_TYPE_SUB_STATE_MACHINE.equals(state.getType())) { + if (!StateType.SUB_STATE_MACHINE.equals(state.getType())) { continue; } if (stateMachine.getName().equals(((SubStateMachine) state).getStateMachineName())) { diff --git a/saga/seata-saga-statemachine-designer/package-lock.json b/saga/seata-saga-statemachine-designer/package-lock.json index 2cb1f08c0d7..d6225eb2023 100644 --- a/saga/seata-saga-statemachine-designer/package-lock.json +++ b/saga/seata-saga-statemachine-designer/package-lock.json @@ -3602,9 +3602,9 @@ "dev": true }, "node_modules/cookie": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz", - "integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==", + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.1.tgz", + "integrity": "sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==", "dev": true, "engines": { "node": ">= 0.6" @@ -5069,9 +5069,9 @@ } }, "node_modules/express": { - "version": "4.21.0", - "resolved": "https://registry.npmjs.org/express/-/express-4.21.0.tgz", - "integrity": "sha512-VqcNGcj/Id5ZT1LZ/cfihi3ttTn+NJmkli2eZADigjq29qTlWi/hAQ43t/VLPq8+UX06FCEx3ByOYet6ZFblng==", + "version": "4.21.1", + "resolved": "https://registry.npmjs.org/express/-/express-4.21.1.tgz", + "integrity": "sha512-YSFlK1Ee0/GC8QaO91tHcDxJiE/X4FbpAyQWkxAvG6AXCuR65YzK8ua6D9hvi/TzUfZMpc+BwuM1IPw8fmQBiQ==", "dev": true, "dependencies": { "accepts": "~1.3.8", @@ -5079,7 +5079,7 @@ "body-parser": "1.20.3", "content-disposition": "0.5.4", "content-type": "~1.0.4", - "cookie": "0.6.0", + "cookie": "0.7.1", "cookie-signature": "1.0.6", "debug": "2.6.9", "depd": "2.0.0", @@ -5947,9 +5947,9 @@ } }, "node_modules/http-proxy-middleware": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-2.0.6.tgz", - "integrity": "sha512-ya/UeJ6HVBYxrgYotAZo1KvPWlgB48kUJLDePFeneHsVujFaW5WNj2NgWCAE//B1Dl02BIfYlpNgBy8Kf8Rjmw==", + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-2.0.7.tgz", + "integrity": "sha512-fgVY8AV7qU7z/MmXJ/rxwbrtQH4jBQ9m7kp3llF0liB7glmFeVZFBepQb32T3y8n8k2+AEYuMPCpinYW+/CuRA==", "dev": true, "dependencies": { "@types/http-proxy": "^1.17.8", diff --git a/script/client/conf/registry.conf b/script/client/conf/registry.conf index 1aabbc507f6..d21f03f7232 100644 --- a/script/client/conf/registry.conf +++ b/script/client/conf/registry.conf @@ -18,7 +18,9 @@ registry { # file 、nacos 、eureka、redis、zk、consul、etcd3、sofa、custom、raft、seata type = "file" - + # Supports address translation parameters, currently only supported in raft mode, + # if match the preferredNetworks rule return the first, eg: preferredNetworks = "192.168.*" + preferredNetworks = "" raft { metadata-max-age-ms = 30000 serverAddr = "127.0.0.1:7091" diff --git a/script/client/spring/application.properties b/script/client/spring/application.properties index 2a72d1e5f79..620cbd9714d 100755 --- a/script/client/spring/application.properties +++ b/script/client/spring/application.properties @@ -123,6 +123,10 @@ seata.config.custom.name= seata.registry.type=file +# Supports address translation parameters, currently only supported in raft mode? +# if match the preferredNetworks rule return the first, eg: preferredNetworks = "192.168.*" +seata.registry.preferredNetworks = "" + seata.registry.raft.server-addr= seata.registry.raft.metadata-max-age-ms=30000 seata.registry.raft.username=seata diff --git a/script/client/spring/application.yml b/script/client/spring/application.yml index a6100f05740..580cb0180e0 100755 --- a/script/client/spring/application.yml +++ b/script/client/spring/application.yml @@ -132,6 +132,9 @@ seata: name: registry: type: file + # Supports address translation parameters, currently only supported in raft mode, + # if match the preferredNetworks rule return the first, eg: preferredNetworks = "192.168.*" + preferredNetworks: "" seata: server-addr: 127.0.0.1:8081 namespace: public diff --git a/script/config-center/config.txt b/script/config-center/config.txt index 8cf986f3f94..af831bc5d23 100644 --- a/script/config-center/config.txt +++ b/script/config-center/config.txt @@ -140,7 +140,7 @@ server.recovery.rollbackingRetryPeriod=1000 server.recovery.timeoutRetryPeriod=1000 server.maxCommitRetryTimeout=-1 server.maxRollbackRetryTimeout=-1 -server.rollbackRetryTimeoutUnlockEnable=false +server.rollbackFailedUnlockEnable=false server.distributedLockExpireTime=10000 server.session.branchAsyncQueueSize=5000 server.session.enableBranchAsyncRemove=false diff --git a/seata-spring-autoconfigure/seata-spring-autoconfigure-core/pom.xml b/seata-spring-autoconfigure/seata-spring-autoconfigure-core/pom.xml index a3767ecb0ab..9c24e75a961 100644 --- a/seata-spring-autoconfigure/seata-spring-autoconfigure-core/pom.xml +++ b/seata-spring-autoconfigure/seata-spring-autoconfigure-core/pom.xml @@ -38,5 +38,16 @@ ${project.version} test + + org.springframework.boot + spring-boot-starter-test + test + + + ch.qos.logback + logback-classic + + + \ No newline at end of file diff --git a/seata-spring-autoconfigure/seata-spring-autoconfigure-core/src/main/java/org/apache/seata/spring/boot/autoconfigure/SeataCoreEnvironmentPostProcessor.java b/seata-spring-autoconfigure/seata-spring-autoconfigure-core/src/main/java/org/apache/seata/spring/boot/autoconfigure/SeataCoreEnvironmentPostProcessor.java index 67bc7bd75ab..bfa4923469f 100644 --- a/seata-spring-autoconfigure/seata-spring-autoconfigure-core/src/main/java/org/apache/seata/spring/boot/autoconfigure/SeataCoreEnvironmentPostProcessor.java +++ b/seata-spring-autoconfigure/seata-spring-autoconfigure-core/src/main/java/org/apache/seata/spring/boot/autoconfigure/SeataCoreEnvironmentPostProcessor.java @@ -40,6 +40,7 @@ import org.apache.seata.spring.boot.autoconfigure.properties.registry.RegistryRedisProperties; import org.apache.seata.spring.boot.autoconfigure.properties.registry.RegistrySofaProperties; import org.apache.seata.spring.boot.autoconfigure.properties.registry.RegistryZooKeeperProperties; +import org.apache.seata.spring.boot.autoconfigure.properties.registry.RegistryMetadataProperties; import org.springframework.boot.SpringApplication; import org.springframework.boot.env.EnvironmentPostProcessor; import org.springframework.core.Ordered; @@ -69,6 +70,7 @@ import static org.apache.seata.spring.boot.autoconfigure.StarterConstants.SHUTDOWN_PREFIX; import static org.apache.seata.spring.boot.autoconfigure.StarterConstants.THREAD_FACTORY_PREFIX; import static org.apache.seata.spring.boot.autoconfigure.StarterConstants.TRANSPORT_PREFIX; +import static org.apache.seata.spring.boot.autoconfigure.StarterConstants.REGISTRY_METADATA_PREFIX; public class SeataCoreEnvironmentPostProcessor implements EnvironmentPostProcessor, Ordered { @@ -90,6 +92,7 @@ public static void init() { PROPERTY_BEAN_MAP.put(CONFIG_PREFIX, ConfigProperties.class); PROPERTY_BEAN_MAP.put(CONFIG_FILE_PREFIX, ConfigFileProperties.class); PROPERTY_BEAN_MAP.put(REGISTRY_PREFIX, RegistryProperties.class); + PROPERTY_BEAN_MAP.put(REGISTRY_METADATA_PREFIX, RegistryMetadataProperties.class); PROPERTY_BEAN_MAP.put(CONFIG_NACOS_PREFIX, ConfigNacosProperties.class); PROPERTY_BEAN_MAP.put(CONFIG_CONSUL_PREFIX, ConfigConsulProperties.class); diff --git a/seata-spring-autoconfigure/seata-spring-autoconfigure-core/src/main/java/org/apache/seata/spring/boot/autoconfigure/StarterConstants.java b/seata-spring-autoconfigure/seata-spring-autoconfigure-core/src/main/java/org/apache/seata/spring/boot/autoconfigure/StarterConstants.java index 5ec088d43ff..db09911af18 100644 --- a/seata-spring-autoconfigure/seata-spring-autoconfigure-core/src/main/java/org/apache/seata/spring/boot/autoconfigure/StarterConstants.java +++ b/seata-spring-autoconfigure/seata-spring-autoconfigure-core/src/main/java/org/apache/seata/spring/boot/autoconfigure/StarterConstants.java @@ -58,6 +58,8 @@ public interface StarterConstants { String REGISTRY_SOFA_PREFIX = REGISTRY_PREFIX + ".sofa"; String REGISTRY_CUSTOM_PREFIX = REGISTRY_PREFIX + ".custom"; + String REGISTRY_METADATA_PREFIX = REGISTRY_PREFIX + ".metadata"; + String CONFIG_PREFIX = SEATA_PREFIX + ".config"; String CONFIG_NACOS_PREFIX = CONFIG_PREFIX + ".nacos"; String CONFIG_CONSUL_PREFIX = CONFIG_PREFIX + ".consul"; diff --git a/seata-spring-autoconfigure/seata-spring-autoconfigure-core/src/main/java/org/apache/seata/spring/boot/autoconfigure/listener/SeataApplicationListener.java b/seata-spring-autoconfigure/seata-spring-autoconfigure-core/src/main/java/org/apache/seata/spring/boot/autoconfigure/listener/SeataApplicationListener.java new file mode 100644 index 00000000000..b347490a1a4 --- /dev/null +++ b/seata-spring-autoconfigure/seata-spring-autoconfigure-core/src/main/java/org/apache/seata/spring/boot/autoconfigure/listener/SeataApplicationListener.java @@ -0,0 +1,59 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.seata.spring.boot.autoconfigure.listener; + +import org.apache.seata.common.holder.ObjectHolder; +import org.springframework.boot.context.event.ApplicationEnvironmentPreparedEvent; +import org.springframework.boot.context.event.ApplicationReadyEvent; +import org.springframework.boot.context.logging.LoggingApplicationListener; +import org.springframework.context.ApplicationEvent; +import org.springframework.context.event.GenericApplicationListener; +import org.springframework.core.ResolvableType; +import org.springframework.core.env.ConfigurableEnvironment; + +import static org.apache.seata.common.Constants.OBJECT_KEY_SPRING_CONFIGURABLE_ENVIRONMENT; + + +public class SeataApplicationListener implements GenericApplicationListener { + + @Override + public boolean supportsEventType(ResolvableType eventType) { + return eventType.getRawClass() != null + && (ApplicationEnvironmentPreparedEvent.class.isAssignableFrom(eventType.getRawClass()) || + ApplicationReadyEvent.class.isAssignableFrom(eventType.getRawClass())); + } + + @Override + public void onApplicationEvent(ApplicationEvent event) { + if (!(event instanceof ApplicationEnvironmentPreparedEvent)) { + return; + } + ApplicationEnvironmentPreparedEvent environmentPreparedEvent = (ApplicationEnvironmentPreparedEvent)event; + ConfigurableEnvironment environment = environmentPreparedEvent.getEnvironment(); + ObjectHolder.INSTANCE.setObject(OBJECT_KEY_SPRING_CONFIGURABLE_ENVIRONMENT, environment); + } + + /** + * higher than LoggingApplicationListener + * + * @return the order + */ + @Override + public int getOrder() { + return LoggingApplicationListener.DEFAULT_ORDER - 1; + } +} \ No newline at end of file diff --git a/server/src/main/java/org/apache/seata/server/spring/listener/SeataPropertiesLoader.java b/seata-spring-autoconfigure/seata-spring-autoconfigure-core/src/main/java/org/apache/seata/spring/boot/autoconfigure/loader/SeataPropertiesLoader.java similarity index 70% rename from server/src/main/java/org/apache/seata/server/spring/listener/SeataPropertiesLoader.java rename to seata-spring-autoconfigure/seata-spring-autoconfigure-core/src/main/java/org/apache/seata/spring/boot/autoconfigure/loader/SeataPropertiesLoader.java index 974416b26c5..05931662eee 100644 --- a/server/src/main/java/org/apache/seata/server/spring/listener/SeataPropertiesLoader.java +++ b/seata-spring-autoconfigure/seata-spring-autoconfigure-core/src/main/java/org/apache/seata/spring/boot/autoconfigure/loader/SeataPropertiesLoader.java @@ -14,14 +14,13 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.apache.seata.server.spring.listener; +package org.apache.seata.spring.boot.autoconfigure.loader; import org.apache.seata.common.util.CollectionUtils; import org.apache.seata.common.util.StringUtils; import org.apache.seata.config.ConfigurationFactory; import org.apache.seata.config.FileConfiguration; import org.apache.seata.config.file.FileConfig; -import org.apache.seata.server.store.StoreConfig; import org.springframework.context.ApplicationContextInitializer; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.core.Ordered; @@ -29,6 +28,8 @@ import org.springframework.core.env.ConfigurableEnvironment; import org.springframework.core.env.PropertiesPropertySource; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; import java.util.Arrays; import java.util.List; import java.util.Map; @@ -74,8 +75,30 @@ public void initialize(ConfigurableApplicationContext applicationContext) { environment.getPropertySources().addLast(new PropertiesPropertySource("seataOldConfig", properties)); } // Load by priority - System.setProperty("sessionMode", StoreConfig.getSessionMode().getName()); - System.setProperty("lockMode", StoreConfig.getLockMode().getName()); + loadSessionAndLockModes(); } + public void loadSessionAndLockModes() { + try { + Class storeConfigClass = Class.forName("org.apache.seata.server.store.StoreConfig"); + Optional sessionMode = invokeEnumMethod(storeConfigClass, "getSessionMode", "getName"); + Optional lockMode = invokeEnumMethod(storeConfigClass, "getLockMode", "getName"); + sessionMode.ifPresent(value -> System.setProperty("sessionMode", value)); + lockMode.ifPresent(value -> System.setProperty("lockMode", value)); + } catch (ClassNotFoundException | NoSuchMethodException | InvocationTargetException | IllegalAccessException e) { + // The exception is not printed because it is an expected behavior and does not affect the normal operation of the program. + // StoreConfig only exists on the server side + } + } + + private Optional invokeEnumMethod(Class clazz, String enumMethodName, String getterMethodName) + throws NoSuchMethodException, InvocationTargetException, IllegalAccessException { + Method enumMethod = clazz.getMethod(enumMethodName); + Object enumValue = enumMethod.invoke(null); + if (enumValue != null) { + Method getterMethod = enumValue.getClass().getMethod(getterMethodName); + return Optional.ofNullable((String) getterMethod.invoke(enumValue)); + } + return Optional.empty(); + } } diff --git a/seata-spring-autoconfigure/seata-spring-autoconfigure-core/src/main/java/org/apache/seata/spring/boot/autoconfigure/properties/registry/RegistryMetadataProperties.java b/seata-spring-autoconfigure/seata-spring-autoconfigure-core/src/main/java/org/apache/seata/spring/boot/autoconfigure/properties/registry/RegistryMetadataProperties.java new file mode 100644 index 00000000000..595b322b79a --- /dev/null +++ b/seata-spring-autoconfigure/seata-spring-autoconfigure-core/src/main/java/org/apache/seata/spring/boot/autoconfigure/properties/registry/RegistryMetadataProperties.java @@ -0,0 +1,36 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.seata.spring.boot.autoconfigure.properties.registry; + +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.stereotype.Component; +import static org.apache.seata.spring.boot.autoconfigure.StarterConstants.REGISTRY_METADATA_PREFIX; + +@Component +@ConfigurationProperties(prefix = REGISTRY_METADATA_PREFIX) +public class RegistryMetadataProperties { + private String external; + + public String getExternal() { + return external; + } + + public RegistryMetadataProperties setExternal(String external) { + this.external = external; + return this; + } +} diff --git a/seata-spring-autoconfigure/seata-spring-autoconfigure-core/src/main/resources/META-INF/spring.factories b/seata-spring-autoconfigure/seata-spring-autoconfigure-core/src/main/resources/META-INF/spring.factories index df445bb6973..27e069aa2ef 100644 --- a/seata-spring-autoconfigure/seata-spring-autoconfigure-core/src/main/resources/META-INF/spring.factories +++ b/seata-spring-autoconfigure/seata-spring-autoconfigure-core/src/main/resources/META-INF/spring.factories @@ -21,3 +21,9 @@ org.apache.seata.spring.boot.autoconfigure.SeataCoreAutoConfiguration # Environment Post Processors org.springframework.boot.env.EnvironmentPostProcessor=\ org.apache.seata.spring.boot.autoconfigure.SeataCoreEnvironmentPostProcessor + +org.springframework.context.ApplicationContextInitializer=\ +org.apache.seata.spring.boot.autoconfigure.loader.SeataPropertiesLoader + +org.springframework.context.ApplicationListener=\ +org.apache.seata.spring.boot.autoconfigure.listener.SeataApplicationListener \ No newline at end of file diff --git a/seata-spring-autoconfigure/seata-spring-autoconfigure-core/src/test/java/org/apache/seata/spring/boot/autoconfigure/SeataCoreAutoConfigurationTest.java b/seata-spring-autoconfigure/seata-spring-autoconfigure-core/src/test/java/org/apache/seata/spring/boot/autoconfigure/SeataCoreAutoConfigurationTest.java new file mode 100644 index 00000000000..8fbf0075e43 --- /dev/null +++ b/seata-spring-autoconfigure/seata-spring-autoconfigure-core/src/test/java/org/apache/seata/spring/boot/autoconfigure/SeataCoreAutoConfigurationTest.java @@ -0,0 +1,88 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.seata.spring.boot.autoconfigure; + +import org.apache.seata.config.Configuration; +import org.apache.seata.config.ConfigurationFactory; +import org.apache.seata.config.FileConfiguration; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.core.env.Environment; +import org.springframework.test.context.TestPropertySource; +import org.springframework.test.context.junit.jupiter.SpringExtension; + +import java.util.Optional; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +@ExtendWith(SpringExtension.class) +@SpringBootTest(classes = SeataCoreAutoConfiguration.class) +@TestPropertySource(locations = "classpath:application-test.properties") +public class SeataCoreAutoConfigurationTest { + + @Autowired + private Environment environment; + + @Test + public void testSeataPropertiesLoaded() { + ConfigurationFactory.reload(); + + // default file.conf + String dbUrl = environment.getProperty("seata.store.db.url"); + assertEquals("jdbc:mysql://127.0.0.1:3306/seata?rewriteBatchedStatements=true", dbUrl, "The DB URL should be correctly loaded from configuration"); + + // overridden by application-test.properties + String registryType = environment.getProperty("seata.config.type"); + assertEquals("file", registryType, "The config type should be 'file'"); + + // overridden by application-test.properties + String seataNamespaces = environment.getProperty("seata.config.nacos.namespace"); + assertEquals("seata-test-application.yml", seataNamespaces, "The Nacos namespace should be 'seata-test-application.yml'"); + } + + @Test + public void testConfigFromFileUsingGetInstance() { + Configuration configFromFile = ConfigurationFactory.getInstance(); + String dbUrlFromFile = configFromFile.getConfig("store.db.url"); + assertEquals("jdbc:mysql://127.0.0.1:3306/seata?rewriteBatchedStatements=true", dbUrlFromFile, "The DB URL should be correctly loaded from file.conf"); + String storeFileDirFromFile = configFromFile.getConfig("store.file.dir"); + assertEquals("sessionStore", storeFileDirFromFile, "The storeFileDir should be 'sessionStore' in file.conf"); + } + + @Test + public void testConfigFromFileUsingGetOriginFileInstance() { + Optional optionalConfigFromFile = ConfigurationFactory.getOriginFileInstance(); + assertTrue(optionalConfigFromFile.isPresent(), "The configuration from file.conf should be present"); + FileConfiguration configFromFile = optionalConfigFromFile.get(); + String dbUrlFromFile = configFromFile.getConfig("store.db.url"); + assertEquals("jdbc:mysql://127.0.0.1:3306/seata?rewriteBatchedStatements=true", dbUrlFromFile, "The DB URL should be correctly loaded from file.conf"); + String storeFileDirFromFile = configFromFile.getConfig("store.file.dir"); + assertEquals("sessionStore", storeFileDirFromFile, "The storeFileDir should be 'sessionStore' in file.conf"); + } + + @Test + public void testConfigFromRegistryUsingGetOriginFileInstanceRegistry() { + Configuration configFromRegistry = ConfigurationFactory.getOriginFileInstanceRegistry(); + String registryTypeFromRegistry = configFromRegistry.getConfig("config.type"); + assertEquals("file", registryTypeFromRegistry, "The config type should be 'file' in registry.conf"); + String seataNamespaceFromRegistry = configFromRegistry.getConfig("config.nacos.namespace"); + assertEquals("seata-test", seataNamespaceFromRegistry, "The Nacos namespace should be 'seata-test' in registry.conf"); + } +} \ No newline at end of file diff --git a/seata-spring-autoconfigure/seata-spring-autoconfigure-core/src/test/resources/application-test.properties b/seata-spring-autoconfigure/seata-spring-autoconfigure-core/src/test/resources/application-test.properties index 8467d6cc7b9..c492f36092b 100755 --- a/seata-spring-autoconfigure/seata-spring-autoconfigure-core/src/test/resources/application-test.properties +++ b/seata-spring-autoconfigure/seata-spring-autoconfigure-core/src/test/resources/application-test.properties @@ -17,7 +17,7 @@ seata.config.type=file seata.config.data-type=bbb -seata.config.file.name=aaa +seata.config.file.name=file.conf seata.config.consul.server-addr=aaa seata.config.consul.acl-token=bbb @@ -33,7 +33,7 @@ seata.config.apollo.apollo-config-service=fff seata.config.etcd3.server-addr=aaa seata.config.etcd3.key=bbb -seata.config.nacos.namespace=ddd +seata.config.nacos.namespace=seata-test-application.yml seata.config.nacos.server-addr=aaa seata.config.nacos.group=ccc seata.config.nacos.username=eee diff --git a/seata-spring-autoconfigure/seata-spring-autoconfigure-core/src/test/resources/file.conf b/seata-spring-autoconfigure/seata-spring-autoconfigure-core/src/test/resources/file.conf new file mode 100644 index 00000000000..422c6dd5836 --- /dev/null +++ b/seata-spring-autoconfigure/seata-spring-autoconfigure-core/src/test/resources/file.conf @@ -0,0 +1,67 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You 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. +# + +#reduce delay for test +## transaction log store, only used in seata-server +store { + ## store mode: file、db + mode = "file" + + ## file store property + file { + ## store location dir + dir = "sessionStore" + } + + ## database store property + db { + ## the implement of javax.sql.DataSource, such as DruidDataSource(druid)/BasicDataSource(dbcp) etc. + datasource = "dbcp" + ## mysql/oracle/h2/oceanbase etc. + dbType = "mysql" + driverClassName = "com.mysql.jdbc.Driver" + ## if using mysql to store the data, recommend add rewriteBatchedStatements=true in jdbc connection param + url = "jdbc:mysql://127.0.0.1:3306/seata?rewriteBatchedStatements=true" + user = "mysql" + password = "mysql" + } +} +server { + recovery { + #schedule committing retry period in milliseconds + committingRetryPeriod = 100 + #schedule asyn committing retry period in milliseconds + asynCommittingRetryPeriod = 100 + #schedule rollbacking retry period in milliseconds + rollbackingRetryPeriod = 100 + #schedule timeout retry period in milliseconds + timeoutRetryPeriod = 100 + } + undo { + logSaveDays = 2 + #schedule delete expired undo_log in milliseconds + logDeletePeriod = 86400000 + } +} +## metrics settings +metrics { + enabled = true + registryType = "compact" + # multi exporters use comma divided + exporterList = "prometheus" + exporterPrometheusPort = 9898 +} \ No newline at end of file diff --git a/seata-spring-autoconfigure/seata-spring-autoconfigure-core/src/test/resources/registry.conf b/seata-spring-autoconfigure/seata-spring-autoconfigure-core/src/test/resources/registry.conf new file mode 100644 index 00000000000..ceb312b5e7e --- /dev/null +++ b/seata-spring-autoconfigure/seata-spring-autoconfigure-core/src/test/resources/registry.conf @@ -0,0 +1,30 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You 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. +# + +config { + type = "file" + + file { + name = "file.conf" + } + nacos { + application = "seata-server" + serverAddr = "127.0.0.1:8848" + namespace = "seata-test" + cluster = "default" + } +} \ No newline at end of file diff --git a/serializer/pom.xml b/serializer/pom.xml index 246ba6c2757..e4435a724d1 100644 --- a/serializer/pom.xml +++ b/serializer/pom.xml @@ -38,6 +38,7 @@ seata-serializer-kryo seata-serializer-hessian seata-serializer-fastjson2 + seata-serializer-fury diff --git a/serializer/seata-serializer-all/pom.xml b/serializer/seata-serializer-all/pom.xml index a0d816153bf..d34e9f6b14e 100644 --- a/serializer/seata-serializer-all/pom.xml +++ b/serializer/seata-serializer-all/pom.xml @@ -55,5 +55,10 @@ seata-serializer-fastjson2 ${project.version} + + ${project.groupId} + seata-serializer-fury + ${project.version} + diff --git a/serializer/seata-serializer-fury/pom.xml b/serializer/seata-serializer-fury/pom.xml new file mode 100644 index 00000000000..18a40ac2026 --- /dev/null +++ b/serializer/seata-serializer-fury/pom.xml @@ -0,0 +1,46 @@ + + + + + org.apache.seata + seata-serializer + ${revision} + + 4.0.0 + seata-serializer-fury + jar + seata-serializer-fury ${project.version} + serializer-fury for Seata built with Maven + + + + ${project.groupId} + seata-core + ${project.version} + + + + org.apache.fury + fury-core + + + \ No newline at end of file diff --git a/serializer/seata-serializer-fury/src/main/java/org/apache/seata/serializer/fury/FurySerializer.java b/serializer/seata-serializer-fury/src/main/java/org/apache/seata/serializer/fury/FurySerializer.java new file mode 100644 index 00000000000..1f39a6dbba5 --- /dev/null +++ b/serializer/seata-serializer-fury/src/main/java/org/apache/seata/serializer/fury/FurySerializer.java @@ -0,0 +1,43 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.seata.serializer.fury; + +import org.apache.fury.ThreadSafeFury; +import org.apache.seata.common.loader.LoadLevel; +import org.apache.seata.core.protocol.AbstractMessage; +import org.apache.seata.core.serializer.Serializer; + +@LoadLevel(name = "FURY") +public class FurySerializer implements Serializer { + @Override + public byte[] serialize(T t) { + if (!(t instanceof AbstractMessage)) { + throw new IllegalArgumentException("AbstractMessage isn't available."); + } + ThreadSafeFury threadSafeFury = FurySerializerFactory.getInstance().get(); + return threadSafeFury.serialize(t); + } + + @Override + public T deserialize(byte[] bytes) { + if (bytes == null || bytes.length == 0) { + throw new IllegalArgumentException("bytes is null"); + } + ThreadSafeFury threadSafeFury = FurySerializerFactory.getInstance().get(); + return (T) threadSafeFury.deserialize(bytes); + } +} diff --git a/serializer/seata-serializer-fury/src/main/java/org/apache/seata/serializer/fury/FurySerializerFactory.java b/serializer/seata-serializer-fury/src/main/java/org/apache/seata/serializer/fury/FurySerializerFactory.java new file mode 100644 index 00000000000..806e29878bb --- /dev/null +++ b/serializer/seata-serializer-fury/src/main/java/org/apache/seata/serializer/fury/FurySerializerFactory.java @@ -0,0 +1,53 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.seata.serializer.fury; + +import org.apache.fury.Fury; +import org.apache.fury.ThreadLocalFury; +import org.apache.fury.ThreadSafeFury; +import org.apache.fury.config.CompatibleMode; +import org.apache.fury.config.Language; +import org.apache.seata.core.serializer.SerializerSecurityRegistry; + +public class FurySerializerFactory { + private static final FurySerializerFactory FACTORY = new FurySerializerFactory(); + + private static final ThreadSafeFury FURY = new ThreadLocalFury(classLoader -> { + Fury f = Fury.builder() + .withLanguage(Language.JAVA) + // In JAVA mode, classes cannot be registered by tag, and the different registration order between the server and the client will cause deserialization failure + // In XLANG cross-language mode has problems with Java class serialization, such as enum classes [https://github.com/apache/fury/issues/1644]. + .requireClassRegistration(false) + //enable reference tracking for shared/circular reference. + .withRefTracking(true) + .withClassLoader(classLoader) + .withCompatibleMode(CompatibleMode.COMPATIBLE) + .build(); + + // register allow class + f.getClassResolver().setClassChecker((classResolver,className) -> SerializerSecurityRegistry.getAllowClassPattern().contains(className)); + return f; + }); + + public static FurySerializerFactory getInstance() { + return FACTORY; + } + + public ThreadSafeFury get() { + return FURY; + } +} diff --git a/serializer/seata-serializer-fury/src/main/resources/META-INF/services/org.apache.seata.core.serializer.Serializer b/serializer/seata-serializer-fury/src/main/resources/META-INF/services/org.apache.seata.core.serializer.Serializer new file mode 100644 index 00000000000..0e125a30674 --- /dev/null +++ b/serializer/seata-serializer-fury/src/main/resources/META-INF/services/org.apache.seata.core.serializer.Serializer @@ -0,0 +1,17 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You 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. +# +org.apache.seata.serializer.fury.FurySerializer \ No newline at end of file diff --git a/serializer/seata-serializer-fury/src/test/java/org/apache/seata/serializer/fury/FurySerializerTest.java b/serializer/seata-serializer-fury/src/test/java/org/apache/seata/serializer/fury/FurySerializerTest.java new file mode 100644 index 00000000000..2f5aad11de2 --- /dev/null +++ b/serializer/seata-serializer-fury/src/test/java/org/apache/seata/serializer/fury/FurySerializerTest.java @@ -0,0 +1,81 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.seata.serializer.fury; + +import org.apache.seata.core.exception.TransactionExceptionCode; +import org.apache.seata.core.model.BranchStatus; +import org.apache.seata.core.model.BranchType; +import org.apache.seata.core.protocol.ResultCode; +import org.apache.seata.core.protocol.transaction.BranchCommitRequest; +import org.apache.seata.core.protocol.transaction.BranchCommitResponse; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +public class FurySerializerTest { + private static FurySerializer furySerializer; + + @BeforeAll + public static void before() { + furySerializer = new FurySerializer(); + } + + @Test + public void testBranchCommitRequest() { + + BranchCommitRequest branchCommitRequest = new BranchCommitRequest(); + branchCommitRequest.setBranchType(BranchType.AT); + branchCommitRequest.setXid("xid"); + branchCommitRequest.setResourceId("resourceId"); + branchCommitRequest.setBranchId(20190809); + branchCommitRequest.setApplicationData("app"); + + byte[] bytes = furySerializer.serialize(branchCommitRequest); + BranchCommitRequest t = furySerializer.deserialize(bytes); + + assertThat(t.getTypeCode()).isEqualTo(branchCommitRequest.getTypeCode()); + assertThat(t.getBranchType()).isEqualTo(branchCommitRequest.getBranchType()); + assertThat(t.getXid()).isEqualTo(branchCommitRequest.getXid()); + assertThat(t.getResourceId()).isEqualTo(branchCommitRequest.getResourceId()); + assertThat(t.getBranchId()).isEqualTo(branchCommitRequest.getBranchId()); + assertThat(t.getApplicationData()).isEqualTo(branchCommitRequest.getApplicationData()); + + } + + @Test + public void testBranchCommitResponse() { + + BranchCommitResponse branchCommitResponse = new BranchCommitResponse(); + branchCommitResponse.setTransactionExceptionCode(TransactionExceptionCode.BranchTransactionNotExist); + branchCommitResponse.setBranchId(20190809); + branchCommitResponse.setBranchStatus(BranchStatus.PhaseOne_Done); + branchCommitResponse.setMsg("20190809"); + branchCommitResponse.setXid("20190809"); + branchCommitResponse.setResultCode(ResultCode.Failed); + + byte[] bytes = furySerializer.serialize(branchCommitResponse); + BranchCommitResponse t = furySerializer.deserialize(bytes); + + assertThat(t.getTransactionExceptionCode()).isEqualTo(branchCommitResponse.getTransactionExceptionCode()); + assertThat(t.getBranchId()).isEqualTo(branchCommitResponse.getBranchId()); + assertThat(t.getBranchStatus()).isEqualTo(branchCommitResponse.getBranchStatus()); + assertThat(t.getMsg()).isEqualTo(branchCommitResponse.getMsg()); + assertThat(t.getResultCode()).isEqualTo(branchCommitResponse.getResultCode()); + + } +} diff --git a/serializer/seata-serializer-protobuf/src/main/java/org/apache/seata/serializer/protobuf/GrpcSerializer.java b/serializer/seata-serializer-protobuf/src/main/java/org/apache/seata/serializer/protobuf/GrpcSerializer.java new file mode 100644 index 00000000000..2ef8eac784e --- /dev/null +++ b/serializer/seata-serializer-protobuf/src/main/java/org/apache/seata/serializer/protobuf/GrpcSerializer.java @@ -0,0 +1,60 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.seata.serializer.protobuf; + +import com.google.protobuf.Any; +import com.google.protobuf.Message; +import org.apache.seata.common.exception.ShouldNeverHappenException; +import org.apache.seata.common.loader.LoadLevel; +import org.apache.seata.core.serializer.Serializer; +import org.apache.seata.serializer.protobuf.convertor.PbConvertor; +import org.apache.seata.serializer.protobuf.manager.ProtobufConvertManager; + +@LoadLevel(name = "GRPC") +public class GrpcSerializer implements Serializer { + @Override + public byte[] serialize(T t) { + PbConvertor pbConvertor = ProtobufConvertManager.getInstance() + .fetchConvertor(t.getClass().getName()); + Any grpcBody = Any.pack((Message) pbConvertor.convert2Proto(t)); + + return grpcBody.toByteArray(); + } + + @Override + public T deserialize(byte[] bytes) { + try { + Any body = Any.parseFrom(bytes); + final Class clazz = ProtobufConvertManager.getInstance().fetchProtoClass(getTypeNameFromTypeUrl(body.getTypeUrl())); + if (body.is(clazz)) { + Object ob = body.unpack(clazz); + PbConvertor pbConvertor = ProtobufConvertManager.getInstance().fetchReversedConvertor(clazz.getName()); + + return (T) pbConvertor.convert2Model(ob); + } + } catch (Throwable e) { + throw new ShouldNeverHappenException("GrpcSerializer deserialize error", e); + } + + return null; + } + + private String getTypeNameFromTypeUrl(String typeUri) { + int pos = typeUri.lastIndexOf('/'); + return pos == -1 ? "" : typeUri.substring(pos + 1); + } +} diff --git a/serializer/seata-serializer-protobuf/src/main/resources/META-INF/services/org.apache.seata.core.serializer.Serializer b/serializer/seata-serializer-protobuf/src/main/resources/META-INF/services/org.apache.seata.core.serializer.Serializer index 71098c53674..f6fbf709dea 100644 --- a/serializer/seata-serializer-protobuf/src/main/resources/META-INF/services/org.apache.seata.core.serializer.Serializer +++ b/serializer/seata-serializer-protobuf/src/main/resources/META-INF/services/org.apache.seata.core.serializer.Serializer @@ -14,4 +14,5 @@ # See the License for the specific language governing permissions and # limitations under the License. # -org.apache.seata.serializer.protobuf.ProtobufSerializer \ No newline at end of file +org.apache.seata.serializer.protobuf.ProtobufSerializer +org.apache.seata.serializer.protobuf.GrpcSerializer \ No newline at end of file diff --git a/server/pom.xml b/server/pom.xml index 63d34056a5d..5ba3e8dd9e9 100644 --- a/server/pom.xml +++ b/server/pom.xml @@ -362,31 +362,31 @@ ${dependencies.copy.skip} - - - - - - - - - - - - - - - - - - - - - - - - - + + copy-mysql + package + + copy + + + + + mysql + mysql-connector-java + ${mysql.jdbc.version} + + + mysql + mysql-connector-java + ${mysql8.jdbc.version} + + + + ${project.build.directory}/lib/jdbc + + ${mysql.copy.skip} + + diff --git a/server/src/main/java/org/apache/seata/server/Server.java b/server/src/main/java/org/apache/seata/server/Server.java index c699ef037b1..39648dae824 100644 --- a/server/src/main/java/org/apache/seata/server/Server.java +++ b/server/src/main/java/org/apache/seata/server/Server.java @@ -31,12 +31,11 @@ import org.apache.seata.core.rpc.netty.NettyRemotingServer; import org.apache.seata.core.rpc.netty.NettyServerConfig; import org.apache.seata.server.coordinator.DefaultCoordinator; -import org.apache.seata.server.instance.ServerInstance; +import org.apache.seata.server.instance.ServerInstanceFactory; import org.apache.seata.server.lock.LockerManagerFactory; import org.apache.seata.server.metrics.MetricsManager; import org.apache.seata.server.session.SessionHolder; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; + import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.context.ApplicationListener; import org.springframework.stereotype.Component; @@ -52,10 +51,9 @@ */ @Component("seataServer") public class Server { - private static final Logger LOGGER = LoggerFactory.getLogger(Server.class); @Resource - ServerInstance serverInstance; + ServerInstanceFactory serverInstanceFactory; /** * The entry point of application. @@ -106,7 +104,7 @@ public void start(String[] args) { coordinator.init(); nettyRemotingServer.setHandler(coordinator); - serverInstance.serverInstanceInit(); + serverInstanceFactory.serverInstanceInit(); // let ServerRunner do destroy instead ShutdownHook, see https://github.com/seata/seata/issues/4028 ServerRunner.addDisposable(coordinator); nettyRemotingServer.init(); diff --git a/server/src/main/java/org/apache/seata/server/cluster/raft/RaftServer.java b/server/src/main/java/org/apache/seata/server/cluster/raft/RaftServer.java index 37d18976144..ff3113e4298 100644 --- a/server/src/main/java/org/apache/seata/server/cluster/raft/RaftServer.java +++ b/server/src/main/java/org/apache/seata/server/cluster/raft/RaftServer.java @@ -21,6 +21,7 @@ import java.io.IOException; import java.util.Optional; import java.util.concurrent.TimeUnit; + import com.alipay.sofa.jraft.Node; import com.alipay.sofa.jraft.RaftGroupService; import com.alipay.sofa.jraft.RouteTable; @@ -28,14 +29,23 @@ import com.alipay.sofa.jraft.option.NodeOptions; import com.alipay.sofa.jraft.rpc.RpcServer; import com.codahale.metrics.Slf4jReporter; +import org.apache.commons.io.FileUtils; +import org.apache.seata.config.Configuration; import org.apache.seata.config.ConfigurationFactory; import org.apache.seata.core.rpc.Disposable; -import org.apache.commons.io.FileUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import static org.apache.seata.common.ConfigurationKeys.SERVER_RAFT_REPORTER_ENABLED; import static org.apache.seata.common.ConfigurationKeys.SERVER_RAFT_REPORTER_INITIAL_DELAY; +import static org.apache.seata.common.ConfigurationKeys.SERVER_RAFT_SSL_CLIENT_KEYSTORE_PASSWORD; +import static org.apache.seata.common.ConfigurationKeys.SERVER_RAFT_SSL_CLIENT_KEYSTORE_PATH; +import static org.apache.seata.common.ConfigurationKeys.SERVER_RAFT_SSL_ENABLED; +import static org.apache.seata.common.ConfigurationKeys.SERVER_RAFT_SSL_KEYSTORE_TYPE; +import static org.apache.seata.common.ConfigurationKeys.SERVER_RAFT_SSL_KMF_ALGORITHM; +import static org.apache.seata.common.ConfigurationKeys.SERVER_RAFT_SSL_SERVER_KEYSTORE_PASSWORD; +import static org.apache.seata.common.ConfigurationKeys.SERVER_RAFT_SSL_SERVER_KEYSTORE_PATH; +import static org.apache.seata.common.DefaultValues.DEFAULT_RAFT_SSL_ENABLED; /** */ @@ -80,6 +90,11 @@ public void start() throws IOException { this.raftGroupService = new RaftGroupService(groupId, serverId, nodeOptions, rpcServer, true); this.node = this.raftGroupService.start(false); RouteTable.getInstance().updateConfiguration(groupId, node.getOptions().getInitialConf()); + // Enable SSL authentication for the Raft group if SSL is enabled. + boolean sslEnabled = ConfigurationFactory.getInstance().getBoolean(SERVER_RAFT_SSL_ENABLED, DEFAULT_RAFT_SSL_ENABLED); + if (sslEnabled) { + enableSSL(); + } if (reporterEnabled) { final Slf4jReporter reporter = Slf4jReporter.forRegistry(node.getNodeMetrics().getMetricRegistry()) .outputTo(logger).convertRatesTo(TimeUnit.SECONDS) @@ -119,4 +134,31 @@ public void destroy() { }); } + private void enableSSL() { + setSystemProperty("bolt.server.ssl.enable", "true"); + setSystemProperty("bolt.server.ssl.clientAuth", "true"); + setSystemProperty("bolt.client.ssl.enable", "true"); + + Configuration instance = ConfigurationFactory.getInstance(); + setSystemProperty("bolt.server.ssl.keystore", instance.getConfig(SERVER_RAFT_SSL_SERVER_KEYSTORE_PATH)); + setSystemProperty("bolt.server.ssl.keystore.password", + instance.getConfig(SERVER_RAFT_SSL_SERVER_KEYSTORE_PASSWORD)); + setSystemProperty("bolt.server.ssl.keystore.type", instance.getConfig(SERVER_RAFT_SSL_KEYSTORE_TYPE)); + setSystemProperty("bolt.server.ssl.kmf.algorithm", instance.getConfig(SERVER_RAFT_SSL_KMF_ALGORITHM)); + setSystemProperty("bolt.client.ssl.keystore", instance.getConfig(SERVER_RAFT_SSL_CLIENT_KEYSTORE_PATH)); + setSystemProperty("bolt.client.ssl.keystore.password", + instance.getConfig(SERVER_RAFT_SSL_CLIENT_KEYSTORE_PASSWORD)); + setSystemProperty("bolt.client.ssl.keystore.type", instance.getConfig(SERVER_RAFT_SSL_KEYSTORE_TYPE)); + setSystemProperty("bolt.client.ssl.tmf.algorithm", instance.getConfig(SERVER_RAFT_SSL_KMF_ALGORITHM)); + + logger.info("Enable ssl communication between raft nodes"); + } + + private void setSystemProperty(String property, String value) { + if (value == null || value.isEmpty()) { + throw new IllegalArgumentException("Configuration value for " + property + " cannot be null or empty"); + } + System.setProperty(property, value); + } + } diff --git a/server/src/main/java/org/apache/seata/server/cluster/raft/RaftServerManager.java b/server/src/main/java/org/apache/seata/server/cluster/raft/RaftServerManager.java index b891bea8484..47e7ee4432b 100644 --- a/server/src/main/java/org/apache/seata/server/cluster/raft/RaftServerManager.java +++ b/server/src/main/java/org/apache/seata/server/cluster/raft/RaftServerManager.java @@ -38,6 +38,7 @@ import com.alipay.sofa.jraft.rpc.impl.cli.CliClientServiceImpl; import org.apache.seata.common.ConfigurationKeys; import org.apache.seata.common.XID; +import org.apache.seata.common.store.SessionMode; import org.apache.seata.common.util.StringUtils; import org.apache.seata.config.ConfigurationFactory; import org.apache.seata.core.serializer.SerializerType; @@ -87,7 +88,7 @@ public static CliClientService getCliClientServiceInstance() { public static void init() { if (INIT.compareAndSet(false, true)) { String initConfStr = CONFIG.getConfig(ConfigurationKeys.SERVER_RAFT_SERVER_ADDR); - RAFT_MODE = StoreConfig.getSessionMode().equals(StoreConfig.SessionMode.RAFT); + RAFT_MODE = StoreConfig.getSessionMode().equals(SessionMode.RAFT); if (StringUtils.isBlank(initConfStr)) { if (RAFT_MODE) { throw new IllegalArgumentException( diff --git a/server/src/main/java/org/apache/seata/server/cluster/raft/RaftStateMachine.java b/server/src/main/java/org/apache/seata/server/cluster/raft/RaftStateMachine.java index 9b49ba011ff..2b14b2a5cb9 100644 --- a/server/src/main/java/org/apache/seata/server/cluster/raft/RaftStateMachine.java +++ b/server/src/main/java/org/apache/seata/server/cluster/raft/RaftStateMachine.java @@ -49,6 +49,8 @@ import org.apache.seata.common.holder.ObjectHolder; import org.apache.seata.common.metadata.ClusterRole; import org.apache.seata.common.metadata.Node; +import org.apache.seata.common.store.SessionMode; +import org.apache.seata.common.store.StoreMode; import org.apache.seata.common.thread.NamedThreadFactory; import org.apache.seata.common.util.CollectionUtils; import org.apache.seata.common.util.StringUtils; @@ -140,7 +142,7 @@ public RaftStateMachine(String group) { return null; }); registryStoreSnapshotFile(new LeaderMetadataSnapshotFile(group)); - if (StoreConfig.StoreMode.RAFT.getName().equalsIgnoreCase(mode)) { + if (StoreMode.RAFT.getName().equalsIgnoreCase(mode)) { registryStoreSnapshotFile(new SessionSnapshotFile(group)); EXECUTES.put(ADD_GLOBAL_SESSION, new AddGlobalSessionExecute()); EXECUTES.put(ADD_BRANCH_SESSION, new AddBranchSessionExecute()); @@ -180,7 +182,7 @@ public void onApply(Iterator iterator) { @Override public void onSnapshotSave(final SnapshotWriter writer, final Closure done) { - if (!StringUtils.equals(StoreConfig.SessionMode.RAFT.getName(), mode)) { + if (!StringUtils.equals(SessionMode.RAFT.getName(), mode)) { done.run(Status.OK()); return; } @@ -198,7 +200,7 @@ public void onSnapshotSave(final SnapshotWriter writer, final Closure done) { @Override public boolean onSnapshotLoad(final SnapshotReader reader) { - if (!StringUtils.equals(StoreConfig.SessionMode.RAFT.getName(), mode)) { + if (!StringUtils.equals(SessionMode.RAFT.getName(), mode)) { return true; } if (isLeader()) { @@ -232,7 +234,7 @@ public void onLeaderStart(final long term) { try { // become the leader again,reloading global session SessionHolder.reload(SessionHolder.getRootSessionManager().allSessions(), - StoreConfig.SessionMode.RAFT, false); + SessionMode.RAFT, false); } finally { SeataClusterContext.unbindGroup(); } diff --git a/server/src/main/java/org/apache/seata/server/cluster/raft/execute/branch/AddBranchSessionExecute.java b/server/src/main/java/org/apache/seata/server/cluster/raft/execute/branch/AddBranchSessionExecute.java index 0bf2f122215..9d3086ad7ae 100644 --- a/server/src/main/java/org/apache/seata/server/cluster/raft/execute/branch/AddBranchSessionExecute.java +++ b/server/src/main/java/org/apache/seata/server/cluster/raft/execute/branch/AddBranchSessionExecute.java @@ -35,7 +35,16 @@ public Boolean execute(RaftBaseMsg syncMsg) throws Throwable { RaftBranchSessionSyncMsg sessionSyncMsg = (RaftBranchSessionSyncMsg)syncMsg; RaftSessionManager raftSessionManager = (RaftSessionManager) SessionHolder.getRootSessionManager(sessionSyncMsg.getGroup()); BranchTransactionDTO branchTransactionDTO = sessionSyncMsg.getBranchSession(); - GlobalSession globalSession = raftSessionManager.findGlobalSession(branchTransactionDTO.getXid()); + String xid = branchTransactionDTO.getXid(); + GlobalSession globalSession = raftSessionManager.findGlobalSession(xid); + if (globalSession == null) { + if (logger.isWarnEnabled()) { + logger.warn( + "The transaction corresponding to the XID: {} does not exist, which may cause a two-phase concurrency issue, msg type: {}", + xid, syncMsg.getMsgType()); + } + return false; + } BranchSession branchSession = SessionConverter.convertBranchSession(branchTransactionDTO); branchSession.lock(); globalSession.add(branchSession); diff --git a/server/src/main/java/org/apache/seata/server/cluster/raft/execute/branch/UpdateBranchSessionExecute.java b/server/src/main/java/org/apache/seata/server/cluster/raft/execute/branch/UpdateBranchSessionExecute.java index c98f3820476..e096ea36cdc 100644 --- a/server/src/main/java/org/apache/seata/server/cluster/raft/execute/branch/UpdateBranchSessionExecute.java +++ b/server/src/main/java/org/apache/seata/server/cluster/raft/execute/branch/UpdateBranchSessionExecute.java @@ -33,8 +33,26 @@ public class UpdateBranchSessionExecute extends AbstractRaftMsgExecute { public Boolean execute(RaftBaseMsg syncMsg) throws Throwable { RaftBranchSessionSyncMsg sessionSyncMsg = (RaftBranchSessionSyncMsg)syncMsg; RaftSessionManager raftSessionManager = (RaftSessionManager) SessionHolder.getRootSessionManager(sessionSyncMsg.getGroup()); - GlobalSession globalSession = raftSessionManager.findGlobalSession(sessionSyncMsg.getBranchSession().getXid()); - BranchSession branchSession = globalSession.getBranch(sessionSyncMsg.getBranchSession().getBranchId()); + String xid = sessionSyncMsg.getBranchSession().getXid(); + GlobalSession globalSession = raftSessionManager.findGlobalSession(xid); + if (globalSession == null) { + if (logger.isWarnEnabled()) { + logger.warn( + "The transaction corresponding to the XID: {} does not exist, which may cause a two-phase concurrency issue, msg type: {}", + xid, syncMsg.getMsgType()); + } + return false; + } + long branchId = sessionSyncMsg.getBranchSession().getBranchId(); + BranchSession branchSession = globalSession.getBranch(branchId); + if (branchSession == null) { + if (logger.isWarnEnabled()) { + logger.warn( + "The branch session corresponding to the branchId: {} does not exist, which may cause a two-phase concurrency issue, msg type: {}", + sessionSyncMsg.getBranchSession().getBranchId(), syncMsg.getMsgType()); + } + return false; + } BranchStatus status = BranchStatus.get(sessionSyncMsg.getBranchSession().getStatus()); branchSession.setStatus(status); if (logger.isDebugEnabled()) { diff --git a/server/src/main/java/org/apache/seata/server/cluster/raft/execute/lock/BranchReleaseLockExecute.java b/server/src/main/java/org/apache/seata/server/cluster/raft/execute/lock/BranchReleaseLockExecute.java index 805ef1b7f7b..206fe77e3cd 100644 --- a/server/src/main/java/org/apache/seata/server/cluster/raft/execute/lock/BranchReleaseLockExecute.java +++ b/server/src/main/java/org/apache/seata/server/cluster/raft/execute/lock/BranchReleaseLockExecute.java @@ -30,8 +30,16 @@ public class BranchReleaseLockExecute extends AbstractRaftMsgExecute { @Override public Boolean execute(RaftBaseMsg syncMsg) throws Throwable { RaftBranchSessionSyncMsg sessionSyncMsg = (RaftBranchSessionSyncMsg)syncMsg; - GlobalSession globalSession = - SessionHolder.getRootSessionManager().findGlobalSession(sessionSyncMsg.getBranchSession().getXid()); + String xid = sessionSyncMsg.getBranchSession().getXid(); + GlobalSession globalSession = SessionHolder.getRootSessionManager().findGlobalSession(xid); + if (globalSession == null) { + if (logger.isWarnEnabled()) { + logger.warn( + "The transaction corresponding to the XID: {} does not exist, which may cause a two-phase concurrency issue, msg type: {}", + xid, syncMsg.getMsgType()); + } + return false; + } BranchSession branchSession = globalSession.getBranch(sessionSyncMsg.getBranchSession().getBranchId()); if (branchSession != null) { if (logger.isDebugEnabled()) { diff --git a/server/src/main/java/org/apache/seata/server/cluster/raft/serializer/CustomDeserializer.java b/server/src/main/java/org/apache/seata/server/cluster/raft/serializer/CustomDeserializer.java index 6f5a6614a56..533f5c3d678 100644 --- a/server/src/main/java/org/apache/seata/server/cluster/raft/serializer/CustomDeserializer.java +++ b/server/src/main/java/org/apache/seata/server/cluster/raft/serializer/CustomDeserializer.java @@ -20,6 +20,8 @@ import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.databind.DeserializationContext; import com.fasterxml.jackson.databind.JsonDeserializer; +import org.apache.seata.common.exception.ErrorCode; +import org.apache.seata.common.exception.SeataRuntimeException; public class CustomDeserializer extends JsonDeserializer> { @@ -27,6 +29,8 @@ public class CustomDeserializer extends JsonDeserializer> { String currentPackage = "org.apache.seata.server"; + String permitPackage = "org.apache.seata"; + @Override public Class deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException { @@ -34,11 +38,15 @@ public Class deserialize(JsonParser jsonParser, DeserializationContext deseri if (className.startsWith(oldPackage)) { className = className.replaceFirst(oldPackage, currentPackage); } - try { - return Class.forName(className); - } catch (ClassNotFoundException e) { - throw new RuntimeException(e.getMessage(), e); + if (className.startsWith(permitPackage)) { + try { + return Class.forName(className); + } catch (ClassNotFoundException e) { + throw new RuntimeException(e.getMessage(), e); + } } + throw new SeataRuntimeException(ErrorCode.ERR_DESERIALIZATION_SECURITY, + "Failed to deserialize object: " + className + " is not permitted"); } } diff --git a/server/src/main/java/org/apache/seata/server/cluster/raft/snapshot/RaftSnapshotSerializer.java b/server/src/main/java/org/apache/seata/server/cluster/raft/snapshot/RaftSnapshotSerializer.java index 1d275920326..28aa70b9937 100644 --- a/server/src/main/java/org/apache/seata/server/cluster/raft/snapshot/RaftSnapshotSerializer.java +++ b/server/src/main/java/org/apache/seata/server/cluster/raft/snapshot/RaftSnapshotSerializer.java @@ -26,6 +26,7 @@ import java.util.List; import java.util.Optional; +import com.fasterxml.jackson.databind.JsonMappingException; import org.apache.seata.common.exception.ErrorCode; import org.apache.seata.common.exception.SeataRuntimeException; import org.slf4j.Logger; @@ -108,11 +109,18 @@ protected Class resolveClass(ObjectStreamClass desc) throws IOException, Clas .getCompressor(raftSnapshot.getCompressor()).decompress((byte[])raftSnapshot.getBody())))); return raftSnapshot; } catch (Exception e) { - LOGGER.info("Failed to read raft snapshot: {}", e.getMessage(), e); - if (e instanceof SeataRuntimeException) { - throw (SeataRuntimeException)e; + LOGGER.error("Failed to read raft snapshot: {}", e.getMessage(), e); + if (e instanceof RuntimeException) { + Throwable cause = e.getCause(); + if (cause instanceof JsonMappingException) { + Throwable jsonCause = cause.getCause(); + if (jsonCause instanceof SeataRuntimeException) { + throw (SeataRuntimeException)jsonCause; + } + } + throw (RuntimeException)e; } - throw new IOException(e); + throw new RuntimeException(e); } } diff --git a/server/src/main/java/org/apache/seata/server/cluster/raft/sync/RaftSyncMessageSerializer.java b/server/src/main/java/org/apache/seata/server/cluster/raft/sync/RaftSyncMessageSerializer.java index cd3a7eb4af2..0a472e05733 100644 --- a/server/src/main/java/org/apache/seata/server/cluster/raft/sync/RaftSyncMessageSerializer.java +++ b/server/src/main/java/org/apache/seata/server/cluster/raft/sync/RaftSyncMessageSerializer.java @@ -25,6 +25,7 @@ import java.util.ArrayList; import java.util.List; import java.util.Optional; +import com.fasterxml.jackson.databind.JsonMappingException; import org.apache.seata.common.exception.ErrorCode; import org.apache.seata.common.exception.SeataRuntimeException; import org.apache.seata.common.loader.EnhancedServiceLoader; @@ -82,7 +83,6 @@ protected Class resolveClass(ObjectStreamClass desc) throws IOException, Clas throw new SeataRuntimeException(ErrorCode.ERR_DESERIALIZATION_SECURITY, "Failed to deserialize object: " + desc.getName() + " is not permitted"); } - return super.resolveClass(desc); } }) { @@ -106,9 +106,16 @@ protected Class resolveClass(ObjectStreamClass desc) throws IOException, Clas .getCompressor(raftSyncMessage.getCompressor()).decompress((byte[])raftSyncMessage.getBody())))); return raftSyncMessage; } catch (Exception e) { - LOGGER.info("Failed to read raft synchronization log: {}", e.getMessage(), e); - if (e instanceof SeataRuntimeException) { - throw (SeataRuntimeException)e; + LOGGER.error("Failed to read raft synchronization log: {}", e.getMessage(), e); + if (e instanceof RuntimeException) { + Throwable cause = e.getCause(); + if (cause instanceof JsonMappingException) { + Throwable jsonCause = cause.getCause(); + if (jsonCause instanceof SeataRuntimeException) { + throw (SeataRuntimeException)jsonCause; + } + } + throw (RuntimeException)e; } throw new RuntimeException(e); } diff --git a/server/src/main/java/org/apache/seata/server/cluster/raft/sync/msg/dto/RaftClusterMetadata.java b/server/src/main/java/org/apache/seata/server/cluster/raft/sync/msg/dto/RaftClusterMetadata.java index 2e3e217a96c..7f51ff9efac 100644 --- a/server/src/main/java/org/apache/seata/server/cluster/raft/sync/msg/dto/RaftClusterMetadata.java +++ b/server/src/main/java/org/apache/seata/server/cluster/raft/sync/msg/dto/RaftClusterMetadata.java @@ -21,10 +21,12 @@ import java.util.List; import java.util.Map; import java.util.Optional; - +import org.apache.seata.common.holder.ObjectHolder; import org.apache.seata.common.metadata.Node; import org.apache.seata.common.util.StringUtils; import org.apache.seata.core.protocol.Version; +import org.springframework.core.env.ConfigurableEnvironment; +import static org.apache.seata.common.Constants.OBJECT_KEY_SPRING_CONFIGURABLE_ENVIRONMENT; /** */ @@ -55,7 +57,16 @@ public Node createNode(String host, int txPort, int internalPort, int controlPor node.setGroup(group); node.setVersion(Version.getCurrent()); node.setInternal(node.createEndpoint(host, internalPort, "raft")); - Optional.ofNullable(metadata).ifPresent(node::setMetadata); + ConfigurableEnvironment environment = (ConfigurableEnvironment) ObjectHolder.INSTANCE.getObject(OBJECT_KEY_SPRING_CONFIGURABLE_ENVIRONMENT); + String seataRegistryMetadataExternalValue = environment.resolvePlaceholders("${SEATA_REGISTRY_METADATA_EXTERNAL:${seata.registry.metadata.external:}}"); + if (metadata != null) { + if (StringUtils.isNotEmpty(seataRegistryMetadataExternalValue)) { + Map newMetadata = node.updateMetadataWithExternalEndpoints(metadata, node.createExternalEndpoints(seataRegistryMetadataExternalValue)); + Optional.ofNullable(newMetadata).ifPresent(node::setMetadata); + } else { + node.setMetadata(metadata); + } + } return node; } diff --git a/server/src/main/java/org/apache/seata/server/controller/VGroupMappingController.java b/server/src/main/java/org/apache/seata/server/controller/VGroupMappingController.java index 348a7502830..bf9edf97876 100644 --- a/server/src/main/java/org/apache/seata/server/controller/VGroupMappingController.java +++ b/server/src/main/java/org/apache/seata/server/controller/VGroupMappingController.java @@ -16,9 +16,8 @@ */ package org.apache.seata.server.controller; -import org.apache.seata.common.metadata.namingserver.Instance; +import org.apache.seata.common.metadata.Instance; import org.apache.seata.common.result.Result; -import org.apache.seata.common.util.StringUtils; import org.apache.seata.config.Configuration; import org.apache.seata.config.ConfigurationFactory; import org.apache.seata.core.store.MappingDO; @@ -29,13 +28,6 @@ import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; -import javax.annotation.PostConstruct; - -import static org.apache.seata.common.ConfigurationKeys.FILE_CONFIG_SPLIT_CHAR; -import static org.apache.seata.common.ConfigurationKeys.FILE_ROOT_REGISTRY; -import static org.apache.seata.common.ConfigurationKeys.FILE_ROOT_TYPE; -import static org.apache.seata.common.ConfigurationKeys.NAMING_SERVER; - @RestController @RequestMapping("/vgroup/v1") public class VGroupMappingController { @@ -44,15 +36,6 @@ public class VGroupMappingController { protected static final Configuration CONFIG = ConfigurationFactory.getInstance(); - @PostConstruct - private void init() { - String type = - ConfigurationFactory.getInstance().getConfig(FILE_ROOT_REGISTRY + FILE_CONFIG_SPLIT_CHAR + FILE_ROOT_TYPE); - if (StringUtils.equals(type, NAMING_SERVER)) { - vGroupMappingStoreManager = SessionHolder.getRootVGroupMappingManager(); - } - } - /** * add vGroup in cluster * @@ -67,7 +50,7 @@ public Result addVGroup(@RequestParam String vGroup, @RequestParam String uni mappingDO.setCluster(Instance.getInstance().getClusterName()); mappingDO.setUnit(unit); mappingDO.setVGroup(vGroup); - boolean rst = vGroupMappingStoreManager.addVGroup(mappingDO); + boolean rst = SessionHolder.getRootVGroupMappingManager().addVGroup(mappingDO); Instance.getInstance().setTerm(System.currentTimeMillis()); if (!rst) { result.setCode("500"); @@ -85,7 +68,7 @@ public Result addVGroup(@RequestParam String vGroup, @RequestParam String uni @GetMapping("/removeVGroup") public Result removeVGroup(@RequestParam String vGroup) { Result result = new Result<>(); - boolean rst = vGroupMappingStoreManager.removeVGroup(vGroup); + boolean rst = SessionHolder.getRootVGroupMappingManager().removeVGroup(vGroup); Instance.getInstance().setTerm(System.currentTimeMillis()); if (!rst) { result.setCode("500"); diff --git a/server/src/main/java/org/apache/seata/server/coordinator/DefaultCoordinator.java b/server/src/main/java/org/apache/seata/server/coordinator/DefaultCoordinator.java index 9003fe268aa..468e9ccc5ff 100644 --- a/server/src/main/java/org/apache/seata/server/coordinator/DefaultCoordinator.java +++ b/server/src/main/java/org/apache/seata/server/coordinator/DefaultCoordinator.java @@ -28,7 +28,9 @@ import java.util.concurrent.TimeUnit; import io.netty.channel.Channel; +import org.apache.commons.lang.time.DateFormatUtils; import org.apache.seata.common.DefaultValues; +import org.apache.seata.common.store.SessionMode; import org.apache.seata.common.thread.NamedThreadFactory; import org.apache.seata.common.util.CollectionUtils; import org.apache.seata.config.ConfigurationFactory; @@ -71,7 +73,6 @@ import org.apache.seata.server.session.SessionHelper; import org.apache.seata.server.session.SessionHolder; import org.apache.seata.server.store.StoreConfig; -import org.apache.commons.lang.time.DateFormatUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.slf4j.MDC; @@ -90,7 +91,7 @@ import static org.apache.seata.common.DefaultValues.DEFAULT_MAX_COMMIT_RETRY_TIMEOUT; import static org.apache.seata.common.DefaultValues.DEFAULT_MAX_ROLLBACK_RETRY_TIMEOUT; import static org.apache.seata.common.DefaultValues.DEFAULT_ROLLBACKING_RETRY_PERIOD; -import static org.apache.seata.common.DefaultValues.DEFAULT_ROLLBACK_RETRY_TIMEOUT_UNLOCK_ENABLE; +import static org.apache.seata.common.DefaultValues.DEFAULT_ROLLBACK_FAILED_UNLOCK_ENABLE; import static org.apache.seata.common.DefaultValues.DEFAULT_TIMEOUT_RETRY_PERIOD; import static org.apache.seata.common.DefaultValues.DEFAULT_UNDO_LOG_DELETE_PERIOD; @@ -159,7 +160,10 @@ public class DefaultCoordinator extends AbstractTCInboundHandler implements Tran ConfigurationKeys.MAX_ROLLBACK_RETRY_TIMEOUT, DEFAULT_MAX_ROLLBACK_RETRY_TIMEOUT); private static final boolean ROLLBACK_RETRY_TIMEOUT_UNLOCK_ENABLE = ConfigurationFactory.getInstance().getBoolean( - ConfigurationKeys.ROLLBACK_RETRY_TIMEOUT_UNLOCK_ENABLE, DEFAULT_ROLLBACK_RETRY_TIMEOUT_UNLOCK_ENABLE); + ConfigurationKeys.ROLLBACK_RETRY_TIMEOUT_UNLOCK_ENABLE, DEFAULT_ROLLBACK_FAILED_UNLOCK_ENABLE); + + private static final boolean ROLLBACK_FAILED_UNLOCK_ENABLE = ConfigurationFactory.getInstance().getBoolean( + ConfigurationKeys.ROLLBACK_FAILED_UNLOCK_ENABLE, DEFAULT_ROLLBACK_FAILED_UNLOCK_ENABLE); private static final int RETRY_DEAD_THRESHOLD = ConfigurationFactory.getInstance() .getInt(org.apache.seata.common.ConfigurationKeys.RETRY_DEAD_THRESHOLD, DefaultValues.DEFAULT_RETRY_DEAD_THRESHOLD); @@ -213,7 +217,7 @@ protected DefaultCoordinator(RemotingServer remotingServer) { boolean enableBranchAsyncRemove = CONFIG.getBoolean( ConfigurationKeys.ENABLE_BRANCH_ASYNC_REMOVE, DEFAULT_ENABLE_BRANCH_ASYNC_REMOVE); // create branchRemoveExecutor - if (enableBranchAsyncRemove && StoreConfig.getSessionMode() != StoreConfig.SessionMode.FILE) { + if (enableBranchAsyncRemove && StoreConfig.getSessionMode() != SessionMode.FILE) { branchRemoveExecutor = new ThreadPoolExecutor(BRANCH_ASYNC_POOL_SIZE, BRANCH_ASYNC_POOL_SIZE, Integer.MAX_VALUE, TimeUnit.MILLISECONDS, new ArrayBlockingQueue<>( @@ -229,8 +233,8 @@ public static DefaultCoordinator getInstance(RemotingServer remotingServer) { if (null == instance) { synchronized (DefaultCoordinator.class) { if (null == instance) { - StoreConfig.SessionMode storeMode = StoreConfig.getSessionMode(); - instance = Objects.equals(StoreConfig.SessionMode.RAFT, storeMode) + SessionMode storeMode = StoreConfig.getSessionMode(); + instance = Objects.equals(SessionMode.RAFT, storeMode) ? new RaftCoordinator(remotingServer) : new DefaultCoordinator(remotingServer); } } @@ -341,15 +345,15 @@ protected void doLockCheck(GlobalLockQueryRequest request, GlobalLockQueryRespon protected void timeoutCheck() { SessionCondition sessionCondition = new SessionCondition(GlobalStatus.Begin); sessionCondition.setLazyLoadBranch(true); - Collection beginGlobalsessions = + Collection beginGlobalSessions = SessionHolder.getRootSessionManager().findGlobalSessions(sessionCondition); - if (CollectionUtils.isEmpty(beginGlobalsessions)) { + if (CollectionUtils.isEmpty(beginGlobalSessions)) { return; } - if (!beginGlobalsessions.isEmpty() && LOGGER.isDebugEnabled()) { - LOGGER.debug("Global transaction timeout check begin, size: {}", beginGlobalsessions.size()); + if (!beginGlobalSessions.isEmpty() && LOGGER.isDebugEnabled()) { + LOGGER.debug("Global transaction timeout check begin, size: {}", beginGlobalSessions.size()); } - SessionHelper.forEach(beginGlobalsessions, globalSession -> { + SessionHelper.forEach(beginGlobalSessions, globalSession -> { if (LOGGER.isDebugEnabled()) { LOGGER.debug( globalSession.getXid() + " " + globalSession.getStatus() + " " + globalSession.getBeginTime() + " " @@ -372,7 +376,7 @@ protected void timeoutCheck() { return true; }); }); - if (!beginGlobalsessions.isEmpty() && LOGGER.isDebugEnabled()) { + if (!beginGlobalSessions.isEmpty() && LOGGER.isDebugEnabled()) { LOGGER.debug("Global transaction timeout check end. "); } @@ -394,7 +398,7 @@ protected void handleRetryRollbacking() { SessionHelper.forEach(rollbackingSessions, rollbackingSession -> { try { if (isRetryTimeout(now, MAX_ROLLBACK_RETRY_TIMEOUT, rollbackingSession.getBeginTime())) { - if (ROLLBACK_RETRY_TIMEOUT_UNLOCK_ENABLE) { + if (ROLLBACK_RETRY_TIMEOUT_UNLOCK_ENABLE || ROLLBACK_FAILED_UNLOCK_ENABLE) { rollbackingSession.clean(); } @@ -520,7 +524,7 @@ protected void handleRollbackingByScheduled() { SessionHelper.forEach(needDoRollbackingSessions, rollbackingSession -> { try { if (isRetryTimeout(now, MAX_ROLLBACK_RETRY_TIMEOUT, rollbackingSession.getBeginTime())) { - if (ROLLBACK_RETRY_TIMEOUT_UNLOCK_ENABLE) { + if (ROLLBACK_RETRY_TIMEOUT_UNLOCK_ENABLE || ROLLBACK_FAILED_UNLOCK_ENABLE) { rollbackingSession.clean(); } diff --git a/server/src/main/java/org/apache/seata/server/coordinator/DefaultCore.java b/server/src/main/java/org/apache/seata/server/coordinator/DefaultCore.java index d6c93532fa7..5acbc8988de 100644 --- a/server/src/main/java/org/apache/seata/server/coordinator/DefaultCore.java +++ b/server/src/main/java/org/apache/seata/server/coordinator/DefaultCore.java @@ -57,7 +57,7 @@ public class DefaultCore implements Core { private static final int RETRY_XAER_NOTA_TIMEOUT = ConfigurationFactory.getInstance().getInt(XAER_NOTA_RETRY_TIMEOUT, DefaultValues.DEFAULT_XAER_NOTA_RETRY_TIMEOUT); - private static Map coreMap = new ConcurrentHashMap<>(); + private static final Map CORE_MAP = new ConcurrentHashMap<>(); private static final boolean PARALLEL_HANDLE_BRANCH = ConfigurationFactory.getInstance().getBoolean(ENABLE_PARALLEL_HANDLE_BRANCH_KEY, false); @@ -72,7 +72,7 @@ public DefaultCore(RemotingServer remotingServer) { new Class[] {RemotingServer.class}, new Object[] {remotingServer}); if (CollectionUtils.isNotEmpty(allCore)) { for (AbstractCore core : allCore) { - coreMap.put(core.getHandleBranchType(), core); + CORE_MAP.put(core.getHandleBranchType(), core); } } } @@ -84,7 +84,7 @@ public DefaultCore(RemotingServer remotingServer) { * @return the core */ public AbstractCore getCore(BranchType branchType) { - AbstractCore core = coreMap.get(branchType); + AbstractCore core = CORE_MAP.get(branchType); if (core == null) { throw new NotSupportYetException("unsupported type:" + branchType.name()); } @@ -98,7 +98,7 @@ public AbstractCore getCore(BranchType branchType) { * @param core the core */ public void mockCore(BranchType branchType, AbstractCore core) { - coreMap.put(branchType, core); + CORE_MAP.put(branchType, core); } @Override diff --git a/server/src/main/java/org/apache/seata/server/coordinator/RaftCoordinator.java b/server/src/main/java/org/apache/seata/server/coordinator/RaftCoordinator.java index db9af1926bf..8e01601b953 100644 --- a/server/src/main/java/org/apache/seata/server/coordinator/RaftCoordinator.java +++ b/server/src/main/java/org/apache/seata/server/coordinator/RaftCoordinator.java @@ -16,6 +16,7 @@ */ package org.apache.seata.server.coordinator; +import org.apache.seata.common.store.SessionMode; import org.apache.seata.core.exception.TransactionException; import org.apache.seata.core.exception.TransactionExceptionCode; import org.apache.seata.core.protocol.transaction.AbstractTransactionRequest; @@ -64,7 +65,7 @@ private boolean isPass(String group) { } public static void setPrevent(String group, boolean prevent) { - if (StoreConfig.getSessionMode() == StoreConfig.SessionMode.RAFT) { + if (StoreConfig.getSessionMode() == SessionMode.RAFT) { GROUP_PREVENT.put(group, prevent); } } diff --git a/server/src/main/java/org/apache/seata/server/instance/ServerInstance.java b/server/src/main/java/org/apache/seata/server/instance/ServerInstanceFactory.java similarity index 58% rename from server/src/main/java/org/apache/seata/server/instance/ServerInstance.java rename to server/src/main/java/org/apache/seata/server/instance/ServerInstanceFactory.java index eecf39b0717..78842461e73 100644 --- a/server/src/main/java/org/apache/seata/server/instance/ServerInstance.java +++ b/server/src/main/java/org/apache/seata/server/instance/ServerInstanceFactory.java @@ -16,11 +16,11 @@ */ package org.apache.seata.server.instance; +import org.apache.seata.common.XID; import org.apache.seata.common.holder.ObjectHolder; +import org.apache.seata.common.metadata.Instance; import org.apache.seata.common.metadata.Node; -import org.apache.seata.common.metadata.namingserver.Instance; import org.apache.seata.common.thread.NamedThreadFactory; -import org.apache.seata.common.util.NetUtil; import org.apache.seata.common.util.StringUtils; import org.apache.seata.server.Server; import org.apache.seata.server.ServerRunner; @@ -48,8 +48,8 @@ import static org.apache.seata.common.Constants.OBJECT_KEY_SPRING_CONFIGURABLE_ENVIRONMENT; -@Component("serverInstance") -public class ServerInstance { +@Component("serverInstanceFactory") +public class ServerInstanceFactory { @Resource private RegistryProperties registryProperties; @@ -61,53 +61,58 @@ public class ServerInstance { private static final Logger LOGGER = LoggerFactory.getLogger(Server.class); public void serverInstanceInit() { - if (StringUtils.equals(registryProperties.getType(), NAMING_SERVER)) { - VGroupMappingStoreManager vGroupMappingStoreManager = SessionHolder.getRootVGroupMappingManager(); - EXECUTOR_SERVICE = new ScheduledThreadPoolExecutor(1, new NamedThreadFactory("scheduledExcuter", 1, true)); - ConfigurableEnvironment environment = (ConfigurableEnvironment) ObjectHolder.INSTANCE.getObject(OBJECT_KEY_SPRING_CONFIGURABLE_ENVIRONMENT); - - // load node properties - Instance instance = Instance.getInstance(); - // load namespace - String namespace = registryNamingServerProperties.getNamespace(); - instance.setNamespace(namespace); - // load cluster name - String clusterName = registryNamingServerProperties.getCluster(); - instance.setClusterName(clusterName); - - // load cluster type - String clusterType = String.valueOf(StoreConfig.getSessionMode()); - instance.addMetadata("cluster-type", "raft".equals(clusterType) ? clusterType : "default"); - - // load unit name - instance.setUnit(String.valueOf(UUID.randomUUID())); - - instance.setTerm(System.currentTimeMillis()); - - // load node Endpoint - instance.setControl(new Node.Endpoint(NetUtil.getLocalIp(), Integer.parseInt(Objects.requireNonNull(environment.getProperty("server.port"))), "http")); - - // load metadata - for (PropertySource propertySource : environment.getPropertySources()) { - if (propertySource instanceof EnumerablePropertySource) { - EnumerablePropertySource enumerablePropertySource = (EnumerablePropertySource) propertySource; - for (String propertyName : enumerablePropertySource.getPropertyNames()) { - if (propertyName.startsWith(META_PREFIX)) { - instance.addMetadata(propertyName.substring(META_PREFIX.length()), enumerablePropertySource.getProperty(propertyName)); - } + VGroupMappingStoreManager vGroupMappingStoreManager = SessionHolder.getRootVGroupMappingManager(); + ConfigurableEnvironment environment = + (ConfigurableEnvironment)ObjectHolder.INSTANCE.getObject(OBJECT_KEY_SPRING_CONFIGURABLE_ENVIRONMENT); + + // load node properties + Instance instance = Instance.getInstance(); + // load namespace + String namespace = registryNamingServerProperties.getNamespace(); + instance.setNamespace(namespace); + // load cluster name + String clusterName = registryNamingServerProperties.getCluster(); + instance.setClusterName(clusterName); + + // load cluster type + String clusterType = String.valueOf(StoreConfig.getSessionMode()); + instance.addMetadata("cluster-type", "raft".equals(clusterType) ? clusterType : "default"); + + // load unit name + instance.setUnit(String.valueOf(UUID.randomUUID())); + + instance.setTerm(System.currentTimeMillis()); + + // load node Endpoint + instance.setControl(new Node.Endpoint(XID.getIpAddress(), + Integer.parseInt(Objects.requireNonNull(environment.getProperty("server.port"))), "http")); + + // load metadata + for (PropertySource propertySource : environment.getPropertySources()) { + if (propertySource instanceof EnumerablePropertySource) { + EnumerablePropertySource enumerablePropertySource = (EnumerablePropertySource)propertySource; + for (String propertyName : enumerablePropertySource.getPropertyNames()) { + if (propertyName.startsWith(META_PREFIX)) { + instance.addMetadata(propertyName.substring(META_PREFIX.length()), + enumerablePropertySource.getProperty(propertyName)); } } } + } + instance.setTransaction(new Node.Endpoint(XID.getIpAddress(), XID.getPort(), "netty")); + if (StringUtils.equals(registryProperties.getType(), NAMING_SERVER)) { // load vgroup mapping relationship instance.addMetadata("vGroup", vGroupMappingStoreManager.loadVGroups()); - + EXECUTOR_SERVICE = + new ScheduledThreadPoolExecutor(1, new NamedThreadFactory("heartbeat-namingserver", 1, true)); EXECUTOR_SERVICE.scheduleAtFixedRate(() -> { try { vGroupMappingStoreManager.notifyMapping(); } catch (Exception e) { LOGGER.error("Naming server register Exception", e); } - }, registryNamingServerProperties.getHeartbeatPeriod(), registryNamingServerProperties.getHeartbeatPeriod(), TimeUnit.MILLISECONDS); + }, registryNamingServerProperties.getHeartbeatPeriod(), registryNamingServerProperties.getHeartbeatPeriod(), + TimeUnit.MILLISECONDS); ServerRunner.addDisposable(EXECUTOR_SERVICE::shutdown); } } diff --git a/server/src/main/java/org/apache/seata/server/lock/LockerManagerFactory.java b/server/src/main/java/org/apache/seata/server/lock/LockerManagerFactory.java index ae9e2534115..45129d12f93 100644 --- a/server/src/main/java/org/apache/seata/server/lock/LockerManagerFactory.java +++ b/server/src/main/java/org/apache/seata/server/lock/LockerManagerFactory.java @@ -17,10 +17,11 @@ package org.apache.seata.server.lock; import org.apache.seata.common.loader.EnhancedServiceLoader; +import org.apache.seata.common.store.LockMode; +import org.apache.seata.common.store.StoreMode; import org.apache.seata.config.Configuration; import org.apache.seata.config.ConfigurationFactory; import org.apache.seata.server.store.StoreConfig; -import org.apache.seata.server.store.StoreConfig.LockMode; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -65,7 +66,7 @@ public static void init(LockMode lockMode) { } LOGGER.info("use lock store mode: {}", lockMode.getName()); //if not exist the lock mode, throw exception - if (null != StoreConfig.StoreMode.get(lockMode.name())) { + if (null != StoreMode.get(lockMode.name())) { LOCK_MANAGER = EnhancedServiceLoader.load(LockManager.class, lockMode.getName()); } } diff --git a/server/src/main/java/org/apache/seata/server/metrics/MeterIdConstants.java b/server/src/main/java/org/apache/seata/server/metrics/MeterIdConstants.java index 4babd4ca2e0..18787594f8f 100644 --- a/server/src/main/java/org/apache/seata/server/metrics/MeterIdConstants.java +++ b/server/src/main/java/org/apache/seata/server/metrics/MeterIdConstants.java @@ -85,7 +85,7 @@ public interface MeterIdConstants { .withTag(IdConstants.METER_KEY, IdConstants.METER_VALUE_TIMER) .withTag(IdConstants.STATUS_KEY, IdConstants.STATUS_VALUE_COMMITTED); - Id TIMER_ROLLBACK = new Id(IdConstants.SEATA_TRANSACTION) + Id TIMER_ROLLBACKED = new Id(IdConstants.SEATA_TRANSACTION) .withTag(IdConstants.ROLE_KEY, IdConstants.ROLE_VALUE_TC) .withTag(IdConstants.METER_KEY, IdConstants.METER_VALUE_TIMER) .withTag(IdConstants.STATUS_KEY, IdConstants.STATUS_VALUE_ROLLBACKED); diff --git a/server/src/main/java/org/apache/seata/server/metrics/MetricsSubscriber.java b/server/src/main/java/org/apache/seata/server/metrics/MetricsSubscriber.java index 8beed4ba543..2a937d0b98c 100644 --- a/server/src/main/java/org/apache/seata/server/metrics/MetricsSubscriber.java +++ b/server/src/main/java/org/apache/seata/server/metrics/MetricsSubscriber.java @@ -25,6 +25,7 @@ import org.apache.seata.core.event.ExceptionEvent; import org.apache.seata.core.event.GlobalTransactionEvent; import org.apache.seata.core.model.GlobalStatus; +import org.apache.seata.metrics.Id; import org.apache.seata.metrics.registry.Registry; import org.apache.seata.server.event.EventBusManager; import org.slf4j.Logger; @@ -48,21 +49,45 @@ public class MetricsSubscriber { public MetricsSubscriber(Registry registry) { this.registry = registry; - consumers = new HashMap<>(); - consumers.put(GlobalStatus.Begin.name(), this::processGlobalStatusBegin); - consumers.put(GlobalStatus.Committed.name(), this::processGlobalStatusCommitted); - consumers.put(GlobalStatus.Rollbacked.name(), this::processGlobalStatusRollbacked); + this.consumers = initializeConsumers(); + } + + private Map> initializeConsumers() { + Map> consumerMap = new HashMap<>(); + consumerMap.put(GlobalStatus.Begin.name(), this::processGlobalStatusBegin); + consumerMap.put(GlobalStatus.Committed.name(), this::processGlobalStatusCommitted); + consumerMap.put(GlobalStatus.Rollbacked.name(), this::processGlobalStatusRollbacked); + + consumerMap.put(GlobalStatus.CommitFailed.name(), this::processGlobalStatusCommitFailed); + consumerMap.put(GlobalStatus.RollbackFailed.name(), this::processGlobalStatusRollbackFailed); + consumerMap.put(GlobalStatus.TimeoutRollbacked.name(), this::processGlobalStatusTimeoutRollbacked); + consumerMap.put(GlobalStatus.TimeoutRollbackFailed.name(), this::processGlobalStatusTimeoutRollbackFailed); + + consumerMap.put(GlobalStatus.CommitRetryTimeout.name(), this::processGlobalStatusCommitRetryTimeout); + consumerMap.put(GlobalStatus.RollbackRetryTimeout.name(), this::processGlobalStatusTimeoutRollbackRetryTimeout); + + consumerMap.put(STATUS_VALUE_AFTER_COMMITTED_KEY, this::processAfterGlobalCommitted); + consumerMap.put(STATUS_VALUE_AFTER_ROLLBACKED_KEY, this::processAfterGlobalRollbacked); + return consumerMap; + } - consumers.put(GlobalStatus.CommitFailed.name(), this::processGlobalStatusCommitFailed); - consumers.put(GlobalStatus.RollbackFailed.name(), this::processGlobalStatusRollbackFailed); - consumers.put(GlobalStatus.TimeoutRollbacked.name(), this::processGlobalStatusTimeoutRollbacked); - consumers.put(GlobalStatus.TimeoutRollbackFailed.name(), this::processGlobalStatusTimeoutRollbackFailed); + private void increaseCounter(Id counterId, GlobalTransactionEvent event) { + registry.getCounter(counterId.withTag(APP_ID_KEY, event.getApplicationId()) + .withTag(GROUP_KEY, event.getGroup())).increase(1); + } + private void decreaseCounter(Id counterId, GlobalTransactionEvent event) { + registry.getCounter(counterId.withTag(APP_ID_KEY, event.getApplicationId()) + .withTag(GROUP_KEY, event.getGroup())).decrease(1); + } - consumers.put(GlobalStatus.CommitRetryTimeout.name(), this::processGlobalStatusCommitRetryTimeout); - consumers.put(GlobalStatus.RollbackRetryTimeout.name(), this::processGlobalStatusTimeoutRollbackRetryTimeout); + private void increaseSummary(Id summaryId, GlobalTransactionEvent event, long value) { + registry.getSummary( + summaryId.withTag(APP_ID_KEY, event.getApplicationId()).withTag(GROUP_KEY, event.getGroup())).increase(value); + } - consumers.put(STATUS_VALUE_AFTER_COMMITTED_KEY, this::processAfterGlobalCommitted); - consumers.put(STATUS_VALUE_AFTER_ROLLBACKED_KEY, this::processAfterGlobalRollbacked); + private void increaseTimer(Id timerId, GlobalTransactionEvent event) { + registry.getTimer( + timerId.withTag(APP_ID_KEY, event.getApplicationId()).withTag(GROUP_KEY, event.getGroup())).record(event.getEndTime() - event.getBeginTime(), TimeUnit.MILLISECONDS); } private void processGlobalStatusBegin(GlobalTransactionEvent event) { @@ -72,124 +97,84 @@ private void processGlobalStatusBegin(GlobalTransactionEvent event) { LOGGER.debug("subscribe:{},threadName:{}", object.toString(), Thread.currentThread().getName()); } } - registry.getCounter(MeterIdConstants.COUNTER_ACTIVE.withTag(APP_ID_KEY, event.getApplicationId()) - .withTag(GROUP_KEY, event.getGroup())).increase(1); + increaseCounter(MeterIdConstants.COUNTER_ACTIVE, event); } private void processGlobalStatusCommitted(GlobalTransactionEvent event) { if (event.isRetryGlobal()) { return; } - decreaseActive(event); - registry.getCounter(MeterIdConstants.COUNTER_COMMITTED - .withTag(APP_ID_KEY, event.getApplicationId()) - .withTag(GROUP_KEY, event.getGroup())).increase(1); - registry.getSummary(MeterIdConstants.SUMMARY_COMMITTED - .withTag(APP_ID_KEY, event.getApplicationId()) - .withTag(GROUP_KEY, event.getGroup())).increase(1); - registry.getTimer(MeterIdConstants.TIMER_COMMITTED - .withTag(APP_ID_KEY, event.getApplicationId()) - .withTag(GROUP_KEY, event.getGroup())) - .record(event.getEndTime() - event.getBeginTime(), TimeUnit.MILLISECONDS); + decreaseCounter(MeterIdConstants.COUNTER_ACTIVE, event); + increaseCounter(MeterIdConstants.COUNTER_COMMITTED, event); + increaseSummary(MeterIdConstants.SUMMARY_COMMITTED, event, 1); + increaseTimer(MeterIdConstants.TIMER_COMMITTED, event); } private void processGlobalStatusRollbacked(GlobalTransactionEvent event) { if (event.isRetryGlobal()) { return; } - decreaseActive(event); - registry.getCounter(MeterIdConstants.COUNTER_ROLLBACKED - .withTag(APP_ID_KEY, event.getApplicationId()) - .withTag(GROUP_KEY, event.getGroup())).increase(1); - registry.getSummary(MeterIdConstants.SUMMARY_ROLLBACKED - .withTag(APP_ID_KEY, event.getApplicationId()) - .withTag(GROUP_KEY, event.getGroup())).increase(1); - registry.getTimer(MeterIdConstants.TIMER_ROLLBACK - .withTag(APP_ID_KEY, event.getApplicationId()) - .withTag(GROUP_KEY, event.getGroup())) - .record(event.getEndTime() - event.getBeginTime(), TimeUnit.MILLISECONDS); + decreaseCounter(MeterIdConstants.COUNTER_ACTIVE, event); + increaseCounter(MeterIdConstants.COUNTER_ROLLBACKED, event); + increaseSummary(MeterIdConstants.SUMMARY_ROLLBACKED, event, 1); + increaseTimer(MeterIdConstants.TIMER_ROLLBACKED, event); } private void processAfterGlobalRollbacked(GlobalTransactionEvent event) { if (event.isRetryGlobal() && event.isRetryBranch()) { - decreaseActive(event); + decreaseCounter(MeterIdConstants.COUNTER_ACTIVE, event); } - registry.getCounter(MeterIdConstants.COUNTER_AFTER_ROLLBACKED - .withTag(APP_ID_KEY, event.getApplicationId()) - .withTag(GROUP_KEY, event.getGroup())).increase(1); - registry.getSummary(MeterIdConstants.SUMMARY_AFTER_ROLLBACKED - .withTag(APP_ID_KEY, event.getApplicationId()) - .withTag(GROUP_KEY, event.getGroup())).increase(1); - registry.getTimer(MeterIdConstants.TIMER_AFTER_ROLLBACKED - .withTag(APP_ID_KEY, event.getApplicationId()) - .withTag(GROUP_KEY, event.getGroup())) - .record(event.getEndTime() - event.getBeginTime(), TimeUnit.MILLISECONDS); + increaseCounter(MeterIdConstants.COUNTER_AFTER_ROLLBACKED, event); + increaseSummary(MeterIdConstants.SUMMARY_AFTER_ROLLBACKED, event, 1); + increaseTimer(MeterIdConstants.TIMER_AFTER_ROLLBACKED, event); } private void processAfterGlobalCommitted(GlobalTransactionEvent event) { if (event.isRetryGlobal() && event.isRetryBranch()) { - decreaseActive(event); + decreaseCounter(MeterIdConstants.COUNTER_ACTIVE, event); } - registry.getCounter(MeterIdConstants.COUNTER_AFTER_COMMITTED - .withTag(APP_ID_KEY, event.getApplicationId()) - .withTag(GROUP_KEY, event.getGroup())).increase(1); - registry.getSummary(MeterIdConstants.SUMMARY_AFTER_COMMITTED - .withTag(APP_ID_KEY, event.getApplicationId()) - .withTag(GROUP_KEY, event.getGroup())).increase(1); - registry.getTimer(MeterIdConstants.TIMER_AFTER_COMMITTED - .withTag(APP_ID_KEY, event.getApplicationId()) - .withTag(GROUP_KEY, event.getGroup())) - .record(event.getEndTime() - event.getBeginTime(), TimeUnit.MILLISECONDS); + increaseCounter(MeterIdConstants.COUNTER_AFTER_COMMITTED, event); + increaseSummary(MeterIdConstants.SUMMARY_AFTER_COMMITTED, event, 1); + increaseTimer(MeterIdConstants.TIMER_AFTER_COMMITTED, event); } private void processGlobalStatusCommitFailed(GlobalTransactionEvent event) { - decreaseActive(event); + decreaseCounter(MeterIdConstants.COUNTER_ACTIVE, event); reportFailed(event); } private void processGlobalStatusRollbackFailed(GlobalTransactionEvent event) { - decreaseActive(event); + decreaseCounter(MeterIdConstants.COUNTER_ACTIVE, event); reportFailed(event); } private void processGlobalStatusTimeoutRollbacked(GlobalTransactionEvent event) { - decreaseActive(event); + decreaseCounter(MeterIdConstants.COUNTER_ACTIVE, event); } private void processGlobalStatusTimeoutRollbackFailed(GlobalTransactionEvent event) { - decreaseActive(event); - reportTwoPhaseTimeout(event); + decreaseCounter(MeterIdConstants.COUNTER_ACTIVE, event); + increaseSummary(MeterIdConstants.SUMMARY_TWO_PHASE_TIMEOUT, event, 1); + reportFailed(event); } private void processGlobalStatusCommitRetryTimeout(GlobalTransactionEvent event) { - decreaseActive(event); - reportTwoPhaseTimeout(event); + decreaseCounter(MeterIdConstants.COUNTER_ACTIVE, event); + increaseSummary(MeterIdConstants.SUMMARY_TWO_PHASE_TIMEOUT, event, 1); + //The phase 2 retry timeout state should be considered a transaction failed + reportFailed(event); } private void processGlobalStatusTimeoutRollbackRetryTimeout(GlobalTransactionEvent event) { - decreaseActive(event); - } - - private void decreaseActive(GlobalTransactionEvent event) { - registry.getCounter(MeterIdConstants.COUNTER_ACTIVE - .withTag(APP_ID_KEY, event.getApplicationId()) - .withTag(GROUP_KEY, event.getGroup())).decrease(1); + decreaseCounter(MeterIdConstants.COUNTER_ACTIVE, event); + increaseSummary(MeterIdConstants.SUMMARY_TWO_PHASE_TIMEOUT, event, 1); + //The phase 2 retry timeout state should be considered a transaction failed + reportFailed(event); } private void reportFailed(GlobalTransactionEvent event) { - registry.getSummary(MeterIdConstants.SUMMARY_FAILED - .withTag(APP_ID_KEY, event.getApplicationId()) - .withTag(GROUP_KEY, event.getGroup())).increase(1); - registry.getTimer(MeterIdConstants.TIMER_FAILED - .withTag(APP_ID_KEY, event.getApplicationId()) - .withTag(GROUP_KEY, event.getGroup())) - .record(event.getEndTime() - event.getBeginTime(), TimeUnit.MILLISECONDS); - } - - private void reportTwoPhaseTimeout(GlobalTransactionEvent event) { - registry.getSummary(MeterIdConstants.SUMMARY_TWO_PHASE_TIMEOUT - .withTag(APP_ID_KEY, event.getApplicationId()) - .withTag(GROUP_KEY, event.getGroup())).increase(1); + increaseSummary(MeterIdConstants.SUMMARY_FAILED, event, 1); + increaseTimer(MeterIdConstants.TIMER_FAILED, event); } diff --git a/server/src/main/java/org/apache/seata/server/session/AbstractSessionManager.java b/server/src/main/java/org/apache/seata/server/session/AbstractSessionManager.java index 611993b1594..8b2e7095936 100644 --- a/server/src/main/java/org/apache/seata/server/session/AbstractSessionManager.java +++ b/server/src/main/java/org/apache/seata/server/session/AbstractSessionManager.java @@ -16,6 +16,8 @@ */ package org.apache.seata.server.session; +import org.apache.seata.config.ConfigurationFactory; +import org.apache.seata.core.constants.ConfigurationKeys; import org.apache.seata.core.exception.BranchTransactionException; import org.apache.seata.core.exception.GlobalTransactionException; import org.apache.seata.core.exception.TransactionException; @@ -29,10 +31,14 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import static org.apache.seata.common.DefaultValues.DEFAULT_ROLLBACK_FAILED_UNLOCK_ENABLE; + /** * The type Abstract session manager. */ public abstract class AbstractSessionManager implements SessionManager { + boolean rollbackFailedUnlockEnable = ConfigurationFactory.getInstance().getBoolean( + ConfigurationKeys.ROLLBACK_FAILED_UNLOCK_ENABLE, DEFAULT_ROLLBACK_FAILED_UNLOCK_ENABLE); /** * The constant LOGGER. @@ -157,6 +163,11 @@ public void onSuccessEnd(GlobalSession globalSession) throws TransactionExceptio @Override public void onFailEnd(GlobalSession globalSession) throws TransactionException { + if (rollbackFailedUnlockEnable) { + globalSession.clean(); + LOGGER.info("xid:{} fail end and remove lock, transaction:{}", globalSession.getXid(), globalSession); + return; + } LOGGER.info("xid:{} fail end, transaction:{}", globalSession.getXid(), globalSession); } diff --git a/server/src/main/java/org/apache/seata/server/session/GlobalSession.java b/server/src/main/java/org/apache/seata/server/session/GlobalSession.java index 8cfc0ecbc67..251b9876e5b 100644 --- a/server/src/main/java/org/apache/seata/server/session/GlobalSession.java +++ b/server/src/main/java/org/apache/seata/server/session/GlobalSession.java @@ -33,6 +33,7 @@ import org.apache.seata.common.XID; import org.apache.seata.common.util.BufferUtils; import org.apache.seata.common.util.StringUtils; +import org.apache.seata.common.util.UUIDGenerator; import org.apache.seata.config.ConfigurationFactory; import org.apache.seata.core.exception.GlobalTransactionException; import org.apache.seata.core.exception.TransactionException; @@ -41,7 +42,6 @@ import org.apache.seata.core.model.BranchType; import org.apache.seata.core.model.GlobalStatus; import org.apache.seata.core.model.LockStatus; -import org.apache.seata.common.util.UUIDGenerator; import org.apache.seata.server.cluster.raft.RaftServerManager; import org.apache.seata.server.lock.LockerManagerFactory; import org.apache.seata.server.store.SessionStorable; @@ -793,7 +793,7 @@ public void queueToRetryCommit() throws TransactionException { public void queueToRetryRollback() throws TransactionException { GlobalStatus currentStatus = this.getStatus(); GlobalStatus newStatus; - if (SessionStatusValidator.isTimeoutGlobalStatus(currentStatus)) { + if (GlobalStatus.TimeoutRollbacking == currentStatus) { newStatus = GlobalStatus.TimeoutRollbackRetrying; } else { newStatus = GlobalStatus.RollbackRetrying; diff --git a/server/src/main/java/org/apache/seata/server/session/SessionHelper.java b/server/src/main/java/org/apache/seata/server/session/SessionHelper.java index 7ffab5f14be..356a50cb109 100644 --- a/server/src/main/java/org/apache/seata/server/session/SessionHelper.java +++ b/server/src/main/java/org/apache/seata/server/session/SessionHelper.java @@ -28,6 +28,7 @@ import java.util.stream.StreamSupport; import org.apache.seata.common.ConfigurationKeys; +import org.apache.seata.common.store.SessionMode; import org.apache.seata.common.util.CollectionUtils; import org.apache.seata.config.Configuration; import org.apache.seata.config.ConfigurationFactory; @@ -42,7 +43,6 @@ import org.apache.seata.server.coordinator.DefaultCoordinator; import org.apache.seata.server.metrics.MetricsPublisher; import org.apache.seata.server.store.StoreConfig; -import org.apache.seata.server.store.StoreConfig.SessionMode; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.slf4j.MDC; @@ -212,7 +212,7 @@ public static void endRollbacked(GlobalSession globalSession, boolean retryGloba boolean retryBranch = currentStatus == GlobalStatus.TimeoutRollbackRetrying || currentStatus == GlobalStatus.RollbackRetrying; if (!currentStatus.equals(GlobalStatus.TimeoutRollbacked) - && SessionStatusValidator.isTimeoutGlobalStatus(currentStatus)) { + && SessionStatusValidator.isTimeoutRollbacking(currentStatus)) { globalSession.changeGlobalStatus(GlobalStatus.TimeoutRollbacked); } else if (!globalSession.getStatus().equals(GlobalStatus.Rollbacked)) { globalSession.changeGlobalStatus(GlobalStatus.Rollbacked); @@ -255,7 +255,7 @@ public static void endRollbackFailed(GlobalSession globalSession, boolean retryG GlobalStatus currentStatus = globalSession.getStatus(); if (isRetryTimeout) { globalSession.changeGlobalStatus(GlobalStatus.RollbackRetryTimeout); - } else if (SessionStatusValidator.isTimeoutGlobalStatus(currentStatus)) { + } else if (SessionStatusValidator.isTimeoutRollbacking(currentStatus)) { globalSession.changeGlobalStatus(GlobalStatus.TimeoutRollbackFailed); } else { globalSession.changeGlobalStatus(GlobalStatus.RollbackFailed); diff --git a/server/src/main/java/org/apache/seata/server/session/SessionHolder.java b/server/src/main/java/org/apache/seata/server/session/SessionHolder.java index aa32ea001e6..e0e7a41b795 100644 --- a/server/src/main/java/org/apache/seata/server/session/SessionHolder.java +++ b/server/src/main/java/org/apache/seata/server/session/SessionHolder.java @@ -29,6 +29,7 @@ import org.apache.seata.common.exception.ShouldNeverHappenException; import org.apache.seata.common.exception.StoreException; import org.apache.seata.common.loader.EnhancedServiceLoader; +import org.apache.seata.common.store.SessionMode; import org.apache.seata.common.util.CollectionUtils; import org.apache.seata.common.util.StringUtils; import org.apache.seata.config.Configuration; @@ -42,7 +43,6 @@ import org.apache.seata.server.cluster.raft.context.SeataClusterContext; import org.apache.seata.server.lock.distributed.DistributedLockerFactory; import org.apache.seata.server.store.StoreConfig; -import org.apache.seata.server.store.StoreConfig.SessionMode; import org.apache.seata.server.store.VGroupMappingStoreManager; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -170,6 +170,7 @@ public static void reload(Collection allSessions, SessionMode sto public static void reload(Collection allSessions, SessionMode storeMode, boolean acquireLock) { if ((SessionMode.FILE == storeMode || SessionMode.RAFT == storeMode) && CollectionUtils.isNotEmpty(allSessions)) { + long currentTimeMillis = System.currentTimeMillis(); for (GlobalSession globalSession : allSessions) { GlobalStatus globalStatus = globalSession.getStatus(); switch (globalStatus) { @@ -227,12 +228,17 @@ public static void reload(Collection allSessions, SessionMode sto break; case Begin: if (Objects.equals(storeMode, SessionMode.RAFT)) { - try { - globalSession.changeGlobalStatus(GlobalStatus.RollbackRetrying); - LOGGER.info("change global status: {}, xid: {}", globalSession.getStatus(), - globalSession.getXid()); - } catch (TransactionException e) { - LOGGER.error("change global status fail: {}", e.getMessage(), e); + // Avoid rolling back the global session created after becoming the leader. + if (globalSession.getBeginTime() < currentTimeMillis) { + try { + globalSession.changeGlobalStatus(GlobalStatus.RollbackRetrying); + LOGGER.info("change global status: {}, xid: {}", globalSession.getStatus(), + globalSession.getXid()); + } catch (TransactionException e) { + LOGGER.error("change global status fail: {}", e.getMessage(), e); + } + } else { + globalSession.setActive(true); } } else { globalSession.setActive(true); diff --git a/server/src/main/java/org/apache/seata/server/session/SessionStatusValidator.java b/server/src/main/java/org/apache/seata/server/session/SessionStatusValidator.java index 52722a76d36..f6fa7f074aa 100644 --- a/server/src/main/java/org/apache/seata/server/session/SessionStatusValidator.java +++ b/server/src/main/java/org/apache/seata/server/session/SessionStatusValidator.java @@ -36,6 +36,11 @@ public static boolean isTimeoutGlobalStatus(GlobalStatus status) { || status == GlobalStatus.TimeoutRollbackRetrying; } + public static boolean isTimeoutRollbacking(GlobalStatus status) { + return status == GlobalStatus.TimeoutRollbacking + || status == GlobalStatus.TimeoutRollbackRetrying; + } + /** * is rollback global status * @@ -49,6 +54,11 @@ public static boolean isRollbackGlobalStatus(GlobalStatus status) { || status == GlobalStatus.RollbackRetryTimeout; } + public static boolean isEndGlobalStatus(GlobalStatus status) { + return status == GlobalStatus.Rollbacked || status == GlobalStatus.TimeoutRollbacked + || status == GlobalStatus.Committed || status == GlobalStatus.Finished; + } + /** * is commit global status * diff --git a/server/src/main/java/org/apache/seata/server/storage/db/store/DataBaseVGroupMappingStoreManager.java b/server/src/main/java/org/apache/seata/server/storage/db/store/DataBaseVGroupMappingStoreManager.java index d83ecf34c37..20072d8945c 100644 --- a/server/src/main/java/org/apache/seata/server/storage/db/store/DataBaseVGroupMappingStoreManager.java +++ b/server/src/main/java/org/apache/seata/server/storage/db/store/DataBaseVGroupMappingStoreManager.java @@ -19,7 +19,7 @@ import org.apache.seata.common.ConfigurationKeys; import org.apache.seata.common.loader.EnhancedServiceLoader; import org.apache.seata.common.loader.LoadLevel; -import org.apache.seata.common.metadata.namingserver.Instance; +import org.apache.seata.common.metadata.Instance; import org.apache.seata.config.Configuration; import org.apache.seata.config.ConfigurationFactory; import org.apache.seata.core.store.MappingDO; diff --git a/server/src/main/java/org/apache/seata/server/storage/db/store/VGroupMappingDataBaseDAO.java b/server/src/main/java/org/apache/seata/server/storage/db/store/VGroupMappingDataBaseDAO.java index 84fea262d57..1749ca04cb2 100644 --- a/server/src/main/java/org/apache/seata/server/storage/db/store/VGroupMappingDataBaseDAO.java +++ b/server/src/main/java/org/apache/seata/server/storage/db/store/VGroupMappingDataBaseDAO.java @@ -18,7 +18,7 @@ import org.apache.seata.common.exception.ErrorCode; import org.apache.seata.common.exception.SeataRuntimeException; -import org.apache.seata.common.metadata.namingserver.Instance; +import org.apache.seata.common.metadata.Instance; import org.apache.seata.common.util.IOUtil; import org.apache.seata.config.Configuration; import org.apache.seata.config.ConfigurationFactory; diff --git a/server/src/main/java/org/apache/seata/server/storage/file/lock/FileLocker.java b/server/src/main/java/org/apache/seata/server/storage/file/lock/FileLocker.java index c9630e0aa10..898c10935a0 100644 --- a/server/src/main/java/org/apache/seata/server/storage/file/lock/FileLocker.java +++ b/server/src/main/java/org/apache/seata/server/storage/file/lock/FileLocker.java @@ -97,7 +97,8 @@ public boolean acquireLock(List rowLocks, boolean autoCommit, boolean s } else if (previousLockBranchSession.getTransactionId() == transactionId) { // Locked by me before } else { - LOGGER.info("Global lock on [" + tableName + ":" + pk + "] is holding by " + previousLockBranchSession.getBranchId()); + LOGGER.info("Global lock on [{}:{}] is holding by xid {} branchId {}", tableName, pk, + previousLockBranchSession.getXid(), previousLockBranchSession.getBranchId()); try { // Release all acquired locks. branchSession.unlock(); diff --git a/server/src/main/java/org/apache/seata/server/storage/file/session/FileSessionManager.java b/server/src/main/java/org/apache/seata/server/storage/file/session/FileSessionManager.java index d7a097fed29..c58f046434e 100644 --- a/server/src/main/java/org/apache/seata/server/storage/file/session/FileSessionManager.java +++ b/server/src/main/java/org/apache/seata/server/storage/file/session/FileSessionManager.java @@ -66,7 +66,7 @@ public class FileSessionManager extends AbstractSessionManager implements Reload /** * The Session map. */ - private Map sessionMap = new ConcurrentHashMap<>(64); + protected Map sessionMap = new ConcurrentHashMap<>(64); /** diff --git a/server/src/main/java/org/apache/seata/server/storage/raft/session/RaftSessionManager.java b/server/src/main/java/org/apache/seata/server/storage/raft/session/RaftSessionManager.java index b59c8d34ad8..32d56c8e670 100644 --- a/server/src/main/java/org/apache/seata/server/storage/raft/session/RaftSessionManager.java +++ b/server/src/main/java/org/apache/seata/server/storage/raft/session/RaftSessionManager.java @@ -17,10 +17,12 @@ package org.apache.seata.server.storage.raft.session; import java.io.IOException; +import java.util.List; import java.util.concurrent.CompletableFuture; import com.alipay.sofa.jraft.Closure; import org.apache.seata.common.loader.LoadLevel; import org.apache.seata.common.loader.Scope; +import org.apache.seata.common.util.CollectionUtils; import org.apache.seata.core.exception.TransactionException; import org.apache.seata.core.exception.TransactionExceptionCode; import org.apache.seata.core.model.BranchStatus; @@ -80,7 +82,7 @@ public void onBegin(GlobalSession globalSession) throws TransactionException { "seata raft state machine exception: " + status.getErrorMsg())); } finally { try { - super.removeGlobalSession(globalSession); + removeGlobalSession(globalSession); } catch (TransactionException e) { completableFuture.completeExceptionally(e); } @@ -93,6 +95,23 @@ public void onBegin(GlobalSession globalSession) throws TransactionException { RaftTaskUtil.createTask(closure, raftSyncMsg, completableFuture); } + @Override + public void removeGlobalSession(GlobalSession session) throws TransactionException { + GlobalSession globalSession = sessionMap.remove(session.getXid()); + if (globalSession != null) { + List branchSessionList = globalSession.getBranchSessions(); + // For the follower, the following code will not be executed because when the follower receives the remove global session + // the branch session on the leader side has already been completely cleared. + if (CollectionUtils.isNotEmpty(branchSessionList)) { + for (BranchSession branchSession : branchSessionList) { + branchSession.unlock(); + onRemoveBranch(globalSession, branchSession); + } + end(globalSession); + } + } + } + @Override public void onStatusChange(GlobalSession globalSession, GlobalStatus globalStatus) throws TransactionException { CompletableFuture completableFuture = new CompletableFuture<>(); @@ -187,11 +206,15 @@ public void onRemoveBranch(GlobalSession globalSession, BranchSession branchSess @Override public void onSuccessEnd(GlobalSession globalSession) throws TransactionException { + end(globalSession); + } + + public void end(GlobalSession globalSession) throws TransactionException { CompletableFuture completableFuture = new CompletableFuture<>(); Closure closure = status -> { if (status.isOk()) { try { - super.removeGlobalSession(globalSession); + removeGlobalSession(globalSession); completableFuture.complete(true); } catch (TransactionException e) { completableFuture.completeExceptionally(e); diff --git a/server/src/main/java/org/apache/seata/server/storage/redis/store/RedisVGroupMappingStoreManager.java b/server/src/main/java/org/apache/seata/server/storage/redis/store/RedisVGroupMappingStoreManager.java index 1367ad281f3..4422bdc33f8 100644 --- a/server/src/main/java/org/apache/seata/server/storage/redis/store/RedisVGroupMappingStoreManager.java +++ b/server/src/main/java/org/apache/seata/server/storage/redis/store/RedisVGroupMappingStoreManager.java @@ -18,7 +18,7 @@ import org.apache.seata.common.exception.RedisException; import org.apache.seata.common.loader.LoadLevel; -import org.apache.seata.common.metadata.namingserver.Instance; +import org.apache.seata.common.metadata.Instance; import org.apache.seata.common.util.StringUtils; import org.apache.seata.core.store.MappingDO; import org.apache.seata.server.storage.redis.JedisPooledFactory; diff --git a/server/src/main/java/org/apache/seata/server/store/StoreConfig.java b/server/src/main/java/org/apache/seata/server/store/StoreConfig.java index adf949cf880..057753e21c7 100644 --- a/server/src/main/java/org/apache/seata/server/store/StoreConfig.java +++ b/server/src/main/java/org/apache/seata/server/store/StoreConfig.java @@ -16,6 +16,9 @@ */ package org.apache.seata.server.store; +import org.apache.seata.common.store.LockMode; +import org.apache.seata.common.store.SessionMode; +import org.apache.seata.common.store.StoreMode; import org.apache.seata.common.util.StringUtils; import org.apache.seata.config.Configuration; import org.apache.seata.config.ConfigurationFactory; @@ -142,119 +145,4 @@ public static LockMode getLockMode() { // complication old config return LockMode.get(getStoreMode().name()); } - - public enum StoreMode { - /** - * The File store mode. - */ - FILE("file"), - /** - * The Db store mode. - */ - DB("db"), - /** - * The Redis store mode. - */ - REDIS("redis"), - /** - * The Raft store mode. - */ - RAFT("raft"); - - private String name; - - StoreMode(String name) { - this.name = name; - } - - public static StoreMode get(String name) { - for (StoreMode mode : StoreMode.values()) { - if (mode.getName().equalsIgnoreCase(name)) { - return mode; - } - } - throw new IllegalArgumentException("unknown store mode:" + name); - } - - public String getName() { - return name; - } - } - - public enum SessionMode { - /** - * The File store mode. - */ - FILE("file"), - /** - * The Db store mode. - */ - DB("db"), - /** - * The Redis store mode. - */ - REDIS("redis"), - /** - * raft store - */ - RAFT("raft"); - - private String name; - - SessionMode(String name) { - this.name = name; - } - - public static SessionMode get(String name) { - for (SessionMode mode : SessionMode.values()) { - if (mode.getName().equalsIgnoreCase(name)) { - return mode; - } - } - throw new IllegalArgumentException("unknown session mode:" + name); - } - - public String getName() { - return name; - } - } - - public enum LockMode { - /** - * The File store mode. - */ - FILE("file"), - /** - * The Db store mode. - */ - DB("db"), - /** - * The Redis store mode. - */ - REDIS("redis"), - /** - * raft store - */ - RAFT("raft"); - - private String name; - - LockMode(String name) { - this.name = name; - } - - public static LockMode get(String name) { - for (LockMode mode : LockMode.values()) { - if (mode.getName().equalsIgnoreCase(name)) { - return mode; - } - } - throw new IllegalArgumentException("unknown lock mode:" + name); - } - - public String getName() { - return name; - } - } - } diff --git a/server/src/main/java/org/apache/seata/server/store/VGroupMappingStoreManager.java b/server/src/main/java/org/apache/seata/server/store/VGroupMappingStoreManager.java index d4ce6a5412f..f2b2b8bd4f1 100644 --- a/server/src/main/java/org/apache/seata/server/store/VGroupMappingStoreManager.java +++ b/server/src/main/java/org/apache/seata/server/store/VGroupMappingStoreManager.java @@ -17,7 +17,7 @@ package org.apache.seata.server.store; import org.apache.seata.common.XID; -import org.apache.seata.common.metadata.namingserver.Instance; +import org.apache.seata.common.metadata.Instance; import org.apache.seata.core.store.MappingDO; import org.apache.seata.discovery.registry.MultiRegistryFactory; import org.apache.seata.discovery.registry.RegistryService; diff --git a/server/src/main/java/org/apache/seata/server/transaction/saga/SagaAnnotationCore.java b/server/src/main/java/org/apache/seata/server/transaction/saga/SagaAnnotationCore.java new file mode 100644 index 00000000000..03a2709967c --- /dev/null +++ b/server/src/main/java/org/apache/seata/server/transaction/saga/SagaAnnotationCore.java @@ -0,0 +1,46 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.seata.server.transaction.saga; + +import org.apache.seata.core.exception.TransactionException; +import org.apache.seata.core.model.BranchStatus; +import org.apache.seata.core.model.BranchType; +import org.apache.seata.core.rpc.RemotingServer; +import org.apache.seata.server.coordinator.AbstractCore; +import org.apache.seata.server.session.BranchSession; +import org.apache.seata.server.session.GlobalSession; + +/** + * The type saga annotation core. + */ +public class SagaAnnotationCore extends AbstractCore { + + public SagaAnnotationCore(RemotingServer remotingServer) { + super(remotingServer); + } + + @Override + public BranchStatus branchCommit(GlobalSession globalSession, BranchSession branchSession) throws TransactionException { + //SAGA_ANNOTATION branch type, just mock commit + return BranchStatus.PhaseTwo_Committed; + } + + @Override + public BranchType getHandleBranchType() { + return BranchType.SAGA_ANNOTATION; + } +} diff --git a/server/src/main/resources/META-INF/services/org.apache.seata.server.coordinator.AbstractCore b/server/src/main/resources/META-INF/services/org.apache.seata.server.coordinator.AbstractCore index 538e5a4cae0..462a3cefc99 100644 --- a/server/src/main/resources/META-INF/services/org.apache.seata.server.coordinator.AbstractCore +++ b/server/src/main/resources/META-INF/services/org.apache.seata.server.coordinator.AbstractCore @@ -17,4 +17,5 @@ org.apache.seata.server.transaction.at.ATCore org.apache.seata.server.transaction.tcc.TccCore org.apache.seata.server.transaction.saga.SagaCore -org.apache.seata.server.transaction.xa.XACore \ No newline at end of file +org.apache.seata.server.transaction.xa.XACore +org.apache.seata.server.transaction.saga.SagaAnnotationCore \ No newline at end of file diff --git a/server/src/main/resources/META-INF/spring.factories b/server/src/main/resources/META-INF/spring.factories index 4b97deb2101..649fa8a356c 100644 --- a/server/src/main/resources/META-INF/spring.factories +++ b/server/src/main/resources/META-INF/spring.factories @@ -15,6 +15,4 @@ # limitations under the License. # org.springframework.context.ApplicationListener=\ -org.apache.seata.server.spring.listener.ServerApplicationListener -org.springframework.context.ApplicationContextInitializer=\ -org.apache.seata.server.spring.listener.SeataPropertiesLoader \ No newline at end of file +org.apache.seata.server.spring.listener.ServerApplicationListener \ No newline at end of file diff --git a/server/src/main/resources/application.example.yml b/server/src/main/resources/application.example.yml index 5d92d069284..059312ae856 100644 --- a/server/src/main/resources/application.example.yml +++ b/server/src/main/resources/application.example.yml @@ -139,7 +139,7 @@ seata: service-port: 8091 #If not configured, the default is '${server.port} + 1000' max-commit-retry-timeout: -1 max-rollback-retry-timeout: -1 - rollback-retry-timeout-unlock-enable: false + rollback-failed-unlock-enable: false enable-check-auth: true enable-parallel-request-handle: true enable-parallel-handle-branch: false diff --git a/server/src/main/resources/application.raft.example.yml b/server/src/main/resources/application.raft.example.yml index 6c96b9e9fac..241820a1d21 100644 --- a/server/src/main/resources/application.raft.example.yml +++ b/server/src/main/resources/application.raft.example.yml @@ -91,6 +91,23 @@ seata: serialization: jackson compressor: none sync: true # sync log&snapshot to disk + # raft nodes ssl config + ssl: + enabled: false + client: + keystore: + path: ssl/cbolt.pfx + password: seata + type: pkcs12 + server: + keystore: + path: ssl/bolt.pfx + password: seata + type: pkcs12 + kmf: + algorithm: SunX509 + tmf: + algorithm: SunX509 service-port: 8091 #If not configured, the default is '${server.port} + 1000' max-commit-retry-timeout: -1 max-rollback-retry-timeout: -1 @@ -116,7 +133,7 @@ seata: enable-branch-async-remove: false #enable to asynchronous remove branchSession store: # support: file - mode: file + mode: raft file: dir: sessionStore max-branch-session-size: 16384 diff --git a/server/src/test/java/org/apache/seata/server/coordinator/DefaultCoordinatorTest.java b/server/src/test/java/org/apache/seata/server/coordinator/DefaultCoordinatorTest.java index 1de1f04c0ca..02a31496a5f 100644 --- a/server/src/test/java/org/apache/seata/server/coordinator/DefaultCoordinatorTest.java +++ b/server/src/test/java/org/apache/seata/server/coordinator/DefaultCoordinatorTest.java @@ -27,6 +27,7 @@ import org.apache.seata.common.DefaultValues; import org.apache.seata.common.XID; import org.apache.seata.common.loader.EnhancedServiceLoader; +import org.apache.seata.common.store.SessionMode; import org.apache.seata.common.util.NetUtil; import org.apache.seata.common.util.ReflectionUtil; import org.apache.seata.config.Configuration; @@ -45,7 +46,6 @@ import org.apache.seata.server.metrics.MetricsManager; import org.apache.seata.server.session.GlobalSession; import org.apache.seata.server.session.SessionHolder; -import org.apache.seata.server.store.StoreConfig.SessionMode; import org.apache.seata.server.util.StoreUtil; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.AfterEach; diff --git a/server/src/test/java/org/apache/seata/server/coordinator/DefaultCoreTest.java b/server/src/test/java/org/apache/seata/server/coordinator/DefaultCoreTest.java index 6db53df7db9..93a5fbc0542 100644 --- a/server/src/test/java/org/apache/seata/server/coordinator/DefaultCoreTest.java +++ b/server/src/test/java/org/apache/seata/server/coordinator/DefaultCoreTest.java @@ -19,6 +19,7 @@ import java.util.Collection; import java.util.stream.Stream; +import org.apache.seata.common.store.SessionMode; import org.apache.seata.core.exception.TransactionException; import org.apache.seata.core.model.BranchStatus; import org.apache.seata.core.model.BranchType; @@ -28,7 +29,6 @@ import org.apache.seata.server.session.GlobalSession; import org.apache.seata.server.session.SessionHelper; import org.apache.seata.server.session.SessionHolder; -import org.apache.seata.server.store.StoreConfig.SessionMode; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Assertions; diff --git a/server/src/test/java/org/apache/seata/server/event/DefaultCoreForEventBusTest.java b/server/src/test/java/org/apache/seata/server/event/DefaultCoreForEventBusTest.java index 8b94efd10a8..0569efe000e 100644 --- a/server/src/test/java/org/apache/seata/server/event/DefaultCoreForEventBusTest.java +++ b/server/src/test/java/org/apache/seata/server/event/DefaultCoreForEventBusTest.java @@ -26,6 +26,7 @@ import com.google.common.eventbus.AllowConcurrentEvents; import com.google.common.eventbus.Subscribe; +import org.apache.seata.common.store.SessionMode; import org.apache.seata.core.event.GlobalTransactionEvent; import org.apache.seata.core.exception.TransactionException; import org.apache.seata.core.model.GlobalStatus; @@ -37,7 +38,6 @@ import org.apache.seata.server.metrics.MetricsManager; import org.apache.seata.server.session.SessionHolder; import org.apache.seata.server.store.StoreConfig; -import org.apache.seata.server.store.StoreConfig.SessionMode; import org.apache.seata.server.util.StoreUtil; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.Assertions; diff --git a/server/src/test/java/org/apache/seata/server/lock/LockManagerTest.java b/server/src/test/java/org/apache/seata/server/lock/LockManagerTest.java index 6d6889c8045..c96c002ee29 100644 --- a/server/src/test/java/org/apache/seata/server/lock/LockManagerTest.java +++ b/server/src/test/java/org/apache/seata/server/lock/LockManagerTest.java @@ -26,6 +26,7 @@ import javax.annotation.Resource; +import org.apache.seata.common.store.SessionMode; import org.apache.seata.common.util.CollectionUtils; import org.apache.seata.common.result.PageResult; import org.apache.seata.core.exception.TransactionException; @@ -39,7 +40,6 @@ import org.apache.seata.server.session.GlobalSession; import org.apache.seata.server.session.SessionHolder; import org.apache.seata.server.session.SessionManager; -import org.apache.seata.server.store.StoreConfig.SessionMode; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.params.ParameterizedTest; diff --git a/server/src/test/java/org/apache/seata/server/raft/RaftSyncMessageTest.java b/server/src/test/java/org/apache/seata/server/raft/RaftSyncMessageTest.java index e6f9c8f905d..3e36d55a520 100644 --- a/server/src/test/java/org/apache/seata/server/raft/RaftSyncMessageTest.java +++ b/server/src/test/java/org/apache/seata/server/raft/RaftSyncMessageTest.java @@ -20,6 +20,7 @@ import java.io.IOException; import java.io.ObjectOutputStream; import java.util.ArrayList; +import java.util.Base64; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -27,6 +28,7 @@ import org.apache.seata.common.exception.SeataRuntimeException; import org.apache.seata.common.metadata.ClusterRole; import org.apache.seata.common.metadata.Node; +import org.apache.seata.common.store.SessionMode; import org.apache.seata.core.exception.TransactionException; import org.apache.seata.core.model.BranchType; import org.apache.seata.server.cluster.raft.snapshot.RaftSnapshot; @@ -43,7 +45,6 @@ import org.apache.seata.server.session.GlobalSession; import org.apache.seata.server.session.SessionHelper; import org.apache.seata.server.session.SessionHolder; -import org.apache.seata.server.store.StoreConfig; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeAll; @@ -58,7 +59,7 @@ public class RaftSyncMessageTest { @BeforeAll public static void setUp(ApplicationContext context){ - SessionHolder.init(StoreConfig.SessionMode.FILE); + SessionHolder.init(SessionMode.FILE); } @AfterAll @@ -78,6 +79,33 @@ public void testSecurityMsgSerialize() throws IOException { Assertions.assertThrows(SeataRuntimeException.class,()->RaftSyncMessageSerializer.decode(bytes)); } + @Test + public void testSecurityMsgAndSnapshotSerialize() throws IOException { + String jndiUrl = "oracle://127.0.0.1:1234/test"; + String basePayload = "{\"dataSourceName\":\"" + jndiUrl + "\",\"command\":\"123\"}"; + String payload = "{\"obj\":\"" + Base64.getEncoder().encodeToString(basePayload.getBytes()) + + "\",\"clz\":\"dm.jdbc.driver.DmdbJdbcRowSet\"}"; + byte[] payloadBytes = payload.getBytes(); + byte[] bytes; + RaftSyncMessage raftSyncMessage = new RaftSyncMessage(); + raftSyncMessage.setBody(payloadBytes); + try (ByteArrayOutputStream bos = new ByteArrayOutputStream(); + ObjectOutputStream oos = new ObjectOutputStream(bos)) { + oos.writeObject(raftSyncMessage); + bytes = bos.toByteArray(); + } + Assertions.assertThrows(SeataRuntimeException.class,()->RaftSyncMessageSerializer.decode(bytes)); + RaftSnapshot raftSnapshot = new RaftSnapshot(); + raftSnapshot.setBody(payloadBytes); + byte[] snapshotBytes; + try (ByteArrayOutputStream bos = new ByteArrayOutputStream(); + ObjectOutputStream oos = new ObjectOutputStream(bos)) { + oos.writeObject(raftSnapshot); + snapshotBytes = bos.toByteArray(); + } + Assertions.assertThrows(SeataRuntimeException.class,()->RaftSnapshotSerializer.decode(snapshotBytes)); + } + @Test public void testMsgSerialize() throws IOException { RaftSyncMessage raftSyncMessage = new RaftSyncMessage(); diff --git a/server/src/test/java/org/apache/seata/server/raft/execute/BranchSessionExecuteTest.java b/server/src/test/java/org/apache/seata/server/raft/execute/BranchSessionExecuteTest.java index ade3a25ef9c..df21b9216ff 100644 --- a/server/src/test/java/org/apache/seata/server/raft/execute/BranchSessionExecuteTest.java +++ b/server/src/test/java/org/apache/seata/server/raft/execute/BranchSessionExecuteTest.java @@ -16,7 +16,13 @@ */ package org.apache.seata.server.raft.execute; +import org.apache.seata.common.store.LockMode; +import org.apache.seata.common.store.SessionMode; +import java.util.ArrayList; +import java.util.List; +import org.apache.seata.common.XID; import org.apache.seata.common.util.NetUtil; +import org.apache.seata.common.util.UUIDGenerator; import org.apache.seata.config.ConfigurationCache; import org.apache.seata.core.exception.TransactionException; import org.apache.seata.core.model.BranchStatus; @@ -31,12 +37,9 @@ import org.apache.seata.server.session.GlobalSession; import org.apache.seata.server.session.SessionHolder; import org.apache.seata.server.storage.SessionConverter; -import org.apache.seata.server.store.StoreConfig; import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.context.ApplicationContext; @@ -51,20 +54,22 @@ class BranchSessionExecuteTest { private static GlobalSession GLOBAL_SESSION; - private static final String XID = "123:123"; - private static final long BRANCH_ID = 0L; @BeforeAll public static void setUp(ApplicationContext context) throws TransactionException { System.setProperty("server.raft.serverAddr", NetUtil.getLocalIp() + ":9091"); - SessionHolder.init(StoreConfig.SessionMode.RAFT); + SessionHolder.init(SessionMode.RAFT); LockerManagerFactory.destroy(); - LockerManagerFactory.init(StoreConfig.LockMode.RAFT); + LockerManagerFactory.init(LockMode.RAFT); + GLOBAL_SESSION = mockGlobalSession(); + SessionHolder.getRootSessionManager().addGlobalSession(GLOBAL_SESSION); } @AfterAll - public static void destroy() throws TransactionException { + public static void destroy() throws Throwable { + testRemove(); + SessionHolder.getRootSessionManager().removeGlobalSession(GLOBAL_SESSION); // Clear configuration ConfigurationCache.clear(); System.clearProperty("server.raft.serverAddr"); @@ -75,75 +80,66 @@ public static void destroy() throws TransactionException { LockerManagerFactory.destroy(); } - @BeforeEach - public void addGlobalSession() throws TransactionException { - GLOBAL_SESSION = mockGlobalSession(); - SessionHolder.getRootSessionManager().addGlobalSession(GLOBAL_SESSION); - } - @AfterEach - public void removeTestSession() throws TransactionException { - SessionHolder.getRootSessionManager().removeGlobalSession(GLOBAL_SESSION); - } @Test public void testAdd() throws Throwable { - BranchSession expected = mockBranchSession(); + BranchSession expected = mockBranchSession(GLOBAL_SESSION.getXid(), GLOBAL_SESSION.getTransactionId()); AddBranchSessionExecute execute = new AddBranchSessionExecute(); boolean success = execute.execute(convertToBranchSessionMsg(expected)); Assertions.assertTrue(success); - BranchSession branchSession = GLOBAL_SESSION.getBranch(BRANCH_ID); + BranchSession branchSession = GLOBAL_SESSION.getBranch(expected.getBranchId()); assertBranchSessionValid(expected, branchSession); } - @Test - public void testRemove() throws Throwable { - GLOBAL_SESSION.add(mockBranchSession()); - - BranchSession branchSession = GLOBAL_SESSION.getBranch(BRANCH_ID); - Assertions.assertNotNull(branchSession); + public static void testRemove() throws Throwable { + List list = new ArrayList<>(GLOBAL_SESSION.getBranchSessions()); + for (BranchSession branchSession : list) { + Assertions.assertNotNull(branchSession); - RemoveBranchSessionExecute execute = new RemoveBranchSessionExecute(); - boolean success = execute.execute(convertToBranchSessionMsg(branchSession)); - Assertions.assertTrue(success); + RemoveBranchSessionExecute execute = new RemoveBranchSessionExecute(); + boolean success = execute.execute(convertToBranchSessionMsg(branchSession)); + Assertions.assertTrue(success); - branchSession = GLOBAL_SESSION.getBranch(BRANCH_ID); - Assertions.assertNull(branchSession); + branchSession = GLOBAL_SESSION.getBranch(branchSession.getBranchId()); + Assertions.assertNull(branchSession); + } } @Test public void testUpdate() throws Throwable { - GLOBAL_SESSION.add(mockBranchSession()); + BranchSession branchSession = mockBranchSession(GLOBAL_SESSION.getXid(), GLOBAL_SESSION.getTransactionId()); + GLOBAL_SESSION.add(branchSession); - BranchSession branchSession = GLOBAL_SESSION.getBranch(BRANCH_ID); + branchSession = GLOBAL_SESSION.getBranch(branchSession.getBranchId()); Assertions.assertNotNull(branchSession); - BranchSession expected = mockBranchSession(); - expected.setStatus(BranchStatus.PhaseTwo_Committed); + branchSession.setStatus(BranchStatus.PhaseTwo_Committed); UpdateBranchSessionExecute execute = new UpdateBranchSessionExecute(); - boolean success = execute.execute(convertToBranchSessionMsg(expected)); + boolean success = execute.execute(convertToBranchSessionMsg(branchSession)); Assertions.assertTrue(success); - branchSession = GLOBAL_SESSION.getBranch(BRANCH_ID); - assertBranchSessionValid(expected, branchSession); + branchSession = GLOBAL_SESSION.getBranch(branchSession.getBranchId()); + assertBranchSessionValid(branchSession, branchSession); } private static GlobalSession mockGlobalSession() { + long txId = UUIDGenerator.generateUUID(); GlobalSession session = new GlobalSession("test", "test", "test", 5000); - session.setXid(XID); + session.setXid(XID.generateXID(txId)); session.setApplicationData("hello, world"); - session.setTransactionId(123); + session.setTransactionId(txId); session.setBeginTime(System.currentTimeMillis()); return session; } - private static BranchSession mockBranchSession() { + private static BranchSession mockBranchSession(String xid,long transactionId) { BranchSession session = new BranchSession(); - session.setXid(XID); - session.setTransactionId(123); - session.setBranchId(BRANCH_ID); + session.setXid(xid); + session.setTransactionId(transactionId); + session.setBranchId(UUIDGenerator.generateUUID()); session.setClientId("client"); session.setResourceGroupId(DEFAULT_TX_GROUP); session.setResourceId("resource"); diff --git a/server/src/test/java/org/apache/seata/server/raft/execute/GlobalSessionExecuteTest.java b/server/src/test/java/org/apache/seata/server/raft/execute/GlobalSessionExecuteTest.java index 9594eb7de56..45d191790ee 100644 --- a/server/src/test/java/org/apache/seata/server/raft/execute/GlobalSessionExecuteTest.java +++ b/server/src/test/java/org/apache/seata/server/raft/execute/GlobalSessionExecuteTest.java @@ -16,6 +16,8 @@ */ package org.apache.seata.server.raft.execute; +import org.apache.seata.common.store.LockMode; +import org.apache.seata.common.store.SessionMode; import org.apache.seata.common.util.NetUtil; import org.apache.seata.config.ConfigurationCache; import org.apache.seata.core.exception.TransactionException; @@ -30,7 +32,6 @@ import org.apache.seata.server.session.SessionHolder; import org.apache.seata.server.session.SessionManager; import org.apache.seata.server.storage.SessionConverter; -import org.apache.seata.server.store.StoreConfig; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Assertions; @@ -47,9 +48,9 @@ class GlobalSessionExecuteTest { @BeforeAll public static void setUp(ApplicationContext context){ System.setProperty("server.raft.serverAddr", NetUtil.getLocalIp() + ":9091"); - SessionHolder.init(StoreConfig.SessionMode.RAFT); + SessionHolder.init(SessionMode.RAFT); LockerManagerFactory.destroy(); - LockerManagerFactory.init(StoreConfig.LockMode.RAFT); + LockerManagerFactory.init(LockMode.RAFT); } @AfterAll diff --git a/server/src/test/java/org/apache/seata/server/raft/execute/LockExecuteTest.java b/server/src/test/java/org/apache/seata/server/raft/execute/LockExecuteTest.java index 36cbbaf0d8a..19c4c24f230 100644 --- a/server/src/test/java/org/apache/seata/server/raft/execute/LockExecuteTest.java +++ b/server/src/test/java/org/apache/seata/server/raft/execute/LockExecuteTest.java @@ -16,10 +16,16 @@ */ package org.apache.seata.server.raft.execute; +import org.apache.seata.common.store.LockMode; +import org.apache.seata.common.store.SessionMode; +import java.util.ArrayList; +import java.util.List; import org.apache.seata.common.util.NetUtil; +import org.apache.seata.common.util.UUIDGenerator; import org.apache.seata.config.ConfigurationCache; import org.apache.seata.core.exception.TransactionException; import org.apache.seata.core.model.BranchType; +import org.apache.seata.server.cluster.raft.execute.branch.RemoveBranchSessionExecute; import org.apache.seata.server.cluster.raft.execute.lock.BranchReleaseLockExecute; import org.apache.seata.server.cluster.raft.execute.lock.GlobalReleaseLockExecute; import org.apache.seata.server.cluster.raft.sync.msg.RaftBranchSessionSyncMsg; @@ -32,7 +38,6 @@ import org.apache.seata.server.session.GlobalSession; import org.apache.seata.server.session.SessionHolder; import org.apache.seata.server.storage.SessionConverter; -import org.apache.seata.server.store.StoreConfig; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Assertions; @@ -59,9 +64,9 @@ class LockExecuteTest { @BeforeAll public static void setUp(ApplicationContext context) throws TransactionException { System.setProperty("server.raft.serverAddr", NetUtil.getLocalIp() + ":9091"); - SessionHolder.init(StoreConfig.SessionMode.RAFT); + SessionHolder.init(SessionMode.RAFT); LockerManagerFactory.destroy(); - LockerManagerFactory.init(StoreConfig.LockMode.RAFT); + LockerManagerFactory.init(LockMode.RAFT); } @AfterAll @@ -83,14 +88,29 @@ public void addGlobalSession() throws TransactionException { } @AfterEach - public void removeTestSession() throws TransactionException { + public void removeTestSession() throws Throwable { + testRemove(); SessionHolder.getRootSessionManager().removeGlobalSession(GLOBAL_SESSION); } + public void testRemove() throws Throwable { + List list = new ArrayList<>(GLOBAL_SESSION.getBranchSessions()); + for (BranchSession branchSession : list) { + Assertions.assertNotNull(branchSession); + + RemoveBranchSessionExecute execute = new RemoveBranchSessionExecute(); + boolean success = execute.execute(convertToBranchSessionMsg(branchSession)); + Assertions.assertTrue(success); + + branchSession = GLOBAL_SESSION.getBranch(branchSession.getBranchId()); + Assertions.assertNull(branchSession); + } + } + @Test public void testGlobalRelease() throws Throwable { - BranchSession branchSession1 = mockBranchSession("test:0"); - BranchSession branchSession2 = mockBranchSession("test:1"); + BranchSession branchSession1 = mockBranchSession(GLOBAL_SESSION.getXid(),GLOBAL_SESSION.getTransactionId(),"t1:53"); + BranchSession branchSession2 = mockBranchSession(GLOBAL_SESSION.getXid(),GLOBAL_SESSION.getTransactionId(),"t1:54"); GLOBAL_SESSION.add(branchSession1); GLOBAL_SESSION.add(branchSession2); @@ -109,7 +129,7 @@ public void testGlobalRelease() throws Throwable { @Test public void testBranchRelease() throws Throwable { - BranchSession branchSession = mockBranchSession("test:0"); + BranchSession branchSession = mockBranchSession(GLOBAL_SESSION.getXid(),GLOBAL_SESSION.getTransactionId(),"t1:55"); GLOBAL_SESSION.add(branchSession); LockManager lockerManager = LockerManagerFactory.getLockManager(); @@ -131,14 +151,14 @@ private static GlobalSession mockGlobalSession() { return session; } - private static BranchSession mockBranchSession(String lockKey) { + private static BranchSession mockBranchSession(String xid,long transactionId,String lockKey) { BranchSession session = new BranchSession(); - session.setXid(XID); - session.setTransactionId(123); - session.setBranchId(BRANCH_ID); + session.setXid(xid); + session.setTransactionId(transactionId); + session.setBranchId(UUIDGenerator.generateUUID()); session.setClientId("client"); session.setResourceGroupId(DEFAULT_TX_GROUP); - session.setResourceId("db"); + session.setResourceId("resource"); session.setLockKey(lockKey); session.setBranchType(BranchType.AT); session.setApplicationData("hello, world"); diff --git a/server/src/test/java/org/apache/seata/server/session/FileSessionManagerTest.java b/server/src/test/java/org/apache/seata/server/session/FileSessionManagerTest.java index 56ce51d343f..478529866af 100644 --- a/server/src/test/java/org/apache/seata/server/session/FileSessionManagerTest.java +++ b/server/src/test/java/org/apache/seata/server/session/FileSessionManagerTest.java @@ -30,6 +30,7 @@ import org.apache.seata.common.XID; import org.apache.seata.common.loader.EnhancedServiceLoader; import org.apache.seata.common.result.PageResult; +import org.apache.seata.common.store.SessionMode; import org.apache.seata.core.model.BranchStatus; import org.apache.seata.core.model.BranchType; import org.apache.seata.core.model.GlobalStatus; @@ -38,10 +39,8 @@ import org.apache.seata.server.console.service.GlobalSessionService; import org.apache.seata.server.console.vo.GlobalSessionVO; import org.apache.seata.server.storage.file.session.FileSessionManager; -import org.apache.seata.server.store.StoreConfig.SessionMode; import org.apache.seata.server.util.StoreUtil; import org.apache.commons.lang.time.DateUtils; -import org.apache.seata.server.session.SessionHolder; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.params.ParameterizedTest; diff --git a/server/src/test/java/org/apache/seata/server/session/GlobalSessionTest.java b/server/src/test/java/org/apache/seata/server/session/GlobalSessionTest.java index b9083874c19..08529720e2f 100644 --- a/server/src/test/java/org/apache/seata/server/session/GlobalSessionTest.java +++ b/server/src/test/java/org/apache/seata/server/session/GlobalSessionTest.java @@ -19,11 +19,10 @@ import java.io.IOException; import java.util.stream.Stream; +import org.apache.seata.common.store.SessionMode; import org.apache.seata.core.model.BranchStatus; import org.apache.seata.core.model.BranchType; import org.apache.seata.core.model.GlobalStatus; -import org.apache.seata.server.store.StoreConfig; -import org.apache.seata.server.session.SessionHolder; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeAll; @@ -47,7 +46,7 @@ public class GlobalSessionTest { @BeforeAll public static void init(ApplicationContext context){ - SessionHolder.init(StoreConfig.SessionMode.FILE); + SessionHolder.init(SessionMode.FILE); } @AfterAll public static void destroy(){ diff --git a/server/src/test/java/org/apache/seata/server/session/SessionHolderTest.java b/server/src/test/java/org/apache/seata/server/session/SessionHolderTest.java index 9ac513731c4..6e6fe855c6f 100644 --- a/server/src/test/java/org/apache/seata/server/session/SessionHolderTest.java +++ b/server/src/test/java/org/apache/seata/server/session/SessionHolderTest.java @@ -19,9 +19,8 @@ import java.io.File; import java.io.IOException; import org.apache.seata.common.XID; +import org.apache.seata.common.store.SessionMode; import org.apache.seata.core.constants.ConfigurationKeys; -import org.apache.seata.server.store.StoreConfig.SessionMode; -import org.apache.seata.server.session.SessionHolder; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; diff --git a/server/src/test/java/org/apache/seata/server/session/redis/RedisDistributedLockerTest.java b/server/src/test/java/org/apache/seata/server/session/redis/RedisDistributedLockerTest.java index d4f38a2bcc3..215571d3eb9 100644 --- a/server/src/test/java/org/apache/seata/server/session/redis/RedisDistributedLockerTest.java +++ b/server/src/test/java/org/apache/seata/server/session/redis/RedisDistributedLockerTest.java @@ -20,6 +20,8 @@ import org.apache.seata.common.XID; import org.apache.seata.common.loader.EnhancedServiceLoader; +import org.apache.seata.common.store.SessionMode; +import org.apache.seata.common.store.StoreMode; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeAll; @@ -33,8 +35,6 @@ import org.apache.seata.server.lock.distributed.DistributedLockerFactory; import org.apache.seata.server.session.SessionHolder; import org.apache.seata.server.storage.redis.JedisPooledFactory; -import org.apache.seata.server.store.StoreConfig.SessionMode; -import static org.apache.seata.server.store.StoreConfig.StoreMode; /** * @description redis distributed lock test diff --git a/server/src/test/java/org/apache/seata/server/storage/redis/store/RedisVGroupMappingStoreManagerTest.java b/server/src/test/java/org/apache/seata/server/storage/redis/store/RedisVGroupMappingStoreManagerTest.java index 755c10759c6..49418ae8075 100644 --- a/server/src/test/java/org/apache/seata/server/storage/redis/store/RedisVGroupMappingStoreManagerTest.java +++ b/server/src/test/java/org/apache/seata/server/storage/redis/store/RedisVGroupMappingStoreManagerTest.java @@ -16,7 +16,7 @@ */ package org.apache.seata.server.storage.redis.store; -import org.apache.seata.common.metadata.namingserver.Instance; +import org.apache.seata.common.metadata.Instance; import org.apache.seata.core.store.MappingDO; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; diff --git a/server/src/test/java/org/apache/seata/server/store/SessionStoreTest.java b/server/src/test/java/org/apache/seata/server/store/SessionStoreTest.java index 50c677df211..23da64b7297 100644 --- a/server/src/test/java/org/apache/seata/server/store/SessionStoreTest.java +++ b/server/src/test/java/org/apache/seata/server/store/SessionStoreTest.java @@ -19,6 +19,7 @@ import java.io.File; import org.apache.seata.common.XID; +import org.apache.seata.common.store.SessionMode; import org.apache.seata.config.Configuration; import org.apache.seata.config.ConfigurationFactory; import org.apache.seata.core.constants.ConfigurationKeys; @@ -31,7 +32,6 @@ import org.apache.seata.server.session.GlobalSession; import org.apache.seata.server.session.SessionHelper; import org.apache.seata.server.session.SessionHolder; -import org.apache.seata.server.store.StoreConfig.SessionMode; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; diff --git a/server/src/test/java/org/apache/seata/server/store/file/FileTransactionStoreManagerTest.java b/server/src/test/java/org/apache/seata/server/store/file/FileTransactionStoreManagerTest.java index d9c8ec08310..dca761e1238 100644 --- a/server/src/test/java/org/apache/seata/server/store/file/FileTransactionStoreManagerTest.java +++ b/server/src/test/java/org/apache/seata/server/store/file/FileTransactionStoreManagerTest.java @@ -23,6 +23,7 @@ import java.util.Collection; import java.util.List; +import org.apache.seata.common.store.SessionMode; import org.apache.seata.server.session.SessionHolder; import org.assertj.core.util.Files; import org.junit.jupiter.api.AfterAll; @@ -51,7 +52,7 @@ public class FileTransactionStoreManagerTest { @BeforeAll public static void init(ApplicationContext context){ - SessionHolder.init(StoreConfig.SessionMode.FILE); + SessionHolder.init(SessionMode.FILE); } @AfterAll public static void destroy(){ diff --git a/sqlparser/seata-sqlparser-druid/src/main/java/org/apache/seata/sqlparser/druid/dm/DmSelectForUpdateRecognizer.java b/sqlparser/seata-sqlparser-druid/src/main/java/org/apache/seata/sqlparser/druid/dm/DmSelectForUpdateRecognizer.java index 6b8b27be27a..0a2ca76371a 100644 --- a/sqlparser/seata-sqlparser-druid/src/main/java/org/apache/seata/sqlparser/druid/dm/DmSelectForUpdateRecognizer.java +++ b/sqlparser/seata-sqlparser-druid/src/main/java/org/apache/seata/sqlparser/druid/dm/DmSelectForUpdateRecognizer.java @@ -76,7 +76,7 @@ private SQLSelectQueryBlock getSelect() { if (select == null) { throw new SQLParsingException("should never happen!"); } - SQLSelectQueryBlock selectQueryBlock = select.getQueryBlock(); + SQLSelectQueryBlock selectQueryBlock = select.getFirstQueryBlock(); if (selectQueryBlock == null) { throw new SQLParsingException("should never happen!"); } diff --git a/sqlparser/seata-sqlparser-druid/src/main/java/org/apache/seata/sqlparser/druid/mysql/MySQLSelectForUpdateRecognizer.java b/sqlparser/seata-sqlparser-druid/src/main/java/org/apache/seata/sqlparser/druid/mysql/MySQLSelectForUpdateRecognizer.java index ff7ca613756..2d34a52927e 100644 --- a/sqlparser/seata-sqlparser-druid/src/main/java/org/apache/seata/sqlparser/druid/mysql/MySQLSelectForUpdateRecognizer.java +++ b/sqlparser/seata-sqlparser-druid/src/main/java/org/apache/seata/sqlparser/druid/mysql/MySQLSelectForUpdateRecognizer.java @@ -102,7 +102,7 @@ private SQLSelectQueryBlock getSelect() { if (select == null) { throw new SQLParsingException("should never happen!"); } - SQLSelectQueryBlock selectQueryBlock = select.getQueryBlock(); + SQLSelectQueryBlock selectQueryBlock = select.getFirstQueryBlock(); if (selectQueryBlock == null) { throw new SQLParsingException("should never happen!"); } diff --git a/sqlparser/seata-sqlparser-druid/src/main/java/org/apache/seata/sqlparser/druid/oracle/OracleSelectForUpdateRecognizer.java b/sqlparser/seata-sqlparser-druid/src/main/java/org/apache/seata/sqlparser/druid/oracle/OracleSelectForUpdateRecognizer.java index 2e22524e14a..91be41cf40a 100644 --- a/sqlparser/seata-sqlparser-druid/src/main/java/org/apache/seata/sqlparser/druid/oracle/OracleSelectForUpdateRecognizer.java +++ b/sqlparser/seata-sqlparser-druid/src/main/java/org/apache/seata/sqlparser/druid/oracle/OracleSelectForUpdateRecognizer.java @@ -102,7 +102,7 @@ private SQLSelectQueryBlock getSelect() { if (select == null) { throw new SQLParsingException("should never happen!"); } - SQLSelectQueryBlock selectQueryBlock = select.getQueryBlock(); + SQLSelectQueryBlock selectQueryBlock = select.getFirstQueryBlock(); if (selectQueryBlock == null) { throw new SQLParsingException("should never happen!"); } diff --git a/sqlparser/seata-sqlparser-druid/src/main/java/org/apache/seata/sqlparser/druid/postgresql/PostgresqlSelectForUpdateRecognizer.java b/sqlparser/seata-sqlparser-druid/src/main/java/org/apache/seata/sqlparser/druid/postgresql/PostgresqlSelectForUpdateRecognizer.java index 88a7f9af924..f7827a57f6d 100644 --- a/sqlparser/seata-sqlparser-druid/src/main/java/org/apache/seata/sqlparser/druid/postgresql/PostgresqlSelectForUpdateRecognizer.java +++ b/sqlparser/seata-sqlparser-druid/src/main/java/org/apache/seata/sqlparser/druid/postgresql/PostgresqlSelectForUpdateRecognizer.java @@ -74,7 +74,7 @@ private SQLSelectQueryBlock getSelect() { if (select == null) { throw new SQLParsingException("should never happen!"); } - SQLSelectQueryBlock selectQueryBlock = select.getQueryBlock(); + SQLSelectQueryBlock selectQueryBlock = select.getFirstQueryBlock(); if (selectQueryBlock == null) { throw new SQLParsingException("should never happen!"); } diff --git a/sqlparser/seata-sqlparser-druid/src/main/java/org/apache/seata/sqlparser/druid/sqlserver/SqlServerOperateRecognizerHolder.java b/sqlparser/seata-sqlparser-druid/src/main/java/org/apache/seata/sqlparser/druid/sqlserver/SqlServerOperateRecognizerHolder.java index d5711a2e159..3af2c98522d 100644 --- a/sqlparser/seata-sqlparser-druid/src/main/java/org/apache/seata/sqlparser/druid/sqlserver/SqlServerOperateRecognizerHolder.java +++ b/sqlparser/seata-sqlparser-druid/src/main/java/org/apache/seata/sqlparser/druid/sqlserver/SqlServerOperateRecognizerHolder.java @@ -55,7 +55,7 @@ public SQLRecognizer getUpdateRecognizer(String sql, SQLStatement ast) { @Override public SQLRecognizer getSelectForUpdateRecognizer(String sql, SQLStatement ast) { - List hints = ((SQLSelectStatement) ast).getSelect().getQueryBlock().getFrom().getHints(); + List hints = ((SQLSelectStatement) ast).getSelect().getFirstQueryBlock().getFrom().getHints(); if (CollectionUtils.isNotEmpty(hints)) { List hintsTexts = hints .stream() diff --git a/tcc/src/main/java/org/apache/seata/rm/tcc/interceptor/parser/TccActionInterceptorParser.java b/tcc/src/main/java/org/apache/seata/rm/tcc/interceptor/parser/TccActionInterceptorParser.java index 740690b19db..9d506919aaa 100644 --- a/tcc/src/main/java/org/apache/seata/rm/tcc/interceptor/parser/TccActionInterceptorParser.java +++ b/tcc/src/main/java/org/apache/seata/rm/tcc/interceptor/parser/TccActionInterceptorParser.java @@ -16,37 +16,41 @@ */ package org.apache.seata.rm.tcc.interceptor.parser; -import java.lang.reflect.Method; -import java.util.Arrays; -import java.util.Collections; -import java.util.HashSet; -import java.util.Set; +import org.apache.seata.common.exception.FrameworkException; import org.apache.seata.common.util.ReflectionUtil; +import org.apache.seata.common.util.StringUtils; +import org.apache.seata.core.model.Resource; +import org.apache.seata.integration.tx.api.interceptor.ActionContextUtil; import org.apache.seata.integration.tx.api.interceptor.handler.ProxyInvocationHandler; -import org.apache.seata.integration.tx.api.interceptor.parser.DefaultResourceRegisterParser; import org.apache.seata.integration.tx.api.interceptor.parser.IfNeedEnhanceBean; import org.apache.seata.integration.tx.api.interceptor.parser.InterfaceParser; import org.apache.seata.integration.tx.api.interceptor.parser.NeedEnhanceEnum; import org.apache.seata.integration.tx.api.remoting.parser.DefaultRemotingParser; +import org.apache.seata.rm.DefaultResourceManager; +import org.apache.seata.rm.tcc.TCCResource; import org.apache.seata.rm.tcc.api.TwoPhaseBusinessAction; import org.apache.seata.rm.tcc.interceptor.TccActionInterceptorHandler; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -public class TccActionInterceptorParser implements InterfaceParser { +import java.lang.annotation.Annotation; +import java.lang.reflect.Method; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; - private static final Logger LOGGER = LoggerFactory.getLogger(TccActionInterceptorParser.class); +public class TccActionInterceptorParser implements InterfaceParser { @Override public ProxyInvocationHandler parserInterfaceToProxy(Object target, String objectName) { - // eliminate the bean without two phase annotation. - Set methodsToProxy = this.tccProxyTargetMethod(target); + Map> methodClassMap = ReflectionUtil.findMatchMethodClazzMap(target.getClass(), method -> method.isAnnotationPresent(getAnnotationClass())); + Set methodsToProxy = methodClassMap.keySet(); if (methodsToProxy.isEmpty()) { return null; } + // register resource and enhance with interceptor - DefaultResourceRegisterParser.get().registerResource(target, objectName); - return new TccActionInterceptorHandler(target, methodsToProxy); + registerResource(target, methodClassMap); + + return new TccActionInterceptorHandler(target, methodsToProxy.stream().map(Method::getName).collect(Collectors.toSet())); } @Override @@ -59,37 +63,50 @@ public IfNeedEnhanceBean parseIfNeedEnhancement(Class beanClass) { return ifNeedEnhanceBean; } - /** - * is TCC proxy-bean/target-bean: LocalTCC , the proxy bean of sofa:reference/dubbo:reference - * - * @param target the remoting desc - * @return boolean boolean - */ - - private Set tccProxyTargetMethod(Object target) { - Set methodsToProxy = new HashSet<>(); - //check if it is TCC bean - Class tccServiceClazz = target.getClass(); - 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())); + protected void registerResource(Object target, Map> methodClassMap) { + try { + for (Map.Entry> methodClassEntry : methodClassMap.entrySet()) { + Method method = methodClassEntry.getKey(); + Annotation annotation = method.getAnnotation(getAnnotationClass()); + if (annotation != null) { + Resource resource = createResource(target, methodClassEntry.getValue(), method, annotation); + //registry resource + DefaultResourceManager.get().registerResource(resource); + } } + } catch (Throwable t) { + throw new FrameworkException(t, "register tcc resource error"); } + } - TwoPhaseBusinessAction twoPhaseBusinessAction; - for (Method method : methods) { - twoPhaseBusinessAction = method.getAnnotation(TwoPhaseBusinessAction.class); - if (twoPhaseBusinessAction != null) { - methodsToProxy.add(method.getName()); - } - } - if (methodsToProxy.isEmpty()) { - return Collections.emptySet(); + protected Class getAnnotationClass() { + return TwoPhaseBusinessAction.class; + } + + protected Resource createResource(Object target, Class targetServiceClass, Method m, Annotation annotation) throws NoSuchMethodException { + TwoPhaseBusinessAction twoPhaseBusinessAction = (TwoPhaseBusinessAction) annotation; + TCCResource tccResource = new TCCResource(); + if (StringUtils.isBlank(twoPhaseBusinessAction.name())) { + throw new FrameworkException("TCC bean name cannot be null or empty"); } - // sofa:reference / dubbo:reference, AOP - return methodsToProxy; + tccResource.setActionName(twoPhaseBusinessAction.name()); + tccResource.setTargetBean(target); + tccResource.setPrepareMethod(m); + tccResource.setCommitMethodName(twoPhaseBusinessAction.commitMethod()); + tccResource.setCommitMethod(targetServiceClass.getMethod(twoPhaseBusinessAction.commitMethod(), + twoPhaseBusinessAction.commitArgsClasses())); + tccResource.setRollbackMethodName(twoPhaseBusinessAction.rollbackMethod()); + tccResource.setRollbackMethod(targetServiceClass.getMethod(twoPhaseBusinessAction.rollbackMethod(), + twoPhaseBusinessAction.rollbackArgsClasses())); + // set argsClasses + tccResource.setCommitArgsClasses(twoPhaseBusinessAction.commitArgsClasses()); + tccResource.setRollbackArgsClasses(twoPhaseBusinessAction.rollbackArgsClasses()); + // set phase two method's keys + tccResource.setPhaseTwoCommitKeys(ActionContextUtil.getTwoPhaseArgs(tccResource.getCommitMethod(), + twoPhaseBusinessAction.commitArgsClasses())); + tccResource.setPhaseTwoRollbackKeys(ActionContextUtil.getTwoPhaseArgs(tccResource.getRollbackMethod(), + twoPhaseBusinessAction.rollbackArgsClasses())); + return tccResource; } } diff --git a/tcc/src/main/java/org/apache/seata/rm/tcc/resource/parser/TccRegisterResourceParser.java b/tcc/src/main/java/org/apache/seata/rm/tcc/resource/parser/TccRegisterResourceParser.java deleted file mode 100644 index 0d72824bf92..00000000000 --- a/tcc/src/main/java/org/apache/seata/rm/tcc/resource/parser/TccRegisterResourceParser.java +++ /dev/null @@ -1,111 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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 org.apache.seata.rm.tcc.resource.parser; - -import java.lang.annotation.Annotation; -import java.lang.reflect.Method; -import java.util.Arrays; -import java.util.HashSet; -import java.util.Set; - -import org.apache.seata.common.exception.FrameworkException; -import org.apache.seata.common.util.ReflectionUtil; -import org.apache.seata.common.util.StringUtils; -import org.apache.seata.integration.tx.api.interceptor.ActionContextUtil; -import org.apache.seata.integration.tx.api.interceptor.parser.RegisterResourceParser; -import org.apache.seata.rm.DefaultResourceManager; -import org.apache.seata.rm.tcc.TCCResource; -import org.apache.seata.rm.tcc.api.BusinessActionContext; -import org.apache.seata.rm.tcc.api.BusinessActionContextParameter; -import org.apache.seata.rm.tcc.api.TwoPhaseBusinessAction; - -public class TccRegisterResourceParser implements RegisterResourceParser { - - @Override - public void registerResource(Object target, String beanName) { - try { - //service bean, registry resource - Class serviceClass = target.getClass(); - executeRegisterResource(target, new HashSet<>(Arrays.asList(serviceClass.getMethods())), target.getClass()); - Set> interfaceClasses = ReflectionUtil.getInterfaces(serviceClass); - for (Class interClass : interfaceClasses) { - executeRegisterResource(target, new HashSet<>(Arrays.asList(interClass.getMethods())), interClass); - } - } catch (Throwable t) { - throw new FrameworkException(t, "parser remoting service error"); - } - } - - - protected void executeRegisterResource(Object target, Set methods, Class targetServiceClass) throws NoSuchMethodException { - for (Method m : methods) { - TwoPhaseBusinessAction twoPhaseBusinessAction = m.getAnnotation(TwoPhaseBusinessAction.class); - if (twoPhaseBusinessAction != null) { - TCCResource tccResource = new TCCResource(); - if (StringUtils.isBlank(twoPhaseBusinessAction.name())) { - throw new FrameworkException("TCC bean name cannot be null or empty"); - } - tccResource.setActionName(twoPhaseBusinessAction.name()); - tccResource.setTargetBean(target); - tccResource.setPrepareMethod(m); - tccResource.setCommitMethodName(twoPhaseBusinessAction.commitMethod()); - tccResource.setCommitMethod(targetServiceClass.getMethod(twoPhaseBusinessAction.commitMethod(), - twoPhaseBusinessAction.commitArgsClasses())); - tccResource.setRollbackMethodName(twoPhaseBusinessAction.rollbackMethod()); - tccResource.setRollbackMethod(targetServiceClass.getMethod(twoPhaseBusinessAction.rollbackMethod(), - twoPhaseBusinessAction.rollbackArgsClasses())); - // set argsClasses - tccResource.setCommitArgsClasses(twoPhaseBusinessAction.commitArgsClasses()); - tccResource.setRollbackArgsClasses(twoPhaseBusinessAction.rollbackArgsClasses()); - // set phase two method's keys - tccResource.setPhaseTwoCommitKeys(getTwoPhaseArgs(tccResource.getCommitMethod(), - twoPhaseBusinessAction.commitArgsClasses())); - tccResource.setPhaseTwoRollbackKeys(getTwoPhaseArgs(tccResource.getRollbackMethod(), - twoPhaseBusinessAction.rollbackArgsClasses())); - //registry tcc resource - DefaultResourceManager.get().registerResource(tccResource); - } - } - } - - 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/test/java/org/apache/seata/rm/tcc/resource/parser/TccRegisterResourceParserTest.java b/tcc/src/test/java/org/apache/seata/rm/tcc/resource/parser/TccRegisterResourceParserTest.java deleted file mode 100644 index b7f651afae9..00000000000 --- a/tcc/src/test/java/org/apache/seata/rm/tcc/resource/parser/TccRegisterResourceParserTest.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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 org.apache.seata.rm.tcc.resource.parser; - -import java.lang.reflect.Method; - -import org.apache.seata.rm.tcc.TccAction; -import org.apache.seata.rm.tcc.TccParam; -import org.apache.seata.rm.tcc.api.BusinessActionContext; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Test; - - -class TccRegisterResourceParserTest { - - TccRegisterResourceParser tccRegisterResourceParser = new TccRegisterResourceParser(); - - @Test - public void testGetTwoPhaseArgs() throws Exception { - Class[] argsCommitClasses = new Class[]{BusinessActionContext.class, TccParam.class, Integer.class}; - Method commitMethod = TccAction.class.getMethod("commitWithArg", argsCommitClasses); - Assertions.assertThrows(IllegalArgumentException.class, () -> { - tccRegisterResourceParser.getTwoPhaseArgs(commitMethod, argsCommitClasses); - }); - Class[] argsRollbackClasses = new Class[]{BusinessActionContext.class, TccParam.class}; - Method rollbackMethod = TccAction.class.getMethod("rollbackWithArg", argsRollbackClasses); - String[] keys = tccRegisterResourceParser.getTwoPhaseArgs(rollbackMethod, argsRollbackClasses); - Assertions.assertNull(keys[0]); - Assertions.assertEquals("tccParam", keys[1]); - } -} diff --git a/test-mock-server/src/main/resources/application.yml b/test-mock-server/src/main/resources/application.yml deleted file mode 100644 index 978398eb831..00000000000 --- a/test-mock-server/src/main/resources/application.yml +++ /dev/null @@ -1,57 +0,0 @@ -# -# Licensed to the Apache Software Foundation (ASF) under one or more -# contributor license agreements. See the NOTICE file distributed with -# this work for additional information regarding copyright ownership. -# The ASF licenses this file to You 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. -# - -server: - port: 7091 - service-port: 8091 - -spring: - application: - name: seata-server - -logging: - config: classpath:logback-spring.xml - file: - path: ${log.home:${user.home}/logs/seata} - extend: - logstash-appender: - destination: 127.0.0.1:4560 - kafka-appender: - bootstrap-servers: 127.0.0.1:9092 - topic: logback_to_logstash - -console: - user: - username: seata - password: seata -seata: - config: - # support: nacos, consul, apollo, zk, etcd3 - type: file - registry: - # support: nacos, eureka, redis, zk, consul, etcd3, sofa - type: file - store: - # support: file 、 db 、 redis 、 raft - mode: file - # server: - # service-port: 8091 #If not configured, the default is '${server.port} + 1000' - security: - secretKey: SeataSecretKey0c382ef121d778043159209298fd40bf3850a017 - tokenValidityInMilliseconds: 1800000 - ignore: - urls: /,/**/*.css,/**/*.js,/**/*.html,/**/*.map,/**/*.svg,/**/*.png,/**/*.jpeg,/**/*.ico,/api/v1/auth/login,/metadata/v1/** diff --git a/test-mock-server/src/main/resources/logback-spring.xml b/test-mock-server/src/main/resources/logback-spring.xml deleted file mode 100644 index 9650c0b56ee..00000000000 --- a/test-mock-server/src/main/resources/logback-spring.xml +++ /dev/null @@ -1,112 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - true - 0 - 2048 - true - - - - - true - 0 - 2048 - true - - - - true - 0 - 1024 - true - - - - true - 0 - 1024 - true - - - - - - - - - - - - - - - - - - - diff --git a/test-old-version/src/test/java/io/seata/MockTest.java b/test-old-version/src/test/java/io/seata/MockTest.java index 1a06b872d13..00c1029262a 100644 --- a/test-old-version/src/test/java/io/seata/MockTest.java +++ b/test-old-version/src/test/java/io/seata/MockTest.java @@ -46,7 +46,6 @@ public class MockTest { @BeforeAll public static void before() { - System.setProperty("server.servicePort", ProtocolTestConstants.MOCK_SERVER_PORT+""); MockServer.start(ProtocolTestConstants.MOCK_SERVER_PORT); } diff --git a/test-old-version/src/test/java/io/seata/core/rpc/netty/Action1Impl.java b/test-old-version/src/test/java/io/seata/core/rpc/netty/Action1Impl.java index c13a344043b..eb4f9ea27cf 100644 --- a/test-old-version/src/test/java/io/seata/core/rpc/netty/Action1Impl.java +++ b/test-old-version/src/test/java/io/seata/core/rpc/netty/Action1Impl.java @@ -16,11 +16,11 @@ */ package io.seata.core.rpc.netty; +import java.util.HashMap; +import java.util.Map; + import io.seata.rm.tcc.api.BusinessActionContext; import org.springframework.stereotype.Service; -import vlsi.utils.CompactHashMap; - -import java.util.Map; /** * The type Action1. @@ -28,8 +28,8 @@ @Service public class Action1Impl implements Action1 { - private static Map commitMap = new CompactHashMap<>(); - private static Map rollbackMap = new CompactHashMap<>(); + private static final Map COMMIT_MAP = new HashMap<>(); + private static final Map ROLLBACK_MAP = new HashMap<>(); @Override public String insert(Long reqId, Map params) { @@ -37,12 +37,11 @@ public String insert(Long reqId, Map params) { return "prepare"; } - @Override public boolean commitTcc(BusinessActionContext actionContext) { String xid = actionContext.getXid(); System.out.println("commitTcc:" + xid + "," + actionContext.getActionContext()); - commitMap.compute(xid, (k, v) -> v == null ? 1 : v + 1); + COMMIT_MAP.compute(xid, (k, v) -> v == null ? 1 : v + 1); return true; } @@ -50,15 +49,15 @@ public boolean commitTcc(BusinessActionContext actionContext) { public boolean cancel(BusinessActionContext actionContext) { String xid = actionContext.getXid(); System.out.println("cancelTcc:" + xid + "," + actionContext.getActionContext()); - rollbackMap.compute(xid, (k, v) -> v == null ? 1 : v + 1); + ROLLBACK_MAP.compute(xid, (k, v) -> v == null ? 1 : v + 1); return true; } public static int getCommitTimes(String xid) { - return commitMap.getOrDefault(xid, 0); + return COMMIT_MAP.getOrDefault(xid, 0); } public static int getRollbackTimes(String xid) { - return rollbackMap.getOrDefault(xid, 0); + return ROLLBACK_MAP.getOrDefault(xid, 0); } } diff --git a/test-old-version/src/test/java/io/seata/core/rpc/netty/TmClientTest.java b/test-old-version/src/test/java/io/seata/core/rpc/netty/TmClientTest.java index 254caffa873..7d02cbcc4ed 100644 --- a/test-old-version/src/test/java/io/seata/core/rpc/netty/TmClientTest.java +++ b/test-old-version/src/test/java/io/seata/core/rpc/netty/TmClientTest.java @@ -16,7 +16,6 @@ */ package io.seata.core.rpc.netty; -import io.netty.channel.Channel; import io.seata.core.model.GlobalStatus; import io.seata.core.model.TransactionManager; import io.seata.tm.DefaultTransactionManager; @@ -24,9 +23,6 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; - /** * TmClient Test **/ diff --git a/test-old-version/src/test/resources/registry.conf b/test-old-version/src/test/resources/registry.conf index bab6e8ec0ef..961283a7f82 100644 --- a/test-old-version/src/test/resources/registry.conf +++ b/test-old-version/src/test/resources/registry.conf @@ -16,74 +16,14 @@ # registry { - # file 、nacos 、eureka、redis、zk、consul、etcd3、sofa type = "file" - - nacos { - serverAddr = "localhost" - namespace = "" - cluster = "default" - } - eureka { - serviceUrl = "http://localhost:8761/eureka" - application = "default" - weight = "1" - } - redis { - serverAddr = "localhost:6379" - db = "0" - } - zk { - cluster = "default" - serverAddr = "127.0.0.1:2181" - sessionTimeout = 6000 - connectTimeout = 2000 - } - consul { - cluster = "default" - serverAddr = "127.0.0.1:8500" - } - etcd3 { - cluster = "default" - serverAddr = "http://localhost:2379" - } - sofa { - serverAddr = "127.0.0.1:9603" - application = "default" - region = "DEFAULT_ZONE" - datacenter = "DefaultDataCenter" - cluster = "default" - group = "SEATA_GROUP" - addressWaitTime = "3000" - } file { name = "file.conf" } } config { - # file、nacos 、apollo、zk、consul、etcd3 type = "file" - - nacos { - serverAddr = "localhost" - namespace = "" - } - consul { - serverAddr = "127.0.0.1:8500" - } - apollo { - appId = "seata-server" - apolloMeta = "http://192.168.1.204:8801" - } - zk { - serverAddr = "127.0.0.1:2181" - sessionTimeout = 6000 - connectTimeout = 2000 - } - etcd3 { - serverAddr = "http://localhost:2379" - } file { name = "file.conf" } diff --git a/test/pom.xml b/test/pom.xml index d35f25bad5e..3c43350502f 100644 --- a/test/pom.xml +++ b/test/pom.xml @@ -50,7 +50,7 @@ grpc-java - io.grpc:protoc-gen-grpc-java:1.66.0:exe:${os.detected.classifier} + io.grpc:protoc-gen-grpc-java:1.55.1:exe:${os.detected.classifier} @@ -71,6 +71,11 @@ seata-tm ${project.version} + + org.apache.seata + seata-serializer-protobuf + ${project.version} + io.grpc grpc-alts @@ -108,7 +113,6 @@ seata-spring ${project.version} - com.h2database h2 @@ -178,6 +182,12 @@ rocketmq-client test + + org.apache.seata + seata-saga-annotation + ${project.version} + test + diff --git a/test/src/test/java/org/apache/seata/core/rpc/netty/mockserver/GrpcTest.java b/test/src/test/java/org/apache/seata/core/rpc/netty/mockserver/GrpcTest.java index 0d63d2eb70f..042160a9ba2 100644 --- a/test/src/test/java/org/apache/seata/core/rpc/netty/mockserver/GrpcTest.java +++ b/test/src/test/java/org/apache/seata/core/rpc/netty/mockserver/GrpcTest.java @@ -16,6 +16,7 @@ */ package org.apache.seata.core.rpc.netty.mockserver; +import com.google.protobuf.Any; import io.grpc.ManagedChannel; import io.grpc.ManagedChannelBuilder; import io.grpc.stub.StreamObserver; @@ -25,6 +26,8 @@ import org.apache.seata.core.protocol.generated.GrpcMessageProto; import org.apache.seata.core.rpc.netty.RmNettyRemotingClient; import org.apache.seata.core.rpc.netty.TmNettyRemotingClient; +import org.apache.seata.core.rpc.netty.grpc.GrpcHeaderEnum; +import org.apache.seata.core.serializer.SerializerType; import org.apache.seata.mockserver.MockServer; import org.apache.seata.serializer.protobuf.generated.*; import org.apache.seata.core.protocol.generated.SeataServiceGrpc; @@ -69,7 +72,7 @@ private GrpcMessageProto getRegisterTMRequest() { .setAbstractIdentifyRequest(abstractIdentifyRequestProto) .build(); - return GrpcMessageProto.newBuilder().setBody(registerTMRequestProto.toByteString()).build(); + return GrpcMessageProto.newBuilder().putHeadMap(GrpcHeaderEnum.CODEC_TYPE.header, String.valueOf(SerializerType.GRPC.getCode())).setBody(Any.pack(registerTMRequestProto).toByteString()).build(); } private GrpcMessageProto getGlobalBeginRequest() { @@ -77,7 +80,7 @@ private GrpcMessageProto getGlobalBeginRequest() { .setTransactionName("test-transaction") .setTimeout(2000) .build(); - return GrpcMessageProto.newBuilder().setBody(globalBeginRequestProto.toByteString()).build(); + return GrpcMessageProto.newBuilder().putHeadMap(GrpcHeaderEnum.CODEC_TYPE.header, String.valueOf(SerializerType.GRPC.getCode())).setBody(Any.pack(globalBeginRequestProto).toByteString()).build(); } private GrpcMessageProto getBranchRegisterRequest() { @@ -89,7 +92,7 @@ private GrpcMessageProto getBranchRegisterRequest() { .setApplicationData("{\"mock\":\"mock\"}") .build(); - return GrpcMessageProto.newBuilder().setBody(branchRegisterRequestProto.toByteString()).build(); + return GrpcMessageProto.newBuilder().putHeadMap(GrpcHeaderEnum.CODEC_TYPE.header, String.valueOf(SerializerType.GRPC.getCode())).setBody(Any.pack(branchRegisterRequestProto).toByteString()).build(); } private GrpcMessageProto getGlobalCommitRequest() { @@ -100,7 +103,7 @@ private GrpcMessageProto getGlobalCommitRequest() { .setAbstractGlobalEndRequest(globalEndRequestProto) .build(); - return GrpcMessageProto.newBuilder().setBody(globalCommitRequestProto.toByteString()).build(); + return GrpcMessageProto.newBuilder().putHeadMap(GrpcHeaderEnum.CODEC_TYPE.header, String.valueOf(SerializerType.GRPC.getCode())).setBody(Any.pack(globalCommitRequestProto).toByteString()).build(); } private GrpcMessageProto getGlobalRollbackRequest() { @@ -111,7 +114,7 @@ private GrpcMessageProto getGlobalRollbackRequest() { .setAbstractGlobalEndRequest(globalEndRequestProto) .build(); - return GrpcMessageProto.newBuilder().setBody(globalRollbackRequestProto.toByteString()).build(); + return GrpcMessageProto.newBuilder().putHeadMap(GrpcHeaderEnum.CODEC_TYPE.header, String.valueOf(SerializerType.GRPC.getCode())).setBody(Any.pack(globalRollbackRequestProto).toByteString()).build(); } @Test diff --git a/test/src/test/java/org/apache/seata/core/rpc/netty/mockserver/RmClientTest.java b/test/src/test/java/org/apache/seata/core/rpc/netty/mockserver/RmClientTest.java index c7f100bce74..c9bd6d0f941 100644 --- a/test/src/test/java/org/apache/seata/core/rpc/netty/mockserver/RmClientTest.java +++ b/test/src/test/java/org/apache/seata/core/rpc/netty/mockserver/RmClientTest.java @@ -17,6 +17,9 @@ package org.apache.seata.core.rpc.netty.mockserver; import io.netty.channel.Channel; +import org.apache.seata.common.exception.FrameworkException; +import org.apache.seata.common.util.ReflectionUtil; +import org.apache.seata.common.util.StringUtils; import org.apache.seata.core.context.RootContext; import org.apache.seata.core.exception.TransactionException; import org.apache.seata.core.model.BranchStatus; @@ -24,13 +27,17 @@ import org.apache.seata.core.protocol.HeartbeatMessage; import org.apache.seata.core.rpc.netty.ChannelManagerTestHelper; import org.apache.seata.core.rpc.netty.RmNettyRemotingClient; -import org.apache.seata.integration.tx.api.interceptor.parser.DefaultResourceRegisterParser; +import org.apache.seata.integration.tx.api.interceptor.ActionContextUtil; import org.apache.seata.rm.DefaultResourceManager; import org.apache.seata.rm.RMClient; +import org.apache.seata.rm.tcc.TCCResource; +import org.apache.seata.rm.tcc.api.TwoPhaseBusinessAction; import org.junit.jupiter.api.Assertions; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.lang.reflect.Method; +import java.util.Map; import java.util.concurrent.ConcurrentMap; /** @@ -76,10 +83,55 @@ public static DefaultResourceManager getRm(String resourceId) { //register:TYPE_REG_RM = 103 , TYPE_REG_RM_RESULT = 104 Action1 target = new Action1Impl(); - DefaultResourceRegisterParser.get().registerResource(target, resourceId); + registryTccResource(target); LOGGER.info("registerResource ok"); return rm; } + /** + * only compatible history ut + * TODO fix + */ + @Deprecated + private static void registryTccResource(Action1 target) { + Map> matchMethodClazzMap = ReflectionUtil.findMatchMethodClazzMap(target.getClass(), method -> method.isAnnotationPresent(TwoPhaseBusinessAction.class)); + if (matchMethodClazzMap.keySet().isEmpty()) { + return; + } + + try { + for (Map.Entry> methodClassEntry : matchMethodClazzMap.entrySet()) { + Method method = methodClassEntry.getKey(); + Class methodClass = methodClassEntry.getValue(); + + TwoPhaseBusinessAction twoPhaseBusinessAction = method.getAnnotation(TwoPhaseBusinessAction.class); + TCCResource tccResource = new TCCResource(); + if (StringUtils.isBlank(twoPhaseBusinessAction.name())) { + throw new FrameworkException("TCC bean name cannot be null or empty"); + } + tccResource.setActionName(twoPhaseBusinessAction.name()); + tccResource.setTargetBean(target); + tccResource.setPrepareMethod(method); + tccResource.setCommitMethodName(twoPhaseBusinessAction.commitMethod()); + tccResource.setCommitMethod(methodClass.getMethod(twoPhaseBusinessAction.commitMethod(), + twoPhaseBusinessAction.commitArgsClasses())); + tccResource.setRollbackMethodName(twoPhaseBusinessAction.rollbackMethod()); + tccResource.setRollbackMethod(methodClass.getMethod(twoPhaseBusinessAction.rollbackMethod(), + twoPhaseBusinessAction.rollbackArgsClasses())); + // set argsClasses + tccResource.setCommitArgsClasses(twoPhaseBusinessAction.commitArgsClasses()); + tccResource.setRollbackArgsClasses(twoPhaseBusinessAction.rollbackArgsClasses()); + // set phase two method's keys + tccResource.setPhaseTwoCommitKeys(ActionContextUtil.getTwoPhaseArgs(tccResource.getCommitMethod(), + twoPhaseBusinessAction.commitArgsClasses())); + tccResource.setPhaseTwoRollbackKeys(ActionContextUtil.getTwoPhaseArgs(tccResource.getRollbackMethod(), + twoPhaseBusinessAction.rollbackArgsClasses())); + DefaultResourceManager.get().registerResource(tccResource); + } + } catch (Throwable t) { + throw new FrameworkException(t, "register tcc resource error"); + } + } + } diff --git a/test/src/test/java/org/apache/seata/saga/annotation/BranchSessionMock.java b/test/src/test/java/org/apache/seata/saga/annotation/BranchSessionMock.java new file mode 100644 index 00000000000..26fc1c0a962 --- /dev/null +++ b/test/src/test/java/org/apache/seata/saga/annotation/BranchSessionMock.java @@ -0,0 +1,86 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.seata.saga.annotation; + + +import org.apache.seata.core.model.BranchType; + +public class BranchSessionMock { + + private String xid; + + private long branchId; + + private String resourceGroupId; + + private String resourceId; + + + private BranchType branchType; + + + private String applicationData; + + + public String getXid() { + return xid; + } + + public void setXid(String xid) { + this.xid = xid; + } + + public long getBranchId() { + return branchId; + } + + public void setBranchId(long branchId) { + this.branchId = branchId; + } + + public String getResourceGroupId() { + return resourceGroupId; + } + + public void setResourceGroupId(String resourceGroupId) { + this.resourceGroupId = resourceGroupId; + } + + public String getResourceId() { + return resourceId; + } + + public void setResourceId(String resourceId) { + this.resourceId = resourceId; + } + + public BranchType getBranchType() { + return branchType; + } + + public void setBranchType(BranchType branchType) { + this.branchType = branchType; + } + + public String getApplicationData() { + return applicationData; + } + + public void setApplicationData(String applicationData) { + this.applicationData = applicationData; + } +} \ No newline at end of file diff --git a/test/src/test/java/org/apache/seata/saga/annotation/NormalSagaAnnotationAction.java b/test/src/test/java/org/apache/seata/saga/annotation/NormalSagaAnnotationAction.java new file mode 100644 index 00000000000..11aadcb88f7 --- /dev/null +++ b/test/src/test/java/org/apache/seata/saga/annotation/NormalSagaAnnotationAction.java @@ -0,0 +1,38 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.seata.saga.annotation; + +import org.apache.seata.rm.tcc.api.BusinessActionContext; + +import java.util.List; + +/** + * The interface saga action. + */ +public interface NormalSagaAnnotationAction { + + + boolean commit(BusinessActionContext actionContext, int a, List b, SagaParam sagaParam); + + /** + * Rollback boolean. + * + * @param actionContext the action context + * @return the boolean + */ + boolean compensation(BusinessActionContext actionContext, SagaParam param); +} \ No newline at end of file diff --git a/test/src/test/java/org/apache/seata/saga/annotation/NormalSagaAnnotationActionImpl.java b/test/src/test/java/org/apache/seata/saga/annotation/NormalSagaAnnotationActionImpl.java new file mode 100644 index 00000000000..6062cce4597 --- /dev/null +++ b/test/src/test/java/org/apache/seata/saga/annotation/NormalSagaAnnotationActionImpl.java @@ -0,0 +1,49 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.seata.saga.annotation; + +import org.apache.seata.rm.tcc.api.BusinessActionContext; +import org.apache.seata.rm.tcc.api.BusinessActionContextParameter; +import org.apache.seata.saga.rm.api.CompensationBusinessAction; + +import java.util.List; + +/** + * + */ +public class NormalSagaAnnotationActionImpl implements NormalSagaAnnotationAction { + + private boolean isCommit; + + + @Override + @CompensationBusinessAction(name = "sagaActionForTest", compensationMethod = "compensation", compensationArgsClasses = {BusinessActionContext.class, SagaParam.class}) + public boolean commit(BusinessActionContext actionContext, @BusinessActionContextParameter("a") int a, @BusinessActionContextParameter(paramName = "b", index = 0) List b, @BusinessActionContextParameter(isParamInProperty = true) SagaParam sagaParam) { + isCommit = true; + return a > 1; + } + + @Override + public boolean compensation(BusinessActionContext actionContext, @BusinessActionContextParameter("sagaParam") SagaParam param) { + isCommit = false; + return true; + } + + public boolean isCommit() { + return isCommit; + } +} \ No newline at end of file diff --git a/test/src/test/java/org/apache/seata/saga/annotation/SagaParam.java b/test/src/test/java/org/apache/seata/saga/annotation/SagaParam.java new file mode 100644 index 00000000000..63ee611e3a7 --- /dev/null +++ b/test/src/test/java/org/apache/seata/saga/annotation/SagaParam.java @@ -0,0 +1,65 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.seata.saga.annotation; + +import org.apache.seata.rm.tcc.api.BusinessActionContextParameter; + +/** + * The type param. + */ +public class SagaParam { + + /** + * The Num. + */ + protected int num; + + /** + * The Email. + */ + @BusinessActionContextParameter(paramName = "email") + protected String email; + + /** + * Instantiates a new param. + * + * @param num the num + * @param email the email + */ + public SagaParam(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; + } +} \ No newline at end of file diff --git a/test/src/test/java/org/apache/seata/saga/annotation/rm/interceptor/parser/SagaActionInterceptorParserTest.java b/test/src/test/java/org/apache/seata/saga/annotation/rm/interceptor/parser/SagaActionInterceptorParserTest.java new file mode 100644 index 00000000000..ca6f2317da1 --- /dev/null +++ b/test/src/test/java/org/apache/seata/saga/annotation/rm/interceptor/parser/SagaActionInterceptorParserTest.java @@ -0,0 +1,210 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.seata.saga.annotation.rm.interceptor.parser; + +import org.apache.seata.core.exception.TransactionException; +import org.apache.seata.core.model.BranchType; +import org.apache.seata.core.model.GlobalStatus; +import org.apache.seata.core.model.ResourceManager; +import org.apache.seata.core.model.TransactionManager; +import org.apache.seata.integration.tx.api.interceptor.handler.ProxyInvocationHandler; +import org.apache.seata.integration.tx.api.util.ProxyUtil; +import org.apache.seata.rm.DefaultResourceManager; +import org.apache.seata.saga.annotation.BranchSessionMock; +import org.apache.seata.saga.annotation.NormalSagaAnnotationActionImpl; +import org.apache.seata.saga.annotation.SagaParam; +import org.apache.seata.saga.rm.SagaAnnotationResourceManager; +import org.apache.seata.saga.rm.interceptor.parser.SagaAnnotationActionInterceptorParser; +import org.apache.seata.tm.TransactionManagerHolder; +import org.apache.seata.tm.api.GlobalTransaction; +import org.apache.seata.tm.api.GlobalTransactionContext; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +/** + * + */ +public class SagaActionInterceptorParserTest { + + public static String DEFAULT_XID = "default_xid"; + + @BeforeAll + public static void init() throws IOException { + System.setProperty("config.type", "file"); + System.setProperty("config.file.name", "file.conf"); + System.setProperty("txServiceGroup", "default_tx_group"); + System.setProperty("service.vgroupMapping.default_tx_group", "default"); + } + + @AfterEach + public void clearTccResource() { + DefaultResourceManager.get().getResourceManager(BranchType.SAGA_ANNOTATION).getManagedResources().clear(); + } + + @Test + void parserInterfaceToProxy() { + NormalSagaAnnotationActionImpl sagaAction = new NormalSagaAnnotationActionImpl(); + + SagaAnnotationActionInterceptorParser sagaAnnotationActionInterceptorParser = new SagaAnnotationActionInterceptorParser(); + + ProxyInvocationHandler proxyInvocationHandler = sagaAnnotationActionInterceptorParser.parserInterfaceToProxy(sagaAction, "sagaAction"); + Assertions.assertNotNull(proxyInvocationHandler); + } + + + @Test + public void testSagaAnnotation_should_commit() throws TransactionException { + DefaultResourceManager.get(); + DefaultResourceManager.mockResourceManager(BranchType.SAGA_ANNOTATION, resourceManager); + + TransactionManagerHolder.set(transactionManager); + + NormalSagaAnnotationActionImpl sagaActionProxy = ProxyUtil.createProxy(new NormalSagaAnnotationActionImpl()); + + SagaParam sagaParam = new SagaParam(2, "abc@163.com"); + List listB = Arrays.asList("b"); + + GlobalTransaction tx = GlobalTransactionContext.getCurrentOrCreate(); + + try { + tx.begin(60000, "testBiz"); + + boolean result = sagaActionProxy.commit(null, 2, listB, sagaParam); + + Assertions.assertTrue(result); + + if (result) { + tx.commit(); + } else { + tx.rollback(); + } + } catch (Exception exx) { + tx.rollback(); + throw exx; + } + + Assertions.assertTrue(sagaActionProxy.isCommit()); + } + + @Test + public void testSagaAnnotation_should_rollback() throws TransactionException { + DefaultResourceManager.get(); + DefaultResourceManager.mockResourceManager(BranchType.SAGA_ANNOTATION, resourceManager); + + TransactionManagerHolder.set(transactionManager); + + NormalSagaAnnotationActionImpl sagaActionProxy = ProxyUtil.createProxy(new NormalSagaAnnotationActionImpl()); + + SagaParam sagaParam = new SagaParam(1, "abc@163.com"); + List listB = Arrays.asList("b"); + + GlobalTransaction tx = GlobalTransactionContext.getCurrentOrCreate(); + + try { + tx.begin(60000, "testBiz"); + + boolean result = sagaActionProxy.commit(null, 1, listB, sagaParam); + + Assertions.assertFalse(result); + + if (result) { + tx.commit(); + } else { + tx.rollback(); + } + } catch (Exception exx) { + tx.rollback(); + throw exx; + } + + Assertions.assertFalse(sagaActionProxy.isCommit()); + } + + private static Map> applicationDataMap = new ConcurrentHashMap<>(); + + + private static TransactionManager transactionManager = 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 { + + rollbackAll(xid); + + return GlobalStatus.Rollbacked; + } + + @Override + public GlobalStatus getStatus(String xid) throws TransactionException { + return GlobalStatus.Begin; + } + + @Override + public GlobalStatus globalReport(String xid, GlobalStatus globalStatus) throws TransactionException { + return globalStatus; + } + }; + + + private static ResourceManager resourceManager = new SagaAnnotationResourceManager() { + + @Override + public Long branchRegister(BranchType branchType, String resourceId, String clientId, String xid, String applicationData, String lockKeys) throws TransactionException { + + long branchId = System.currentTimeMillis(); + + List branches = applicationDataMap.computeIfAbsent(xid, s -> new ArrayList<>()); + BranchSessionMock branchSessionMock = new BranchSessionMock(); + branchSessionMock.setXid(xid); + branchSessionMock.setBranchType(branchType); + branchSessionMock.setResourceId(resourceId); + branchSessionMock.setApplicationData(applicationData); + branchSessionMock.setBranchId(branchId); + + branches.add(branchSessionMock); + + return branchId; + } + }; + + + public static void rollbackAll(String xid) throws TransactionException { + + List branches = applicationDataMap.computeIfAbsent(xid, s -> new ArrayList<>()); + for (BranchSessionMock branch : branches) { + resourceManager.branchRollback(branch.getBranchType(), branch.getXid(), branch.getBranchId(), branch.getResourceId(), branch.getApplicationData()); + } + } + +} \ No newline at end of file diff --git a/test/src/test/resources/META-INF/services/org.apache.seata.core.serializer.Serializer b/test/src/test/resources/META-INF/services/org.apache.seata.core.serializer.Serializer new file mode 100644 index 00000000000..81c5235e259 --- /dev/null +++ b/test/src/test/resources/META-INF/services/org.apache.seata.core.serializer.Serializer @@ -0,0 +1,17 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You 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. +# +org.apache.seata.serializer.protobuf.GrpcSerializer \ No newline at end of file diff --git a/tm/src/test/java/org/apache/seata/tm/DefaultTransactionManagerTest.java b/tm/src/test/java/org/apache/seata/tm/DefaultTransactionManagerTest.java new file mode 100644 index 00000000000..1841e56b44e --- /dev/null +++ b/tm/src/test/java/org/apache/seata/tm/DefaultTransactionManagerTest.java @@ -0,0 +1,168 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.seata.tm; + +import org.apache.seata.core.exception.TransactionException; +import org.apache.seata.core.model.GlobalStatus; +import org.apache.seata.core.protocol.ResultCode; +import org.apache.seata.core.protocol.transaction.AbstractTransactionRequest; +import org.apache.seata.core.protocol.transaction.GlobalBeginRequest; +import org.apache.seata.core.protocol.transaction.GlobalBeginResponse; +import org.apache.seata.core.protocol.transaction.GlobalCommitRequest; +import org.apache.seata.core.protocol.transaction.GlobalCommitResponse; +import org.apache.seata.core.protocol.transaction.GlobalReportRequest; +import org.apache.seata.core.protocol.transaction.GlobalReportResponse; +import org.apache.seata.core.protocol.transaction.GlobalRollbackRequest; +import org.apache.seata.core.protocol.transaction.GlobalRollbackResponse; +import org.apache.seata.core.protocol.transaction.GlobalStatusRequest; +import org.apache.seata.core.protocol.transaction.GlobalStatusResponse; +import org.apache.seata.core.rpc.netty.TmNettyRemotingClient; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.Mock; +import org.mockito.MockedStatic; +import org.mockito.Mockito; +import org.mockito.MockitoAnnotations; + +import java.util.concurrent.TimeoutException; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.when; + +/** + * the type DefaultTransactionManager + */ +public class DefaultTransactionManagerTest { + + private final static String DEFAULT_XID = "1234567890"; + + private DefaultTransactionManager defaultTransactionManager; + + private MockedStatic tmNettyRemotingClientMockedStatic; + + @Mock + private TmNettyRemotingClient tmNettyRemotingClient; + + + @BeforeEach + void init() { + MockitoAnnotations.openMocks(this); + tmNettyRemotingClientMockedStatic = Mockito.mockStatic(TmNettyRemotingClient.class); + tmNettyRemotingClientMockedStatic.when(TmNettyRemotingClient::getInstance).thenReturn(tmNettyRemotingClient); + defaultTransactionManager = new DefaultTransactionManager(); + } + + @AfterEach + void destory(){ + tmNettyRemotingClientMockedStatic.close(); + } + + @Test + void testBeginSuccess() throws Exception { + GlobalBeginResponse mockResponse = new GlobalBeginResponse(); + mockResponse.setResultCode(ResultCode.Success); + mockResponse.setXid(DEFAULT_XID); + + when(tmNettyRemotingClient.sendSyncRequest(any(GlobalBeginRequest.class))).thenReturn(mockResponse); + + String xid = defaultTransactionManager.begin("appId", "txGroup", "testName", 1000); + + Assertions.assertEquals("1234567890", xid); + Mockito.verify(tmNettyRemotingClient).sendSyncRequest(any(GlobalBeginRequest.class)); + } + + @Test + void testBeginFailure() throws Exception { + GlobalBeginResponse mockResponse = new GlobalBeginResponse(); + mockResponse.setResultCode(ResultCode.Failed); + mockResponse.setMsg("Failed to begin transaction"); + + when(tmNettyRemotingClient.sendSyncRequest(any(GlobalBeginRequest.class))).thenReturn(mockResponse); + + TransactionException exception = Assertions.assertThrows(TransactionException.class, + () -> defaultTransactionManager.begin("appId", "txGroup", "testName", 1000)); + + Assertions.assertTrue(exception.getMessage().contains("Failed to begin transaction")); + Mockito.verify(tmNettyRemotingClient).sendSyncRequest(any(GlobalBeginRequest.class)); + } + + @Test + void testCommitSuccess() throws Exception { + GlobalCommitResponse mockResponse = new GlobalCommitResponse(); + mockResponse.setGlobalStatus(GlobalStatus.Committed); + + when(tmNettyRemotingClient.sendSyncRequest(any(GlobalCommitRequest.class))).thenReturn(mockResponse); + + GlobalStatus status = defaultTransactionManager.commit(DEFAULT_XID); + + Assertions.assertEquals(GlobalStatus.Committed, status); + Mockito.verify(tmNettyRemotingClient).sendSyncRequest(any(GlobalCommitRequest.class)); + } + + @Test + void testRollbackSuccess() throws Exception { + GlobalRollbackResponse mockResponse = new GlobalRollbackResponse(); + mockResponse.setGlobalStatus(GlobalStatus.Rollbacked); + + when(tmNettyRemotingClient.sendSyncRequest(any(GlobalRollbackRequest.class))).thenReturn(mockResponse); + + GlobalStatus status = defaultTransactionManager.rollback(DEFAULT_XID); + + Assertions.assertEquals(GlobalStatus.Rollbacked, status); + Mockito.verify(tmNettyRemotingClient).sendSyncRequest(any(GlobalRollbackRequest.class)); + } + + @Test + void testGetStatusSuccess() throws Exception { + GlobalStatusResponse mockResponse = new GlobalStatusResponse(); + mockResponse.setGlobalStatus(GlobalStatus.Committing); + + when(tmNettyRemotingClient.sendSyncRequest(any(GlobalStatusRequest.class))).thenReturn(mockResponse); + + GlobalStatus status = defaultTransactionManager.getStatus(DEFAULT_XID); + + Assertions.assertEquals(GlobalStatus.Committing, status); + Mockito.verify(tmNettyRemotingClient).sendSyncRequest(any(GlobalStatusRequest.class)); + } + + @Test + void testGlobalReportSuccess() throws Exception { + GlobalReportResponse mockResponse = new GlobalReportResponse(); + mockResponse.setGlobalStatus(GlobalStatus.Committed); + + when(tmNettyRemotingClient.sendSyncRequest(any(GlobalReportRequest.class))).thenReturn(mockResponse); + + GlobalStatus status = defaultTransactionManager.globalReport(DEFAULT_XID, GlobalStatus.Committed); + + Assertions.assertEquals(GlobalStatus.Committed, status); + Mockito.verify(tmNettyRemotingClient).sendSyncRequest(any(GlobalReportRequest.class)); + } + + @Test + void testSyncCallTimeout() throws Exception { + when(tmNettyRemotingClient.sendSyncRequest(any(AbstractTransactionRequest.class))) + .thenThrow(new TimeoutException("Timeout occurred")); + + TransactionException exception = Assertions.assertThrows(TransactionException.class, + () -> defaultTransactionManager.getStatus(DEFAULT_XID)); + + Assertions.assertTrue(exception.getMessage().contains("RPC timeout")); + Mockito.verify(tmNettyRemotingClient).sendSyncRequest(any(AbstractTransactionRequest.class)); + } +} diff --git a/tm/src/test/java/org/apache/seata/tm/TransactionManagerHolderTest.java b/tm/src/test/java/org/apache/seata/tm/TransactionManagerHolderTest.java index 2b673e1dac8..9ee20b30738 100644 --- a/tm/src/test/java/org/apache/seata/tm/TransactionManagerHolderTest.java +++ b/tm/src/test/java/org/apache/seata/tm/TransactionManagerHolderTest.java @@ -18,10 +18,11 @@ import org.apache.seata.common.exception.ShouldNeverHappenException; +import org.apache.seata.core.model.TransactionManager; +import org.apache.seata.tm.api.transaction.MockTransactionManager; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; - class TransactionManagerHolderTest { @@ -31,4 +32,13 @@ void getTest() { TransactionManagerHolder.get();}); } + + @Test + void getInstanceTest() { + MockTransactionManager mockTransactionManager = new MockTransactionManager(); + TransactionManagerHolder.set(mockTransactionManager); + TransactionManager transactionManager = TransactionManagerHolder.get(); + Assertions.assertTrue(transactionManager instanceof MockTransactionManager); + } + } diff --git a/tm/src/test/java/org/apache/seata/tm/api/FailureHandlerHolderTest.java b/tm/src/test/java/org/apache/seata/tm/api/FailureHandlerHolderTest.java new file mode 100644 index 00000000000..0d28c5277fe --- /dev/null +++ b/tm/src/test/java/org/apache/seata/tm/api/FailureHandlerHolderTest.java @@ -0,0 +1,35 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.seata.tm.api; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +/** + * the type FailureHandlerHolder + */ +public class FailureHandlerHolderTest { + + @Test + void testSetFailureHandlerWithCustomHandler() { + MockFailureHandlerImpl mockFailureHandlerImpl = new MockFailureHandlerImpl(); + + FailureHandlerHolder.setFailureHandler(mockFailureHandlerImpl); + + Assertions.assertEquals(mockFailureHandlerImpl, FailureHandlerHolder.getFailureHandler()); + } +} diff --git a/tm/src/test/java/org/apache/seata/tm/api/MockFailureHandlerImpl.java b/tm/src/test/java/org/apache/seata/tm/api/MockFailureHandlerImpl.java new file mode 100644 index 00000000000..a99173ce2c8 --- /dev/null +++ b/tm/src/test/java/org/apache/seata/tm/api/MockFailureHandlerImpl.java @@ -0,0 +1,39 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.seata.tm.api; + +public class MockFailureHandlerImpl implements FailureHandler{ + @Override + public void onBeginFailure(BaseTransaction tx, Throwable cause) { + + } + + @Override + public void onCommitFailure(BaseTransaction tx, Throwable cause) { + + } + + @Override + public void onRollbackFailure(BaseTransaction tx, Throwable originalException) { + + } + + @Override + public void onRollbacking(BaseTransaction tx, Throwable originalException) { + + } +} diff --git a/tm/src/test/java/org/apache/seata/tm/api/transaction/MockTransactionManager.java b/tm/src/test/java/org/apache/seata/tm/api/transaction/MockTransactionManager.java new file mode 100644 index 00000000000..2d2d41b820f --- /dev/null +++ b/tm/src/test/java/org/apache/seata/tm/api/transaction/MockTransactionManager.java @@ -0,0 +1,49 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.seata.tm.api.transaction; + + +import org.apache.seata.core.exception.TransactionException; +import org.apache.seata.core.model.GlobalStatus; +import org.apache.seata.core.model.TransactionManager; + +public class MockTransactionManager implements TransactionManager { + @Override + public String begin(String applicationId, String transactionServiceGroup, String name, int timeout) throws TransactionException { + return null; + } + + @Override + public GlobalStatus commit(String xid) throws TransactionException { + return null; + } + + @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; + } +} diff --git a/tm/src/test/java/org/apache/seata/tm/api/transaction/SuspendedResourcesHolderTest.java b/tm/src/test/java/org/apache/seata/tm/api/transaction/SuspendedResourcesHolderTest.java new file mode 100644 index 00000000000..00ad2c9b999 --- /dev/null +++ b/tm/src/test/java/org/apache/seata/tm/api/transaction/SuspendedResourcesHolderTest.java @@ -0,0 +1,41 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.seata.tm.api.transaction; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +/** + * the type SuspendedResourcesHolder + */ +public class SuspendedResourcesHolderTest { + + private final static String DEFAULT_XID = "1234567890"; + + @Test + void testIllegalArgumentException() { + Assertions.assertThrows(IllegalArgumentException.class, () -> { + new SuspendedResourcesHolder(null); + }); + } + + @Test + void getXidTest() { + SuspendedResourcesHolder suspendedResourcesHolder = new SuspendedResourcesHolder(DEFAULT_XID); + Assertions.assertEquals(DEFAULT_XID, suspendedResourcesHolder.getXid()); + } +}