chore(project): 添加项目配置文件和忽略规则
- 添加 Babel 配置文件支持 ES6+ 语法转换 - 添加 ESLint 忽略规则和配置文件 - 添加 Git 忽略规则文件 - 添加 Travis CI 配置文件 - 添加 1.4.2 版本变更日志文件 - 添加 Helm 图表辅助模板文件 - 添加 Helm 忽略规则文件
This commit is contained in:
133
test/pom.xml
Normal file
133
test/pom.xml
Normal file
@@ -0,0 +1,133 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
~ Copyright 1999-2019 Seata.io Group.
|
||||
~
|
||||
~ Licensed under the Apache License, Version 2.0 (the "License");
|
||||
~ you may not use this file except in compliance with the License.
|
||||
~ You may obtain a copy of the License at
|
||||
~
|
||||
~ http://www.apache.org/licenses/LICENSE-2.0
|
||||
~
|
||||
~ Unless required by applicable law or agreed to in writing, software
|
||||
~ distributed under the License is distributed on an "AS IS" BASIS,
|
||||
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
~ See the License for the specific language governing permissions and
|
||||
~ limitations under the License.
|
||||
-->
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<groupId>io.seata</groupId>
|
||||
<artifactId>seata-parent</artifactId>
|
||||
<version>${revision}</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>seata-test</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
<name>seata-test ${project.version}</name>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-deploy-plugin</artifactId>
|
||||
<version>2.8.2</version>
|
||||
<configuration>
|
||||
<skip>true</skip>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>${project.groupId}</groupId>
|
||||
<artifactId>seata-tm</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>${project.groupId}</groupId>
|
||||
<artifactId>seata-server</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>${project.groupId}</groupId>
|
||||
<artifactId>seata-rm-datasource</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>${project.groupId}</groupId>
|
||||
<artifactId>seata-saga-engine-store</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>${project.groupId}</groupId>
|
||||
<artifactId>seata-spring</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.h2database</groupId>
|
||||
<artifactId>h2</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-jdbc</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.dubbo</groupId>
|
||||
<artifactId>dubbo</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-core</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-context</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-context-support</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>mysql</groupId>
|
||||
<artifactId>mysql-connector-java</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.postgresql</groupId>
|
||||
<artifactId>postgresql</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.zookeeper</groupId>
|
||||
<artifactId>zookeeper</artifactId>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<artifactId>slf4j-log4j12</artifactId>
|
||||
<groupId>org.slf4j</groupId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<artifactId>log4j</artifactId>
|
||||
<groupId>log4j</groupId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>${project.groupId}</groupId>
|
||||
<artifactId>seata-serializer-seata</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
|
||||
</project>
|
||||
132
test/src/test/java/AppTest.java
Normal file
132
test/src/test/java/AppTest.java
Normal file
@@ -0,0 +1,132 @@
|
||||
/*
|
||||
* Copyright 1999-2019 Seata.io Group.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import io.seata.common.ApplicationKeeper;
|
||||
import io.seata.rm.RMClient;
|
||||
import io.seata.tm.TMClient;
|
||||
import io.seata.tm.api.TransactionalExecutor;
|
||||
import io.seata.tm.api.TransactionalTemplate;
|
||||
import io.seata.tm.api.transaction.TransactionInfo;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.context.support.ClassPathXmlApplicationContext;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
|
||||
/**
|
||||
* The type App test.
|
||||
*
|
||||
* @author sharajava
|
||||
*/
|
||||
public class AppTest {
|
||||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(AppTest.class);
|
||||
|
||||
private static final String APPLICATION_ID = "my_test_app";
|
||||
private static final String TX_SERVICE_GROUP = "my_test_tx_group";
|
||||
private static final String TX_NAME = "my_tx_instance";
|
||||
private static final int TX_TIME_OUT = 60000;
|
||||
|
||||
/**
|
||||
* The entry point of application.
|
||||
*
|
||||
* @param args the input arguments
|
||||
*/
|
||||
public static void main(String[] args) throws Throwable {
|
||||
TMClient.init(APPLICATION_ID, TX_SERVICE_GROUP);
|
||||
RMClient.init(APPLICATION_ID, TX_SERVICE_GROUP);
|
||||
|
||||
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(
|
||||
"basic-test-context.xml");
|
||||
|
||||
final JdbcTemplate jdbcTemplate = (JdbcTemplate) context
|
||||
.getBean("jdbcTemplate");
|
||||
|
||||
jdbcTemplate.update("delete from undo_log");
|
||||
jdbcTemplate.update("delete from user0");
|
||||
jdbcTemplate.update("insert into user0 (id, name, gmt) values (1, 'user0', '2019-01-01')");
|
||||
jdbcTemplate.update("delete from user1");
|
||||
final MyBusinessException bizException = new MyBusinessException("mock bizException");
|
||||
TransactionalTemplate transactionalTemplate = new TransactionalTemplate();
|
||||
try {
|
||||
transactionalTemplate.execute(new TransactionalExecutor() {
|
||||
@Override
|
||||
public Object execute() throws Throwable {
|
||||
if (LOGGER.isInfoEnabled()) {
|
||||
LOGGER.info("Exception Rollback Business Begin ...");
|
||||
}
|
||||
jdbcTemplate.update("update user0 set name = 'xxx' where id = ?", new Object[]{1});
|
||||
jdbcTemplate.update("insert into user1 (id, name, gmt) values (1, 'user1', '2019-01-01')");
|
||||
throw bizException;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TransactionInfo getTransactionInfo() {
|
||||
TransactionInfo txInfo = new TransactionInfo();
|
||||
txInfo.setTimeOut(TX_TIME_OUT);
|
||||
txInfo.setName(TX_NAME);
|
||||
return txInfo;
|
||||
}
|
||||
|
||||
});
|
||||
} catch (TransactionalExecutor.ExecutionException e) {
|
||||
TransactionalExecutor.Code code = e.getCode();
|
||||
if (code == TransactionalExecutor.Code.RollbackDone) {
|
||||
Throwable businessEx = e.getOriginalException();
|
||||
if (businessEx instanceof MyBusinessException) {
|
||||
Assertions.assertEquals(((MyBusinessException) businessEx).getBusinessErrorCode(),
|
||||
bizException.businessErrorCode);
|
||||
}
|
||||
} else {
|
||||
Assertions.assertFalse(false, "Not expected," + e.getMessage());
|
||||
|
||||
}
|
||||
}
|
||||
new ApplicationKeeper(context).keep();
|
||||
|
||||
}
|
||||
|
||||
private static class MyBusinessException extends Exception {
|
||||
|
||||
private String businessErrorCode;
|
||||
|
||||
/**
|
||||
* Gets business error code.
|
||||
*
|
||||
* @return the business error code
|
||||
*/
|
||||
public String getBusinessErrorCode() {
|
||||
return businessErrorCode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets business error code.
|
||||
*
|
||||
* @param businessErrorCode the business error code
|
||||
*/
|
||||
public void setBusinessErrorCode(String businessErrorCode) {
|
||||
this.businessErrorCode = businessErrorCode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiates a new My business exception.
|
||||
*
|
||||
* @param businessErrorCode the business error code
|
||||
*/
|
||||
public MyBusinessException(String businessErrorCode) {
|
||||
this.businessErrorCode = businessErrorCode;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,283 @@
|
||||
/*
|
||||
* Copyright 1999-2019 Seata.io Group.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import io.seata.core.context.RootContext;
|
||||
import io.seata.core.exception.TransactionException;
|
||||
import io.seata.core.model.BranchStatus;
|
||||
import io.seata.core.model.BranchType;
|
||||
import io.seata.core.model.Resource;
|
||||
import io.seata.rm.DefaultResourceManager;
|
||||
import io.seata.rm.RMClient;
|
||||
import io.seata.rm.datasource.DataSourceManager;
|
||||
import io.seata.tm.TMClient;
|
||||
import org.junit.jupiter.api.AfterAll;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.Disabled;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.context.support.ClassPathXmlApplicationContext;
|
||||
import org.springframework.jdbc.UncategorizedSQLException;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* The type Data source basic test.
|
||||
*
|
||||
* @author services
|
||||
*/
|
||||
@Disabled
|
||||
public class LocalTransactionWithGlobalLockDataSourceBasicTest {
|
||||
|
||||
private static ClassPathXmlApplicationContext context;
|
||||
private static JdbcTemplate jdbcTemplate;
|
||||
private static JdbcTemplate directJdbcTemplate;
|
||||
private static final String APPLICATION_ID = "my_test_app";
|
||||
private static final String TX_SERVICE_GROUP = "my_test_tx_group";
|
||||
|
||||
/**
|
||||
* Before.
|
||||
*/
|
||||
@BeforeAll
|
||||
public static void before() {
|
||||
// Mock DataSourceManager
|
||||
initClient();
|
||||
DefaultResourceManager.mockResourceManager(BranchType.AT, new MockDataSourceManager());
|
||||
context = new ClassPathXmlApplicationContext(
|
||||
"basic-test-context.xml");
|
||||
jdbcTemplate = (JdbcTemplate) context
|
||||
.getBean("jdbcTemplate");
|
||||
directJdbcTemplate = (JdbcTemplate) context
|
||||
.getBean("directJdbcTemplate");
|
||||
|
||||
directJdbcTemplate.execute("delete from user0");
|
||||
directJdbcTemplate.execute("delete from user1");
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Test insert.
|
||||
*/
|
||||
@Test
|
||||
public void testInsert() {
|
||||
RootContext.bindGlobalLockFlag();
|
||||
jdbcTemplate.update("insert into user0 (id, name, gmt) values (?, ?, ?)",
|
||||
new Object[]{2, "xxx", new Date()});
|
||||
}
|
||||
|
||||
/**
|
||||
* Test insert.
|
||||
*/
|
||||
@Test
|
||||
public void testInsertWithLock() {
|
||||
RootContext.bindGlobalLockFlag();
|
||||
new AbstractLockConflictExecuteTemplate() {
|
||||
@Override
|
||||
public void doExecute() {
|
||||
jdbcTemplate.update("insert into user0 (id, name, gmt) values (?, ?, ?)",
|
||||
new Object[]{3, "xxx", new Date()});
|
||||
}
|
||||
}.execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* Test update.
|
||||
*/
|
||||
@Test
|
||||
public void testUpdate() {
|
||||
RootContext.bindGlobalLockFlag();
|
||||
jdbcTemplate.update("update user0 a set a.name = 'yyyy' where a.id = ?", new Object[]{1});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUpdateWithLock() {
|
||||
RootContext.bindGlobalLockFlag();
|
||||
RootContext.bindGlobalLockFlag();
|
||||
new AbstractLockConflictExecuteTemplate() {
|
||||
@Override
|
||||
public void doExecute() {
|
||||
jdbcTemplate.update("update user0 a set a.name = 'yyyy' where a.id = ?", new Object[]{1});
|
||||
|
||||
}
|
||||
}.execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* Test update with alias 1.
|
||||
*/
|
||||
@Test
|
||||
public void testUpdateWithAlias1() {
|
||||
|
||||
directJdbcTemplate.update("delete from User1 where Id = ?",
|
||||
new Object[]{1});
|
||||
directJdbcTemplate.update("insert into User1 (Id, Name, gMt) values (?, ?, ?)",
|
||||
new Object[]{1, "xxx", new Date()});
|
||||
|
||||
RootContext.bindGlobalLockFlag();
|
||||
jdbcTemplate.update("update User1 a set a.Name = 'yyy' where a.Name = ?", new Object[]{"xxx"});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUpdateWithAlias1WithLockConflict() {
|
||||
|
||||
directJdbcTemplate.update("delete from User1 where Id = ?",
|
||||
new Object[]{1});
|
||||
directJdbcTemplate.update("insert into User1 (Id, Name, gMt) values (?, ?, ?)",
|
||||
new Object[]{1, "xxx", new Date()});
|
||||
|
||||
RootContext.bindGlobalLockFlag();
|
||||
new AbstractLockConflictExecuteTemplate() {
|
||||
|
||||
@Override
|
||||
public void doExecute() {
|
||||
jdbcTemplate.update("update User1 a set a.Name = 'yyy' where a.Name = ?", new Object[]{"xxx"});
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Test delete.
|
||||
*/
|
||||
@Test
|
||||
public void testDelete() {
|
||||
RootContext.bindGlobalLockFlag();
|
||||
jdbcTemplate.update("delete from user0 where id = ?", new Object[]{2});
|
||||
}
|
||||
|
||||
/**
|
||||
* Test delete.
|
||||
*/
|
||||
@Test
|
||||
public void testDeleteForLockConflict() {
|
||||
RootContext.bindGlobalLockFlag();
|
||||
new AbstractLockConflictExecuteTemplate() {
|
||||
|
||||
@Override
|
||||
public void doExecute() {
|
||||
jdbcTemplate.update("delete from user0 where id = ?", new Object[]{2});
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Test select for update.
|
||||
*/
|
||||
@Test
|
||||
public void testSelectForUpdate() {
|
||||
RootContext.bindGlobalLockFlag();
|
||||
jdbcTemplate.queryForRowSet("select a.name from user0 a where a.id = ? for update", new Object[]{1});
|
||||
}
|
||||
|
||||
/**
|
||||
* Test select for update.
|
||||
*/
|
||||
@Test
|
||||
public void testSelectForUpdateWithLockConflict() {
|
||||
RootContext.bindGlobalLockFlag();
|
||||
new AbstractLockConflictExecuteTemplate() {
|
||||
|
||||
@Override
|
||||
public void doExecute() {
|
||||
jdbcTemplate.queryForRowSet("select a.name from user0 a where a.id = ? for update", new Object[]{1});
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public static class MockDataSourceManager extends DataSourceManager {
|
||||
|
||||
@Override
|
||||
public Long branchRegister(BranchType branchType, String resourceId, String clientId, String xid, String applicationData, String lockKeys)
|
||||
throws TransactionException {
|
||||
throw new RuntimeException("this method should not be called!");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void branchReport(BranchType branchType, String xid, long branchId, BranchStatus status, String applicationData) throws TransactionException {
|
||||
throw new RuntimeException("this method should not be called!");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean lockQuery(BranchType branchType, String resourceId, String xid, String lockKeys)
|
||||
throws TransactionException {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerResource(Resource resource) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unregisterResource(Resource resource) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public BranchStatus branchCommit(BranchType branchType, String xid, long branchId, String resourceId, String applicationData) throws TransactionException {
|
||||
throw new RuntimeException("this method should not be called!");
|
||||
}
|
||||
|
||||
@Override
|
||||
public BranchStatus branchRollback(BranchType branchType, String xid, long branchId, String resourceId, String applicationData)
|
||||
throws TransactionException {
|
||||
throw new RuntimeException("this method should not be called!");
|
||||
}
|
||||
}
|
||||
|
||||
private static void initClient() {
|
||||
TMClient.init(APPLICATION_ID, TX_SERVICE_GROUP);
|
||||
RMClient.init(APPLICATION_ID, TX_SERVICE_GROUP);
|
||||
}
|
||||
|
||||
private abstract static class AbstractLockConflictExecuteTemplate {
|
||||
public void execute() {
|
||||
synchronized (LocalTransactionWithGlobalLockDataSourceBasicTest.class) {
|
||||
DefaultResourceManager.mockResourceManager(BranchType.AT, new MockDataSourceManager() {
|
||||
@Override
|
||||
public boolean lockQuery(BranchType branchType, String resourceId, String xid, String lockKeys)
|
||||
throws TransactionException {
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
boolean exceptionOccour = false;
|
||||
try {
|
||||
doExecute();
|
||||
} catch (UncategorizedSQLException e) {
|
||||
exceptionOccour = true;
|
||||
Assertions.assertTrue(e.getMessage().contains("LockConflict"), "not lock Conflict exception");
|
||||
} finally {
|
||||
DefaultResourceManager.mockResourceManager(BranchType.AT, new MockDataSourceManager());
|
||||
}
|
||||
|
||||
Assertions.assertTrue(exceptionOccour, "Lock Exception not occur!");
|
||||
}
|
||||
}
|
||||
|
||||
public abstract void doExecute();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* After.
|
||||
*/
|
||||
@AfterAll
|
||||
public static void after() {
|
||||
if (context != null) {
|
||||
context.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
81
test/src/test/java/io/seata/common/ApplicationKeeper.java
Normal file
81
test/src/test/java/io/seata/common/ApplicationKeeper.java
Normal file
@@ -0,0 +1,81 @@
|
||||
/*
|
||||
* Copyright 1999-2019 Seata.io Group.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package io.seata.common;
|
||||
|
||||
import java.util.concurrent.locks.Condition;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.context.support.AbstractApplicationContext;
|
||||
|
||||
/**
|
||||
* The type Application keeper.
|
||||
*
|
||||
* @author sharajava
|
||||
*/
|
||||
public class ApplicationKeeper {
|
||||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(ApplicationKeeper.class);
|
||||
|
||||
private final ReentrantLock LOCK = new ReentrantLock();
|
||||
private final Condition STOP = LOCK.newCondition();
|
||||
|
||||
/**
|
||||
* Instantiates a new Application keeper.
|
||||
*
|
||||
* @param applicationContext the application context
|
||||
*/
|
||||
public ApplicationKeeper(AbstractApplicationContext applicationContext) {
|
||||
addShutdownHook(applicationContext);
|
||||
}
|
||||
|
||||
private void addShutdownHook(final AbstractApplicationContext applicationContext) {
|
||||
Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
applicationContext.close();
|
||||
LOGGER.info("ApplicationContext " + applicationContext + " is closed.");
|
||||
} catch (Exception e) {
|
||||
LOGGER.error("Failed to close ApplicationContext", e);
|
||||
}
|
||||
|
||||
try {
|
||||
LOCK.lock();
|
||||
STOP.signal();
|
||||
} finally {
|
||||
LOCK.unlock();
|
||||
}
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
/**
|
||||
* Keep.
|
||||
*/
|
||||
public void keep() {
|
||||
LOCK.lock();
|
||||
try {
|
||||
LOGGER.info("Application is keep running ... ");
|
||||
STOP.await();
|
||||
} catch (InterruptedException e) {
|
||||
LOGGER.error("interrupted error ", e);
|
||||
} finally {
|
||||
LOCK.unlock();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Copyright 1999-2019 Seata.io Group.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package io.seata.common.loader;
|
||||
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* test EnhancedServiceLoader
|
||||
* @author zhangsen
|
||||
*/
|
||||
public class EnhancedServiceLoaderTest {
|
||||
|
||||
@Test
|
||||
public void testLoadBeanByOrder(){
|
||||
LoaderTestSPI loader = EnhancedServiceLoader.load(LoaderTestSPI.class, EnhancedServiceLoaderTest.class.getClassLoader());
|
||||
System.out.println(loader.echo());
|
||||
Assertions.assertEquals("impl_2", loader.echo());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLoadAll(){
|
||||
List<LoaderTestSPI> list = EnhancedServiceLoader.loadAll(LoaderTestSPI.class);
|
||||
Assertions.assertEquals(2, list.size());
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright 1999-2019 Seata.io Group.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package io.seata.common.loader;
|
||||
|
||||
/**
|
||||
* @author zhangsen
|
||||
*/
|
||||
@LoadLevel(name = "one", order = 1)
|
||||
public class LoaderTestImpl1 implements LoaderTestSPI {
|
||||
@Override
|
||||
public String echo() {
|
||||
return "impl_1";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright 1999-2019 Seata.io Group.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package io.seata.common.loader;
|
||||
|
||||
/**
|
||||
* @author zhangsen
|
||||
*/
|
||||
@LoadLevel(name = "two", order = 2)
|
||||
public class LoaderTestImpl2 implements LoaderTestSPI {
|
||||
@Override
|
||||
public String echo() {
|
||||
return "impl_2";
|
||||
}
|
||||
}
|
||||
24
test/src/test/java/io/seata/common/loader/LoaderTestSPI.java
Normal file
24
test/src/test/java/io/seata/common/loader/LoaderTestSPI.java
Normal file
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* Copyright 1999-2019 Seata.io Group.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package io.seata.common.loader;
|
||||
|
||||
/**
|
||||
* @author zhangsen
|
||||
*/
|
||||
public interface LoaderTestSPI {
|
||||
|
||||
String echo();
|
||||
}
|
||||
@@ -0,0 +1,145 @@
|
||||
/*
|
||||
* Copyright 1999-2019 Seata.io Group.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package io.seata.core.rpc.netty;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
import java.util.concurrent.ThreadPoolExecutor;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import io.netty.channel.Channel;
|
||||
import io.seata.core.protocol.ResultCode;
|
||||
import io.seata.core.protocol.transaction.BranchRegisterRequest;
|
||||
import io.seata.core.protocol.transaction.BranchRegisterResponse;
|
||||
import io.seata.server.UUIDGenerator;
|
||||
import io.seata.server.coordinator.DefaultCoordinator;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.Disabled;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
/**
|
||||
* @author slievrly
|
||||
*/
|
||||
@Disabled
|
||||
public class TmNettyClientTest {
|
||||
|
||||
private static final ThreadPoolExecutor
|
||||
workingThreads = new ThreadPoolExecutor(100, 500, 500, TimeUnit.SECONDS,
|
||||
new LinkedBlockingQueue(20000), new ThreadPoolExecutor.CallerRunsPolicy());
|
||||
|
||||
/**
|
||||
* Client rely on server's starting first
|
||||
*
|
||||
* @throws Exception
|
||||
*/
|
||||
@Test
|
||||
public void testDoConnect() throws Exception {
|
||||
|
||||
//start services server first
|
||||
workingThreads.submit(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
NettyRemotingServer nettyRemotingServer = new NettyRemotingServer(workingThreads);
|
||||
nettyRemotingServer.setHandler(new DefaultCoordinator(nettyRemotingServer));
|
||||
UUIDGenerator.init(1L);
|
||||
nettyRemotingServer.init();
|
||||
}
|
||||
});
|
||||
|
||||
//then test client
|
||||
Thread.sleep(3000);
|
||||
|
||||
String applicationId = "app 1";
|
||||
String transactionServiceGroup = "group A";
|
||||
TmNettyRemotingClient tmNettyRemotingClient = TmNettyRemotingClient.getInstance(applicationId, transactionServiceGroup);
|
||||
|
||||
tmNettyRemotingClient.init();
|
||||
|
||||
Method doConnectMethod = TmNettyRemotingClient.class.getDeclaredMethod("doConnect", String.class);
|
||||
doConnectMethod.setAccessible(true);
|
||||
String serverAddress = "0.0.0.0:8091";
|
||||
Channel channel = (Channel) doConnectMethod.invoke(tmNettyRemotingClient, serverAddress);
|
||||
Assertions.assertNotNull(channel);
|
||||
}
|
||||
|
||||
/**
|
||||
* Client rely on server's starting first
|
||||
*
|
||||
* @throws Exception
|
||||
*/
|
||||
@Test
|
||||
public void testReconnect() throws Exception {
|
||||
|
||||
//start services server first
|
||||
workingThreads.submit(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
NettyRemotingServer nettyRemotingServer = new NettyRemotingServer(workingThreads);
|
||||
nettyRemotingServer.setHandler(new DefaultCoordinator(nettyRemotingServer));
|
||||
UUIDGenerator.init(1L);
|
||||
nettyRemotingServer.init();
|
||||
}
|
||||
});
|
||||
|
||||
//then test client
|
||||
Thread.sleep(3000);
|
||||
|
||||
String applicationId = "app 1";
|
||||
String transactionServiceGroup = "my_test_tx_group";
|
||||
TmNettyRemotingClient tmNettyRemotingClient = TmNettyRemotingClient.getInstance(applicationId, transactionServiceGroup);
|
||||
|
||||
tmNettyRemotingClient.init();
|
||||
|
||||
Method doConnectMethod = TmNettyRemotingClient.class.getDeclaredMethod("reconnect");
|
||||
doConnectMethod.setAccessible(true);
|
||||
doConnectMethod.invoke(tmNettyRemotingClient);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSendMsgWithResponse() throws Exception {
|
||||
workingThreads.submit(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
NettyRemotingServer nettyRemotingServer = new NettyRemotingServer(workingThreads);
|
||||
nettyRemotingServer.setHandler(new DefaultCoordinator(nettyRemotingServer));
|
||||
UUIDGenerator.init(1L);
|
||||
nettyRemotingServer.init();
|
||||
}
|
||||
});
|
||||
Thread.sleep(3000);
|
||||
|
||||
String applicationId = "app 1";
|
||||
String transactionServiceGroup = "my_test_tx_group";
|
||||
TmNettyRemotingClient tmNettyRemotingClient = TmNettyRemotingClient.getInstance(applicationId, transactionServiceGroup);
|
||||
tmNettyRemotingClient.init();
|
||||
|
||||
Method doConnectMethod = TmNettyRemotingClient.class.getDeclaredMethod("doConnect", String.class);
|
||||
doConnectMethod.setAccessible(true);
|
||||
String serverAddress = "0.0.0.0:8091";
|
||||
Channel channel = (Channel) doConnectMethod.invoke(tmNettyRemotingClient, serverAddress);
|
||||
Assertions.assertNotNull(channel);
|
||||
|
||||
BranchRegisterRequest request = new BranchRegisterRequest();
|
||||
request.setXid("127.0.0.1:8091:1249853");
|
||||
request.setLockKey("lock key testSendMsgWithResponse");
|
||||
request.setResourceId("resoutceId1");
|
||||
BranchRegisterResponse branchRegisterResponse = (BranchRegisterResponse) tmNettyRemotingClient.sendSyncRequest(request);
|
||||
Assertions.assertNotNull(branchRegisterResponse);
|
||||
Assertions.assertEquals(ResultCode.Failed, branchRegisterResponse.getResultCode());
|
||||
Assertions.assertEquals("RuntimeException[SessionManager is NOT init!]",
|
||||
branchRegisterResponse.getMsg());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* Copyright 1999-2019 Seata.io Group.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package io.seata.core.rpc.netty.v1;
|
||||
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.ChannelInboundHandlerAdapter;
|
||||
import io.netty.util.concurrent.DefaultPromise;
|
||||
import io.seata.core.protocol.RpcMessage;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* @author Geng Zhang
|
||||
*/
|
||||
public class ClientChannelHandler extends ChannelInboundHandlerAdapter {
|
||||
|
||||
/**
|
||||
* Logger for ClientChannelHandler
|
||||
**/
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(ClientChannelHandler.class);
|
||||
|
||||
private ProtocolV1Client client;
|
||||
|
||||
public ClientChannelHandler(ProtocolV1Client client) {
|
||||
this.client = client;
|
||||
}
|
||||
|
||||
public void channelActive(final ChannelHandlerContext ctx) throws Exception {
|
||||
LOGGER.info("Channel active: {}", ctx.channel());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void channelInactive(final ChannelHandlerContext ctx) throws Exception {
|
||||
LOGGER.info("Channel inactive: {}", ctx.channel());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
|
||||
if (msg instanceof RpcMessage) {
|
||||
RpcMessage rpcMessage = (RpcMessage) msg;
|
||||
int msgId = rpcMessage.getId();
|
||||
DefaultPromise future = (DefaultPromise) client.futureMap.remove(msgId);
|
||||
if (future != null) {
|
||||
future.setSuccess(msg);
|
||||
} else {
|
||||
LOGGER.warn("miss msg id:{}", msgId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void exceptionCaught(ChannelHandlerContext ctx, final Throwable cause) throws Exception {
|
||||
LOGGER.warn("", cause);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
/*
|
||||
* Copyright 1999-2019 Seata.io Group.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package io.seata.core.rpc.netty.v1;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.ByteBufAllocator;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author Geng Zhang
|
||||
*/
|
||||
class HeadMapSerializerTest {
|
||||
|
||||
@Test
|
||||
public void encode() throws Exception {
|
||||
HeadMapSerializer simpleMapSerializer = HeadMapSerializer.getInstance();
|
||||
Map<String, String> map = null;
|
||||
int bs = simpleMapSerializer.encode(map, null);
|
||||
Assertions.assertEquals(bs, 0);
|
||||
|
||||
ByteBuf byteBuf = ByteBufAllocator.DEFAULT.heapBuffer();
|
||||
bs = simpleMapSerializer.encode(map, byteBuf);
|
||||
Assertions.assertEquals(bs, 0);
|
||||
|
||||
map = new HashMap<String, String>();
|
||||
bs = simpleMapSerializer.encode(map, byteBuf);
|
||||
Assertions.assertEquals(bs, 0);
|
||||
|
||||
map.put("1", "2");
|
||||
map.put("", "x");
|
||||
map.put("a", "");
|
||||
map.put("b", null);
|
||||
bs = simpleMapSerializer.encode(map, byteBuf);
|
||||
Assertions.assertEquals(21, bs);
|
||||
|
||||
Map<String, String> map1 = simpleMapSerializer.decode(byteBuf, 21);
|
||||
Assertions.assertNotNull(map1);
|
||||
Assertions.assertEquals(4, map1.size());
|
||||
Assertions.assertEquals("2", map1.get("1"));
|
||||
Assertions.assertEquals("x", map1.get(""));
|
||||
Assertions.assertEquals("", map1.get("a"));
|
||||
Assertions.assertEquals(null, map1.get("b"));
|
||||
|
||||
map1 = simpleMapSerializer.decode(byteBuf, 21);
|
||||
Assertions.assertNotNull(map1);
|
||||
Assertions.assertEquals(0, map1.size());
|
||||
|
||||
map1 = simpleMapSerializer.decode(null, 21);
|
||||
Assertions.assertNotNull(map1);
|
||||
Assertions.assertEquals(0, map1.size());
|
||||
|
||||
byteBuf.release();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUTF8() throws Exception {
|
||||
HeadMapSerializer mapSerializer = HeadMapSerializer.getInstance();
|
||||
String s = "test";
|
||||
// utf-8 and gbk same in English
|
||||
Assertions.assertArrayEquals(s.getBytes(StandardCharsets.UTF_8), s.getBytes("GBK"));
|
||||
|
||||
Map<String, String> map = new HashMap<String, String>();
|
||||
map.put("11", "22");
|
||||
map.put("222", "333");
|
||||
ByteBuf byteBuf = ByteBufAllocator.DEFAULT.heapBuffer();
|
||||
int bs = mapSerializer.encode(map, byteBuf);
|
||||
Map newmap = mapSerializer.decode(byteBuf, bs);
|
||||
Assertions.assertEquals(map, newmap);
|
||||
|
||||
// support chinese
|
||||
map.put("你好", "你好?");
|
||||
bs = mapSerializer.encode(map, byteBuf);
|
||||
newmap = mapSerializer.decode(byteBuf, bs);
|
||||
Assertions.assertEquals(map, newmap);
|
||||
|
||||
byteBuf.release();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,197 @@
|
||||
/*
|
||||
* Copyright 1999-2019 Seata.io Group.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package io.seata.core.rpc.netty.v1;
|
||||
|
||||
import io.netty.bootstrap.Bootstrap;
|
||||
import io.netty.buffer.ByteBufAllocator;
|
||||
import io.netty.channel.AdaptiveRecvByteBufAllocator;
|
||||
import io.netty.channel.Channel;
|
||||
import io.netty.channel.ChannelFuture;
|
||||
import io.netty.channel.ChannelInitializer;
|
||||
import io.netty.channel.ChannelOption;
|
||||
import io.netty.channel.ChannelPipeline;
|
||||
import io.netty.channel.EventLoopGroup;
|
||||
import io.netty.channel.nio.NioEventLoopGroup;
|
||||
import io.netty.channel.socket.nio.NioSocketChannel;
|
||||
import io.netty.util.concurrent.DefaultEventExecutor;
|
||||
import io.netty.util.concurrent.DefaultPromise;
|
||||
import io.seata.common.thread.NamedThreadFactory;
|
||||
import io.seata.common.thread.PositiveAtomicCounter;
|
||||
import io.seata.core.serializer.SerializerType;
|
||||
import io.seata.core.model.BranchType;
|
||||
import io.seata.core.protocol.ProtocolConstants;
|
||||
import io.seata.core.protocol.RpcMessage;
|
||||
import io.seata.core.protocol.transaction.BranchCommitRequest;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.SynchronousQueue;
|
||||
import java.util.concurrent.ThreadPoolExecutor;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
|
||||
/**
|
||||
* @author Geng Zhang
|
||||
*/
|
||||
public class ProtocolV1Client {
|
||||
|
||||
/**
|
||||
* Logger for ProtocolV1Client
|
||||
**/
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(ProtocolV1Client.class);
|
||||
|
||||
Channel channel;
|
||||
|
||||
PositiveAtomicCounter idGenerator = new PositiveAtomicCounter();
|
||||
|
||||
Map<Integer, Future> futureMap = new ConcurrentHashMap<>();
|
||||
|
||||
private EventLoopGroup eventLoopGroup = createWorkerGroup();
|
||||
|
||||
private DefaultEventExecutor defaultEventExecutor = new DefaultEventExecutor(eventLoopGroup);
|
||||
|
||||
public void connect(String host, int port, int connectTimeout) {
|
||||
|
||||
Bootstrap bootstrap = new Bootstrap();
|
||||
bootstrap.group(eventLoopGroup).channel(NioSocketChannel.class);
|
||||
bootstrap.option(ChannelOption.SO_KEEPALIVE, true);
|
||||
bootstrap.option(ChannelOption.ALLOCATOR, ByteBufAllocator.DEFAULT);
|
||||
bootstrap.option(ChannelOption.WRITE_BUFFER_HIGH_WATER_MARK, 32 * 1024);
|
||||
bootstrap.option(ChannelOption.WRITE_BUFFER_LOW_WATER_MARK, 8 * 1024);
|
||||
bootstrap.option(ChannelOption.RCVBUF_ALLOCATOR, AdaptiveRecvByteBufAllocator.DEFAULT);
|
||||
bootstrap.handler(new ChannelInitializer() {
|
||||
@Override
|
||||
protected void initChannel(Channel channel) throws Exception {
|
||||
ChannelPipeline pipeline = channel.pipeline();
|
||||
pipeline.addLast(new ProtocolV1Encoder());
|
||||
pipeline.addLast(new ProtocolV1Decoder(8 * 1024 * 1024));
|
||||
pipeline.addLast(new ClientChannelHandler(ProtocolV1Client.this));
|
||||
}
|
||||
});
|
||||
// Bind and start to accept incoming connections.
|
||||
ChannelFuture channelFuture = bootstrap.connect(host, port);
|
||||
channelFuture.awaitUninterruptibly(connectTimeout, TimeUnit.MILLISECONDS);
|
||||
if (channelFuture.isSuccess()) {
|
||||
channel = channelFuture.channel();
|
||||
} else {
|
||||
Throwable cause = channelFuture.cause();
|
||||
throw new RuntimeException("Failed to connect " + host + ":" + port +
|
||||
(cause != null ? ". Cause by: " + cause.getMessage() : "."));
|
||||
}
|
||||
}
|
||||
|
||||
private EventLoopGroup createWorkerGroup() {
|
||||
NamedThreadFactory threadName =
|
||||
new NamedThreadFactory("CLI-WORKER", false);
|
||||
return new NioEventLoopGroup(10, threadName);
|
||||
}
|
||||
|
||||
public void close() {
|
||||
if (channel != null) {
|
||||
channel.close();
|
||||
}
|
||||
if (eventLoopGroup != null) {
|
||||
eventLoopGroup.shutdownGracefully();
|
||||
}
|
||||
channel = null;
|
||||
}
|
||||
|
||||
public Future sendRpc(Map<String, String> head, Object body) {
|
||||
int msgId = idGenerator.incrementAndGet();
|
||||
|
||||
RpcMessage rpcMessage = new RpcMessage();
|
||||
rpcMessage.setId(msgId);
|
||||
rpcMessage.setCodec(SerializerType.SEATA.getCode());
|
||||
rpcMessage.setCompressor(ProtocolConstants.CONFIGURED_COMPRESSOR);
|
||||
rpcMessage.setHeadMap(head);
|
||||
rpcMessage.setBody(body);
|
||||
rpcMessage.setMessageType(ProtocolConstants.MSGTYPE_RESQUEST_SYNC);
|
||||
|
||||
if (channel != null) {
|
||||
DefaultPromise promise = new DefaultPromise(defaultEventExecutor);
|
||||
futureMap.put(msgId, promise);
|
||||
channel.writeAndFlush(rpcMessage);
|
||||
return promise;
|
||||
} else {
|
||||
LOGGER.warn("channel is null");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// can test tps
|
||||
public static void main(String[] args) {
|
||||
ProtocolV1Client client = new ProtocolV1Client();
|
||||
client.connect("127.0.0.1", 8811, 500);
|
||||
|
||||
Map<String, String> head = new HashMap<>();
|
||||
head.put("tracerId", "xxadadadada");
|
||||
head.put("token", "adadadad");
|
||||
|
||||
BranchCommitRequest body = new BranchCommitRequest();
|
||||
body.setBranchId(12345L);
|
||||
body.setApplicationData("application");
|
||||
body.setBranchType(BranchType.AT);
|
||||
body.setResourceId("resource-1234");
|
||||
body.setXid("xid-1234");
|
||||
|
||||
final int threads = 50;
|
||||
final AtomicLong cnt = new AtomicLong(0);
|
||||
// no queue
|
||||
final ThreadPoolExecutor service1 = new ThreadPoolExecutor(threads, threads, 0L, TimeUnit.MILLISECONDS,
|
||||
new SynchronousQueue<Runnable>(), new NamedThreadFactory("client-", false));
|
||||
for (int i = 0; i < threads; i++) {
|
||||
service1.execute(() -> {
|
||||
while (true) {
|
||||
try {
|
||||
Future future = client.sendRpc(head, body);
|
||||
RpcMessage resp = (RpcMessage) future.get(200, TimeUnit.MILLISECONDS);
|
||||
if (resp != null) {
|
||||
cnt.incrementAndGet();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Thread thread = new Thread(new Runnable() {
|
||||
private long last = 0;
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
while (true) {
|
||||
long count = cnt.get();
|
||||
long tps = count - last;
|
||||
LOGGER.error("last 1s invoke: {}, queue: {}", tps, service1.getQueue().size());
|
||||
last = count;
|
||||
|
||||
try {
|
||||
Thread.sleep(1000);
|
||||
} catch (InterruptedException e) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}, "Print-tps-THREAD");
|
||||
thread.start();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
/*
|
||||
* Copyright 1999-2019 Seata.io Group.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package io.seata.core.rpc.netty.v1;
|
||||
|
||||
import io.seata.common.thread.NamedThreadFactory;
|
||||
import io.seata.core.model.BranchType;
|
||||
import io.seata.core.protocol.RpcMessage;
|
||||
import io.seata.core.protocol.transaction.BranchCommitRequest;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.SynchronousQueue;
|
||||
import java.util.concurrent.ThreadPoolExecutor;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:zhanggeng.zg@antfin.com">GengZhang</a>
|
||||
*/
|
||||
public class ProtocolV1SerializerTest {
|
||||
|
||||
/**
|
||||
* Logger for ProtocolV1CodecTest
|
||||
**/
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(ProtocolV1SerializerTest.class);
|
||||
|
||||
@Test
|
||||
public void testAll() {
|
||||
ProtocolV1Server server = new ProtocolV1Server();
|
||||
ProtocolV1Client client = new ProtocolV1Client();
|
||||
try {
|
||||
server.start();
|
||||
client.connect("127.0.0.1", 8811, 500);
|
||||
|
||||
Assertions.assertTrue(client.channel.isActive());
|
||||
|
||||
Map<String, String> head = new HashMap<>();
|
||||
head.put("tracerId", "xxadadadada");
|
||||
head.put("token", "adadadad");
|
||||
head.put("hello", null);
|
||||
|
||||
BranchCommitRequest body = new BranchCommitRequest();
|
||||
body.setBranchId(12345L);
|
||||
body.setApplicationData("application");
|
||||
body.setBranchType(BranchType.AT);
|
||||
body.setResourceId("resource-1234");
|
||||
body.setXid("xid-1234");
|
||||
|
||||
// test run times
|
||||
int runTimes = 100000;
|
||||
|
||||
final int threads = 50;
|
||||
final CountDownLatch cnt = new CountDownLatch(runTimes);
|
||||
final AtomicInteger tag = new AtomicInteger(0);
|
||||
final AtomicInteger success = new AtomicInteger(0);
|
||||
// no queue
|
||||
final ThreadPoolExecutor service1 = new ThreadPoolExecutor(threads, threads, 0L, TimeUnit.MILLISECONDS,
|
||||
new SynchronousQueue<>(), new NamedThreadFactory("client-", false));
|
||||
for (int i = 0; i < threads; i++) {
|
||||
service1.execute(() -> {
|
||||
while (tag.getAndIncrement() < runTimes) {
|
||||
try {
|
||||
Future future = client.sendRpc(head, body);
|
||||
RpcMessage resp = (RpcMessage) future.get(10, TimeUnit.SECONDS);
|
||||
if (resp != null) {
|
||||
success.incrementAndGet();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
LOGGER.error("Client send error", e);
|
||||
} finally {
|
||||
cnt.countDown();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
cnt.await();
|
||||
LOGGER.info("success {}/{}", success.get(), runTimes);
|
||||
Assertions.assertEquals(success.get(), runTimes);
|
||||
} catch (InterruptedException e) {
|
||||
LOGGER.error("Thread interrupted", e);
|
||||
} finally {
|
||||
client.close();
|
||||
server.stop();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,122 @@
|
||||
/*
|
||||
* Copyright 1999-2019 Seata.io Group.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package io.seata.core.rpc.netty.v1;
|
||||
|
||||
import io.netty.bootstrap.ServerBootstrap;
|
||||
import io.netty.buffer.ByteBufAllocator;
|
||||
import io.netty.channel.AdaptiveRecvByteBufAllocator;
|
||||
import io.netty.channel.Channel;
|
||||
import io.netty.channel.ChannelFuture;
|
||||
import io.netty.channel.ChannelFutureListener;
|
||||
import io.netty.channel.ChannelInitializer;
|
||||
import io.netty.channel.ChannelOption;
|
||||
import io.netty.channel.ChannelPipeline;
|
||||
import io.netty.channel.EventLoopGroup;
|
||||
import io.netty.channel.WriteBufferWaterMark;
|
||||
import io.netty.channel.nio.NioEventLoopGroup;
|
||||
import io.netty.channel.socket.nio.NioServerSocketChannel;
|
||||
import io.netty.handler.logging.LogLevel;
|
||||
import io.netty.handler.logging.LoggingHandler;
|
||||
import io.seata.common.thread.NamedThreadFactory;
|
||||
|
||||
import java.net.InetSocketAddress;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* @author Geng Zhang
|
||||
*/
|
||||
public class ProtocolV1Server {
|
||||
|
||||
private int port = 8811;
|
||||
private ServerBootstrap serverBootstrap;
|
||||
|
||||
private EventLoopGroup bossGroup;
|
||||
private EventLoopGroup workerGroup;
|
||||
|
||||
public void start() {
|
||||
|
||||
bossGroup = createBossGroup();
|
||||
workerGroup = createWorkerGroup();
|
||||
|
||||
serverBootstrap = new ServerBootstrap().group(bossGroup, workerGroup)
|
||||
.channel(NioServerSocketChannel.class)
|
||||
.option(ChannelOption.RCVBUF_ALLOCATOR, AdaptiveRecvByteBufAllocator.DEFAULT)
|
||||
.option(ChannelOption.ALLOCATOR, ByteBufAllocator.DEFAULT)
|
||||
.childOption(ChannelOption.SO_KEEPALIVE, true)
|
||||
.childOption(ChannelOption.TCP_NODELAY, true)
|
||||
.childOption(ChannelOption.SO_RCVBUF, 8192 * 128)
|
||||
.childOption(ChannelOption.SO_SNDBUF, 8192 * 128)
|
||||
.handler(new LoggingHandler(LogLevel.DEBUG))
|
||||
.childOption(ChannelOption.ALLOCATOR, ByteBufAllocator.DEFAULT)
|
||||
.childOption(ChannelOption.WRITE_BUFFER_WATER_MARK, new WriteBufferWaterMark(
|
||||
8192, 31768))
|
||||
.childHandler(new ChannelInitializer() {
|
||||
@Override
|
||||
protected void initChannel(Channel channel) throws Exception {
|
||||
ChannelPipeline pipeline = channel.pipeline();
|
||||
pipeline.addLast(new ProtocolV1Decoder(8 * 1024 * 1024));
|
||||
pipeline.addLast(new ProtocolV1Encoder());
|
||||
pipeline.addLast(new ServerChannelHandler());
|
||||
}
|
||||
});
|
||||
|
||||
String host = "0.0.0.0";
|
||||
|
||||
ChannelFuture future = serverBootstrap.bind(new InetSocketAddress(host, port));
|
||||
ChannelFuture channelFuture = future.addListener(new ChannelFutureListener() {
|
||||
|
||||
@Override
|
||||
public void operationComplete(ChannelFuture future) throws Exception {
|
||||
if (!future.isSuccess()) {
|
||||
throw new RuntimeException("Server start fail !", future.cause());
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
try {
|
||||
channelFuture.await(5000, TimeUnit.MILLISECONDS);
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public void stop() {
|
||||
if (bossGroup != null) {
|
||||
bossGroup.shutdownGracefully();
|
||||
}
|
||||
if (workerGroup != null) {
|
||||
workerGroup.shutdownGracefully();
|
||||
}
|
||||
serverBootstrap = null;
|
||||
}
|
||||
|
||||
private EventLoopGroup createBossGroup() {
|
||||
NamedThreadFactory threadName =
|
||||
new NamedThreadFactory("SEV-BOSS-" + port, false);
|
||||
return new NioEventLoopGroup(2, threadName);
|
||||
}
|
||||
|
||||
private EventLoopGroup createWorkerGroup() {
|
||||
NamedThreadFactory threadName =
|
||||
new NamedThreadFactory("SEV-WORKER-" + port, false);
|
||||
return new NioEventLoopGroup(10, threadName);
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
ProtocolV1Server server = new ProtocolV1Server();
|
||||
server.start();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* Copyright 1999-2019 Seata.io Group.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package io.seata.core.rpc.netty.v1;
|
||||
|
||||
import io.netty.channel.Channel;
|
||||
import io.netty.channel.ChannelHandler;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.ChannelInboundHandlerAdapter;
|
||||
import io.seata.core.protocol.ProtocolConstants;
|
||||
import io.seata.core.protocol.RpcMessage;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* @author Geng Zhang
|
||||
*/
|
||||
@ChannelHandler.Sharable
|
||||
public class ServerChannelHandler extends ChannelInboundHandlerAdapter {
|
||||
|
||||
/**
|
||||
* Logger for ServerChannelHandler
|
||||
**/
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(ServerChannelHandler.class);
|
||||
|
||||
@Override
|
||||
public void channelRead(ChannelHandlerContext ctx, Object msg) {
|
||||
Channel channel = ctx.channel();
|
||||
|
||||
if (msg instanceof RpcMessage) {
|
||||
((RpcMessage) msg).setMessageType(ProtocolConstants.MSGTYPE_RESPONSE);
|
||||
}
|
||||
|
||||
channel.writeAndFlush(msg);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void channelActive(ChannelHandlerContext ctx) throws Exception {
|
||||
LOGGER.info(ctx.channel().remoteAddress() + " connected!");
|
||||
ctx.fireChannelActive();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
|
||||
LOGGER.info(ctx.channel().remoteAddress() + " disconnected!");
|
||||
ctx.fireChannelInactive();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,303 @@
|
||||
/*
|
||||
* Copyright 1999-2019 Seata.io Group.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package io.seata.saga.engine;
|
||||
|
||||
import io.seata.saga.engine.mock.DemoService.People;
|
||||
import io.seata.saga.proctrl.ProcessContext;
|
||||
import io.seata.saga.statelang.domain.ExecutionStatus;
|
||||
import io.seata.saga.statelang.domain.StateMachineInstance;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.support.ClassPathXmlApplicationContext;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* State machine async tests
|
||||
* @author lorne.cl
|
||||
*/
|
||||
public class StateMachineAsyncTests {
|
||||
|
||||
private static StateMachineEngine stateMachineEngine;
|
||||
|
||||
@BeforeAll
|
||||
public static void initApplicationContext() {
|
||||
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:saga/spring/statemachine_engine_test.xml");
|
||||
stateMachineEngine = applicationContext.getBean("stateMachineEngine", StateMachineEngine.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSimpleCatchesStateMachine() {
|
||||
|
||||
long start = System.currentTimeMillis();
|
||||
|
||||
Map<String, Object> paramMap = new HashMap<>(1);
|
||||
paramMap.put("a", 1);
|
||||
paramMap.put("barThrowException", "true");
|
||||
|
||||
String stateMachineName = "simpleCachesStateMachine";
|
||||
|
||||
StateMachineInstance inst = stateMachineEngine.startAsync(stateMachineName, null, paramMap, callback);
|
||||
|
||||
waittingForFinish(inst);
|
||||
|
||||
long cost = System.currentTimeMillis() - start;
|
||||
System.out.println("====== cost :" + cost);
|
||||
|
||||
Assertions.assertNotNull(inst.getException());
|
||||
Assertions.assertTrue(ExecutionStatus.FA.equals(inst.getStatus()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSimpleScriptTaskStateMachine() {
|
||||
|
||||
long start = System.currentTimeMillis();
|
||||
|
||||
Map<String, Object> paramMap = new HashMap<>(1);
|
||||
paramMap.put("a", 1);
|
||||
|
||||
String stateMachineName = "simpleScriptTaskStateMachine";
|
||||
|
||||
StateMachineInstance inst = stateMachineEngine.start(stateMachineName, null, paramMap);
|
||||
|
||||
waittingForFinish(inst);
|
||||
|
||||
long cost = System.currentTimeMillis() - start;
|
||||
System.out.println("====== cost :" + cost);
|
||||
|
||||
Assertions.assertTrue(ExecutionStatus.SU.equals(inst.getStatus()));
|
||||
Assertions.assertNotNull(inst.getEndParams().get("scriptStateResult"));
|
||||
|
||||
|
||||
start = System.currentTimeMillis();
|
||||
|
||||
inst = stateMachineEngine.start(stateMachineName, null, paramMap);
|
||||
|
||||
waittingForFinish(inst);
|
||||
|
||||
cost = System.currentTimeMillis() - start;
|
||||
System.out.println("====== cost :" + cost);
|
||||
|
||||
Assertions.assertTrue(ExecutionStatus.SU.equals(inst.getStatus()));
|
||||
|
||||
|
||||
start = System.currentTimeMillis();
|
||||
paramMap.put("scriptThrowException", true);
|
||||
inst = stateMachineEngine.start(stateMachineName, null, paramMap);
|
||||
|
||||
waittingForFinish(inst);
|
||||
|
||||
cost = System.currentTimeMillis() - start;
|
||||
System.out.println("====== cost :" + cost);
|
||||
|
||||
Assertions.assertTrue(ExecutionStatus.FA.equals(inst.getStatus()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSimpleRetryStateMachine() {
|
||||
|
||||
long start = System.currentTimeMillis();
|
||||
|
||||
Map<String, Object> paramMap = new HashMap<>(1);
|
||||
paramMap.put("a", 1);
|
||||
paramMap.put("barThrowException", "true");
|
||||
|
||||
String stateMachineName = "simpleRetryStateMachine";
|
||||
|
||||
StateMachineInstance inst = stateMachineEngine.startAsync(stateMachineName, null, paramMap, callback);
|
||||
|
||||
waittingForFinish(inst);
|
||||
|
||||
long cost = System.currentTimeMillis() - start;
|
||||
System.out.println("====== cost :" + cost);
|
||||
|
||||
|
||||
Assertions.assertNotNull(inst.getException());
|
||||
Assertions.assertTrue(ExecutionStatus.FA.equals(inst.getStatus()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStatusMatchingStateMachine() {
|
||||
|
||||
long start = System.currentTimeMillis();
|
||||
|
||||
Map<String, Object> paramMap = new HashMap<>(1);
|
||||
paramMap.put("a", 1);
|
||||
paramMap.put("barThrowException", "true");
|
||||
|
||||
String stateMachineName = "simpleStatusMatchingStateMachine";
|
||||
|
||||
StateMachineInstance inst = stateMachineEngine.startAsync(stateMachineName, null, paramMap, callback);
|
||||
|
||||
waittingForFinish(inst);
|
||||
|
||||
long cost = System.currentTimeMillis() - start;
|
||||
System.out.println("====== cost :" + cost);
|
||||
|
||||
Assertions.assertNotNull(inst.getException());
|
||||
Assertions.assertTrue(ExecutionStatus.UN.equals(inst.getStatus()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCompensationStateMachine() {
|
||||
|
||||
long start = System.currentTimeMillis();
|
||||
|
||||
Map<String, Object> paramMap = new HashMap<>(1);
|
||||
paramMap.put("a", 1);
|
||||
paramMap.put("barThrowException", "true");
|
||||
|
||||
String stateMachineName = "simpleCompensationStateMachine";
|
||||
|
||||
StateMachineInstance inst = stateMachineEngine.startAsync(stateMachineName, null, paramMap, callback);
|
||||
|
||||
waittingForFinish(inst);
|
||||
|
||||
long cost = System.currentTimeMillis() - start;
|
||||
System.out.println("====== cost :" + cost);
|
||||
|
||||
Assertions.assertTrue(ExecutionStatus.UN.equals(inst.getStatus()));
|
||||
Assertions.assertTrue(ExecutionStatus.SU.equals(inst.getCompensationStatus()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCompensationAndSubStateMachine() {
|
||||
|
||||
long start = System.currentTimeMillis();
|
||||
|
||||
Map<String, Object> paramMap = new HashMap<>(1);
|
||||
paramMap.put("a", 2);
|
||||
paramMap.put("barThrowException", "true");
|
||||
|
||||
String stateMachineName = "simpleStateMachineWithCompensationAndSubMachine";
|
||||
|
||||
StateMachineInstance inst = stateMachineEngine.startAsync(stateMachineName, null, paramMap, callback);
|
||||
|
||||
waittingForFinish(inst);
|
||||
|
||||
long cost = System.currentTimeMillis() - start;
|
||||
System.out.println("====== cost :" + cost);
|
||||
|
||||
Assertions.assertTrue(ExecutionStatus.UN.equals(inst.getStatus()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCompensationAndSubStateMachineWithLayout() {
|
||||
|
||||
long start = System.currentTimeMillis();
|
||||
|
||||
Map<String, Object> paramMap = new HashMap<>(1);
|
||||
paramMap.put("a", 2);
|
||||
paramMap.put("barThrowException", "true");
|
||||
|
||||
String stateMachineName = "simpleStateMachineWithCompensationAndSubMachine_layout";
|
||||
|
||||
StateMachineInstance inst = stateMachineEngine.startAsync(stateMachineName, null, paramMap, callback);
|
||||
|
||||
waittingForFinish(inst);
|
||||
|
||||
long cost = System.currentTimeMillis() - start;
|
||||
System.out.println("====== cost :" + cost);
|
||||
|
||||
Assertions.assertTrue(ExecutionStatus.UN.equals(inst.getStatus()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStateMachineWithComplexParams() {
|
||||
|
||||
long start = System.currentTimeMillis();
|
||||
|
||||
Map<String, Object> paramMap = new HashMap<>(1);
|
||||
People people = new People();
|
||||
people.setName("lilei");
|
||||
people.setAge(18);
|
||||
paramMap.put("people", people);
|
||||
|
||||
String stateMachineName = "simpleStateMachineWithComplexParams";
|
||||
|
||||
StateMachineInstance inst = stateMachineEngine.startAsync(stateMachineName, null, paramMap, callback);
|
||||
|
||||
waittingForFinish(inst);
|
||||
|
||||
long cost = System.currentTimeMillis() - start;
|
||||
|
||||
People peopleResult = (People) inst.getEndParams().get("complexParameterMethodResult");
|
||||
Assertions.assertNotNull(peopleResult);
|
||||
Assertions.assertTrue(people.getName().equals(people.getName()));
|
||||
|
||||
System.out.println("====== cost :" + cost);
|
||||
|
||||
Assertions.assertTrue(ExecutionStatus.SU.equals(inst.getStatus()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSimpleStateMachineWithAsyncState() {
|
||||
|
||||
long start = System.currentTimeMillis();
|
||||
|
||||
Map<String, Object> paramMap = new HashMap<>(1);
|
||||
paramMap.put("a", 1);
|
||||
|
||||
String stateMachineName = "simpleStateMachineWithAsyncState";
|
||||
|
||||
StateMachineInstance inst = stateMachineEngine.startAsync(stateMachineName, null, paramMap, callback);
|
||||
|
||||
waittingForFinish(inst);
|
||||
|
||||
long cost = System.currentTimeMillis() - start;
|
||||
System.out.println("====== cost :" + cost);
|
||||
|
||||
Assertions.assertTrue(ExecutionStatus.SU.equals(inst.getStatus()));
|
||||
|
||||
try {
|
||||
Thread.sleep(500);
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
private void waittingForFinish(StateMachineInstance inst) {
|
||||
synchronized (lock) {
|
||||
if (ExecutionStatus.RU.equals(inst.getStatus())) {
|
||||
try {
|
||||
lock.wait();
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private volatile Object lock = new Object();
|
||||
private AsyncCallback callback = new AsyncCallback() {
|
||||
@Override
|
||||
public void onFinished(ProcessContext context, StateMachineInstance stateMachineInstance) {
|
||||
synchronized (lock) {
|
||||
lock.notifyAll();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(ProcessContext context, StateMachineInstance stateMachineInstance, Exception exp) {
|
||||
synchronized (lock) {
|
||||
lock.notifyAll();
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
364
test/src/test/java/io/seata/saga/engine/StateMachineTests.java
Normal file
364
test/src/test/java/io/seata/saga/engine/StateMachineTests.java
Normal file
@@ -0,0 +1,364 @@
|
||||
/*
|
||||
* Copyright 1999-2019 Seata.io Group.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package io.seata.saga.engine;
|
||||
|
||||
import io.seata.saga.engine.mock.DemoService.Engineer;
|
||||
import io.seata.saga.engine.mock.DemoService.People;
|
||||
import io.seata.saga.statelang.domain.DomainConstants;
|
||||
import io.seata.saga.statelang.domain.ExecutionStatus;
|
||||
import io.seata.saga.statelang.domain.StateMachineInstance;
|
||||
import io.seata.saga.statelang.parser.JsonParserFactory;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.support.ClassPathXmlApplicationContext;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* State machine tests
|
||||
* @author lorne.cl
|
||||
*/
|
||||
public class StateMachineTests {
|
||||
|
||||
private static StateMachineEngine stateMachineEngine;
|
||||
|
||||
@BeforeAll
|
||||
public static void initApplicationContext() {
|
||||
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:saga/spring/statemachine_engine_test.xml");
|
||||
stateMachineEngine = applicationContext.getBean("stateMachineEngine", StateMachineEngine.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSimpleStateMachine() {
|
||||
|
||||
stateMachineEngine.start("simpleTestStateMachine", null, new HashMap<>());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSimpleStateMachineWithChoice() {
|
||||
|
||||
long start = System.currentTimeMillis();
|
||||
|
||||
Map<String, Object> paramMap = new HashMap<>();
|
||||
paramMap.put("a", 1);
|
||||
|
||||
String stateMachineName = "simpleChoiceTestStateMachine";
|
||||
|
||||
stateMachineEngine.start(stateMachineName, null, paramMap);
|
||||
|
||||
long cost = System.currentTimeMillis() - start;
|
||||
System.out.println("====== cost :" + cost);
|
||||
|
||||
start = System.currentTimeMillis();
|
||||
paramMap.put("a", 2);
|
||||
stateMachineEngine.start(stateMachineName, null, paramMap);
|
||||
|
||||
cost = System.currentTimeMillis() - start;
|
||||
System.out.println("====== cost :" + cost);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSimpleStateMachineWithChoiceAndEnd() {
|
||||
|
||||
long start = System.currentTimeMillis();
|
||||
|
||||
Map<String, Object> paramMap = new HashMap<>(1);
|
||||
paramMap.put("a", 1);
|
||||
|
||||
String stateMachineName = "simpleChoiceAndEndTestStateMachine";
|
||||
|
||||
stateMachineEngine.start(stateMachineName, null, paramMap);
|
||||
|
||||
long cost = System.currentTimeMillis() - start;
|
||||
System.out.println("====== cost :" + cost);
|
||||
|
||||
start = System.currentTimeMillis();
|
||||
|
||||
paramMap.put("a", 3);
|
||||
stateMachineEngine.start(stateMachineName, null, paramMap);
|
||||
|
||||
cost = System.currentTimeMillis() - start;
|
||||
System.out.println("====== cost :" + cost);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSimpleInputAssignmentStateMachine() {
|
||||
|
||||
long start = System.currentTimeMillis();
|
||||
|
||||
Map<String, Object> paramMap = new HashMap<>(1);
|
||||
paramMap.put("a", 1);
|
||||
|
||||
String stateMachineName = "simpleInputAssignmentStateMachine";
|
||||
|
||||
StateMachineInstance instance = stateMachineEngine.start(stateMachineName, null, paramMap);
|
||||
|
||||
String businessKey = instance.getStateList().get(0).getBusinessKey();
|
||||
Assertions.assertNotNull(businessKey);
|
||||
System.out.println("====== businessKey :" + businessKey);
|
||||
|
||||
String contextBusinessKey = (String) instance.getEndParams().get(
|
||||
instance.getStateList().get(0).getName() + DomainConstants.VAR_NAME_BUSINESSKEY);
|
||||
Assertions.assertNotNull(contextBusinessKey);
|
||||
System.out.println("====== context businessKey :" + businessKey);
|
||||
|
||||
long cost = System.currentTimeMillis() - start;
|
||||
System.out.println("====== cost :" + cost);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSimpleCatchesStateMachine() {
|
||||
|
||||
long start = System.currentTimeMillis();
|
||||
|
||||
Map<String, Object> paramMap = new HashMap<>(1);
|
||||
paramMap.put("a", 1);
|
||||
paramMap.put("barThrowException", "true");
|
||||
|
||||
String stateMachineName = "simpleCachesStateMachine";
|
||||
|
||||
StateMachineInstance inst = stateMachineEngine.start(stateMachineName, null, paramMap);
|
||||
|
||||
long cost = System.currentTimeMillis() - start;
|
||||
System.out.println("====== cost :" + cost);
|
||||
|
||||
Assertions.assertNotNull(inst.getException());
|
||||
Assertions.assertTrue(ExecutionStatus.FA.equals(inst.getStatus()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSimpleScriptTaskStateMachine() {
|
||||
|
||||
long start = System.currentTimeMillis();
|
||||
|
||||
Map<String, Object> paramMap = new HashMap<>(1);
|
||||
paramMap.put("a", 1);
|
||||
|
||||
String stateMachineName = "simpleScriptTaskStateMachine";
|
||||
|
||||
StateMachineInstance inst = stateMachineEngine.start(stateMachineName, null, paramMap);
|
||||
|
||||
long cost = System.currentTimeMillis() - start;
|
||||
System.out.println("====== cost :" + cost);
|
||||
|
||||
Assertions.assertTrue(ExecutionStatus.SU.equals(inst.getStatus()));
|
||||
Assertions.assertNotNull(inst.getEndParams().get("scriptStateResult"));
|
||||
|
||||
|
||||
start = System.currentTimeMillis();
|
||||
|
||||
inst = stateMachineEngine.start(stateMachineName, null, paramMap);
|
||||
|
||||
cost = System.currentTimeMillis() - start;
|
||||
System.out.println("====== cost :" + cost);
|
||||
|
||||
Assertions.assertTrue(ExecutionStatus.SU.equals(inst.getStatus()));
|
||||
|
||||
|
||||
start = System.currentTimeMillis();
|
||||
paramMap.put("scriptThrowException", true);
|
||||
inst = stateMachineEngine.start(stateMachineName, null, paramMap);
|
||||
|
||||
cost = System.currentTimeMillis() - start;
|
||||
System.out.println("====== cost :" + cost);
|
||||
|
||||
Assertions.assertTrue(ExecutionStatus.FA.equals(inst.getStatus()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSimpleRetryStateMachine() {
|
||||
|
||||
long start = System.currentTimeMillis();
|
||||
|
||||
Map<String, Object> paramMap = new HashMap<>(1);
|
||||
paramMap.put("a", 1);
|
||||
paramMap.put("barThrowException", "true");
|
||||
|
||||
String stateMachineName = "simpleRetryStateMachine";
|
||||
|
||||
StateMachineInstance inst = stateMachineEngine.start(stateMachineName, null, paramMap);
|
||||
|
||||
long cost = System.currentTimeMillis() - start;
|
||||
System.out.println("====== cost :" + cost);
|
||||
|
||||
Assertions.assertNotNull(inst.getException());
|
||||
Assertions.assertTrue(ExecutionStatus.FA.equals(inst.getStatus()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStatusMatchingStateMachine() {
|
||||
|
||||
long start = System.currentTimeMillis();
|
||||
|
||||
Map<String, Object> paramMap = new HashMap<>(1);
|
||||
paramMap.put("a", 1);
|
||||
paramMap.put("barThrowException", "true");
|
||||
|
||||
String stateMachineName = "simpleStatusMatchingStateMachine";
|
||||
|
||||
StateMachineInstance inst = stateMachineEngine.start(stateMachineName, null, paramMap);
|
||||
|
||||
long cost = System.currentTimeMillis() - start;
|
||||
System.out.println("====== cost :" + cost);
|
||||
|
||||
Assertions.assertNotNull(inst.getException());
|
||||
Assertions.assertTrue(ExecutionStatus.UN.equals(inst.getStatus()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCompensationStateMachine() {
|
||||
|
||||
long start = System.currentTimeMillis();
|
||||
|
||||
Map<String, Object> paramMap = new HashMap<>(1);
|
||||
paramMap.put("a", 1);
|
||||
paramMap.put("barThrowException", "true");
|
||||
|
||||
String stateMachineName = "simpleCompensationStateMachine";
|
||||
|
||||
StateMachineInstance inst = stateMachineEngine.start(stateMachineName, null, paramMap);
|
||||
|
||||
long cost = System.currentTimeMillis() - start;
|
||||
System.out.println("====== cost :" + cost);
|
||||
|
||||
Assertions.assertTrue(ExecutionStatus.UN.equals(inst.getStatus()));
|
||||
Assertions.assertTrue(ExecutionStatus.SU.equals(inst.getCompensationStatus()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCompensationAndSubStateMachine() {
|
||||
|
||||
long start = System.currentTimeMillis();
|
||||
|
||||
Map<String, Object> paramMap = new HashMap<>(1);
|
||||
paramMap.put("a", 2);
|
||||
paramMap.put("barThrowException", "true");
|
||||
|
||||
String stateMachineName = "simpleStateMachineWithCompensationAndSubMachine";
|
||||
|
||||
StateMachineInstance inst = stateMachineEngine.start(stateMachineName, null, paramMap);
|
||||
|
||||
long cost = System.currentTimeMillis() - start;
|
||||
System.out.println("====== cost :" + cost);
|
||||
|
||||
Assertions.assertTrue(ExecutionStatus.UN.equals(inst.getStatus()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCompensationAndSubStateMachineWithLayout() {
|
||||
|
||||
long start = System.currentTimeMillis();
|
||||
|
||||
Map<String, Object> paramMap = new HashMap<>(1);
|
||||
paramMap.put("a", 2);
|
||||
paramMap.put("barThrowException", "true");
|
||||
|
||||
String stateMachineName = "simpleStateMachineWithCompensationAndSubMachine_layout";
|
||||
|
||||
StateMachineInstance inst = stateMachineEngine.start(stateMachineName, null, paramMap);
|
||||
|
||||
long cost = System.currentTimeMillis() - start;
|
||||
System.out.println("====== cost :" + cost);
|
||||
|
||||
Assertions.assertTrue(ExecutionStatus.UN.equals(inst.getStatus()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStateComplexParams() {
|
||||
|
||||
People people1 = new People();
|
||||
people1.setName("lilei");
|
||||
people1.setAge(18);
|
||||
|
||||
People people2 = new People();
|
||||
people2.setName("lilei2");
|
||||
people2.setAge(19);
|
||||
|
||||
People people3 = new People();
|
||||
people3.setName("lilei3");
|
||||
people3.setAge(20);
|
||||
|
||||
People people4 = new People();
|
||||
people4.setName("lilei4");
|
||||
people4.setAge(21);
|
||||
|
||||
people1.setChildrenArray(new People[] {people2});
|
||||
people1.setChildrenList(Arrays.asList(people3));
|
||||
Map<String, People> map1 = new HashMap<>(1);
|
||||
map1.put("lilei4", people4);
|
||||
people1.setChildrenMap(map1);
|
||||
|
||||
String json = JsonParserFactory.getJsonParser("jackson").toJsonString(people1, false, true);
|
||||
System.out.println(json);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStateMachineWithComplexParams() {
|
||||
|
||||
long start = System.currentTimeMillis();
|
||||
|
||||
Map<String, Object> paramMap = new HashMap<>(1);
|
||||
People people = new People();
|
||||
people.setName("lilei");
|
||||
people.setAge(18);
|
||||
|
||||
Engineer engineer = new Engineer();
|
||||
engineer.setName("programmer");
|
||||
|
||||
paramMap.put("people", people);
|
||||
paramMap.put("career", engineer);
|
||||
|
||||
String stateMachineName = "simpleStateMachineWithComplexParams";
|
||||
StateMachineInstance instance = stateMachineEngine.start(stateMachineName, null, paramMap);
|
||||
|
||||
People peopleResult = (People) instance.getEndParams().get("complexParameterMethodResult");
|
||||
Assertions.assertNotNull(peopleResult);
|
||||
Assertions.assertTrue(people.getName().equals(people.getName()));
|
||||
|
||||
long cost = System.currentTimeMillis() - start;
|
||||
System.out.println("====== cost :" + cost);
|
||||
|
||||
Assertions.assertTrue(ExecutionStatus.SU.equals(instance.getStatus()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSimpleStateMachineWithAsyncState() {
|
||||
|
||||
long start = System.currentTimeMillis();
|
||||
|
||||
Map<String, Object> paramMap = new HashMap<>(1);
|
||||
paramMap.put("a", 1);
|
||||
|
||||
String stateMachineName = "simpleStateMachineWithAsyncState";
|
||||
|
||||
StateMachineInstance inst = stateMachineEngine.start(stateMachineName, null, paramMap);
|
||||
|
||||
long cost = System.currentTimeMillis() - start;
|
||||
System.out.println("====== cost :" + cost);
|
||||
|
||||
Assertions.assertTrue(ExecutionStatus.SU.equals(inst.getStatus()));
|
||||
|
||||
try {
|
||||
Thread.sleep(500);
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
/*
|
||||
* Copyright 1999-2019 Seata.io Group.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package io.seata.saga.engine.db;
|
||||
|
||||
import io.seata.common.XID;
|
||||
import io.seata.common.util.NetUtil;
|
||||
import io.seata.core.constants.ConfigurationKeys;
|
||||
import io.seata.core.rpc.ShutdownHook;
|
||||
import io.seata.core.rpc.netty.NettyRemotingServer;
|
||||
import io.seata.server.ParameterParser;
|
||||
import io.seata.server.UUIDGenerator;
|
||||
import io.seata.server.coordinator.DefaultCoordinator;
|
||||
import io.seata.server.metrics.MetricsManager;
|
||||
import io.seata.server.session.SessionHolder;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
import java.util.concurrent.ThreadPoolExecutor;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* Abstract Server Test
|
||||
*
|
||||
* @author lorne.cl
|
||||
*/
|
||||
public abstract class AbstractServerTest {
|
||||
|
||||
|
||||
private static NettyRemotingServer nettyServer;
|
||||
private static final ThreadPoolExecutor workingThreads = new ThreadPoolExecutor(100, 500, 500, TimeUnit.SECONDS,
|
||||
new LinkedBlockingQueue(20000), new ThreadPoolExecutor.CallerRunsPolicy());
|
||||
|
||||
protected static void startSeataServer() throws InterruptedException {
|
||||
(new Thread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
File file = new File("sessionStore/root.data");
|
||||
if(file.exists()){
|
||||
file.delete();
|
||||
}
|
||||
|
||||
ParameterParser parameterParser = new ParameterParser(new String[]{});
|
||||
|
||||
//initialize the metrics
|
||||
MetricsManager.get().init();
|
||||
|
||||
System.setProperty(ConfigurationKeys.STORE_MODE, parameterParser.getStoreMode());
|
||||
|
||||
nettyServer = new NettyRemotingServer(workingThreads);
|
||||
//server port
|
||||
nettyServer.setListenPort(parameterParser.getPort());
|
||||
UUIDGenerator.init(parameterParser.getServerNode());
|
||||
//log store mode : file、db
|
||||
SessionHolder.init(parameterParser.getStoreMode());
|
||||
|
||||
DefaultCoordinator coordinator = new DefaultCoordinator(nettyServer);
|
||||
coordinator.init();
|
||||
nettyServer.setHandler(coordinator);
|
||||
// register ShutdownHook
|
||||
ShutdownHook.getInstance().addDisposable(coordinator);
|
||||
|
||||
//127.0.0.1 and 0.0.0.0 are not valid here.
|
||||
if (NetUtil.isValidIp(parameterParser.getHost(), false)) {
|
||||
XID.setIpAddress(parameterParser.getHost());
|
||||
} else {
|
||||
XID.setIpAddress(NetUtil.getLocalIp());
|
||||
}
|
||||
XID.setPort(nettyServer.getListenPort());
|
||||
|
||||
nettyServer.init();
|
||||
}
|
||||
})).start();
|
||||
Thread.sleep(5000);
|
||||
}
|
||||
|
||||
protected static final void stopSeataServer() throws InterruptedException {
|
||||
if(nettyServer != null){
|
||||
nettyServer.destroy();
|
||||
Thread.sleep(5000);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
1277
test/src/test/java/io/seata/saga/engine/db/StateMachineDBTests.java
Normal file
1277
test/src/test/java/io/seata/saga/engine/db/StateMachineDBTests.java
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,261 @@
|
||||
/*
|
||||
* Copyright 1999-2019 Seata.io Group.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package io.seata.saga.engine.db.mockserver;
|
||||
|
||||
import io.seata.saga.engine.AsyncCallback;
|
||||
import io.seata.saga.engine.StateMachineEngine;
|
||||
import io.seata.saga.engine.mock.DemoService.People;
|
||||
import io.seata.saga.proctrl.ProcessContext;
|
||||
import io.seata.saga.statelang.domain.ExecutionStatus;
|
||||
import io.seata.saga.statelang.domain.StateMachineInstance;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.support.ClassPathXmlApplicationContext;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* State machine async tests with db log store
|
||||
* @author lorne.cl
|
||||
*/
|
||||
public class StateMachineAsyncDBMockServerTests {
|
||||
|
||||
private static StateMachineEngine stateMachineEngine;
|
||||
|
||||
@BeforeAll
|
||||
public static void initApplicationContext() throws InterruptedException {
|
||||
ApplicationContext applicationContext = new ClassPathXmlApplicationContext(
|
||||
"classpath:saga/spring/statemachine_engine_db_mockserver_test.xml");
|
||||
stateMachineEngine = applicationContext.getBean("stateMachineEngine", StateMachineEngine.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSimpleCatchesStateMachine() throws Exception {
|
||||
|
||||
long start = System.currentTimeMillis();
|
||||
|
||||
Map<String, Object> paramMap = new HashMap<>(1);
|
||||
paramMap.put("a", 1);
|
||||
paramMap.put("barThrowException", "true");
|
||||
|
||||
String stateMachineName = "simpleCachesStateMachine";
|
||||
|
||||
StateMachineInstance inst = stateMachineEngine.startAsync(stateMachineName, null, paramMap, callback);
|
||||
|
||||
waittingForFinish(inst);
|
||||
|
||||
long cost = System.currentTimeMillis() - start;
|
||||
System.out.println("====== cost :" + cost);
|
||||
|
||||
Assertions.assertNotNull(inst.getException());
|
||||
Assertions.assertTrue(ExecutionStatus.FA.equals(inst.getStatus()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSimpleRetryStateMachine() {
|
||||
|
||||
long start = System.currentTimeMillis();
|
||||
|
||||
Map<String, Object> paramMap = new HashMap<>(1);
|
||||
paramMap.put("a", 1);
|
||||
paramMap.put("barThrowException", "true");
|
||||
|
||||
String stateMachineName = "simpleRetryStateMachine";
|
||||
|
||||
StateMachineInstance inst = stateMachineEngine.startAsync(stateMachineName, null, paramMap, callback);
|
||||
|
||||
waittingForFinish(inst);
|
||||
|
||||
long cost = System.currentTimeMillis() - start;
|
||||
System.out.println("====== cost :" + cost);
|
||||
|
||||
|
||||
Assertions.assertNotNull(inst.getException());
|
||||
Assertions.assertTrue(ExecutionStatus.FA.equals(inst.getStatus()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStatusMatchingStateMachine() throws Exception {
|
||||
|
||||
long start = System.currentTimeMillis();
|
||||
|
||||
Map<String, Object> paramMap = new HashMap<>(1);
|
||||
paramMap.put("a", 1);
|
||||
paramMap.put("barThrowException", "true");
|
||||
|
||||
String stateMachineName = "simpleStatusMatchingStateMachine";
|
||||
|
||||
StateMachineInstance inst = stateMachineEngine.startAsync(stateMachineName, null, paramMap, callback);
|
||||
|
||||
waittingForFinish(inst);
|
||||
|
||||
long cost = System.currentTimeMillis() - start;
|
||||
System.out.println("====== cost :" + cost);
|
||||
|
||||
Assertions.assertNotNull(inst.getException());
|
||||
Assertions.assertTrue(ExecutionStatus.UN.equals(inst.getStatus()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCompensationStateMachine() throws Exception {
|
||||
|
||||
long start = System.currentTimeMillis();
|
||||
|
||||
Map<String, Object> paramMap = new HashMap<>(1);
|
||||
paramMap.put("a", 1);
|
||||
paramMap.put("barThrowException", "true");
|
||||
|
||||
String stateMachineName = "simpleCompensationStateMachine";
|
||||
|
||||
StateMachineInstance inst = stateMachineEngine.startAsync(stateMachineName, null, paramMap, callback);
|
||||
|
||||
waittingForFinish(inst);
|
||||
|
||||
long cost = System.currentTimeMillis() - start;
|
||||
System.out.println("====== cost :" + cost);
|
||||
|
||||
Assertions.assertTrue(ExecutionStatus.UN.equals(inst.getStatus()));
|
||||
Assertions.assertTrue(ExecutionStatus.SU.equals(inst.getCompensationStatus()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCompensationAndSubStateMachine() throws Exception {
|
||||
|
||||
long start = System.currentTimeMillis();
|
||||
|
||||
Map<String, Object> paramMap = new HashMap<>(1);
|
||||
paramMap.put("a", 2);
|
||||
paramMap.put("barThrowException", "true");
|
||||
|
||||
String stateMachineName = "simpleStateMachineWithCompensationAndSubMachine";
|
||||
|
||||
StateMachineInstance inst = stateMachineEngine.startAsync(stateMachineName, null, paramMap, callback);
|
||||
|
||||
waittingForFinish(inst);
|
||||
|
||||
long cost = System.currentTimeMillis() - start;
|
||||
System.out.println("====== cost :" + cost);
|
||||
|
||||
Assertions.assertTrue(ExecutionStatus.UN.equals(inst.getStatus()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCompensationAndSubStateMachineWithLayout() throws Exception {
|
||||
|
||||
long start = System.currentTimeMillis();
|
||||
|
||||
Map<String, Object> paramMap = new HashMap<>(1);
|
||||
paramMap.put("a", 2);
|
||||
paramMap.put("barThrowException", "true");
|
||||
|
||||
String stateMachineName = "simpleStateMachineWithCompensationAndSubMachine_layout";
|
||||
|
||||
StateMachineInstance inst = stateMachineEngine.startAsync(stateMachineName, null, paramMap, callback);
|
||||
|
||||
waittingForFinish(inst);
|
||||
|
||||
long cost = System.currentTimeMillis() - start;
|
||||
System.out.println("====== cost :" + cost);
|
||||
|
||||
Assertions.assertTrue(ExecutionStatus.UN.equals(inst.getStatus()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStateMachineWithComplexParams() {
|
||||
|
||||
long start = System.currentTimeMillis();
|
||||
|
||||
Map<String, Object> paramMap = new HashMap<>(1);
|
||||
People people = new People();
|
||||
people.setName("lilei");
|
||||
people.setAge(18);
|
||||
paramMap.put("people", people);
|
||||
|
||||
String stateMachineName = "simpleStateMachineWithComplexParamsJackson";
|
||||
|
||||
StateMachineInstance inst = stateMachineEngine.startAsync(stateMachineName, null, paramMap, callback);
|
||||
|
||||
waittingForFinish(inst);
|
||||
|
||||
long cost = System.currentTimeMillis() - start;
|
||||
|
||||
People peopleResult = (People) inst.getEndParams().get("complexParameterMethodResult");
|
||||
Assertions.assertNotNull(peopleResult);
|
||||
Assertions.assertTrue(people.getName().equals(people.getName()));
|
||||
|
||||
System.out.println("====== cost :" + cost);
|
||||
|
||||
Assertions.assertTrue(ExecutionStatus.SU.equals(inst.getStatus()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSimpleStateMachineWithAsyncState() {
|
||||
|
||||
long start = System.currentTimeMillis();
|
||||
|
||||
Map<String, Object> paramMap = new HashMap<>(1);
|
||||
paramMap.put("a", 1);
|
||||
|
||||
String stateMachineName = "simpleStateMachineWithAsyncState";
|
||||
|
||||
StateMachineInstance inst = stateMachineEngine.startAsync(stateMachineName, null, paramMap, callback);
|
||||
|
||||
waittingForFinish(inst);
|
||||
|
||||
long cost = System.currentTimeMillis() - start;
|
||||
System.out.println("====== cost :" + cost);
|
||||
|
||||
Assertions.assertTrue(ExecutionStatus.SU.equals(inst.getStatus()));
|
||||
|
||||
try {
|
||||
Thread.sleep(500);
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
private void waittingForFinish(StateMachineInstance inst) {
|
||||
synchronized (lock) {
|
||||
if (ExecutionStatus.RU.equals(inst.getStatus())) {
|
||||
try {
|
||||
lock.wait();
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private volatile Object lock = new Object();
|
||||
private AsyncCallback callback = new AsyncCallback() {
|
||||
@Override
|
||||
public void onFinished(ProcessContext context, StateMachineInstance stateMachineInstance) {
|
||||
synchronized (lock) {
|
||||
lock.notifyAll();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(ProcessContext context, StateMachineInstance stateMachineInstance, Exception exp) {
|
||||
synchronized (lock) {
|
||||
lock.notifyAll();
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,675 @@
|
||||
/*
|
||||
* Copyright 1999-2019 Seata.io Group.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package io.seata.saga.engine.db.mockserver;
|
||||
|
||||
import io.seata.saga.engine.StateMachineEngine;
|
||||
import io.seata.saga.engine.mock.DemoService.Engineer;
|
||||
import io.seata.saga.engine.mock.DemoService.People;
|
||||
import io.seata.saga.statelang.domain.DomainConstants;
|
||||
import io.seata.saga.statelang.domain.ExecutionStatus;
|
||||
import io.seata.saga.statelang.domain.StateMachineInstance;
|
||||
import io.seata.saga.statelang.parser.JsonParser;
|
||||
import io.seata.saga.statelang.parser.JsonParserFactory;
|
||||
import io.seata.saga.statelang.parser.utils.DesignerJsonTransformer;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.support.ClassPathXmlApplicationContext;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* State machine tests with db log store
|
||||
* @author lorne.cl
|
||||
*/
|
||||
public class StateMachineDBMockServerTests {
|
||||
|
||||
private static StateMachineEngine stateMachineEngine;
|
||||
|
||||
@BeforeAll
|
||||
public static void initApplicationContext() throws InterruptedException {
|
||||
ApplicationContext applicationContext = new ClassPathXmlApplicationContext(
|
||||
"classpath:saga/spring/statemachine_engine_db_mockserver_test.xml");
|
||||
stateMachineEngine = applicationContext.getBean("stateMachineEngine", StateMachineEngine.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSimpleStateMachine() {
|
||||
|
||||
stateMachineEngine.start("simpleTestStateMachine", null, new HashMap<>());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSimpleStateMachineWithChoice() {
|
||||
|
||||
long start = System.currentTimeMillis();
|
||||
|
||||
Map<String, Object> paramMap = new HashMap<>(1);
|
||||
paramMap.put("a", 1);
|
||||
|
||||
String stateMachineName = "simpleChoiceTestStateMachine";
|
||||
String businessKey = String.valueOf(start);
|
||||
StateMachineInstance inst = stateMachineEngine.startWithBusinessKey(stateMachineName, null, businessKey, paramMap);
|
||||
|
||||
long cost = System.currentTimeMillis() - start;
|
||||
System.out.println("====== cost :" + cost);
|
||||
Assertions.assertNotNull(inst);
|
||||
Assertions.assertTrue(ExecutionStatus.SU.equals(inst.getStatus()));
|
||||
|
||||
inst = stateMachineEngine.getStateMachineConfig().getStateLogStore().getStateMachineInstanceByBusinessKey(businessKey, null);
|
||||
Assertions.assertNotNull(inst);
|
||||
Assertions.assertTrue(ExecutionStatus.SU.equals(inst.getStatus()));
|
||||
|
||||
start = System.currentTimeMillis();
|
||||
paramMap.put("a", 2);
|
||||
inst = stateMachineEngine.start(stateMachineName, null, paramMap);
|
||||
|
||||
cost = System.currentTimeMillis() - start;
|
||||
System.out.println("====== cost :" + cost);
|
||||
Assertions.assertNotNull(inst);
|
||||
Assertions.assertTrue(ExecutionStatus.SU.equals(inst.getStatus()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSimpleStateMachineWithChoiceAndEnd() {
|
||||
|
||||
long start = System.currentTimeMillis();
|
||||
|
||||
Map<String, Object> paramMap = new HashMap<>(1);
|
||||
paramMap.put("a", 1);
|
||||
|
||||
String stateMachineName = "simpleChoiceAndEndTestStateMachine";
|
||||
|
||||
stateMachineEngine.start(stateMachineName, null, paramMap);
|
||||
|
||||
long cost = System.currentTimeMillis() - start;
|
||||
System.out.println("====== cost :" + cost);
|
||||
|
||||
start = System.currentTimeMillis();
|
||||
|
||||
paramMap.put("a", 3);
|
||||
stateMachineEngine.start(stateMachineName, null, paramMap);
|
||||
|
||||
cost = System.currentTimeMillis() - start;
|
||||
System.out.println("====== cost :" + cost);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSimpleInputAssignmentStateMachine() {
|
||||
|
||||
long start = System.currentTimeMillis();
|
||||
|
||||
Map<String, Object> paramMap = new HashMap<>(1);
|
||||
paramMap.put("a", 1);
|
||||
|
||||
String stateMachineName = "simpleInputAssignmentStateMachine";
|
||||
|
||||
StateMachineInstance instance = stateMachineEngine.start(stateMachineName, null, paramMap);
|
||||
|
||||
String businessKey = instance.getStateList().get(0).getBusinessKey();
|
||||
Assertions.assertNotNull(businessKey);
|
||||
System.out.println("====== businessKey :" + businessKey);
|
||||
|
||||
String contextBusinessKey = (String) instance.getEndParams().get(
|
||||
instance.getStateList().get(0).getName() + DomainConstants.VAR_NAME_BUSINESSKEY);
|
||||
Assertions.assertNotNull(contextBusinessKey);
|
||||
System.out.println("====== context businessKey :" + businessKey);
|
||||
|
||||
long cost = System.currentTimeMillis() - start;
|
||||
System.out.println("====== cost :" + cost);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSimpleCatchesStateMachine() throws Exception {
|
||||
|
||||
long start = System.currentTimeMillis();
|
||||
|
||||
Map<String, Object> paramMap = new HashMap<>(1);
|
||||
paramMap.put("a", 1);
|
||||
paramMap.put("barThrowException", "true");
|
||||
|
||||
String stateMachineName = "simpleCachesStateMachine";
|
||||
|
||||
StateMachineInstance inst = stateMachineEngine.start(stateMachineName, null, paramMap);
|
||||
|
||||
long cost = System.currentTimeMillis() - start;
|
||||
System.out.println("====== cost :" + cost);
|
||||
|
||||
Assertions.assertNotNull(inst.getException());
|
||||
Assertions.assertTrue(ExecutionStatus.FA.equals(inst.getStatus()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSimpleScriptTaskStateMachineWithLayout() {
|
||||
|
||||
long start = System.currentTimeMillis();
|
||||
|
||||
Map<String, Object> paramMap = new HashMap<>(1);
|
||||
paramMap.put("a", 1);
|
||||
|
||||
String stateMachineName = "designerSimpleScriptTaskStateMachine";
|
||||
|
||||
StateMachineInstance inst = stateMachineEngine.start(stateMachineName, null, paramMap);
|
||||
|
||||
long cost = System.currentTimeMillis() - start;
|
||||
System.out.println("====== cost :" + cost);
|
||||
|
||||
Assertions.assertTrue(ExecutionStatus.SU.equals(inst.getStatus()));
|
||||
Assertions.assertNotNull(inst.getEndParams().get("scriptStateResult"));
|
||||
|
||||
|
||||
start = System.currentTimeMillis();
|
||||
|
||||
inst = stateMachineEngine.start(stateMachineName, null, paramMap);
|
||||
|
||||
cost = System.currentTimeMillis() - start;
|
||||
System.out.println("====== cost :" + cost);
|
||||
|
||||
Assertions.assertTrue(ExecutionStatus.SU.equals(inst.getStatus()));
|
||||
|
||||
|
||||
start = System.currentTimeMillis();
|
||||
paramMap.put("scriptThrowException", true);
|
||||
inst = stateMachineEngine.start(stateMachineName, null, paramMap);
|
||||
|
||||
cost = System.currentTimeMillis() - start;
|
||||
System.out.println("====== cost :" + cost);
|
||||
|
||||
Assertions.assertTrue(ExecutionStatus.FA.equals(inst.getStatus()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSimpleRetryStateMachine() {
|
||||
|
||||
long start = System.currentTimeMillis();
|
||||
|
||||
Map<String, Object> paramMap = new HashMap<>(1);
|
||||
paramMap.put("a", 1);
|
||||
paramMap.put("barThrowException", "true");
|
||||
|
||||
String stateMachineName = "simpleRetryStateMachine";
|
||||
|
||||
StateMachineInstance inst = stateMachineEngine.start(stateMachineName, null, paramMap);
|
||||
|
||||
long cost = System.currentTimeMillis() - start;
|
||||
System.out.println("====== cost :" + cost);
|
||||
|
||||
Assertions.assertNotNull(inst.getException());
|
||||
Assertions.assertTrue(ExecutionStatus.FA.equals(inst.getStatus()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStatusMatchingStateMachine() throws Exception {
|
||||
|
||||
long start = System.currentTimeMillis();
|
||||
|
||||
Map<String, Object> paramMap = new HashMap<>(1);
|
||||
paramMap.put("a", 1);
|
||||
paramMap.put("barThrowException", "true");
|
||||
|
||||
String stateMachineName = "simpleStatusMatchingStateMachine";
|
||||
|
||||
StateMachineInstance inst = stateMachineEngine.start(stateMachineName, null, paramMap);
|
||||
|
||||
long cost = System.currentTimeMillis() - start;
|
||||
System.out.println("====== cost :" + cost);
|
||||
|
||||
Assertions.assertNotNull(inst.getException());
|
||||
Assertions.assertTrue(ExecutionStatus.UN.equals(inst.getStatus()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCompensationStateMachine() throws Exception {
|
||||
|
||||
long start = System.currentTimeMillis();
|
||||
|
||||
Map<String, Object> paramMap = new HashMap<>(1);
|
||||
paramMap.put("a", 1);
|
||||
paramMap.put("barThrowException", "true");
|
||||
|
||||
String stateMachineName = "simpleCompensationStateMachine";
|
||||
|
||||
StateMachineInstance inst = stateMachineEngine.start(stateMachineName, null, paramMap);
|
||||
|
||||
long cost = System.currentTimeMillis() - start;
|
||||
System.out.println("====== cost :" + cost);
|
||||
|
||||
Assertions.assertTrue(ExecutionStatus.UN.equals(inst.getStatus()));
|
||||
Assertions.assertTrue(ExecutionStatus.SU.equals(inst.getCompensationStatus()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSubStateMachine() throws Exception {
|
||||
|
||||
long start = System.currentTimeMillis();
|
||||
|
||||
Map<String, Object> paramMap = new HashMap<>(1);
|
||||
paramMap.put("a", 2);
|
||||
paramMap.put("barThrowException", "true");
|
||||
|
||||
String stateMachineName = "simpleStateMachineWithCompensationAndSubMachine";
|
||||
|
||||
StateMachineInstance inst = stateMachineEngine.start(stateMachineName, null, paramMap);
|
||||
|
||||
long cost = System.currentTimeMillis() - start;
|
||||
System.out.println("====== XID: " + inst.getId() + " cost :" + cost);
|
||||
|
||||
Assertions.assertTrue(ExecutionStatus.UN.equals(inst.getStatus()));
|
||||
|
||||
start = System.currentTimeMillis();
|
||||
|
||||
paramMap.put("barThrowException", "false");
|
||||
inst = stateMachineEngine.forward(inst.getId(), paramMap);
|
||||
|
||||
cost = System.currentTimeMillis() - start;
|
||||
System.out.println("====== XID: " + inst.getId() + " cost :" + cost);
|
||||
|
||||
Assertions.assertTrue(ExecutionStatus.SU.equals(inst.getStatus()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSubStateMachineWithLayout() throws Exception {
|
||||
|
||||
long start = System.currentTimeMillis();
|
||||
|
||||
Map<String, Object> paramMap = new HashMap<>(1);
|
||||
paramMap.put("a", 2);
|
||||
paramMap.put("barThrowException", "true");
|
||||
|
||||
String stateMachineName = "simpleStateMachineWithCompensationAndSubMachine_layout";
|
||||
|
||||
StateMachineInstance inst = stateMachineEngine.start(stateMachineName, null, paramMap);
|
||||
|
||||
long cost = System.currentTimeMillis() - start;
|
||||
System.out.println("====== XID: " + inst.getId() + " cost :" + cost);
|
||||
|
||||
Assertions.assertTrue(ExecutionStatus.UN.equals(inst.getStatus()));
|
||||
|
||||
start = System.currentTimeMillis();
|
||||
|
||||
paramMap.put("barThrowException", "false");
|
||||
inst = stateMachineEngine.forward(inst.getId(), paramMap);
|
||||
|
||||
cost = System.currentTimeMillis() - start;
|
||||
System.out.println("====== XID: " + inst.getId() + " cost :" + cost);
|
||||
|
||||
Assertions.assertTrue(ExecutionStatus.SU.equals(inst.getStatus()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testForwardSubStateMachine() throws Exception {
|
||||
|
||||
long start = System.currentTimeMillis();
|
||||
|
||||
Map<String, Object> paramMap = new HashMap<>(1);
|
||||
paramMap.put("a", 2);
|
||||
paramMap.put("fooThrowException", "true");
|
||||
|
||||
String stateMachineName = "simpleStateMachineWithCompensationAndSubMachine";
|
||||
|
||||
StateMachineInstance inst = stateMachineEngine.start(stateMachineName, null, paramMap);
|
||||
|
||||
long cost = System.currentTimeMillis() - start;
|
||||
System.out.println("====== XID: " + inst.getId() + " cost :" + cost);
|
||||
|
||||
Assertions.assertTrue(ExecutionStatus.UN.equals(inst.getStatus()));
|
||||
|
||||
start = System.currentTimeMillis();
|
||||
|
||||
paramMap.put("fooThrowException", "false");
|
||||
inst = stateMachineEngine.forward(inst.getId(), paramMap);
|
||||
|
||||
cost = System.currentTimeMillis() - start;
|
||||
System.out.println("====== XID: " + inst.getId() + " cost :" + cost);
|
||||
|
||||
Assertions.assertTrue(ExecutionStatus.SU.equals(inst.getStatus()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testForwardSubStateMachineWithLayout() throws Exception {
|
||||
|
||||
long start = System.currentTimeMillis();
|
||||
|
||||
Map<String, Object> paramMap = new HashMap<>(1);
|
||||
paramMap.put("a", 2);
|
||||
paramMap.put("fooThrowException", "true");
|
||||
|
||||
String stateMachineName = "simpleStateMachineWithCompensationAndSubMachine_layout";
|
||||
|
||||
StateMachineInstance inst = stateMachineEngine.start(stateMachineName, null, paramMap);
|
||||
|
||||
long cost = System.currentTimeMillis() - start;
|
||||
System.out.println("====== XID: " + inst.getId() + " cost :" + cost);
|
||||
|
||||
Assertions.assertTrue(ExecutionStatus.UN.equals(inst.getStatus()));
|
||||
|
||||
JsonParser jsonParser = JsonParserFactory.getJsonParser("jackson");
|
||||
String graphJson = DesignerJsonTransformer.generateTracingGraphJson(inst, jsonParser);
|
||||
Assertions.assertNotNull(graphJson);
|
||||
System.out.println(graphJson);
|
||||
|
||||
start = System.currentTimeMillis();
|
||||
|
||||
paramMap.put("fooThrowException", "false");
|
||||
inst = stateMachineEngine.forward(inst.getId(), paramMap);
|
||||
|
||||
cost = System.currentTimeMillis() - start;
|
||||
System.out.println("====== XID: " + inst.getId() + " cost :" + cost);
|
||||
|
||||
Assertions.assertTrue(ExecutionStatus.SU.equals(inst.getStatus()));
|
||||
|
||||
String graphJson2 = DesignerJsonTransformer.generateTracingGraphJson(inst, jsonParser);
|
||||
Assertions.assertNotNull(graphJson2);
|
||||
System.out.println(graphJson2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCompensateSubStateMachine() throws Exception {
|
||||
|
||||
long start = System.currentTimeMillis();
|
||||
|
||||
Map<String, Object> paramMap = new HashMap<>(1);
|
||||
paramMap.put("a", 2);
|
||||
paramMap.put("barThrowException", "true");
|
||||
paramMap.put("compensateFooThrowException", "true");
|
||||
|
||||
String stateMachineName = "simpleStateMachineWithCompensationAndSubMachine";
|
||||
|
||||
StateMachineInstance inst = stateMachineEngine.start(stateMachineName, null, paramMap);
|
||||
|
||||
long cost = System.currentTimeMillis() - start;
|
||||
System.out.println("====== XID: " + inst.getId() + " cost :" + cost);
|
||||
|
||||
Assertions.assertTrue(ExecutionStatus.UN.equals(inst.getStatus()));
|
||||
|
||||
start = System.currentTimeMillis();
|
||||
|
||||
inst = stateMachineEngine.compensate(inst.getId(), paramMap);
|
||||
|
||||
cost = System.currentTimeMillis() - start;
|
||||
System.out.println("====== XID: " + inst.getId() + " cost :" + cost);
|
||||
|
||||
Assertions.assertTrue(ExecutionStatus.UN.equals(inst.getCompensationStatus()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCompensateSubStateMachineWithLayout() throws Exception {
|
||||
|
||||
long start = System.currentTimeMillis();
|
||||
|
||||
Map<String, Object> paramMap = new HashMap<>(1);
|
||||
paramMap.put("a", 2);
|
||||
paramMap.put("barThrowException", "true");
|
||||
paramMap.put("compensateFooThrowException", "true");
|
||||
|
||||
String stateMachineName = "simpleStateMachineWithCompensationAndSubMachine_layout";
|
||||
|
||||
StateMachineInstance inst = stateMachineEngine.start(stateMachineName, null, paramMap);
|
||||
|
||||
long cost = System.currentTimeMillis() - start;
|
||||
System.out.println("====== XID: " + inst.getId() + " cost :" + cost);
|
||||
|
||||
Assertions.assertTrue(ExecutionStatus.UN.equals(inst.getStatus()));
|
||||
|
||||
JsonParser jsonParser = JsonParserFactory.getJsonParser("jackson");
|
||||
String graphJson = DesignerJsonTransformer.generateTracingGraphJson(inst, jsonParser);
|
||||
Assertions.assertNotNull(graphJson);
|
||||
System.out.println(graphJson);
|
||||
|
||||
start = System.currentTimeMillis();
|
||||
|
||||
inst = stateMachineEngine.compensate(inst.getId(), paramMap);
|
||||
|
||||
cost = System.currentTimeMillis() - start;
|
||||
System.out.println("====== XID: " + inst.getId() + " cost :" + cost);
|
||||
|
||||
Assertions.assertTrue(ExecutionStatus.UN.equals(inst.getCompensationStatus()));
|
||||
|
||||
String graphJson2 = DesignerJsonTransformer.generateTracingGraphJson(inst, jsonParser);
|
||||
Assertions.assertNotNull(graphJson2);
|
||||
System.out.println(graphJson2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUserDefCompensateSubStateMachine() throws Exception {
|
||||
|
||||
long start = System.currentTimeMillis();
|
||||
|
||||
Map<String, Object> paramMap = new HashMap<>(1);
|
||||
paramMap.put("a", 2);
|
||||
paramMap.put("barThrowException", "true");
|
||||
paramMap.put("compensateFooThrowException", "true");
|
||||
|
||||
String stateMachineName = "simpleStateMachineWithUseDefCompensationSubMachine";
|
||||
|
||||
StateMachineInstance inst = stateMachineEngine.start(stateMachineName, null, paramMap);
|
||||
|
||||
long cost = System.currentTimeMillis() - start;
|
||||
System.out.println("====== XID: " + inst.getId() + " cost :" + cost);
|
||||
|
||||
Assertions.assertTrue(ExecutionStatus.UN.equals(inst.getStatus()));
|
||||
|
||||
start = System.currentTimeMillis();
|
||||
|
||||
paramMap.put("compensateFooThrowException", "false");
|
||||
inst = stateMachineEngine.compensate(inst.getId(), paramMap);
|
||||
|
||||
cost = System.currentTimeMillis() - start;
|
||||
System.out.println("====== XID: " + inst.getId() + " cost :" + cost);
|
||||
|
||||
Assertions.assertTrue(ExecutionStatus.SU.equals(inst.getCompensationStatus()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCommitRetryingThenRetryCommitted() throws Exception {
|
||||
|
||||
long start = System.currentTimeMillis();
|
||||
|
||||
Map<String, Object> paramMap = new HashMap<>(1);
|
||||
paramMap.put("a", 1);
|
||||
paramMap.put("fooThrowException", "true");
|
||||
|
||||
String stateMachineName = "simpleCompensationStateMachineForRecovery";
|
||||
|
||||
StateMachineInstance inst = stateMachineEngine.start(stateMachineName, null, paramMap);
|
||||
|
||||
long cost = System.currentTimeMillis() - start;
|
||||
System.out.println("====== XID: " + inst.getId() + " cost :" + cost);
|
||||
|
||||
Assertions.assertTrue(ExecutionStatus.UN.equals(inst.getStatus()));
|
||||
|
||||
paramMap.put("fooThrowException", "false");
|
||||
|
||||
start = System.currentTimeMillis();
|
||||
|
||||
inst = stateMachineEngine.forward(inst.getId(), paramMap);
|
||||
|
||||
cost = System.currentTimeMillis() - start;
|
||||
System.out.println("====== XID: " + inst.getId() + " cost :" + cost);
|
||||
|
||||
Assertions.assertTrue(ExecutionStatus.SU.equals(inst.getStatus()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCommitRetryingThenRetryRollbacked() throws Exception {
|
||||
|
||||
long start = System.currentTimeMillis();
|
||||
|
||||
Map<String, Object> paramMap = new HashMap<>(1);
|
||||
paramMap.put("a", 1);
|
||||
paramMap.put("fooThrowException", "true");
|
||||
|
||||
String stateMachineName = "simpleCompensationStateMachineForRecovery";
|
||||
|
||||
StateMachineInstance inst = stateMachineEngine.start(stateMachineName, null, paramMap);
|
||||
|
||||
long cost = System.currentTimeMillis() - start;
|
||||
System.out.println("====== XID: " + inst.getId() + " cost :" + cost);
|
||||
|
||||
Assertions.assertTrue(ExecutionStatus.UN.equals(inst.getStatus()));
|
||||
|
||||
paramMap.put("fooThrowException", "false");
|
||||
paramMap.put("barThrowException", "true");
|
||||
|
||||
start = System.currentTimeMillis();
|
||||
|
||||
inst = stateMachineEngine.forward(inst.getId(), paramMap);
|
||||
|
||||
cost = System.currentTimeMillis() - start;
|
||||
System.out.println("====== XID: " + inst.getId() + " cost :" + cost);
|
||||
|
||||
Assertions.assertTrue(ExecutionStatus.SU.equals(inst.getCompensationStatus()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRollbackRetryingThenRetryRollbacked() throws Exception {
|
||||
|
||||
long start = System.currentTimeMillis();
|
||||
|
||||
Map<String, Object> paramMap = new HashMap<>(1);
|
||||
paramMap.put("a", 1);
|
||||
paramMap.put("barThrowException", "true");
|
||||
paramMap.put("compensateFooThrowException", "true");
|
||||
|
||||
String stateMachineName = "simpleCompensationStateMachineForRecovery";
|
||||
|
||||
StateMachineInstance inst = stateMachineEngine.start(stateMachineName, null, paramMap);
|
||||
|
||||
long cost = System.currentTimeMillis() - start;
|
||||
System.out.println("====== XID: " + inst.getId() + " cost :" + cost);
|
||||
|
||||
Assertions.assertTrue(ExecutionStatus.UN.equals(inst.getStatus()));
|
||||
Assertions.assertTrue(ExecutionStatus.UN.equals(inst.getCompensationStatus()));
|
||||
|
||||
paramMap.put("barThrowException", "false");
|
||||
paramMap.put("compensateFooThrowException", "false");
|
||||
|
||||
start = System.currentTimeMillis();
|
||||
|
||||
inst = stateMachineEngine.compensate(inst.getId(), paramMap);
|
||||
|
||||
cost = System.currentTimeMillis() - start;
|
||||
System.out.println("====== XID: " + inst.getId() + " cost :" + cost);
|
||||
|
||||
Assertions.assertTrue(ExecutionStatus.SU.equals(inst.getCompensationStatus()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRollbackRetryingTwiceThenRetryRollbacked() throws Exception {
|
||||
|
||||
long start = System.currentTimeMillis();
|
||||
|
||||
Map<String, Object> paramMap = new HashMap<>(1);
|
||||
paramMap.put("a", 1);
|
||||
paramMap.put("barThrowException", "true");
|
||||
paramMap.put("compensateFooThrowException", "true");
|
||||
|
||||
String stateMachineName = "simpleCompensationStateMachineForRecovery";
|
||||
|
||||
StateMachineInstance inst = stateMachineEngine.start(stateMachineName, null, paramMap);
|
||||
|
||||
long cost = System.currentTimeMillis() - start;
|
||||
System.out.println("====== XID: " + inst.getId() + " cost :" + cost);
|
||||
|
||||
Assertions.assertTrue(ExecutionStatus.UN.equals(inst.getStatus()));
|
||||
Assertions.assertTrue(ExecutionStatus.UN.equals(inst.getCompensationStatus()));
|
||||
|
||||
start = System.currentTimeMillis();
|
||||
|
||||
inst = stateMachineEngine.compensate(inst.getId(), paramMap);
|
||||
|
||||
cost = System.currentTimeMillis() - start;
|
||||
System.out.println("====== XID: " + inst.getId() + " cost :" + cost);
|
||||
|
||||
Assertions.assertTrue(ExecutionStatus.UN.equals(inst.getStatus()));
|
||||
Assertions.assertTrue(ExecutionStatus.UN.equals(inst.getCompensationStatus()));
|
||||
|
||||
paramMap.put("barThrowException", "false");
|
||||
paramMap.put("compensateFooThrowException", "false");
|
||||
|
||||
start = System.currentTimeMillis();
|
||||
|
||||
inst = stateMachineEngine.compensate(inst.getId(), paramMap);
|
||||
|
||||
cost = System.currentTimeMillis() - start;
|
||||
System.out.println("====== XID: " + inst.getId() + " cost :" + cost);
|
||||
|
||||
Assertions.assertTrue(ExecutionStatus.SU.equals(inst.getCompensationStatus()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStateMachineWithComplexParams() {
|
||||
|
||||
long start = System.currentTimeMillis();
|
||||
|
||||
Map<String, Object> paramMap = new HashMap<>(1);
|
||||
People people = new People();
|
||||
people.setName("lilei");
|
||||
people.setAge(18);
|
||||
|
||||
Engineer engineer = new Engineer();
|
||||
engineer.setName("programmer");
|
||||
|
||||
paramMap.put("people", people);
|
||||
paramMap.put("career", engineer);
|
||||
|
||||
String stateMachineName = "simpleStateMachineWithComplexParamsJackson";
|
||||
|
||||
StateMachineInstance instance = stateMachineEngine.start(stateMachineName, null, paramMap);
|
||||
|
||||
People peopleResult = (People) instance.getEndParams().get("complexParameterMethodResult");
|
||||
Assertions.assertNotNull(peopleResult);
|
||||
Assertions.assertTrue(people.getName().equals(people.getName()));
|
||||
|
||||
long cost = System.currentTimeMillis() - start;
|
||||
System.out.println("====== XID: " + instance.getId() + " cost :" + cost);
|
||||
|
||||
Assertions.assertTrue(ExecutionStatus.SU.equals(instance.getStatus()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSimpleStateMachineWithAsyncState() {
|
||||
|
||||
long start = System.currentTimeMillis();
|
||||
|
||||
Map<String, Object> paramMap = new HashMap<>(1);
|
||||
paramMap.put("a", 1);
|
||||
|
||||
String stateMachineName = "simpleStateMachineWithAsyncState";
|
||||
|
||||
StateMachineInstance inst = stateMachineEngine.start(stateMachineName, null, paramMap);
|
||||
|
||||
long cost = System.currentTimeMillis() - start;
|
||||
System.out.println("====== cost :" + cost);
|
||||
|
||||
Assertions.assertTrue(ExecutionStatus.SU.equals(inst.getStatus()));
|
||||
|
||||
try {
|
||||
Thread.sleep(500);
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReloadStateMachineInstance() {
|
||||
StateMachineInstance instance = stateMachineEngine.getStateMachineConfig().getStateLogStore().getStateMachineInstance(
|
||||
"10.15.232.93:8091:2019567124");
|
||||
System.out.println(instance);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright 1999-2019 Seata.io Group.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package io.seata.saga.engine.mock;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author lorne.cl
|
||||
*/
|
||||
public class DemoException extends RuntimeException {
|
||||
|
||||
public DemoException() {
|
||||
}
|
||||
|
||||
public DemoException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public DemoException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
public DemoException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
|
||||
public DemoException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
|
||||
super(message, cause, enableSuppression, writableStackTrace);
|
||||
}
|
||||
}
|
||||
187
test/src/test/java/io/seata/saga/engine/mock/DemoService.java
Normal file
187
test/src/test/java/io/seata/saga/engine/mock/DemoService.java
Normal file
@@ -0,0 +1,187 @@
|
||||
/*
|
||||
* Copyright 1999-2019 Seata.io Group.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package io.seata.saga.engine.mock;
|
||||
|
||||
import java.net.ConnectException;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author lorne.cl
|
||||
*/
|
||||
public class DemoService {
|
||||
|
||||
public Map<String, Object> foo(Map<String, Object> input) {
|
||||
if(input == null){
|
||||
return null;
|
||||
}
|
||||
Integer sleepTime = (Integer) input.get("sleepTime");
|
||||
if(sleepTime != null){
|
||||
try {
|
||||
Thread.sleep(sleepTime);
|
||||
} catch (InterruptedException e) {
|
||||
throw new DemoException(e);
|
||||
}
|
||||
}
|
||||
if("true".equals(input.get("throwException"))){
|
||||
throw new DemoException("foo execute failed");
|
||||
}
|
||||
if("true".equals(input.get("throwExceptionRandomly"))){
|
||||
if(Math.random() > 0.5){
|
||||
throw new DemoException("foo execute failed");
|
||||
}
|
||||
}
|
||||
return input;
|
||||
}
|
||||
|
||||
public Map<String, Object> compensateFoo(Map<String, Object> input) {
|
||||
if(input == null){
|
||||
return null;
|
||||
}
|
||||
if("true".equals(input.get("throwException"))){
|
||||
throw new DemoException("compensateFoo execute failed");
|
||||
}
|
||||
if("true".equals(input.get("throwExceptionRandomly"))){
|
||||
if(Math.random() > 0.8){
|
||||
throw new DemoException("compensateFoo execute failed");
|
||||
}
|
||||
}
|
||||
return input;
|
||||
}
|
||||
|
||||
public Map<String, Object> bar(Map<String, Object> input) {
|
||||
if(input == null){
|
||||
return null;
|
||||
}
|
||||
Integer sleepTime = (Integer) input.get("sleepTime");
|
||||
if(sleepTime != null){
|
||||
try {
|
||||
Thread.sleep(sleepTime);
|
||||
} catch (InterruptedException e) {
|
||||
throw new DemoException(e);
|
||||
}
|
||||
}
|
||||
if("true".equals(input.get("throwException"))){
|
||||
throw new DemoException("bar execute failed");
|
||||
}
|
||||
if("true".equals(input.get("throwExceptionRandomly"))){
|
||||
if(Math.random() > 0.5){
|
||||
throw new DemoException("bar execute failed");
|
||||
}
|
||||
}
|
||||
return input;
|
||||
}
|
||||
|
||||
public Map<String, Object> compensateBar(Map<String, Object> input) {
|
||||
if(input == null){
|
||||
return null;
|
||||
}
|
||||
if("true".equals(input.get("throwException"))){
|
||||
throw new DemoException("compensateBar execute failed");
|
||||
}
|
||||
if("true".equals(input.get("throwExceptionRandomly"))){
|
||||
if(Math.random() > 0.8){
|
||||
throw new DemoException("compensateBar execute failed");
|
||||
}
|
||||
}
|
||||
return input;
|
||||
}
|
||||
|
||||
public People complexParameterMethod(String name, int age, People people, People[] peopleArrya, List<People> peopleList, Map<String, People> peopleMap){
|
||||
return people;
|
||||
}
|
||||
|
||||
public Career interfaceParameterMethod(Career career){
|
||||
return career;
|
||||
}
|
||||
|
||||
public Map<String, Object> randomExceptionMethod(Map<String, Object> input) {
|
||||
|
||||
double random = Math.random();
|
||||
if (random > 0.5) {
|
||||
throw new DemoException("randomExceptionMethod execute failed");
|
||||
}
|
||||
else {
|
||||
throw new RuntimeException(new ConnectException("Connect Exception"));
|
||||
}
|
||||
}
|
||||
|
||||
public static class People {
|
||||
|
||||
private String name;
|
||||
private int age;
|
||||
|
||||
private People[] childrenArray;
|
||||
private List<People> childrenList;
|
||||
private Map<String, People> childrenMap;
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public int getAge() {
|
||||
return age;
|
||||
}
|
||||
|
||||
public void setAge(int age) {
|
||||
this.age = age;
|
||||
}
|
||||
|
||||
public People[] getChildrenArray() {
|
||||
return childrenArray;
|
||||
}
|
||||
|
||||
public void setChildrenArray(People[] childrenArray) {
|
||||
this.childrenArray = childrenArray;
|
||||
}
|
||||
|
||||
public List<People> getChildrenList() {
|
||||
return childrenList;
|
||||
}
|
||||
|
||||
public void setChildrenList(List<People> childrenList) {
|
||||
this.childrenList = childrenList;
|
||||
}
|
||||
|
||||
public Map<String, People> getChildrenMap() {
|
||||
return childrenMap;
|
||||
}
|
||||
|
||||
public void setChildrenMap(Map<String, People> childrenMap) {
|
||||
this.childrenMap = childrenMap;
|
||||
}
|
||||
}
|
||||
|
||||
public interface Career {
|
||||
|
||||
}
|
||||
|
||||
public static class Engineer implements Career {
|
||||
private String name;
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
/*
|
||||
* Copyright 1999-2019 Seata.io Group.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package io.seata.saga.engine.mock;
|
||||
|
||||
import io.seata.core.context.RootContext;
|
||||
import io.seata.core.exception.TransactionException;
|
||||
import io.seata.core.model.GlobalStatus;
|
||||
import io.seata.saga.engine.sequence.SpringJvmUUIDSeqGenerator;
|
||||
import io.seata.tm.api.GlobalTransaction;
|
||||
import io.seata.tm.api.GlobalTransactionRole;
|
||||
import io.seata.tm.api.transaction.SuspendedResourcesHolder;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author lorne.cl
|
||||
*/
|
||||
public class MockGlobalTransaction implements GlobalTransaction {
|
||||
|
||||
private String xid;
|
||||
private GlobalStatus status;
|
||||
|
||||
private static SpringJvmUUIDSeqGenerator uuidSeqGenerator = new SpringJvmUUIDSeqGenerator();
|
||||
|
||||
public MockGlobalTransaction() {}
|
||||
|
||||
public MockGlobalTransaction(String xid) {
|
||||
this.xid = xid;
|
||||
}
|
||||
|
||||
public MockGlobalTransaction(String xid, GlobalStatus status) {
|
||||
this.xid = xid;
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void begin() throws TransactionException {
|
||||
begin(60000);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void begin(int timeout) throws TransactionException {
|
||||
status = GlobalStatus.Begin;
|
||||
xid = uuidSeqGenerator.generate(null).toString();
|
||||
RootContext.bind(xid);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void begin(int timeout, String name) throws TransactionException {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void commit() throws TransactionException {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void rollback() throws TransactionException {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public SuspendedResourcesHolder suspend()
|
||||
throws TransactionException {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resume(SuspendedResourcesHolder suspendedResourcesHolder)
|
||||
throws TransactionException {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public GlobalStatus getStatus() throws TransactionException {
|
||||
return status;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getXid() {
|
||||
return xid;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void globalReport(GlobalStatus globalStatus) throws TransactionException {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public GlobalStatus getLocalStatus() {
|
||||
return status;
|
||||
}
|
||||
|
||||
@Override
|
||||
public GlobalTransactionRole getGlobalTransactionRole() {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
/*
|
||||
* Copyright 1999-2019 Seata.io Group.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package io.seata.saga.engine.mock;
|
||||
|
||||
import io.seata.core.exception.TransactionException;
|
||||
import io.seata.core.model.BranchStatus;
|
||||
import io.seata.core.model.GlobalStatus;
|
||||
import io.seata.saga.tm.SagaTransactionalTemplate;
|
||||
import io.seata.server.UUIDGenerator;
|
||||
import io.seata.tm.api.GlobalTransaction;
|
||||
import io.seata.tm.api.TransactionalExecutor.ExecutionException;
|
||||
import io.seata.tm.api.transaction.TransactionInfo;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author lorne.cl
|
||||
*/
|
||||
public class MockSagaTransactionTemplate implements SagaTransactionalTemplate {
|
||||
|
||||
@Override
|
||||
public void commitTransaction(GlobalTransaction tx) throws ExecutionException {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void rollbackTransaction(GlobalTransaction tx, Throwable ex) throws TransactionException, ExecutionException {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public GlobalTransaction beginTransaction(TransactionInfo txInfo) throws ExecutionException {
|
||||
GlobalTransaction globalTransaction = new MockGlobalTransaction();
|
||||
try {
|
||||
globalTransaction.begin();
|
||||
} catch (TransactionException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return globalTransaction;
|
||||
}
|
||||
|
||||
@Override
|
||||
public GlobalTransaction reloadTransaction(String xid) throws ExecutionException, TransactionException {
|
||||
return new MockGlobalTransaction(xid, GlobalStatus.UnKnown);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reportTransaction(GlobalTransaction tx, GlobalStatus globalStatus) throws ExecutionException {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public long branchRegister(String resourceId, String clientId, String xid, String applicationData, String lockKeys)
|
||||
throws TransactionException {
|
||||
return UUIDGenerator.generateUUID();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void branchReport(String xid, long branchId, BranchStatus status, String applicationData) throws TransactionException {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void triggerAfterCompletion() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cleanUp() {
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* Copyright 1999-2019 Seata.io Group.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package io.seata.saga.engine.mock;
|
||||
|
||||
import io.seata.saga.engine.exception.EngineExecutionException;
|
||||
import io.seata.saga.engine.pcext.InterceptableStateHandler;
|
||||
import io.seata.saga.engine.pcext.StateHandlerInterceptor;
|
||||
import io.seata.saga.proctrl.ProcessContext;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author lorne.cl
|
||||
*/
|
||||
public class MockStateHandlerInterceptor implements StateHandlerInterceptor {
|
||||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(MockStateHandlerInterceptor.class);
|
||||
|
||||
@Override
|
||||
public void preProcess(ProcessContext context) throws EngineExecutionException {
|
||||
LOGGER.info("test StateHandlerInterceptor preProcess");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postProcess(ProcessContext context, Exception e) throws EngineExecutionException {
|
||||
LOGGER.info("test StateHandlerInterceptor postProcess");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean match(Class<? extends InterceptableStateHandler> clazz) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Copyright 1999-2019 Seata.io Group.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package io.seata.saga.engine.mock;
|
||||
|
||||
import io.seata.saga.engine.exception.EngineExecutionException;
|
||||
import io.seata.saga.engine.pcext.InterceptableStateRouter;
|
||||
import io.seata.saga.engine.pcext.StateRouterInterceptor;
|
||||
import io.seata.saga.proctrl.Instruction;
|
||||
import io.seata.saga.proctrl.ProcessContext;
|
||||
import io.seata.saga.statelang.domain.State;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author lorne.cl
|
||||
*/
|
||||
public class MockStateRouterInterceptor implements StateRouterInterceptor {
|
||||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(MockStateRouterInterceptor.class);
|
||||
|
||||
@Override
|
||||
public void preRoute(ProcessContext context, State state) throws EngineExecutionException {
|
||||
LOGGER.info("test StateRouterInterceptor preRoute");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postRoute(ProcessContext context, State state, Instruction instruction, Exception e) throws EngineExecutionException {
|
||||
LOGGER.info("test StateRouterInterceptor postRoute");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean match(Class<? extends InterceptableStateRouter> clazz) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
621
test/src/test/java/io/seata/xa/XAModeTest2.java
Normal file
621
test/src/test/java/io/seata/xa/XAModeTest2.java
Normal file
@@ -0,0 +1,621 @@
|
||||
/*
|
||||
* Copyright 1999-2019 Seata.io Group.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package io.seata.xa;
|
||||
|
||||
import com.alibaba.druid.pool.DruidDataSource;
|
||||
import com.alibaba.druid.pool.xa.DruidXADataSource;
|
||||
import com.alibaba.druid.util.JdbcUtils;
|
||||
import com.mysql.jdbc.jdbc2.optional.MysqlXADataSource;
|
||||
import io.seata.core.context.RootContext;
|
||||
import io.seata.core.exception.TransactionException;
|
||||
import io.seata.core.model.BranchStatus;
|
||||
import io.seata.core.model.BranchType;
|
||||
import io.seata.core.model.Resource;
|
||||
import io.seata.rm.DefaultResourceManager;
|
||||
import io.seata.rm.datasource.xa.AbstractDataSourceProxyXA;
|
||||
import io.seata.rm.datasource.xa.DataSourceProxyXA;
|
||||
import io.seata.rm.datasource.xa.DataSourceProxyXANative;
|
||||
import io.seata.rm.datasource.xa.ResourceManagerXA;
|
||||
import io.seata.rm.datasource.xa.XAXid;
|
||||
import io.seata.rm.datasource.xa.XAXidBuilder;
|
||||
import io.seata.spring.annotation.GlobalTransactionScanner;
|
||||
import io.seata.tm.api.GlobalTransaction;
|
||||
import io.seata.tm.api.GlobalTransactionContext;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.Disabled;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.postgresql.xa.PGXADataSource;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
import javax.sql.XAConnection;
|
||||
import javax.sql.XADataSource;
|
||||
import javax.transaction.xa.XAException;
|
||||
import javax.transaction.xa.XAResource;
|
||||
import javax.transaction.xa.Xid;
|
||||
import java.lang.reflect.Method;
|
||||
import java.sql.Connection;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Statement;
|
||||
|
||||
public class XAModeTest2 {
|
||||
|
||||
private static final int testRecordId = 888;
|
||||
private static final String testRecordName = "xxx";
|
||||
private static final long testTid = 1582688600006L;
|
||||
private static final String mockXid = "127.0.0.1:8091:" + testTid;
|
||||
private static final long mockBranchId = testTid + 1;
|
||||
|
||||
private static final String pg_jdbcUrl = "jdbc:postgresql://127.0.0.1:5432/postgres";
|
||||
private static final String pg_username = "postgres";
|
||||
private static final String pg_password = "postgres";
|
||||
private static final String pg_driverClassName = JdbcUtils.POSTGRESQL_DRIVER;
|
||||
|
||||
private static final String mysql_jdbcUrl = "jdbc:mysql://127.0.0.1:3306/demo";
|
||||
private static final String mysql_username = "demo";
|
||||
private static final String mysql_password = "demo";
|
||||
private static final String mysql_driverClassName = JdbcUtils.MYSQL_DRIVER;
|
||||
|
||||
private static final String mysql8_jdbcUrl = "jdbc:mysql://0.0.0.0:3306/demo?useUnicode=true&characterEncoding=utf-8&useSSL=false";
|
||||
private static final String mysql8_username = "demo";
|
||||
private static final String mysql8_password = "demo";
|
||||
private static final String mysql8_driverClassName = JdbcUtils.MYSQL_DRIVER_6;
|
||||
|
||||
private static final String oracle_jdbcUrl = "jdbc:oracle:thin:@localhost:1521:xe";
|
||||
private static final String oracle_username = "demo";
|
||||
private static final String oracle_password = "demo";
|
||||
private static final String oracle_driverClassName = JdbcUtils.ORACLE_DRIVER;
|
||||
|
||||
// Test on different DB, including: MySQL(5.7, 8.0), PostgreSQL(11), Oracle(11)
|
||||
private static final String dbType = JdbcUtils.MYSQL;
|
||||
|
||||
private static final boolean nativeXA = false;
|
||||
|
||||
private static final boolean mySQL8 = false;
|
||||
|
||||
private DruidDataSource createNewDruidDataSource() throws Throwable {
|
||||
DruidDataSource druidDataSource = new DruidDataSource();
|
||||
initDruidDataSource(druidDataSource);
|
||||
return druidDataSource;
|
||||
|
||||
}
|
||||
|
||||
private DruidXADataSource createNewDruidXADataSource() throws Throwable {
|
||||
DruidXADataSource druidDataSource = new DruidXADataSource();
|
||||
initDruidDataSource(druidDataSource);
|
||||
return druidDataSource;
|
||||
|
||||
}
|
||||
|
||||
private XADataSource createNewNativeXADataSource() throws Throwable {
|
||||
if (dbType.equalsIgnoreCase(JdbcUtils.POSTGRESQL)) {
|
||||
PGXADataSource pgxaDataSource = new PGXADataSource();
|
||||
pgxaDataSource.setUrl(pg_jdbcUrl);
|
||||
pgxaDataSource.setUser(pg_username);
|
||||
pgxaDataSource.setPassword(pg_password);
|
||||
return pgxaDataSource;
|
||||
|
||||
} else if (dbType.equalsIgnoreCase(JdbcUtils.MYSQL)) {
|
||||
MysqlXADataSource mysqlXADataSource = new MysqlXADataSource();
|
||||
if (mySQL8) {
|
||||
mysqlXADataSource.setURL(mysql8_jdbcUrl);
|
||||
mysqlXADataSource.setUser(mysql8_username);
|
||||
mysqlXADataSource.setPassword(mysql8_username);
|
||||
|
||||
} else {
|
||||
mysqlXADataSource.setURL(mysql_jdbcUrl);
|
||||
mysqlXADataSource.setUser(mysql_username);
|
||||
mysqlXADataSource.setPassword(mysql_username);
|
||||
}
|
||||
return mysqlXADataSource;
|
||||
|
||||
} else if (dbType.equalsIgnoreCase(JdbcUtils.ORACLE)) {
|
||||
return createOracleXADataSource();
|
||||
|
||||
} else {
|
||||
throw new IllegalAccessError("Unknown dbType: " + dbType);
|
||||
}
|
||||
}
|
||||
|
||||
private XADataSource createOracleXADataSource() {
|
||||
try {
|
||||
Class oracleXADataSourceClass = Class.forName("oracle.jdbc.xa.client.OracleXADataSource");
|
||||
XADataSource xaDataSource = (XADataSource)oracleXADataSourceClass.newInstance();
|
||||
|
||||
Method setURLMethod = oracleXADataSourceClass.getMethod("setURL", String.class);
|
||||
setURLMethod.invoke(xaDataSource, oracle_jdbcUrl);
|
||||
|
||||
Method setUserMethod = oracleXADataSourceClass.getMethod("setUser", String.class);
|
||||
setUserMethod.invoke(xaDataSource, oracle_username);
|
||||
|
||||
Method setPasswordMethod = oracleXADataSourceClass.getMethod("setPassword", String.class);
|
||||
setPasswordMethod.invoke(xaDataSource, oracle_password);
|
||||
|
||||
Method setDriverTypeMethod = oracleXADataSourceClass.getMethod("setDriverType", String.class);
|
||||
setDriverTypeMethod.invoke(xaDataSource, oracle_driverClassName);
|
||||
|
||||
return xaDataSource;
|
||||
|
||||
} catch (Exception ex) {
|
||||
ex.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void initDruidDataSource(DruidDataSource druidDataSource) throws Throwable {
|
||||
druidDataSource.setDbType(dbType);
|
||||
if (dbType.equalsIgnoreCase(JdbcUtils.POSTGRESQL)) {
|
||||
druidDataSource.setUrl(pg_jdbcUrl);
|
||||
druidDataSource.setUsername(pg_username);
|
||||
druidDataSource.setPassword(pg_password);
|
||||
druidDataSource.setDriverClassName(pg_driverClassName);
|
||||
|
||||
} else if (dbType.equalsIgnoreCase(JdbcUtils.MYSQL)) {
|
||||
if (mySQL8) {
|
||||
druidDataSource.setUrl(mysql8_jdbcUrl);
|
||||
druidDataSource.setUsername(mysql8_username);
|
||||
druidDataSource.setPassword(mysql8_password);
|
||||
druidDataSource.setDriverClassName(mysql8_driverClassName);
|
||||
|
||||
} else {
|
||||
druidDataSource.setUrl(mysql_jdbcUrl);
|
||||
druidDataSource.setUsername(mysql_username);
|
||||
druidDataSource.setPassword(mysql_password);
|
||||
druidDataSource.setDriverClassName(mysql_driverClassName);
|
||||
}
|
||||
|
||||
} else if (dbType.equalsIgnoreCase(JdbcUtils.ORACLE)) {
|
||||
druidDataSource.setUrl(oracle_jdbcUrl);
|
||||
druidDataSource.setUsername(oracle_username);
|
||||
druidDataSource.setPassword(oracle_password);
|
||||
druidDataSource.setDriverClassName(oracle_driverClassName);
|
||||
|
||||
} else {
|
||||
throw new IllegalAccessError("Unknown dbType: " + dbType);
|
||||
}
|
||||
druidDataSource.init();
|
||||
}
|
||||
|
||||
private void initRM() throws Throwable {
|
||||
// init RM
|
||||
DefaultResourceManager.get();
|
||||
// mock the RM of XA
|
||||
DefaultResourceManager.mockResourceManager(BranchType.XA, new ResourceManagerXA() {
|
||||
@Override
|
||||
public void registerResource(Resource resource) {
|
||||
dataSourceCache.put(resource.getResourceId(), resource);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long branchRegister(BranchType branchType, String resourceId, String clientId, String xid,
|
||||
String applicationData, String lockKeys) throws TransactionException {
|
||||
return mockBranchId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void branchReport(BranchType branchType, String xid, long branchId, BranchStatus status,
|
||||
String applicationData) throws TransactionException {
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
@Disabled
|
||||
public void testAllInOne() throws Throwable {
|
||||
testCleanXARecover();
|
||||
doTestXAModeNormalPrepareData();
|
||||
doTestXAModeNormalCaseAllInOne(mockXid, mockBranchId);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Disabled
|
||||
public void testGlobalCommitOnDifferentDataSource() throws Throwable {
|
||||
testCleanXARecover();
|
||||
doTestXAModeNormalPrepareData();
|
||||
doTestXAModeNormalCasePhase1(mockXid, mockBranchId);
|
||||
// Use new DataSource in phase 2
|
||||
doTestXAModeNormalCasePhase2(true, mockXid, mockBranchId);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Disabled
|
||||
public void testGlobalRollbackOnDifferentDataSource() throws Throwable {
|
||||
testCleanXARecover();
|
||||
doTestXAModeNormalPrepareData();
|
||||
doTestXAModeNormalCasePhase1(mockXid, mockBranchId);
|
||||
// Use new DataSource in phase 2
|
||||
doTestXAModeNormalCasePhase2(false, mockXid, mockBranchId);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Disabled
|
||||
public void testOnlyPhase1() throws Throwable {
|
||||
testCleanXARecover();
|
||||
doTestXAModeNormalPrepareData();
|
||||
doTestXAModeNormalCasePhase1(mockXid, mockBranchId);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Disabled
|
||||
public void testOnlyPhase2Commit() throws Throwable {
|
||||
doTestXAModeNormalCasePhase2(true, mockXid, mockBranchId);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Disabled
|
||||
public void testOnlyPhase2Rollback() throws Throwable {
|
||||
doTestXAModeNormalCasePhase2(false, mockXid, mockBranchId);
|
||||
}
|
||||
|
||||
private void doTestXAModeNormalPrepareData() throws Throwable {
|
||||
// init DataSource: helper
|
||||
DruidDataSource helperDS = createNewDruidDataSource();
|
||||
|
||||
// prepare data for test: make sure no test record there
|
||||
Connection helperConn = helperDS.getConnection();
|
||||
Statement helperStat = helperConn.createStatement();
|
||||
ResultSet helperRes = null;
|
||||
helperStat.execute("delete from test where id = " + testRecordId);
|
||||
helperStat.close();
|
||||
helperConn.close();
|
||||
|
||||
}
|
||||
|
||||
private void doTestXAModeNormalCasePhase2(boolean globalCommit, String mockXid, Long mockBranchId) throws Throwable {
|
||||
// init DataSource: helper
|
||||
DruidDataSource helperDS = createNewDruidDataSource();
|
||||
|
||||
Connection helperConn = null;
|
||||
Statement helperStat = null;
|
||||
ResultSet helperRes = null;
|
||||
|
||||
// init RM
|
||||
initRM();
|
||||
|
||||
AbstractDataSourceProxyXA dataSourceProxyXA = null;
|
||||
if (nativeXA) {
|
||||
// init XADataSource runnerXA
|
||||
XADataSource runnerXADS = createNewNativeXADataSource();
|
||||
dataSourceProxyXA = new DataSourceProxyXANative(runnerXADS);
|
||||
} else {
|
||||
// init DataSource: runner
|
||||
DruidDataSource runnerDS = createNewDruidDataSource();
|
||||
dataSourceProxyXA = new DataSourceProxyXA(runnerDS);
|
||||
}
|
||||
|
||||
// Global Tx Phase 2:
|
||||
if (globalCommit) {
|
||||
DefaultResourceManager.get().branchCommit(dataSourceProxyXA.getBranchType(), mockXid, mockBranchId,
|
||||
dataSourceProxyXA.getResourceId(), null);
|
||||
|
||||
// have a check
|
||||
helperConn = helperDS.getConnection();
|
||||
helperStat = helperConn.createStatement();
|
||||
helperRes = helperStat.executeQuery("select * from test where id = " + testRecordId);
|
||||
// should see the test record now
|
||||
Assertions.assertTrue(helperRes.next());
|
||||
Assertions.assertEquals(helperRes.getInt(1), testRecordId);
|
||||
Assertions.assertEquals(helperRes.getString(2), testRecordName);
|
||||
helperRes.close();
|
||||
helperStat.close();
|
||||
helperConn.close();
|
||||
|
||||
} else {
|
||||
DefaultResourceManager.get().branchRollback(dataSourceProxyXA.getBranchType(), mockXid, mockBranchId,
|
||||
dataSourceProxyXA.getResourceId(), null);
|
||||
|
||||
// have a check
|
||||
helperConn = helperDS.getConnection();
|
||||
helperStat = helperConn.createStatement();
|
||||
helperRes = helperStat.executeQuery("select * from test where id = " + testRecordId);
|
||||
// should NOT see the test record now
|
||||
Assertions.assertFalse(helperRes.next());
|
||||
helperRes.close();
|
||||
helperStat.close();
|
||||
helperConn.close();
|
||||
|
||||
}
|
||||
System.out.println("Phase2 looks good!");
|
||||
}
|
||||
|
||||
private void doTestXAModeNormalCasePhase1(String mockXid, Long mockBranchId) throws Throwable {
|
||||
// init DataSource: helper
|
||||
DruidDataSource helperDS = createNewDruidDataSource();
|
||||
|
||||
Connection helperConn = null;
|
||||
Statement helperStat = null;
|
||||
ResultSet helperRes = null;
|
||||
|
||||
// init RM
|
||||
initRM();
|
||||
|
||||
AbstractDataSourceProxyXA dataSourceProxyXA = null;
|
||||
if (nativeXA) {
|
||||
// init XADataSource runnerXA
|
||||
XADataSource runnerXADS = createNewNativeXADataSource();
|
||||
dataSourceProxyXA = new DataSourceProxyXANative(runnerXADS);
|
||||
} else {
|
||||
// init DataSource: runner
|
||||
DruidDataSource runnerDS = createNewDruidDataSource();
|
||||
dataSourceProxyXA = new DataSourceProxyXA(runnerDS);
|
||||
}
|
||||
|
||||
// Global Tx Phase 1:
|
||||
RootContext.bind(mockXid);
|
||||
Connection testConn = dataSourceProxyXA.getConnection();
|
||||
Statement testStat = testConn.createStatement();
|
||||
// >>> insert the test record with XA mode
|
||||
testStat.execute("insert into test(id, name) values(" + testRecordId + ", '" + testRecordName + "')");
|
||||
// >>> close the statement and connection
|
||||
testStat.close();
|
||||
testConn.close();
|
||||
RootContext.unbind();
|
||||
|
||||
// have a check
|
||||
helperConn = helperDS.getConnection();
|
||||
helperStat = helperConn.createStatement();
|
||||
helperRes = helperStat.executeQuery("select * from test where id = " + testRecordId);
|
||||
// should NOT see the record(id=888) now
|
||||
Assertions.assertFalse(helperRes.next());
|
||||
helperRes.close();
|
||||
helperStat.close();
|
||||
helperConn.close();
|
||||
|
||||
if (JdbcUtils.MYSQL.equals(dbType)) {
|
||||
XAXid xaXid = XAXidBuilder.build(mockXid, mockBranchId);
|
||||
dataSourceProxyXA.forceClosePhysicalConnection(xaXid);
|
||||
}
|
||||
|
||||
System.out.println("Phase1 looks good!");
|
||||
}
|
||||
|
||||
private void doTestXAModeNormalCaseAllInOne(String mockXid, Long mockBranchId) throws Throwable {
|
||||
// init DataSource: helper
|
||||
DruidDataSource helperDS = createNewDruidDataSource();
|
||||
|
||||
Connection helperConn = null;
|
||||
Statement helperStat = null;
|
||||
ResultSet helperRes = null;
|
||||
|
||||
// init RM
|
||||
initRM();
|
||||
|
||||
AbstractDataSourceProxyXA dataSourceProxyXA = null;
|
||||
if (nativeXA) {
|
||||
// init XADataSource runnerXA
|
||||
XADataSource runnerXADS = createNewNativeXADataSource();
|
||||
dataSourceProxyXA = new DataSourceProxyXANative(runnerXADS);
|
||||
} else {
|
||||
// init DataSource: runner
|
||||
DruidDataSource runnerDS = createNewDruidDataSource();
|
||||
dataSourceProxyXA = new DataSourceProxyXA(runnerDS);
|
||||
}
|
||||
|
||||
// Global Tx Phase 1:
|
||||
RootContext.bind(mockXid);
|
||||
Connection testConn = dataSourceProxyXA.getConnection();
|
||||
Statement testStat = testConn.createStatement();
|
||||
// >>> insert the test record with XA mode
|
||||
testStat.execute("insert into test(id, name) values(" + testRecordId + ", '" + testRecordName + "')");
|
||||
// >>> close the statement and connection
|
||||
testStat.close();
|
||||
testConn.close();
|
||||
RootContext.unbind();
|
||||
|
||||
// have a check
|
||||
helperConn = helperDS.getConnection();
|
||||
helperStat = helperConn.createStatement();
|
||||
helperRes = helperStat.executeQuery("select * from test where id = " + testRecordId);
|
||||
// should NOT see the record(id=888) now
|
||||
Assertions.assertFalse(helperRes.next());
|
||||
helperRes.close();
|
||||
helperStat.close();
|
||||
helperConn.close();
|
||||
|
||||
// Global Tx Phase 2: run phase 2 with the same runner DS
|
||||
DefaultResourceManager.get().branchCommit(dataSourceProxyXA.getBranchType(), mockXid, mockBranchId,
|
||||
dataSourceProxyXA.getResourceId(), null);
|
||||
|
||||
// have a check
|
||||
helperConn = helperDS.getConnection();
|
||||
helperStat = helperConn.createStatement();
|
||||
helperRes = helperStat.executeQuery("select * from test where id = " + testRecordId);
|
||||
// should see the test record now
|
||||
Assertions.assertTrue(helperRes.next());
|
||||
Assertions.assertEquals(helperRes.getInt(1), testRecordId);
|
||||
Assertions.assertEquals(helperRes.getString(2), testRecordName);
|
||||
helperRes.close();
|
||||
helperStat.close();
|
||||
helperConn.close();
|
||||
|
||||
System.out.println("All in one looks good!");
|
||||
}
|
||||
|
||||
@Test
|
||||
@Disabled
|
||||
public void testXid() throws Throwable {
|
||||
XAXid xaXid = XAXidBuilder.build(mockXid, mockBranchId);
|
||||
|
||||
XAXid retrievedXAXid = XAXidBuilder.build(xaXid.getGlobalTransactionId(), xaXid.getBranchQualifier());
|
||||
String retrievedXid = retrievedXAXid.getGlobalXid();
|
||||
long retrievedBranchId = retrievedXAXid.getBranchId();
|
||||
|
||||
Assertions.assertEquals(mockXid, retrievedXid);
|
||||
Assertions.assertEquals(mockBranchId, retrievedBranchId);
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
@Disabled
|
||||
public void testCleanXARecover() throws Throwable {
|
||||
XADataSource xaDataSource = createNewNativeXADataSource();
|
||||
|
||||
XAConnection xaConnection = xaDataSource.getXAConnection();
|
||||
XAResource xaResource = xaConnection.getXAResource();
|
||||
|
||||
Xid[] xids = xaResource.recover(XAResource.TMSTARTRSCAN|XAResource.TMENDRSCAN);
|
||||
for (Xid xid : xids) {
|
||||
try {
|
||||
xaResource.rollback(xid);
|
||||
} catch (XAException xae) {
|
||||
xae.printStackTrace();
|
||||
}
|
||||
}
|
||||
System.out.println("Unfinished XA branches are ALL cleaned!");
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
@Disabled
|
||||
public void testXADataSourceNative() throws Throwable {
|
||||
XADataSource nativeXADataSource = createOracleXADataSource();
|
||||
|
||||
XAConnection xaConnection = nativeXADataSource.getXAConnection();
|
||||
XAResource xaResource = xaConnection.getXAResource();
|
||||
Xid xid = XAXidBuilder.build("127.0.0.1:8091:1234", 1235L);
|
||||
xaResource.start(xid, XAResource.TMNOFLAGS);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Disabled
|
||||
public void testXADataSourceNormal() throws Throwable {
|
||||
DruidXADataSource druidDataSource = new DruidXADataSource();
|
||||
druidDataSource.setUrl(oracle_jdbcUrl);
|
||||
druidDataSource.setUsername(oracle_username);
|
||||
druidDataSource.setPassword(oracle_password);
|
||||
druidDataSource.setDriverClassName(oracle_driverClassName);
|
||||
|
||||
XAConnection xaConnection = druidDataSource.getXAConnection();
|
||||
XAResource xaResource = xaConnection.getXAResource();
|
||||
Xid xid = XAXidBuilder.build("127.0.0.1:8091:1234", 1235L);
|
||||
// Since issue of Druid(https://github.com/alibaba/druid/issues/3707), XA start will fail.
|
||||
xaResource.start(xid, XAResource.TMNOFLAGS);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Disabled
|
||||
// Should RUN with local Seata Server
|
||||
public void testStandardAppGlobalCommit() throws Throwable {
|
||||
testCleanXARecover();
|
||||
doTestXAModeNormalPrepareData();
|
||||
|
||||
// Create a standard proxy according to non-XA data source
|
||||
DataSource ds = createDataSourceProxyXA();
|
||||
// Create a global tx
|
||||
GlobalTransaction gtx = createGlobalTransaction();
|
||||
|
||||
gtx.begin();
|
||||
runInGlobalTx(ds);
|
||||
gtx.commit();
|
||||
|
||||
Thread.sleep(5000);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Disabled
|
||||
// Should RUN with local Seata Server
|
||||
public void testXANativeAppGlobalCommit() throws Throwable {
|
||||
testCleanXARecover();
|
||||
doTestXAModeNormalPrepareData();
|
||||
|
||||
// Create a native proxy according to XA data source
|
||||
DataSource ds = createDataSourceProxyXANative();
|
||||
// Create a global tx
|
||||
GlobalTransaction gtx = createGlobalTransaction();
|
||||
|
||||
gtx.begin();
|
||||
runInGlobalTx(ds);
|
||||
gtx.commit();
|
||||
|
||||
Thread.sleep(5000);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Disabled
|
||||
// Should RUN with local Seata Server
|
||||
public void testStandardAppGlobalRollback() throws Throwable {
|
||||
testCleanXARecover();
|
||||
doTestXAModeNormalPrepareData();
|
||||
|
||||
// Create a standard proxy according to non-XA data source
|
||||
DataSource ds = createDataSourceProxyXA();
|
||||
// Create a global tx
|
||||
GlobalTransaction gtx = createGlobalTransaction();
|
||||
|
||||
gtx.begin();
|
||||
runInGlobalTx(ds);
|
||||
gtx.rollback();
|
||||
|
||||
Thread.sleep(5000);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Disabled
|
||||
// Should RUN with local Seata Server
|
||||
public void testXANativeAppGlobalRollback() throws Throwable {
|
||||
testCleanXARecover();
|
||||
doTestXAModeNormalPrepareData();
|
||||
|
||||
// Create a native proxy according to XA data source
|
||||
DataSource ds = createDataSourceProxyXANative();
|
||||
// Create a global tx
|
||||
GlobalTransaction gtx = createGlobalTransaction();
|
||||
|
||||
gtx.begin();
|
||||
runInGlobalTx(ds);
|
||||
gtx.rollback();
|
||||
|
||||
Thread.sleep(5000);
|
||||
}
|
||||
|
||||
private void runInGlobalTx(DataSource ds) throws SQLException {
|
||||
System.out.println(RootContext.getXID());
|
||||
|
||||
Connection testConn = ds.getConnection();
|
||||
Statement testStat = testConn.createStatement();
|
||||
// >>> insert the test record with XA mode
|
||||
testStat.execute("insert into test(id, name) values(" + testRecordId + ", '" + testRecordName + "')");
|
||||
// >>> close the statement and connection
|
||||
testStat.close();
|
||||
testConn.close();
|
||||
|
||||
}
|
||||
|
||||
private DataSourceProxyXANative createDataSourceProxyXANative() throws Throwable {
|
||||
XADataSource originalDS = createNewDruidXADataSource();
|
||||
DataSourceProxyXANative dataSourceProxyXA = new DataSourceProxyXANative(originalDS);
|
||||
return dataSourceProxyXA;
|
||||
}
|
||||
|
||||
private DataSourceProxyXA createDataSourceProxyXA() throws Throwable {
|
||||
DataSource originalDS = createNewDruidDataSource();
|
||||
DataSourceProxyXA dataSourceProxyXA = new DataSourceProxyXA(originalDS);
|
||||
return dataSourceProxyXA;
|
||||
}
|
||||
|
||||
private GlobalTransaction createGlobalTransaction() {
|
||||
String vgroup = "my_test_tx_group";
|
||||
GlobalTransactionScanner scanner = new GlobalTransactionScanner(vgroup);
|
||||
scanner.afterPropertiesSet();
|
||||
|
||||
GlobalTransaction gtx = GlobalTransactionContext.getCurrentOrCreate();
|
||||
return gtx;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
io.seata.common.loader.LoaderTestImpl1
|
||||
io.seata.common.loader.LoaderTestImpl2
|
||||
@@ -0,0 +1 @@
|
||||
io.seata.saga.engine.mock.MockStateHandlerInterceptor
|
||||
@@ -0,0 +1 @@
|
||||
io.seata.saga.engine.mock.MockStateRouterInterceptor
|
||||
53
test/src/test/resources/basic-test-context.xml
Normal file
53
test/src/test/resources/basic-test-context.xml
Normal file
@@ -0,0 +1,53 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
~ Copyright 1999-2019 Seata.io Group.
|
||||
~
|
||||
~ Licensed under the Apache License, Version 2.0 (the "License");
|
||||
~ you may not use this file except in compliance with the License.
|
||||
~ You may obtain a copy of the License at
|
||||
~
|
||||
~ http://www.apache.org/licenses/LICENSE-2.0
|
||||
~
|
||||
~ Unless required by applicable law or agreed to in writing, software
|
||||
~ distributed under the License is distributed on an "AS IS" BASIS,
|
||||
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
~ See the License for the specific language governing permissions and
|
||||
~ limitations under the License.
|
||||
-->
|
||||
|
||||
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans
|
||||
http://www.springframework.org/schema/beans/spring-beans.xsd">
|
||||
|
||||
<bean name="mysqlDataSource" class="com.alibaba.druid.pool.DruidDataSource"
|
||||
init-method="init" destroy-method="close">
|
||||
<property name="url" value="jdbc:mysql://xxx:3306/demo"/>
|
||||
<property name="username" value="xxx"/>
|
||||
<property name="password" value="xxx"/>
|
||||
<property name="driverClassName" value="com.mysql.jdbc.Driver" />
|
||||
<property name="initialSize" value="0" />
|
||||
<property name="maxActive" value="180" />
|
||||
<property name="minIdle" value="0" />
|
||||
<property name="maxWait" value="60000" />
|
||||
<property name="validationQuery" value="Select 'x' from DUAL" />
|
||||
<property name="testOnBorrow" value="false" />
|
||||
<property name="testOnReturn" value="false" />
|
||||
<property name="testWhileIdle" value="true" />
|
||||
<property name="timeBetweenEvictionRunsMillis" value="60000" />
|
||||
<property name="minEvictableIdleTimeMillis" value="25200000" />
|
||||
<property name="removeAbandoned" value="true" />
|
||||
<property name="removeAbandonedTimeout" value="1800" />
|
||||
<property name="logAbandoned" value="true" />
|
||||
<property name="filters" value="mergeStat" />
|
||||
</bean>
|
||||
|
||||
<bean id="dataSourceProxy" class="io.seata.rm.datasource.DataSourceProxy">
|
||||
<constructor-arg ref="mysqlDataSource" />
|
||||
</bean>
|
||||
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
|
||||
<property name="dataSource" ref="dataSourceProxy" />
|
||||
</bean>
|
||||
<bean id="directJdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
|
||||
<property name="dataSource" ref="mysqlDataSource" />
|
||||
</bean>
|
||||
|
||||
</beans>
|
||||
13
test/src/test/resources/biz.sql
Normal file
13
test/src/test/resources/biz.sql
Normal file
@@ -0,0 +1,13 @@
|
||||
CREATE TABLE `user0` (
|
||||
`id` int(11) NOT NULL,
|
||||
`name` varchar(255) DEFAULT NULL,
|
||||
`gmt` datetime DEFAULT NULL,
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
|
||||
CREATE TABLE `user1` (
|
||||
`id` int(11) NOT NULL,
|
||||
`name` varchar(255) DEFAULT NULL,
|
||||
`gmt` datetime DEFAULT NULL,
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
22
test/src/test/resources/file.conf
Normal file
22
test/src/test/resources/file.conf
Normal file
@@ -0,0 +1,22 @@
|
||||
service {
|
||||
#transaction service group mapping
|
||||
vgroupMapping.my_test_tx_group = "default"
|
||||
#only support when registry.type=file, please don't set multiple addresses
|
||||
default.grouplist = "127.0.0.1:8091"
|
||||
#disable seata
|
||||
disableGlobalTransaction = false
|
||||
}
|
||||
|
||||
client {
|
||||
rm {
|
||||
reportSuccessEnable = false
|
||||
sagaBranchRegisterEnable = false
|
||||
sagaJsonParser = jackson
|
||||
sagaRetryPersistModeUpdate = false
|
||||
sagaCompensatePersistModeUpdate = false
|
||||
}
|
||||
loadBalance {
|
||||
type = "RandomLoadBalance"
|
||||
virtualNodes = 10
|
||||
}
|
||||
}
|
||||
29
test/src/test/resources/logback.xml
Normal file
29
test/src/test/resources/logback.xml
Normal file
@@ -0,0 +1,29 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<!--
|
||||
~ Copyright 1999-2019 Seata.io Group.
|
||||
~
|
||||
~ Licensed under the Apache License, Version 2.0 (the "License");
|
||||
~ you may not use this file except in compliance with the License.
|
||||
~ You may obtain a copy of the License at
|
||||
~
|
||||
~ http://www.apache.org/licenses/LICENSE-2.0
|
||||
~
|
||||
~ Unless required by applicable law or agreed to in writing, software
|
||||
~ distributed under the License is distributed on an "AS IS" BASIS,
|
||||
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
~ See the License for the specific language governing permissions and
|
||||
~ limitations under the License.
|
||||
-->
|
||||
|
||||
<configuration>
|
||||
<!-- Console output log -->
|
||||
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
|
||||
<layout class="ch.qos.logback.classic.PatternLayout">
|
||||
<Pattern>%date [SEATA] [%thread] %-5level %logger{80} - %msg%n</Pattern>
|
||||
</layout>
|
||||
</appender>
|
||||
|
||||
<root level="INFO">
|
||||
<appender-ref ref="STDOUT" />
|
||||
</root>
|
||||
</configuration>
|
||||
73
test/src/test/resources/registry.conf
Normal file
73
test/src/test/resources/registry.conf
Normal file
@@ -0,0 +1,73 @@
|
||||
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"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<!--
|
||||
~ Copyright 1999-2019 Seata.io Group.
|
||||
~
|
||||
~ Licensed under the Apache License, Version 2.0 (the "License");
|
||||
~ you may not use this file except in compliance with the License.
|
||||
~ You may obtain a copy of the License at
|
||||
~
|
||||
~ http://www.apache.org/licenses/LICENSE-2.0
|
||||
~
|
||||
~ Unless required by applicable law or agreed to in writing, software
|
||||
~ distributed under the License is distributed on an "AS IS" BASIS,
|
||||
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
~ See the License for the specific language governing permissions and
|
||||
~ limitations under the License.
|
||||
-->
|
||||
|
||||
<beans xmlns="http://www.springframework.org/schema/beans"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:context="http://www.springframework.org/schema/context"
|
||||
xmlns:jdbc="http://www.springframework.org/schema/jdbc"
|
||||
xsi:schemaLocation="
|
||||
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
|
||||
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
|
||||
http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc.xsd">
|
||||
|
||||
<bean id="dataSource" class="org.h2.jdbcx.JdbcConnectionPool" destroy-method="dispose">
|
||||
<constructor-arg>
|
||||
<bean class="org.h2.jdbcx.JdbcDataSource">
|
||||
<property name="URL" value="jdbc:h2:mem:seata_saga" />
|
||||
<property name="user" value="sa" />
|
||||
<property name="password" value="sa" />
|
||||
</bean>
|
||||
</constructor-arg>
|
||||
</bean>
|
||||
|
||||
<!-- 初始化数据表结构 -->
|
||||
<jdbc:initialize-database data-source="dataSource">
|
||||
<jdbc:script location="classpath:saga/sql/h2_init.sql" />
|
||||
</jdbc:initialize-database>
|
||||
|
||||
<bean id="stateMachineEngine" class="io.seata.saga.engine.impl.ProcessCtrlStateMachineEngine">
|
||||
<property name="stateMachineConfig" ref="dbStateMachineConfig"></property>
|
||||
</bean>
|
||||
<bean id="dbStateMachineConfig" class="io.seata.saga.engine.config.DbStateMachineConfig">
|
||||
<property name="dataSource" ref="dataSource"></property>
|
||||
<property name="tablePrefix" value="seata_"></property>
|
||||
<property name="resources" value="saga/statelang/*.json"></property>
|
||||
<property name="enableAsync" value="true"></property>
|
||||
<property name="threadPoolExecutor" ref="threadExecutor"></property>
|
||||
<property name="applicationId" value="test_saga"></property>
|
||||
<property name="txServiceGroup" value="my_test_tx_group"></property>
|
||||
<property name="sagaTransactionalTemplate" ref="mockSagaTransactionTemplate"/>
|
||||
</bean>
|
||||
<bean id="threadExecutor"
|
||||
class="org.springframework.scheduling.concurrent.ThreadPoolExecutorFactoryBean">
|
||||
<property name="threadNamePrefix" value="SAGA_ASYNC_EXE_" />
|
||||
<property name="corePoolSize" value="1" />
|
||||
<property name="maxPoolSize" value="20" />
|
||||
<property name="queueCapacity" value="100" />
|
||||
<property name="rejectedExecutionHandler" ref="callerRunsPolicy" />
|
||||
</bean>
|
||||
|
||||
<bean name="callerRunsPolicy" class="java.util.concurrent.ThreadPoolExecutor.CallerRunsPolicy">
|
||||
</bean>
|
||||
|
||||
<bean class="io.seata.saga.rm.StateMachineEngineHolder">
|
||||
<property name="stateMachineEngine" ref="stateMachineEngine"/>
|
||||
</bean>
|
||||
|
||||
|
||||
<bean id="demoService" class="io.seata.saga.engine.mock.DemoService"/>
|
||||
|
||||
<bean id="mockSagaTransactionTemplate" class="io.seata.saga.engine.mock.MockSagaTransactionTemplate"/>
|
||||
</beans>
|
||||
@@ -0,0 +1,77 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<!--
|
||||
~ Copyright 1999-2019 Seata.io Group.
|
||||
~
|
||||
~ Licensed under the Apache License, Version 2.0 (the "License");
|
||||
~ you may not use this file except in compliance with the License.
|
||||
~ You may obtain a copy of the License at
|
||||
~
|
||||
~ http://www.apache.org/licenses/LICENSE-2.0
|
||||
~
|
||||
~ Unless required by applicable law or agreed to in writing, software
|
||||
~ distributed under the License is distributed on an "AS IS" BASIS,
|
||||
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
~ See the License for the specific language governing permissions and
|
||||
~ limitations under the License.
|
||||
-->
|
||||
|
||||
<beans xmlns="http://www.springframework.org/schema/beans"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:context="http://www.springframework.org/schema/context"
|
||||
xmlns:jdbc="http://www.springframework.org/schema/jdbc"
|
||||
xsi:schemaLocation="
|
||||
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
|
||||
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
|
||||
http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc.xsd">
|
||||
|
||||
<bean id="dataSource" class="org.h2.jdbcx.JdbcConnectionPool" destroy-method="dispose">
|
||||
<constructor-arg>
|
||||
<bean class="org.h2.jdbcx.JdbcDataSource">
|
||||
<property name="URL" value="jdbc:h2:mem:seata_saga" />
|
||||
<property name="user" value="sa" />
|
||||
<property name="password" value="sa" />
|
||||
</bean>
|
||||
</constructor-arg>
|
||||
</bean>
|
||||
|
||||
<!-- 初始化数据表结构 -->
|
||||
<jdbc:initialize-database data-source="dataSource">
|
||||
<jdbc:script location="classpath:saga/sql/h2_init.sql" />
|
||||
</jdbc:initialize-database>
|
||||
|
||||
<bean id="stateMachineEngine" class="io.seata.saga.engine.impl.ProcessCtrlStateMachineEngine">
|
||||
<property name="stateMachineConfig" ref="dbStateMachineConfig"></property>
|
||||
</bean>
|
||||
<bean id="dbStateMachineConfig" class="io.seata.saga.engine.config.DbStateMachineConfig">
|
||||
<property name="dataSource" ref="dataSource"></property>
|
||||
<property name="resources" value="saga/statelang/*.json"></property>
|
||||
<property name="enableAsync" value="true"></property>
|
||||
<property name="threadPoolExecutor" ref="threadExecutor"></property>
|
||||
<property name="applicationId" value="test_saga"></property>
|
||||
<property name="txServiceGroup" value="my_test_tx_group"></property>
|
||||
<property name="sagaBranchRegisterEnable" value="false"></property>
|
||||
<property name="sagaJsonParser" value="jackson"></property>
|
||||
<property name="sagaRetryPersistModeUpdate" value="false" />
|
||||
<property name="sagaCompensatePersistModeUpdate" value="false" />
|
||||
</bean>
|
||||
|
||||
<bean id="threadExecutor"
|
||||
class="org.springframework.scheduling.concurrent.ThreadPoolExecutorFactoryBean">
|
||||
<property name="threadNamePrefix" value="SAGA_ASYNC_EXE_" />
|
||||
<property name="corePoolSize" value="20" />
|
||||
<property name="maxPoolSize" value="20" />
|
||||
<property name="queueCapacity" value="100" />
|
||||
<property name="rejectedExecutionHandler" ref="callerRunsPolicy" />
|
||||
</bean>
|
||||
|
||||
<bean name="callerRunsPolicy" class="java.util.concurrent.ThreadPoolExecutor.CallerRunsPolicy">
|
||||
</bean>
|
||||
|
||||
<bean class="io.seata.saga.rm.StateMachineEngineHolder">
|
||||
<property name="stateMachineEngine" ref="stateMachineEngine"/>
|
||||
</bean>
|
||||
|
||||
|
||||
<bean id="demoService" class="io.seata.saga.engine.mock.DemoService"/>
|
||||
</beans>
|
||||
@@ -0,0 +1,49 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<!--
|
||||
~ Copyright 1999-2019 Seata.io Group.
|
||||
~
|
||||
~ Licensed under the Apache License, Version 2.0 (the "License");
|
||||
~ you may not use this file except in compliance with the License.
|
||||
~ You may obtain a copy of the License at
|
||||
~
|
||||
~ http://www.apache.org/licenses/LICENSE-2.0
|
||||
~
|
||||
~ Unless required by applicable law or agreed to in writing, software
|
||||
~ distributed under the License is distributed on an "AS IS" BASIS,
|
||||
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
~ See the License for the specific language governing permissions and
|
||||
~ limitations under the License.
|
||||
-->
|
||||
|
||||
<beans xmlns="http://www.springframework.org/schema/beans"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:context="http://www.springframework.org/schema/context"
|
||||
xsi:schemaLocation="
|
||||
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
|
||||
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
|
||||
|
||||
|
||||
<bean id="stateMachineEngine" class="io.seata.saga.engine.impl.ProcessCtrlStateMachineEngine">
|
||||
<property name="stateMachineConfig" ref="defaultStateMachineConfig"></property>
|
||||
</bean>
|
||||
<bean id="defaultStateMachineConfig" class="io.seata.saga.engine.impl.DefaultStateMachineConfig">
|
||||
<property name="resources" value="saga/statelang/*.json"></property>
|
||||
<property name="enableAsync" value="true"></property>
|
||||
<property name="threadPoolExecutor" ref="threadExecutor" />
|
||||
</bean>
|
||||
<bean id="threadExecutor"
|
||||
class="org.springframework.scheduling.concurrent.ThreadPoolExecutorFactoryBean">
|
||||
<property name="threadNamePrefix" value="SAGA_ASYNC_EXE_" />
|
||||
<property name="corePoolSize" value="1" />
|
||||
<property name="maxPoolSize" value="20" />
|
||||
<property name="queueCapacity" value="100" />
|
||||
<property name="rejectedExecutionHandler" ref="callerRunsPolicy" />
|
||||
</bean>
|
||||
|
||||
<bean name="callerRunsPolicy" class="java.util.concurrent.ThreadPoolExecutor.CallerRunsPolicy">
|
||||
</bean>
|
||||
|
||||
|
||||
<bean id="demoService" class="io.seata.saga.engine.mock.DemoService"/>
|
||||
</beans>
|
||||
64
test/src/test/resources/saga/sql/db2_init.sql
Normal file
64
test/src/test/resources/saga/sql/db2_init.sql
Normal file
@@ -0,0 +1,64 @@
|
||||
create table seata_state_machine_def
|
||||
(
|
||||
id varchar(32) not null,
|
||||
name varchar(128) not null,
|
||||
tenant_id varchar(32) not null,
|
||||
app_name varchar(32) not null,
|
||||
type varchar(20),
|
||||
comment_ varchar(255),
|
||||
ver varchar(16) not null,
|
||||
gmt_create timestamp(3) not null,
|
||||
status varchar(2) not null,
|
||||
content clob(65536) inline length 2048,
|
||||
recover_strategy varchar(16),
|
||||
primary key(id)
|
||||
);
|
||||
|
||||
create table seata_state_machine_inst
|
||||
(
|
||||
id varchar(128) not null,
|
||||
machine_id varchar(32) not null,
|
||||
tenant_id varchar(32) not null,
|
||||
parent_id varchar(128),
|
||||
gmt_started timestamp(3) not null,
|
||||
business_key varchar(48),
|
||||
uni_business_key varchar(128) not null generated always as( --Unique index does not allow empty columns on DB2
|
||||
CASE
|
||||
WHEN "BUSINESS_KEY" IS NULL
|
||||
THEN "ID"
|
||||
ELSE "BUSINESS_KEY"
|
||||
END),
|
||||
start_params clob(65536) inline length 1024,
|
||||
gmt_end timestamp(3),
|
||||
excep blob(10240),
|
||||
end_params clob(65536) inline length 1024,
|
||||
status varchar(2),
|
||||
compensation_status varchar(2),
|
||||
is_running smallint,
|
||||
gmt_updated timestamp(3) not null,
|
||||
primary key(id)
|
||||
);
|
||||
create unique index state_machine_inst_unibuzkey on seata_state_machine_inst(uni_business_key, tenant_id);
|
||||
|
||||
create table seata_state_inst
|
||||
(
|
||||
id varchar(48) not null,
|
||||
machine_inst_id varchar(128) not null,
|
||||
name varchar(128) not null,
|
||||
type varchar(20),
|
||||
service_name varchar(128),
|
||||
service_method varchar(128),
|
||||
service_type varchar(16),
|
||||
business_key varchar(48),
|
||||
state_id_compensated_for varchar(50),
|
||||
state_id_retried_for varchar(50),
|
||||
gmt_started timestamp(3) not null,
|
||||
is_for_update smallint,
|
||||
input_params clob(65536) inline length 1024,
|
||||
output_params clob(65536) inline length 1024,
|
||||
status varchar(2) not null,
|
||||
excep blob(10240),
|
||||
gmt_updated timestamp(3),
|
||||
gmt_end timestamp(3),
|
||||
primary key(id, machine_inst_id)
|
||||
);
|
||||
58
test/src/test/resources/saga/sql/h2_init.sql
Normal file
58
test/src/test/resources/saga/sql/h2_init.sql
Normal file
@@ -0,0 +1,58 @@
|
||||
create table if not exists seata_state_machine_def
|
||||
(
|
||||
id varchar(32) not null comment 'id',
|
||||
name varchar(128) not null comment 'name',
|
||||
tenant_id varchar(32) not null comment 'tenant id',
|
||||
app_name varchar(32) not null comment 'application name',
|
||||
type varchar(20) comment 'state language type',
|
||||
comment_ varchar(255) comment 'comment',
|
||||
ver varchar(16) not null comment 'version',
|
||||
gmt_create timestamp(3) not null comment 'create time',
|
||||
status varchar(2) not null comment 'status(AC:active|IN:inactive)',
|
||||
content clob comment 'content',
|
||||
recover_strategy varchar(16) comment 'transaction recover strategy(compensate|retry)',
|
||||
primary key (id)
|
||||
);
|
||||
|
||||
create table if not exists seata_state_machine_inst
|
||||
(
|
||||
id varchar(128) not null comment 'id',
|
||||
machine_id varchar(32) not null comment 'state machine definition id',
|
||||
tenant_id varchar(32) not null comment 'tenant id',
|
||||
parent_id varchar(128) comment 'parent id',
|
||||
gmt_started timestamp(3) not null comment 'start time',
|
||||
business_key varchar(48) comment 'business key',
|
||||
start_params clob comment 'start parameters',
|
||||
gmt_end timestamp(3) comment 'end time',
|
||||
excep blob comment 'exception',
|
||||
end_params clob comment 'end parameters',
|
||||
status varchar(2) comment 'status(SU succeed|FA failed|UN unknown|SK skipped|RU running)',
|
||||
compensation_status varchar(2) comment 'compensation status(SU succeed|FA failed|UN unknown|SK skipped|RU running)',
|
||||
is_running tinyint(1) comment 'is running(0 no|1 yes)',
|
||||
gmt_updated timestamp(3) not null,
|
||||
primary key (id),
|
||||
unique key unikey_buz_tenant (business_key, tenant_id)
|
||||
);
|
||||
|
||||
create table if not exists seata_state_inst
|
||||
(
|
||||
id varchar(48) not null comment 'id',
|
||||
machine_inst_id varchar(128) not null comment 'state machine instance id',
|
||||
name varchar(128) not null comment 'state name',
|
||||
type varchar(20) comment 'state type',
|
||||
service_name varchar(128) comment 'service name',
|
||||
service_method varchar(128) comment 'method name',
|
||||
service_type varchar(16) comment 'service type',
|
||||
business_key varchar(48) comment 'business key',
|
||||
state_id_compensated_for varchar(50) comment 'state compensated for',
|
||||
state_id_retried_for varchar(50) comment 'state retried for',
|
||||
gmt_started timestamp(3) not null comment 'start time',
|
||||
is_for_update tinyint(1) comment 'is service for update',
|
||||
input_params clob comment 'input parameters',
|
||||
output_params clob comment 'output parameters',
|
||||
status varchar(2) not null comment 'status(SU succeed|FA failed|UN unknown|SK skipped|RU running)',
|
||||
excep blob comment 'exception',
|
||||
gmt_updated timestamp(3) comment 'update time',
|
||||
gmt_end timestamp(3) comment 'end time',
|
||||
primary key (id, machine_inst_id)
|
||||
);
|
||||
64
test/src/test/resources/saga/sql/mysql_init.sql
Normal file
64
test/src/test/resources/saga/sql/mysql_init.sql
Normal file
@@ -0,0 +1,64 @@
|
||||
-- -------------------------------- The script used for sage --------------------------------
|
||||
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `seata_state_machine_def`
|
||||
(
|
||||
`id` VARCHAR(32) NOT NULL COMMENT 'id',
|
||||
`name` VARCHAR(128) NOT NULL COMMENT 'name',
|
||||
`tenant_id` VARCHAR(32) NOT NULL COMMENT 'tenant id',
|
||||
`app_name` VARCHAR(32) NOT NULL COMMENT 'application name',
|
||||
`type` VARCHAR(20) COMMENT 'state language type',
|
||||
`comment_` VARCHAR(255) COMMENT 'comment',
|
||||
`ver` VARCHAR(16) NOT NULL COMMENT 'version',
|
||||
`gmt_create` DATETIME(3) NOT NULL COMMENT 'create time',
|
||||
`status` VARCHAR(2) NOT NULL COMMENT 'status(AC:active|IN:inactive)',
|
||||
`content` TEXT COMMENT 'content',
|
||||
`recover_strategy` VARCHAR(16) COMMENT 'transaction recover strategy(compensate|retry)',
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE = InnoDB
|
||||
DEFAULT CHARSET = utf8;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `seata_state_machine_inst`
|
||||
(
|
||||
`id` VARCHAR(128) NOT NULL COMMENT 'id',
|
||||
`machine_id` VARCHAR(32) NOT NULL COMMENT 'state machine definition id',
|
||||
`tenant_id` VARCHAR(32) NOT NULL COMMENT 'tenant id',
|
||||
`parent_id` VARCHAR(128) COMMENT 'parent id',
|
||||
`gmt_started` DATETIME(3) NOT NULL COMMENT 'start time',
|
||||
`business_key` VARCHAR(48) COMMENT 'business key',
|
||||
`start_params` TEXT COMMENT 'start parameters',
|
||||
`gmt_end` DATETIME(3) COMMENT 'end time',
|
||||
`excep` BLOB COMMENT 'exception',
|
||||
`end_params` TEXT COMMENT 'end parameters',
|
||||
`status` VARCHAR(2) COMMENT 'status(SU succeed|FA failed|UN unknown|SK skipped|RU running)',
|
||||
`compensation_status` VARCHAR(2) COMMENT 'compensation status(SU succeed|FA failed|UN unknown|SK skipped|RU running)',
|
||||
`is_running` TINYINT(1) COMMENT 'is running(0 no|1 yes)',
|
||||
`gmt_updated` DATETIME(3) NOT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `unikey_buz_tenant` (`business_key`, `tenant_id`)
|
||||
) ENGINE = InnoDB
|
||||
DEFAULT CHARSET = utf8;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `seata_state_inst`
|
||||
(
|
||||
`id` VARCHAR(48) NOT NULL COMMENT 'id',
|
||||
`machine_inst_id` VARCHAR(128) NOT NULL COMMENT 'state machine instance id',
|
||||
`name` VARCHAR(128) NOT NULL COMMENT 'state name',
|
||||
`type` VARCHAR(20) COMMENT 'state type',
|
||||
`service_name` VARCHAR(128) COMMENT 'service name',
|
||||
`service_method` VARCHAR(128) COMMENT 'method name',
|
||||
`service_type` VARCHAR(16) COMMENT 'service type',
|
||||
`business_key` VARCHAR(48) COMMENT 'business key',
|
||||
`state_id_compensated_for` VARCHAR(50) COMMENT 'state compensated for',
|
||||
`state_id_retried_for` VARCHAR(50) COMMENT 'state retried for',
|
||||
`gmt_started` DATETIME(3) NOT NULL COMMENT 'start time',
|
||||
`is_for_update` TINYINT(1) COMMENT 'is service for update',
|
||||
`input_params` TEXT COMMENT 'input parameters',
|
||||
`output_params` TEXT COMMENT 'output parameters',
|
||||
`status` VARCHAR(2) NOT NULL COMMENT 'status(SU succeed|FA failed|UN unknown|SK skipped|RU running)',
|
||||
`excep` BLOB COMMENT 'exception',
|
||||
`gmt_updated` DATETIME(3) COMMENT 'update time',
|
||||
`gmt_end` DATETIME(3) COMMENT 'end time',
|
||||
PRIMARY KEY (`id`, `machine_inst_id`)
|
||||
) ENGINE = InnoDB
|
||||
DEFAULT CHARSET = utf8;
|
||||
64
test/src/test/resources/saga/sql/oracle_init.sql
Normal file
64
test/src/test/resources/saga/sql/oracle_init.sql
Normal file
@@ -0,0 +1,64 @@
|
||||
CREATE TABLE seata_state_machine_def
|
||||
(
|
||||
id VARCHAR(32) NOT NULL,
|
||||
name VARCHAR(128) NOT NULL,
|
||||
tenant_id VARCHAR(32) NOT NULL,
|
||||
app_name VARCHAR(32) NOT NULL,
|
||||
type VARCHAR(20),
|
||||
comment_ VARCHAR(255),
|
||||
ver VARCHAR(16) NOT NULL,
|
||||
gmt_create TIMESTAMP(3) NOT NULL,
|
||||
status VARCHAR(2) NOT NULL,
|
||||
content CLOB,
|
||||
recover_strategy VARCHAR(16),
|
||||
PRIMARY KEY (id)
|
||||
);
|
||||
|
||||
CREATE TABLE seata_state_machine_inst
|
||||
(
|
||||
id VARCHAR(128) NOT NULL,
|
||||
machine_id VARCHAR(32) NOT NULL,
|
||||
tenant_id VARCHAR(32) NOT NULL,
|
||||
parent_id VARCHAR(128),
|
||||
gmt_started TIMESTAMP(3) NOT NULL,
|
||||
business_key VARCHAR(48),
|
||||
uni_business_key VARCHAR(128) GENERATED ALWAYS AS (
|
||||
CASE
|
||||
WHEN "BUSINESS_KEY" IS NULL
|
||||
THEN "ID"
|
||||
ELSE "BUSINESS_KEY"
|
||||
END),
|
||||
start_params CLOB,
|
||||
gmt_end TIMESTAMP(3),
|
||||
excep BLOB,
|
||||
end_params CLOB,
|
||||
status VARCHAR(2),
|
||||
compensation_status VARCHAR(2),
|
||||
is_running SMALLINT,
|
||||
gmt_updated TIMESTAMP(3) NOT NULL,
|
||||
PRIMARY KEY (id)
|
||||
);
|
||||
CREATE UNIQUE INDEX state_machine_inst_unibuzkey ON seata_state_machine_inst (uni_business_key, tenant_id);
|
||||
|
||||
CREATE TABLE seata_state_inst
|
||||
(
|
||||
id VARCHAR(48) NOT NULL,
|
||||
machine_inst_id VARCHAR(46) NOT NULL,
|
||||
name VARCHAR(128) NOT NULL,
|
||||
type VARCHAR(20),
|
||||
service_name VARCHAR(128),
|
||||
service_method VARCHAR(128),
|
||||
service_type VARCHAR(16),
|
||||
business_key VARCHAR(48),
|
||||
state_id_compensated_for VARCHAR(50),
|
||||
state_id_retried_for VARCHAR(50),
|
||||
gmt_started TIMESTAMP(3) NOT NULL,
|
||||
is_for_update SMALLINT,
|
||||
input_params CLOB,
|
||||
output_params CLOB,
|
||||
status VARCHAR(2) NOT NULL,
|
||||
excep BLOB,
|
||||
gmt_updated TIMESTAMP(3),
|
||||
gmt_end TIMESTAMP(3),
|
||||
PRIMARY KEY (id, machine_inst_id)
|
||||
);
|
||||
@@ -0,0 +1,260 @@
|
||||
{
|
||||
"nodes": [
|
||||
{
|
||||
"type": "node",
|
||||
"size": "72*72",
|
||||
"shape": "flow-circle",
|
||||
"color": "#FA8C16",
|
||||
"label": "Start",
|
||||
"stateId": "Start1",
|
||||
"stateType": "Start",
|
||||
"stateProps": {
|
||||
"StateMachine": {
|
||||
"Name": "designerSimpleScriptTaskStateMachine",
|
||||
"Comment": "带ScriptTask的测试状态机定义",
|
||||
"Version": "0.0.1"
|
||||
}
|
||||
},
|
||||
"x": 318.875,
|
||||
"y": 102,
|
||||
"id": "9e56dbe7"
|
||||
},
|
||||
{
|
||||
"type": "node",
|
||||
"size": "110*48",
|
||||
"shape": "flow-rect",
|
||||
"color": "#1890FF",
|
||||
"label": "FirstState",
|
||||
"stateId": "FirstState",
|
||||
"stateType": "ServiceTask",
|
||||
"stateProps": {
|
||||
"ServiceName": "demoService",
|
||||
"ServiceMethod": "foo",
|
||||
"Input": [
|
||||
{
|
||||
"fooInput": "$.[a]"
|
||||
}
|
||||
],
|
||||
"Output": {
|
||||
"fooResult": "$.#root"
|
||||
}
|
||||
},
|
||||
"x": 318.875,
|
||||
"y": 202,
|
||||
"id": "d1d71346"
|
||||
},
|
||||
{
|
||||
"type": "node",
|
||||
"size": "110*48",
|
||||
"shape": "flow-rect",
|
||||
"color": "#13C2C2",
|
||||
"label": "ScriptState",
|
||||
"stateId": "ScriptState",
|
||||
"stateType": "ScriptTask",
|
||||
"stateProps": {
|
||||
"ScriptType": "groovy",
|
||||
"ScriptContent": "if(throwException){ throw new RuntimeException(\"test\") } else { 'hello ' + inputA }",
|
||||
"Input": [
|
||||
{
|
||||
"inputA": "$.[a]",
|
||||
"throwException": "$.[scriptThrowException]"
|
||||
}
|
||||
],
|
||||
"Output": {
|
||||
"scriptStateResult": "$.#root"
|
||||
}
|
||||
},
|
||||
"x": 319.375,
|
||||
"y": 290.5,
|
||||
"id": "898649d3"
|
||||
},
|
||||
{
|
||||
"type": "node",
|
||||
"size": "80*72",
|
||||
"shape": "flow-rhombus",
|
||||
"color": "#13C2C2",
|
||||
"label": "Choice",
|
||||
"stateId": "Choice1",
|
||||
"stateType": "Choice",
|
||||
"x": 318.875,
|
||||
"y": 394.5,
|
||||
"id": "39859d0b"
|
||||
},
|
||||
{
|
||||
"type": "node",
|
||||
"size": "110*48",
|
||||
"shape": "flow-rect",
|
||||
"color": "#1890FF",
|
||||
"label": "SecondState",
|
||||
"stateId": "SecondState",
|
||||
"stateType": "ServiceTask",
|
||||
"stateProps": {
|
||||
"ServiceName": "demoService",
|
||||
"ServiceMethod": "bar",
|
||||
"Input": [
|
||||
{
|
||||
"barInput": "$.[fooResult]"
|
||||
}
|
||||
],
|
||||
"Output": {
|
||||
"barResult": "$.#root"
|
||||
}
|
||||
},
|
||||
"x": 173.375,
|
||||
"y": 493.49999999999994,
|
||||
"id": "c65f1c20"
|
||||
},
|
||||
{
|
||||
"type": "node",
|
||||
"size": "110*48",
|
||||
"shape": "flow-rect",
|
||||
"color": "#1890FF",
|
||||
"label": "ThirdState",
|
||||
"stateId": "ThirdState",
|
||||
"stateType": "ServiceTask",
|
||||
"stateProps": {
|
||||
"ServiceName": "demoService",
|
||||
"ServiceMethod": "foo",
|
||||
"Input": [
|
||||
{
|
||||
"fooInput": "$.[fooResult]"
|
||||
}
|
||||
],
|
||||
"Output": {
|
||||
"fooResult": "$.#root"
|
||||
}
|
||||
},
|
||||
"x": 318.875,
|
||||
"y": 492.99999999999994,
|
||||
"id": "5857047e"
|
||||
},
|
||||
{
|
||||
"type": "node",
|
||||
"size": "72*72",
|
||||
"shape": "flow-circle",
|
||||
"color": "#05A465",
|
||||
"label": "Succeed",
|
||||
"stateId": "Succeed1",
|
||||
"stateType": "Succeed",
|
||||
"x": 318.875,
|
||||
"y": 592,
|
||||
"id": "18bba9a0"
|
||||
},
|
||||
{
|
||||
"type": "node",
|
||||
"size": "72*72",
|
||||
"shape": "flow-circle",
|
||||
"color": "red",
|
||||
"label": "Fail",
|
||||
"stateId": "Fail1",
|
||||
"stateType": "Fail",
|
||||
"stateProps": {
|
||||
"ErrorCode": "",
|
||||
"Message": ""
|
||||
},
|
||||
"x": 474.57499999999993,
|
||||
"y": 499.99999999999994,
|
||||
"id": "95def921"
|
||||
},
|
||||
{
|
||||
"type": "node",
|
||||
"size": "39*39",
|
||||
"shape": "flow-circle",
|
||||
"color": "red",
|
||||
"label": "Catch",
|
||||
"stateId": "Catch1",
|
||||
"stateType": "Catch",
|
||||
"x": 374.375,
|
||||
"y": 315,
|
||||
"id": "e264be2d"
|
||||
}
|
||||
],
|
||||
"edges": [
|
||||
{
|
||||
"source": "9e56dbe7",
|
||||
"sourceAnchor": 2,
|
||||
"target": "d1d71346",
|
||||
"targetAnchor": 0,
|
||||
"id": "594d5078"
|
||||
},
|
||||
{
|
||||
"source": "d1d71346",
|
||||
"sourceAnchor": 2,
|
||||
"target": "898649d3",
|
||||
"targetAnchor": 0,
|
||||
"id": "e1f56d5c"
|
||||
},
|
||||
{
|
||||
"source": "898649d3",
|
||||
"sourceAnchor": 2,
|
||||
"target": "39859d0b",
|
||||
"targetAnchor": 0,
|
||||
"id": "d25b97b3"
|
||||
},
|
||||
{
|
||||
"source": "39859d0b",
|
||||
"sourceAnchor": 3,
|
||||
"target": "c65f1c20",
|
||||
"targetAnchor": 0,
|
||||
"id": "d957fcff",
|
||||
"stateProps": {
|
||||
"Expression": "[a] == 1",
|
||||
"Default": false
|
||||
},
|
||||
"label": "",
|
||||
"shape": "flow-smooth"
|
||||
},
|
||||
{
|
||||
"source": "39859d0b",
|
||||
"sourceAnchor": 2,
|
||||
"target": "5857047e",
|
||||
"targetAnchor": 0,
|
||||
"id": "d7053595",
|
||||
"stateProps": {
|
||||
"Expression": "[a] == 2",
|
||||
"Default": false
|
||||
},
|
||||
"label": "",
|
||||
"shape": "flow-smooth"
|
||||
},
|
||||
{
|
||||
"source": "e264be2d",
|
||||
"sourceAnchor": 1,
|
||||
"target": "95def921",
|
||||
"targetAnchor": 0,
|
||||
"id": "39925f4b",
|
||||
"stateProps": {
|
||||
"Exceptions": [
|
||||
"java.lang.Throwable"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"source": "39859d0b",
|
||||
"sourceAnchor": 1,
|
||||
"target": "95def921",
|
||||
"targetAnchor": 0,
|
||||
"id": "5500c8da",
|
||||
"stateProps": {
|
||||
"Expression": "",
|
||||
"Default": true
|
||||
},
|
||||
"label": "",
|
||||
"shape": "flow-smooth"
|
||||
},
|
||||
{
|
||||
"source": "c65f1c20",
|
||||
"sourceAnchor": 2,
|
||||
"target": "18bba9a0",
|
||||
"targetAnchor": 3,
|
||||
"id": "33832e0d"
|
||||
},
|
||||
{
|
||||
"source": "5857047e",
|
||||
"sourceAnchor": 2,
|
||||
"target": "18bba9a0",
|
||||
"targetAnchor": 0,
|
||||
"id": "1191f8f0"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,288 @@
|
||||
{
|
||||
"nodes": [
|
||||
{
|
||||
"type": "node",
|
||||
"size": "72*72",
|
||||
"shape": "flow-circle",
|
||||
"color": "#FA8C16",
|
||||
"label": "Start",
|
||||
"stateId": "Start",
|
||||
"stateType": "Start",
|
||||
"stateProps": {
|
||||
"StateMachine": {
|
||||
"Name": "simpleStateMachineWithCompensationAndSubMachine_layout",
|
||||
"Comment": "带补偿定义和调用子状态机",
|
||||
"Version": "0.0.1"
|
||||
}
|
||||
},
|
||||
"x": 199.875,
|
||||
"y": 95,
|
||||
"id": "e2d86441"
|
||||
},
|
||||
{
|
||||
"type": "node",
|
||||
"size": "110*48",
|
||||
"shape": "flow-rect",
|
||||
"color": "#1890FF",
|
||||
"label": "FirstState",
|
||||
"stateId": "FirstState",
|
||||
"stateType": "ServiceTask",
|
||||
"stateProps": {
|
||||
"ServiceName": "demoService",
|
||||
"ServiceMethod": "foo",
|
||||
"Input": [
|
||||
{
|
||||
"fooInput": "$.[a]"
|
||||
}
|
||||
],
|
||||
"Output": {
|
||||
"fooResult": "$.#root"
|
||||
}
|
||||
},
|
||||
"x": 199.875,
|
||||
"y": 213,
|
||||
"id": "6111bf54"
|
||||
},
|
||||
{
|
||||
"type": "node",
|
||||
"size": "80*72",
|
||||
"shape": "flow-rhombus",
|
||||
"color": "#13C2C2",
|
||||
"label": "ChoiceState",
|
||||
"stateId": "ChoiceState",
|
||||
"stateType": "Choice",
|
||||
"x": 199.875,
|
||||
"y": 341.5,
|
||||
"id": "5610fa37"
|
||||
},
|
||||
{
|
||||
"type": "node",
|
||||
"size": "110*48",
|
||||
"shape": "flow-rect",
|
||||
"color": "#1890FF",
|
||||
"label": "SecondState",
|
||||
"stateId": "SecondState",
|
||||
"stateType": "ServiceTask",
|
||||
"stateProps": {
|
||||
"ServiceName": "demoService",
|
||||
"ServiceMethod": "bar",
|
||||
"Input": [
|
||||
{
|
||||
"barInput": "$.[fooResult]",
|
||||
"throwException": "$.[barThrowException]"
|
||||
}
|
||||
],
|
||||
"Output": {
|
||||
"barResult": "$.#root"
|
||||
},
|
||||
"Status": {
|
||||
"#root != null": "SU",
|
||||
"#root == null": "FA",
|
||||
"$Exception{io.seata.saga.engine.exception.EngineExecutionException}": "UN"
|
||||
}
|
||||
},
|
||||
"x": 199.375,
|
||||
"y": 468,
|
||||
"id": "af5591f9"
|
||||
},
|
||||
{
|
||||
"type": "node",
|
||||
"size": "72*72",
|
||||
"shape": "flow-circle",
|
||||
"color": "#05A465",
|
||||
"label": "Succeed",
|
||||
"stateId": "Succeed",
|
||||
"stateType": "Succeed",
|
||||
"x": 199.375,
|
||||
"y": 609,
|
||||
"id": "2fd4c8de"
|
||||
},
|
||||
{
|
||||
"type": "node",
|
||||
"size": "110*48",
|
||||
"shape": "flow-rect",
|
||||
"color": "#FA8C16",
|
||||
"label": "SubStateMachine",
|
||||
"stateId": "CallSubStateMachine",
|
||||
"stateType": "SubStateMachine",
|
||||
"stateProps": {
|
||||
"StateMachineName": "simpleCompensationStateMachine",
|
||||
"Input": [
|
||||
{
|
||||
"a": "$.1",
|
||||
"barThrowException": "$.[barThrowException]",
|
||||
"fooThrowException": "$.[fooThrowException]",
|
||||
"compensateFooThrowException": "$.[compensateFooThrowException]"
|
||||
}
|
||||
],
|
||||
"Output": {
|
||||
"fooResult": "$.#root"
|
||||
}
|
||||
},
|
||||
"x": 55.875,
|
||||
"y": 467,
|
||||
"id": "04ea55a5"
|
||||
},
|
||||
{
|
||||
"type": "node",
|
||||
"size": "110*48",
|
||||
"shape": "flow-capsule",
|
||||
"color": "#722ED1",
|
||||
"label": "CompenFirstState",
|
||||
"stateId": "CompensateFirstState",
|
||||
"stateType": "Compensation",
|
||||
"stateProps": {
|
||||
"ServiceName": "demoService",
|
||||
"ServiceMethod": "compensateFoo",
|
||||
"Input": [
|
||||
{
|
||||
"compensateFooInput": "$.[fooResult]"
|
||||
}
|
||||
]
|
||||
},
|
||||
"x": 68.875,
|
||||
"y": 126,
|
||||
"id": "6a09a5c2"
|
||||
},
|
||||
{
|
||||
"type": "node",
|
||||
"size": "39*39",
|
||||
"shape": "flow-circle",
|
||||
"color": "red",
|
||||
"label": "Catch",
|
||||
"stateId": "Catch",
|
||||
"stateType": "Catch",
|
||||
"x": 257.875,
|
||||
"y": 492,
|
||||
"id": "e28af1c2"
|
||||
},
|
||||
{
|
||||
"type": "node",
|
||||
"size": "110*48",
|
||||
"shape": "flow-capsule",
|
||||
"color": "red",
|
||||
"label": "Compensation\nTrigger",
|
||||
"stateId": "CompensationTrigger",
|
||||
"stateType": "CompensationTrigger",
|
||||
"x": 366.875,
|
||||
"y": 491.5,
|
||||
"id": "e32417a0"
|
||||
},
|
||||
{
|
||||
"type": "node",
|
||||
"size": "72*72",
|
||||
"shape": "flow-circle",
|
||||
"color": "red",
|
||||
"label": "Fail",
|
||||
"stateId": "Fail",
|
||||
"stateType": "Fail",
|
||||
"stateProps": {
|
||||
"ErrorCode": "NOT_FOUND",
|
||||
"Message": "not found"
|
||||
},
|
||||
"x": 513.375,
|
||||
"y": 491.5,
|
||||
"id": "d21d24c9"
|
||||
}
|
||||
],
|
||||
"edges": [
|
||||
{
|
||||
"source": "e2d86441",
|
||||
"sourceAnchor": 2,
|
||||
"target": "6111bf54",
|
||||
"targetAnchor": 0,
|
||||
"id": "51f30b96"
|
||||
},
|
||||
{
|
||||
"source": "6111bf54",
|
||||
"sourceAnchor": 2,
|
||||
"target": "5610fa37",
|
||||
"targetAnchor": 0,
|
||||
"id": "8c3029b1"
|
||||
},
|
||||
{
|
||||
"source": "5610fa37",
|
||||
"sourceAnchor": 2,
|
||||
"target": "af5591f9",
|
||||
"targetAnchor": 0,
|
||||
"id": "a9e7d5b4",
|
||||
"stateProps": {
|
||||
"Expression": "[a] == 1",
|
||||
"Default": false
|
||||
},
|
||||
"label": "",
|
||||
"shape": "flow-smooth"
|
||||
},
|
||||
{
|
||||
"source": "af5591f9",
|
||||
"sourceAnchor": 2,
|
||||
"target": "2fd4c8de",
|
||||
"targetAnchor": 0,
|
||||
"id": "61f34a49"
|
||||
},
|
||||
{
|
||||
"source": "6111bf54",
|
||||
"sourceAnchor": 3,
|
||||
"target": "6a09a5c2",
|
||||
"targetAnchor": 2,
|
||||
"id": "553384ab",
|
||||
"style": {
|
||||
"lineDash": "4"
|
||||
}
|
||||
},
|
||||
{
|
||||
"source": "5610fa37",
|
||||
"sourceAnchor": 3,
|
||||
"target": "04ea55a5",
|
||||
"targetAnchor": 0,
|
||||
"id": "2ee91c33",
|
||||
"stateProps": {
|
||||
"Expression": "[a] == 2",
|
||||
"Default": false
|
||||
},
|
||||
"label": "",
|
||||
"shape": "flow-smooth"
|
||||
},
|
||||
{
|
||||
"source": "e28af1c2",
|
||||
"sourceAnchor": 1,
|
||||
"target": "e32417a0",
|
||||
"targetAnchor": 3,
|
||||
"id": "d854a4d0",
|
||||
"stateProps": {
|
||||
"Exceptions": [
|
||||
"io.seata.common.exception.FrameworkException"
|
||||
]
|
||||
},
|
||||
"label": "",
|
||||
"shape": "flow-smooth"
|
||||
},
|
||||
{
|
||||
"source": "04ea55a5",
|
||||
"sourceAnchor": 2,
|
||||
"target": "2fd4c8de",
|
||||
"targetAnchor": 3,
|
||||
"id": "28734ad2"
|
||||
},
|
||||
{
|
||||
"source": "5610fa37",
|
||||
"sourceAnchor": 1,
|
||||
"target": "d21d24c9",
|
||||
"targetAnchor": 0,
|
||||
"id": "7c7595c0",
|
||||
"stateProps": {
|
||||
"Expression": "",
|
||||
"Default": true
|
||||
},
|
||||
"label": "",
|
||||
"shape": "flow-smooth"
|
||||
},
|
||||
{
|
||||
"source": "e32417a0",
|
||||
"sourceAnchor": 1,
|
||||
"target": "d21d24c9",
|
||||
"targetAnchor": 3,
|
||||
"id": "16d809ce"
|
||||
}
|
||||
]
|
||||
}
|
||||
19
test/src/test/resources/saga/statelang/simple_statelang.json
Normal file
19
test/src/test/resources/saga/statelang/simple_statelang.json
Normal file
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"Name": "simpleTestStateMachine",
|
||||
"Comment": "测试状态机定义",
|
||||
"StartState": "FirstState",
|
||||
"Version": "0.0.2",
|
||||
"States": {
|
||||
"FirstState": {
|
||||
"Type": "ServiceTask",
|
||||
"ServiceName": "demoService",
|
||||
"ServiceMethod": "foo",
|
||||
"Next": "SecondState"
|
||||
},
|
||||
"SecondState": {
|
||||
"Type": "ServiceTask",
|
||||
"ServiceName": "demoService",
|
||||
"ServiceMethod": "bar"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
{
|
||||
"Name": "simpleInputAssignmentStateMachine",
|
||||
"Comment": "带输入输出参数赋值的测试状态机定义",
|
||||
"StartState": "FirstState",
|
||||
"Version": "0.0.1",
|
||||
"States": {
|
||||
"FirstState": {
|
||||
"Type": "ServiceTask",
|
||||
"ServiceName": "demoService",
|
||||
"ServiceMethod": "foo",
|
||||
"Next": "ChoiceState",
|
||||
"Input": [
|
||||
{
|
||||
"fooInput": "$.[a]",
|
||||
"fooBusinessKey": "$Sequence.BUSINESS_KEY|SIMPLE"
|
||||
}
|
||||
],
|
||||
"Output": {
|
||||
"fooResult": "$.#root"
|
||||
}
|
||||
},
|
||||
"ChoiceState":{
|
||||
"Type": "Choice",
|
||||
"Choices":[
|
||||
{
|
||||
"Expression":"[a] == 1",
|
||||
"Next":"SecondState"
|
||||
},
|
||||
{
|
||||
"Expression":"[a] == 2",
|
||||
"Next":"ThirdState"
|
||||
}
|
||||
],
|
||||
"Default":"Fail"
|
||||
},
|
||||
"SecondState": {
|
||||
"Type": "ServiceTask",
|
||||
"ServiceName": "demoService",
|
||||
"ServiceMethod": "bar",
|
||||
"Input": [
|
||||
{
|
||||
"barInput": "$.[fooResult]"
|
||||
}
|
||||
],
|
||||
"Output": {
|
||||
"barResult": "$.#root"
|
||||
},
|
||||
"Next": "Succeed"
|
||||
},
|
||||
"ThirdState": {
|
||||
"Type": "ServiceTask",
|
||||
"ServiceName": "demoService",
|
||||
"ServiceMethod": "foo",
|
||||
"Input": [
|
||||
{
|
||||
"fooInput": "[fooResult][a].list[0]"
|
||||
}
|
||||
],
|
||||
"Output": {
|
||||
"fooResult": "$.#root"
|
||||
},
|
||||
"listener":"",
|
||||
"Next": "Succeed"
|
||||
},
|
||||
"Succeed": {
|
||||
"Type":"Succeed"
|
||||
},
|
||||
"Fail": {
|
||||
"Type":"Fail",
|
||||
"ErrorCode": "NOT_FOUND",
|
||||
"Message": "not found"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
{
|
||||
"Name": "simpleStateMachineWithAsyncState",
|
||||
"Comment": "带异步执行节点的测试状态机定义",
|
||||
"StartState": "FirstState",
|
||||
"Version": "0.0.1",
|
||||
"States": {
|
||||
"FirstState": {
|
||||
"Type": "ServiceTask",
|
||||
"ServiceName": "demoService",
|
||||
"ServiceMethod": "foo",
|
||||
"Next": "ChoiceState"
|
||||
},
|
||||
"ChoiceState":{
|
||||
"Type": "Choice",
|
||||
"Choices":[
|
||||
{
|
||||
"Expression":"[a] == 1",
|
||||
"Next":"SecondState"
|
||||
},
|
||||
{
|
||||
"Expression":"[a] == 2",
|
||||
"Next":"ThirdState"
|
||||
}
|
||||
],
|
||||
"Default":"Fail"
|
||||
},
|
||||
"SecondState": {
|
||||
"Type": "ServiceTask",
|
||||
"ServiceName": "demoService",
|
||||
"ServiceMethod": "bar",
|
||||
"IsAsync": true,
|
||||
"Next": "Succeed"
|
||||
},
|
||||
"ThirdState": {
|
||||
"Type": "ServiceTask",
|
||||
"ServiceName": "demoService",
|
||||
"ServiceMethod": "foo",
|
||||
"IsAsync": true,
|
||||
"Next": "Succeed"
|
||||
},
|
||||
"Succeed": {
|
||||
"Type":"Succeed"
|
||||
},
|
||||
"Fail": {
|
||||
"Type":"Fail",
|
||||
"ErrorCode": "NOT_FOUND",
|
||||
"Message": "not found"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
{
|
||||
"Name": "simpleCachesStateMachine",
|
||||
"Comment": "带Caches异常路由的测试状态机定义",
|
||||
"StartState": "FirstState",
|
||||
"Version": "0.0.1",
|
||||
"States": {
|
||||
"FirstState": {
|
||||
"Type": "ServiceTask",
|
||||
"ServiceName": "demoService",
|
||||
"ServiceMethod": "foo",
|
||||
"Next": "ChoiceState",
|
||||
"Input": [
|
||||
{
|
||||
"fooInput": "$.[a]"
|
||||
}
|
||||
],
|
||||
"Output": {
|
||||
"fooResult": "$.#root"
|
||||
}
|
||||
},
|
||||
"ChoiceState":{
|
||||
"Type": "Choice",
|
||||
"Choices":[
|
||||
{
|
||||
"Expression":"[a] == 1",
|
||||
"Next":"SecondState"
|
||||
},
|
||||
{
|
||||
"Expression":"[a] == 2",
|
||||
"Next":"ThirdState"
|
||||
}
|
||||
],
|
||||
"Default":"Fail"
|
||||
},
|
||||
"SecondState": {
|
||||
"Type": "ServiceTask",
|
||||
"ServiceName": "demoService",
|
||||
"ServiceMethod": "bar",
|
||||
"Input": [
|
||||
{
|
||||
"barInput": "$.[fooResult]",
|
||||
"throwException": "$.[barThrowException]"
|
||||
}
|
||||
],
|
||||
"Output": {
|
||||
"barResult": "$.#root"
|
||||
},
|
||||
"Catch": [
|
||||
{
|
||||
"Exceptions": [
|
||||
"io.seata.saga.engine.mock.DemoException"
|
||||
],
|
||||
"Next": "Fail"
|
||||
}
|
||||
],
|
||||
"Next": "Succeed"
|
||||
},
|
||||
"ThirdState": {
|
||||
"Type": "ServiceTask",
|
||||
"ServiceName": "demoService",
|
||||
"ServiceMethod": "foo",
|
||||
"Input": [
|
||||
{
|
||||
"fooInput": "$.[fooResult]"
|
||||
}
|
||||
],
|
||||
"Output": {
|
||||
"fooResult": "$.#root"
|
||||
},
|
||||
"Next": "Succeed"
|
||||
},
|
||||
"Succeed": {
|
||||
"Type":"Succeed"
|
||||
},
|
||||
"Fail": {
|
||||
"Type":"Fail",
|
||||
"ErrorCode": "NOT_FOUND",
|
||||
"Message": "not found"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
{
|
||||
"Name": "simpleChoiceTestStateMachine",
|
||||
"Comment": "带条件分支的测试状态机定义",
|
||||
"StartState": "FirstState",
|
||||
"Version": "0.0.1",
|
||||
"States": {
|
||||
"FirstState": {
|
||||
"Type": "ServiceTask",
|
||||
"ServiceName": "demoService",
|
||||
"ServiceMethod": "foo",
|
||||
"Next": "ChoiceState"
|
||||
},
|
||||
"ChoiceState":{
|
||||
"Type": "Choice",
|
||||
"Choices":[
|
||||
{
|
||||
"Expression":"[a] == 1",
|
||||
"Next":"SecondState"
|
||||
},
|
||||
{
|
||||
"Expression":"[a] == 2",
|
||||
"Next":"ThirdState"
|
||||
}
|
||||
],
|
||||
"Default":"SecondState"
|
||||
},
|
||||
"SecondState": {
|
||||
"Type": "ServiceTask",
|
||||
"ServiceName": "demoService",
|
||||
"ServiceMethod": "bar"
|
||||
},
|
||||
"ThirdState": {
|
||||
"Type": "ServiceTask",
|
||||
"ServiceName": "demoService",
|
||||
"ServiceMethod": "foo"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
{
|
||||
"Name": "simpleChoiceAndEndTestStateMachine",
|
||||
"Comment": "带条件分支和结束状态的测试状态机定义",
|
||||
"StartState": "FirstState",
|
||||
"Version": "0.0.1",
|
||||
"States": {
|
||||
"FirstState": {
|
||||
"Type": "ServiceTask",
|
||||
"ServiceName": "demoService",
|
||||
"ServiceMethod": "foo",
|
||||
"Next": "ChoiceState"
|
||||
},
|
||||
"ChoiceState":{
|
||||
"Type": "Choice",
|
||||
"Choices":[
|
||||
{
|
||||
"Expression":"[a] == 1",
|
||||
"Next":"SecondState"
|
||||
},
|
||||
{
|
||||
"Expression":"[a] == 2",
|
||||
"Next":"ThirdState"
|
||||
}
|
||||
],
|
||||
"Default":"Fail"
|
||||
},
|
||||
"SecondState": {
|
||||
"Type": "ServiceTask",
|
||||
"ServiceName": "demoService",
|
||||
"ServiceMethod": "bar",
|
||||
"Next": "Succeed"
|
||||
},
|
||||
"ThirdState": {
|
||||
"Type": "ServiceTask",
|
||||
"ServiceName": "demoService",
|
||||
"ServiceMethod": "foo",
|
||||
"Next": "Succeed"
|
||||
},
|
||||
"Succeed": {
|
||||
"Type":"Succeed"
|
||||
},
|
||||
"Fail": {
|
||||
"Type":"Fail",
|
||||
"ErrorCode": "NOT_FOUND",
|
||||
"Message": "not found"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
{
|
||||
"Name": "simpleChoiceNoDefaultTestStateMachine",
|
||||
"Comment": "带条件分支但没有默认分支的测试状态机定义",
|
||||
"StartState": "FirstState",
|
||||
"Version": "0.0.1",
|
||||
"States": {
|
||||
"FirstState": {
|
||||
"Type": "ServiceTask",
|
||||
"ServiceName": "demoService",
|
||||
"ServiceMethod": "foo",
|
||||
"IsForUpdate": true,
|
||||
"Next": "ChoiceState"
|
||||
},
|
||||
"ChoiceState":{
|
||||
"Type": "Choice",
|
||||
"Choices":[
|
||||
{
|
||||
"Expression":"[a] == 1",
|
||||
"Next":"SecondState"
|
||||
},
|
||||
{
|
||||
"Expression":"[a] == 2",
|
||||
"Next":"ThirdState"
|
||||
}
|
||||
]
|
||||
},
|
||||
"SecondState": {
|
||||
"Type": "ServiceTask",
|
||||
"ServiceName": "demoService",
|
||||
"ServiceMethod": "bar"
|
||||
},
|
||||
"ThirdState": {
|
||||
"Type": "ServiceTask",
|
||||
"ServiceName": "demoService",
|
||||
"ServiceMethod": "foo"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,130 @@
|
||||
{
|
||||
"Name": "simpleCompensationStateMachine",
|
||||
"Comment": "带补偿定义的测试状态机定义",
|
||||
"StartState": "FirstState",
|
||||
"Version": "0.0.1",
|
||||
"States": {
|
||||
"FirstState": {
|
||||
"Type": "ServiceTask",
|
||||
"ServiceName": "demoService",
|
||||
"ServiceMethod": "foo",
|
||||
"CompensateState": "CompensateFirstState",
|
||||
"Next": "ChoiceState",
|
||||
"Input": [
|
||||
{
|
||||
"fooInput": "$.[a]",
|
||||
"throwException": "$.[fooThrowException]",
|
||||
"sleepTime": "$.[fooSleepTime]"
|
||||
}
|
||||
],
|
||||
"Output": {
|
||||
"fooResult": "$.#root"
|
||||
},
|
||||
"Status": {
|
||||
"$Exception{io.seata.saga.engine.mock.DemoException}": "UN",
|
||||
"#root != null": "SU",
|
||||
"#root == null": "FA"
|
||||
},
|
||||
"Catch": [
|
||||
{
|
||||
"Exceptions": [
|
||||
"io.seata.saga.engine.mock.DemoException"
|
||||
],
|
||||
"Next": "CompensationTrigger"
|
||||
}
|
||||
]
|
||||
},
|
||||
"ChoiceState":{
|
||||
"Type": "Choice",
|
||||
"Choices":[
|
||||
{
|
||||
"Expression":"[a] == 1",
|
||||
"Next":"SecondState"
|
||||
},
|
||||
{
|
||||
"Expression":"[a] == 2",
|
||||
"Next":"ThirdState"
|
||||
}
|
||||
],
|
||||
"Default":"Fail"
|
||||
},
|
||||
"SecondState": {
|
||||
"Type": "ServiceTask",
|
||||
"ServiceName": "demoService",
|
||||
"ServiceMethod": "bar",
|
||||
"CompensateState": "CompensateSecondState",
|
||||
"Input": [
|
||||
{
|
||||
"barInput": "$.[fooResult]",
|
||||
"throwException": "$.[barThrowException]",
|
||||
"sleepTime": "$.[barSleepTime]"
|
||||
}
|
||||
],
|
||||
"Output": {
|
||||
"barResult": "$.#root"
|
||||
},
|
||||
"Status": {
|
||||
"$Exception{io.seata.saga.engine.mock.DemoException}": "UN",
|
||||
"#root != null": "SU",
|
||||
"#root == null": "FA"
|
||||
},
|
||||
"Catch": [
|
||||
{
|
||||
"Exceptions": [
|
||||
"io.seata.saga.engine.mock.DemoException"
|
||||
],
|
||||
"Next": "CompensationTrigger"
|
||||
}
|
||||
],
|
||||
"Next": "Succeed"
|
||||
},
|
||||
"ThirdState": {
|
||||
"Type": "ServiceTask",
|
||||
"ServiceName": "demoService",
|
||||
"ServiceMethod": "foo",
|
||||
"Input": [
|
||||
{
|
||||
"fooInput": "$.[fooResult]"
|
||||
}
|
||||
],
|
||||
"Output": {
|
||||
"fooResult": "$.#root"
|
||||
},
|
||||
"Next": "Succeed"
|
||||
},
|
||||
"CompensateFirstState": {
|
||||
"Type": "ServiceTask",
|
||||
"ServiceName": "demoService",
|
||||
"ServiceMethod": "compensateFoo",
|
||||
"Input": [
|
||||
{
|
||||
"compensateFooInput": "$.[fooResult]",
|
||||
"throwException": "$.[compensateFooThrowException]"
|
||||
}
|
||||
]
|
||||
},
|
||||
"CompensateSecondState": {
|
||||
"Type": "ServiceTask",
|
||||
"ServiceName": "demoService",
|
||||
"ServiceMethod": "compensateBar",
|
||||
"Input": [
|
||||
{
|
||||
"compensateBarInput": "$.[barResult]",
|
||||
"throwException": "$.[compensateBarThrowException]"
|
||||
}
|
||||
]
|
||||
},
|
||||
"CompensationTrigger": {
|
||||
"Type": "CompensationTrigger",
|
||||
"Next": "Fail"
|
||||
},
|
||||
"Succeed": {
|
||||
"Type":"Succeed"
|
||||
},
|
||||
"Fail": {
|
||||
"Type":"Fail",
|
||||
"ErrorCode": "NOT_FOUND",
|
||||
"Message": "not found"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,126 @@
|
||||
{
|
||||
"Name": "simpleStateMachineWithCompensationAndSubMachine",
|
||||
"Comment": "带补偿定义和调用子状态机",
|
||||
"StartState": "FirstState",
|
||||
"Version": "0.0.1",
|
||||
"IsRetryPersistModeUpdate": false,
|
||||
"IsCompensatePersistModeUpdate": false,
|
||||
"States": {
|
||||
"FirstState": {
|
||||
"Type": "ServiceTask",
|
||||
"ServiceName": "demoService",
|
||||
"ServiceMethod": "foo",
|
||||
"CompensateState": "CompensateFirstState",
|
||||
"Next": "ChoiceState",
|
||||
"Input": [
|
||||
{
|
||||
"fooInput": "$.[a]"
|
||||
}
|
||||
],
|
||||
"Output": {
|
||||
"fooResult": "$.#root"
|
||||
}
|
||||
},
|
||||
"ChoiceState":{
|
||||
"Type": "Choice",
|
||||
"Choices":[
|
||||
{
|
||||
"Expression":"[a] == 1",
|
||||
"Next":"SecondState"
|
||||
},
|
||||
{
|
||||
"Expression":"[a] == 2",
|
||||
"Next":"CallSubStateMachine"
|
||||
},
|
||||
{
|
||||
"Expression": "[a] == 3 || [a] == 4",
|
||||
"Next": "CallSubStateMachineAsUpdate"
|
||||
}
|
||||
],
|
||||
"Default":"Fail"
|
||||
},
|
||||
"SecondState": {
|
||||
"Type": "ServiceTask",
|
||||
"ServiceName": "demoService",
|
||||
"ServiceMethod": "bar",
|
||||
"Input": [
|
||||
{
|
||||
"barInput": "$.[fooResult]",
|
||||
"throwException": "$.[barThrowException]"
|
||||
}
|
||||
],
|
||||
"Output": {
|
||||
"barResult": "$.#root"
|
||||
},
|
||||
"Status": {
|
||||
"#root != null": "SU",
|
||||
"#root == null": "FA",
|
||||
"$Exception{io.seata.saga.engine.mock.DemoException}": "UN"
|
||||
},
|
||||
"Catch": [
|
||||
{
|
||||
"Exceptions": [
|
||||
"io.seata.saga.engine.mock.DemoException"
|
||||
],
|
||||
"Next": "CompensationTrigger"
|
||||
}
|
||||
],
|
||||
"Next": "Succeed"
|
||||
},
|
||||
"CallSubStateMachine": {
|
||||
"Type": "SubStateMachine",
|
||||
"StateMachineName": "simpleCompensationStateMachine",
|
||||
"Input": [
|
||||
{
|
||||
"a": "$.1",
|
||||
"barThrowException": "$.[barThrowException]",
|
||||
"fooThrowException": "$.[fooThrowException]",
|
||||
"compensateFooThrowException": "$.[compensateFooThrowException]"
|
||||
}
|
||||
],
|
||||
"Output": {
|
||||
"fooResult": "$.#root"
|
||||
},
|
||||
"Next": "Succeed"
|
||||
},
|
||||
"CallSubStateMachineAsUpdate": {
|
||||
"Type": "SubStateMachine",
|
||||
"StateMachineName": "simpleUpdateStateMachine",
|
||||
"IsRetryPersistModeUpdate": true,
|
||||
"IsCompensatePersistModeUpdate": true,
|
||||
"Input": [
|
||||
{
|
||||
"a": "$.[a]-2",
|
||||
"barThrowException": "$.[barThrowException]",
|
||||
"compensateBarThrowException": "$.[compensateBarThrowException]"
|
||||
}
|
||||
],
|
||||
"Output": {
|
||||
"fooResult": "$.#root"
|
||||
},
|
||||
"Next": "Succeed"
|
||||
},
|
||||
"CompensateFirstState": {
|
||||
"Type": "ServiceTask",
|
||||
"ServiceName": "demoService",
|
||||
"ServiceMethod": "compensateFoo",
|
||||
"Input": [
|
||||
{
|
||||
"compensateFooInput": "$.[fooResult]"
|
||||
}
|
||||
]
|
||||
},
|
||||
"CompensationTrigger": {
|
||||
"Type": "CompensationTrigger",
|
||||
"Next": "Fail"
|
||||
},
|
||||
"Succeed": {
|
||||
"Type":"Succeed"
|
||||
},
|
||||
"Fail": {
|
||||
"Type":"Fail",
|
||||
"ErrorCode": "NOT_FOUND",
|
||||
"Message": "not found"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,139 @@
|
||||
{
|
||||
"Name": "simpleCompensationStateMachineForRecovery",
|
||||
"Comment": "用于测试事务恢复的状态机定义",
|
||||
"StartState": "FirstState",
|
||||
"Version": "0.0.1",
|
||||
"States": {
|
||||
"FirstState": {
|
||||
"Type": "ServiceTask",
|
||||
"ServiceName": "demoService",
|
||||
"ServiceMethod": "foo",
|
||||
"CompensateState": "CompensateFirstState",
|
||||
"Next": "ChoiceState",
|
||||
"Input": [
|
||||
{
|
||||
"fooInput": "$.[a]",
|
||||
"throwExceptionRandomly": "$.[fooThrowExceptionRandomly]",
|
||||
"throwException": "$.[fooThrowException]"
|
||||
}
|
||||
],
|
||||
"Output": {
|
||||
"fooResult": "$.#root"
|
||||
},
|
||||
"Status": {
|
||||
"$Exception{io.seata.saga.engine.mock.DemoException}": "UN",
|
||||
"#root != null && #root.size() > 0": "SU",
|
||||
"#root == null || #root.size() == 0": "FA"
|
||||
}
|
||||
},
|
||||
"ChoiceState":{
|
||||
"Type": "Choice",
|
||||
"Choices":[
|
||||
{
|
||||
"Expression":"[a] == 1",
|
||||
"Next":"SecondState"
|
||||
},
|
||||
{
|
||||
"Expression":"[a] == 2",
|
||||
"Next":"ThirdState"
|
||||
}
|
||||
],
|
||||
"Default":"Fail"
|
||||
},
|
||||
"SecondState": {
|
||||
"Type": "ServiceTask",
|
||||
"ServiceName": "demoService",
|
||||
"ServiceMethod": "bar",
|
||||
"CompensateState": "CompensateSecondState",
|
||||
"Input": [
|
||||
{
|
||||
"barInput": "$.[fooResult]",
|
||||
"throwExceptionRandomly": "$.[barThrowExceptionRandomly]",
|
||||
"throwException": "$.[barThrowException]"
|
||||
}
|
||||
],
|
||||
"Output": {
|
||||
"barResult": "$.#root"
|
||||
},
|
||||
"Status": {
|
||||
"$Exception{io.seata.saga.engine.mock.DemoException}": "UN",
|
||||
"#root != null && #root.size() > 0": "SU",
|
||||
"#root == null || #root.size() == 0": "FA"
|
||||
},
|
||||
"Catch": [
|
||||
{
|
||||
"Exceptions": [
|
||||
"io.seata.saga.engine.mock.DemoException"
|
||||
],
|
||||
"Next": "CompensationTrigger"
|
||||
}
|
||||
],
|
||||
"Next": "Succeed"
|
||||
},
|
||||
"ThirdState": {
|
||||
"Type": "ServiceTask",
|
||||
"ServiceName": "demoService",
|
||||
"ServiceMethod": "foo",
|
||||
"Input": [
|
||||
{
|
||||
"fooInput": "$.[fooResult]"
|
||||
}
|
||||
],
|
||||
"Output": {
|
||||
"fooResult": "$.#root"
|
||||
},
|
||||
"Status": {
|
||||
"$Exception{io.seata.saga.engine.mock.DemoException}": "UN",
|
||||
"#root != null && #root.size() > 0": "SU",
|
||||
"#root == null || #root.size() == 0": "FA"
|
||||
},
|
||||
"Next": "Succeed"
|
||||
},
|
||||
"CompensateFirstState": {
|
||||
"Type": "ServiceTask",
|
||||
"ServiceName": "demoService",
|
||||
"ServiceMethod": "compensateFoo",
|
||||
"Input": [
|
||||
{
|
||||
"compensateFooInput": "$.[a]",
|
||||
"throwExceptionRandomly": "$.[compensateFooThrowExceptionRandomly]",
|
||||
"throwException": "$.[compensateFooThrowException]"
|
||||
}
|
||||
],
|
||||
"Status": {
|
||||
"$Exception{io.seata.saga.engine.mock.DemoException}": "UN",
|
||||
"#root != null && #root.size() > 0": "SU",
|
||||
"#root == null || #root.size() == 0": "FA"
|
||||
}
|
||||
},
|
||||
"CompensateSecondState": {
|
||||
"Type": "ServiceTask",
|
||||
"ServiceName": "demoService",
|
||||
"ServiceMethod": "compensateBar",
|
||||
"Input": [
|
||||
{
|
||||
"compensateBarInput": "$.[a]",
|
||||
"throwExceptionRandomly": "$.[compensateBarThrowExceptionRandomly]",
|
||||
"throwException": "$.[compensateBarThrowException]"
|
||||
}
|
||||
],
|
||||
"Status": {
|
||||
"$Exception{io.seata.saga.engine.mock.DemoException}": "UN",
|
||||
"#root != null && #root.size() > 0": "SU",
|
||||
"#root == null || #root.size() == 0": "FA"
|
||||
}
|
||||
},
|
||||
"CompensationTrigger": {
|
||||
"Type": "CompensationTrigger",
|
||||
"Next": "Fail"
|
||||
},
|
||||
"Succeed": {
|
||||
"Type":"Succeed"
|
||||
},
|
||||
"Fail": {
|
||||
"Type":"Fail",
|
||||
"ErrorCode": "NOT_FOUND",
|
||||
"Message": "not found"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,119 @@
|
||||
{
|
||||
"Name": "simpleStateMachineWithComplexParams",
|
||||
"Comment": "带复杂参数的测试状态机定义fastjson格式",
|
||||
"StartState": "FirstState",
|
||||
"Version": "0.0.1",
|
||||
"States": {
|
||||
"FirstState": {
|
||||
"Type": "ServiceTask",
|
||||
"ServiceName": "demoService",
|
||||
"ServiceMethod": "complexParameterMethod",
|
||||
"Next": "ChoiceState",
|
||||
"ParameterTypes" : ["java.lang.String", "int", "io.seata.saga.engine.mock.DemoService$People", "[Lio.seata.saga.engine.mock.DemoService$People;", "java.util.List", "java.util.Map"],
|
||||
"Input": [
|
||||
"$.[people].name",
|
||||
"$.[people].age",
|
||||
{
|
||||
"name": "$.[people].name",
|
||||
"age": "$.[people].age",
|
||||
"childrenArray": [
|
||||
{
|
||||
"name": "$.[people].name",
|
||||
"age": "$.[people].age"
|
||||
},
|
||||
{
|
||||
"name": "$.[people].name",
|
||||
"age": "$.[people].age"
|
||||
}
|
||||
],
|
||||
"childrenList": [
|
||||
{
|
||||
"name": "$.[people].name",
|
||||
"age": "$.[people].age"
|
||||
},
|
||||
{
|
||||
"name": "$.[people].name",
|
||||
"age": "$.[people].age"
|
||||
}
|
||||
],
|
||||
"childrenMap": {
|
||||
"lilei": {
|
||||
"name": "$.[people].name",
|
||||
"age": "$.[people].age"
|
||||
}
|
||||
}
|
||||
},
|
||||
[
|
||||
{
|
||||
"name": "$.[people].name",
|
||||
"age": "$.[people].age"
|
||||
},
|
||||
{
|
||||
"name": "$.[people].name",
|
||||
"age": "$.[people].age"
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
"@type": "io.seata.saga.engine.mock.DemoService$People",
|
||||
"name": "$.[people].name",
|
||||
"age": "$.[people].age"
|
||||
}
|
||||
],
|
||||
{
|
||||
"lilei": {
|
||||
"@type": "io.seata.saga.engine.mock.DemoService$People",
|
||||
"name": "$.[people].name",
|
||||
"age": "$.[people].age"
|
||||
}
|
||||
}
|
||||
],
|
||||
"Output": {
|
||||
"complexParameterMethodResult": "$.#root"
|
||||
}
|
||||
},
|
||||
"ChoiceState":{
|
||||
"Type": "Choice",
|
||||
"Choices":[
|
||||
{
|
||||
"Expression":"[complexParameterMethodResult].age > 0",
|
||||
"Next":"SecondState"
|
||||
},
|
||||
{
|
||||
"Expression":"[complexParameterMethodResult].age <= 0",
|
||||
"Next":"ThirdState"
|
||||
}
|
||||
],
|
||||
"Default":"Fail"
|
||||
},
|
||||
"SecondState": {
|
||||
"Type": "ServiceTask",
|
||||
"ServiceName": "demoService",
|
||||
"ServiceMethod": "interfaceParameterMethod",
|
||||
"Input": [
|
||||
"$.[career]"
|
||||
],
|
||||
"Output": {
|
||||
"secondStateResult": "$.#root"
|
||||
},
|
||||
"Next": "ThirdState"
|
||||
},
|
||||
"ThirdState": {
|
||||
"Type": "ServiceTask",
|
||||
"ServiceName": "demoService",
|
||||
"ServiceMethod": "interfaceParameterMethod",
|
||||
"Input": [
|
||||
"$.[secondStateResult]"
|
||||
],
|
||||
"Next": "Succeed"
|
||||
},
|
||||
"Succeed": {
|
||||
"Type":"Succeed"
|
||||
},
|
||||
"Fail": {
|
||||
"Type":"Fail",
|
||||
"ErrorCode": "NOT_FOUND",
|
||||
"Message": "not found"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,141 @@
|
||||
{
|
||||
"Name": "simpleStateMachineWithComplexParamsJackson",
|
||||
"Comment": "带复杂参数的测试状态机定义jackson格式",
|
||||
"StartState": "FirstState",
|
||||
"Version": "0.0.1",
|
||||
"States": {
|
||||
"FirstState": {
|
||||
"Type": "ServiceTask",
|
||||
"ServiceName": "demoService",
|
||||
"ServiceMethod": "complexParameterMethod",
|
||||
"Next": "ChoiceState",
|
||||
"ParameterTypes" : ["java.lang.String", "int", "io.seata.saga.engine.mock.DemoService$People", "[Lio.seata.saga.engine.mock.DemoService$People;", "java.util.List", "java.util.Map"],
|
||||
"Input": [
|
||||
"$.[people].name",
|
||||
"$.[people].age",
|
||||
{
|
||||
"@type": "io.seata.saga.engine.mock.DemoService$People",
|
||||
"name": "lilei",
|
||||
"age": 18,
|
||||
"childrenArray": [
|
||||
"[Lio.seata.saga.engine.mock.DemoService$People;",
|
||||
[
|
||||
{
|
||||
"@type": "io.seata.saga.engine.mock.DemoService$People",
|
||||
"name": "lilei",
|
||||
"age": 18
|
||||
},
|
||||
{
|
||||
"@type": "io.seata.saga.engine.mock.DemoService$People",
|
||||
"name": "lilei",
|
||||
"age": 18
|
||||
}
|
||||
]
|
||||
],
|
||||
"childrenList": [
|
||||
"java.util.ArrayList",
|
||||
[
|
||||
{
|
||||
"@type": "io.seata.saga.engine.mock.DemoService$People",
|
||||
"name": "lilei",
|
||||
"age": 18
|
||||
},
|
||||
{
|
||||
"@type": "io.seata.saga.engine.mock.DemoService$People",
|
||||
"name": "lilei",
|
||||
"age": 18
|
||||
}
|
||||
]
|
||||
],
|
||||
"childrenMap": {
|
||||
"@type": "java.util.LinkedHashMap",
|
||||
"lilei": {
|
||||
"@type": "io.seata.saga.engine.mock.DemoService$People",
|
||||
"name": "lilei",
|
||||
"age": 18
|
||||
}
|
||||
}
|
||||
},
|
||||
[
|
||||
"[Lio.seata.saga.engine.mock.DemoService$People;",
|
||||
[
|
||||
{
|
||||
"@type": "io.seata.saga.engine.mock.DemoService$People",
|
||||
"name": "$.[people].name",
|
||||
"age": "$.[people].age"
|
||||
},
|
||||
{
|
||||
"@type": "io.seata.saga.engine.mock.DemoService$People",
|
||||
"name": "$.[people].name",
|
||||
"age": "$.[people].age"
|
||||
}
|
||||
]
|
||||
],
|
||||
[
|
||||
"java.util.ArrayList",
|
||||
[
|
||||
{
|
||||
"@type": "io.seata.saga.engine.mock.DemoService$People",
|
||||
"name": "$.[people].name",
|
||||
"age": "$.[people].age"
|
||||
}
|
||||
]
|
||||
],
|
||||
{
|
||||
"@type": "java.util.LinkedHashMap",
|
||||
"lilei": {
|
||||
"@type": "io.seata.saga.engine.mock.DemoService$People",
|
||||
"name": "$.[people].name",
|
||||
"age": "$.[people].age"
|
||||
}
|
||||
}
|
||||
],
|
||||
"Output": {
|
||||
"complexParameterMethodResult": "$.#root"
|
||||
}
|
||||
},
|
||||
"ChoiceState":{
|
||||
"Type": "Choice",
|
||||
"Choices":[
|
||||
{
|
||||
"Expression":"[complexParameterMethodResult].age > 0",
|
||||
"Next":"SecondState"
|
||||
},
|
||||
{
|
||||
"Expression":"[complexParameterMethodResult].age <= 0",
|
||||
"Next":"ThirdState"
|
||||
}
|
||||
],
|
||||
"Default":"Fail"
|
||||
},
|
||||
"SecondState": {
|
||||
"Type": "ServiceTask",
|
||||
"ServiceName": "demoService",
|
||||
"ServiceMethod": "interfaceParameterMethod",
|
||||
"Input": [
|
||||
"$.[career]"
|
||||
],
|
||||
"Output": {
|
||||
"secondStateResult": "$.#root"
|
||||
},
|
||||
"Next": "ThirdState"
|
||||
},
|
||||
"ThirdState": {
|
||||
"Type": "ServiceTask",
|
||||
"ServiceName": "demoService",
|
||||
"ServiceMethod": "interfaceParameterMethod",
|
||||
"Input": [
|
||||
"$.[secondStateResult]"
|
||||
],
|
||||
"Next": "Succeed"
|
||||
},
|
||||
"Succeed": {
|
||||
"Type":"Succeed"
|
||||
},
|
||||
"Fail": {
|
||||
"Type":"Fail",
|
||||
"ErrorCode": "NOT_FOUND",
|
||||
"Message": "not found"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,137 @@
|
||||
{
|
||||
"Name": "simpleLoopTestStateMachine",
|
||||
"Comment": "带循环参数的测试状态机定义",
|
||||
"StartState": "FirstState",
|
||||
"Version": "0.0.1",
|
||||
"States": {
|
||||
"FirstState": {
|
||||
"Type": "ServiceTask",
|
||||
"ServiceName": "demoService",
|
||||
"ServiceMethod": "foo",
|
||||
"CompensateState": "CompensateFirstState",
|
||||
"Loop": {
|
||||
"Parallel": 3,
|
||||
"Collection": "$.[collection]",
|
||||
"ElementVariableName": "element",
|
||||
"ElementIndexName": "loopCounter",
|
||||
"CompletionCondition": "[nrOfCompletedInstances] == ([collection].size()-4)"
|
||||
},
|
||||
"Input": [
|
||||
{
|
||||
"loopCounter": "$.[loopCounter]",
|
||||
"element": "$.[element]",
|
||||
"throwException": "$.[fooThrowException]"
|
||||
}
|
||||
],
|
||||
"Output": {
|
||||
"fooResult": "$.#root"
|
||||
},
|
||||
"Next": "ChoiceState"
|
||||
},
|
||||
"ChoiceState":{
|
||||
"Type": "Choice",
|
||||
"Choices":[
|
||||
{
|
||||
"Expression": "[loopResult].?[#this[fooResult] == null].size() == 0 && [a] == 1",
|
||||
"Next":"SecondState"
|
||||
},
|
||||
{
|
||||
"Expression": "[loopResult].?[#this[fooResult] == null].size() == 0 && [a] == 2",
|
||||
"Next":"CallSubStateMachine"
|
||||
}
|
||||
],
|
||||
"Default":"Fail"
|
||||
},
|
||||
"SecondState": {
|
||||
"Type": "ServiceTask",
|
||||
"ServiceName": "demoService",
|
||||
"ServiceMethod": "bar",
|
||||
"CompensateState": "CompensateSecondState",
|
||||
"Loop": {
|
||||
"Parallel": 3,
|
||||
"Collection": "$.[collection]",
|
||||
"ElementVariableName": "element",
|
||||
"CompletionCondition": "[nrOfCompletedInstances] / [nrOfInstances] >= 0.4",
|
||||
"ElementIndexName": "loopCounter"
|
||||
},
|
||||
"Input": [
|
||||
{
|
||||
"loopCounter": "$.[loopCounter]",
|
||||
"loopElement": "$.[element]",
|
||||
"throwException": "$.[barThrowException]"
|
||||
}
|
||||
],
|
||||
"Catch": [
|
||||
{
|
||||
"Exceptions": [
|
||||
"io.seata.saga.engine.mock.DemoException"
|
||||
],
|
||||
"Next": "CompensationTriggerTest"
|
||||
}
|
||||
]
|
||||
},
|
||||
"CallSubStateMachine": {
|
||||
"Type": "SubStateMachine",
|
||||
"StateMachineName": "simpleCompensationStateMachine",
|
||||
"Loop": {
|
||||
"Parallel": 3,
|
||||
"Collection": "$.[collection]",
|
||||
"ElementVariableName": "element",
|
||||
"CompletionCondition": "[nrOfCompletedInstances] / [nrOfInstances] >= 0.4",
|
||||
"ElementIndexName": "loopCounter"
|
||||
},
|
||||
"Input": [
|
||||
{
|
||||
"a": 1,
|
||||
"collection": "$.[collection]",
|
||||
"loopCounter": "$.[loopCounter]",
|
||||
"element": "$.[element]",
|
||||
"barThrowException": "$.[barThrowException]",
|
||||
"fooThrowException": "$.[fooThrowException]",
|
||||
"compensateFooThrowException": "$.[compensateFooThrowException]"
|
||||
}
|
||||
],
|
||||
"Output": {
|
||||
"fooResult": "$.#root"
|
||||
},
|
||||
"Next": "Succeed"
|
||||
},
|
||||
"CompensateFirstState": {
|
||||
"Type": "ServiceTask",
|
||||
"ServiceName": "demoService",
|
||||
"ServiceMethod": "compensateFoo",
|
||||
"Input": [
|
||||
{
|
||||
"compensateFooInput": "$.[fooResult]",
|
||||
"throwException": "$.[compensateFooThrowException]",
|
||||
"loopCounter": "$.[loopCounter]",
|
||||
"element": "$.[element]"
|
||||
}
|
||||
]
|
||||
},
|
||||
"CompensateSecondState": {
|
||||
"Type": "ServiceTask",
|
||||
"ServiceName": "demoService",
|
||||
"ServiceMethod": "compensateBar",
|
||||
"Input": [
|
||||
{
|
||||
"compensateBarInput": "$.[barResult]",
|
||||
"loopCounter": "$.[loopCounter]",
|
||||
"loopElement": "$.[element]"
|
||||
}
|
||||
]
|
||||
},
|
||||
"CompensationTriggerTest": {
|
||||
"Type": "CompensationTrigger",
|
||||
"Next": "Fail"
|
||||
},
|
||||
"Succeed": {
|
||||
"Type":"Succeed"
|
||||
},
|
||||
"Fail": {
|
||||
"Type":"Fail",
|
||||
"ErrorCode": "NOT_FOUND",
|
||||
"Message": "not found"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,116 @@
|
||||
{
|
||||
"Name": "simpleUpdateStateMachine",
|
||||
"Comment": "自定义中间状态是否持久化的测试状态机定义",
|
||||
"StartState": "FirstState",
|
||||
"Version": "0.0.1",
|
||||
"IsRetryPersistModeUpdate": true,
|
||||
"IsCompensatePersistModeUpdate": true,
|
||||
"States": {
|
||||
"FirstState": {
|
||||
"Type": "ServiceTask",
|
||||
"ServiceName": "demoService",
|
||||
"ServiceMethod": "foo",
|
||||
"Next": "ChoiceState",
|
||||
"CompensateState": "CompensateFirstState",
|
||||
"Input": [
|
||||
{
|
||||
"fooInput": "$.[a]"
|
||||
}
|
||||
],
|
||||
"Output": {
|
||||
"fooResult": "$.#root"
|
||||
}
|
||||
},
|
||||
"ChoiceState":{
|
||||
"Type": "Choice",
|
||||
"Choices":[
|
||||
{
|
||||
"Expression":"[a] == 1",
|
||||
"Next":"SecondState"
|
||||
},
|
||||
{
|
||||
"Expression":"[a] == 2",
|
||||
"Next":"ThirdState"
|
||||
}
|
||||
],
|
||||
"Default":"Fail"
|
||||
},
|
||||
"SecondState": {
|
||||
"Type": "ServiceTask",
|
||||
"ServiceName": "demoService",
|
||||
"ServiceMethod": "bar",
|
||||
"Input": [
|
||||
{
|
||||
"barInput": "$.[fooResult]",
|
||||
"throwException": "$.[barThrowException]"
|
||||
}
|
||||
],
|
||||
"Output": {
|
||||
"barResult": "$.#root"
|
||||
},
|
||||
"Status": {
|
||||
"$Exception{java.lang.Throwable}": "UN",
|
||||
"#root != null": "SU",
|
||||
"#root == null": "FA"
|
||||
},
|
||||
"Next": "Succeed"
|
||||
},
|
||||
"ThirdState": {
|
||||
"Type": "ServiceTask",
|
||||
"ServiceName": "demoService",
|
||||
"ServiceMethod": "randomExceptionMethod",
|
||||
"CompensateState": "CompensateThirdState",
|
||||
"Input": [
|
||||
{
|
||||
"barInput": "$.[fooResult]",
|
||||
"throwException": "$.[barThrowException]"
|
||||
}
|
||||
],
|
||||
"Output": {
|
||||
"barResult": "$.#root"
|
||||
},
|
||||
"Status": {
|
||||
"$Exception{java.lang.Throwable}": "UN",
|
||||
"#root != null": "SU",
|
||||
"#root == null": "FA"
|
||||
},
|
||||
"Catch": [
|
||||
{
|
||||
"Exceptions": [
|
||||
"java.lang.Throwable"
|
||||
],
|
||||
"Next": "CompensationTrigger"
|
||||
}
|
||||
],
|
||||
"Next": "Succeed"
|
||||
},
|
||||
"CompensateFirstState": {
|
||||
"Type": "ServiceTask",
|
||||
"ServiceName": "demoService",
|
||||
"ServiceMethod": "compensateFoo"
|
||||
},
|
||||
"CompensateThirdState": {
|
||||
"Type": "ServiceTask",
|
||||
"ServiceName": "demoService",
|
||||
"ServiceMethod": "compensateBar",
|
||||
"Input": [
|
||||
{
|
||||
"compensateBarInput": "$.[barResult]",
|
||||
"throwException": "$.[compensateBarThrowException]"
|
||||
}
|
||||
]
|
||||
},
|
||||
"CompensationTrigger": {
|
||||
"Type": "CompensationTrigger",
|
||||
"Next": "Fail"
|
||||
},
|
||||
"Succeed": {
|
||||
"Type":"Succeed"
|
||||
},
|
||||
"Fail": {
|
||||
"Type":"Fail",
|
||||
"ErrorCode": "NOT_FOUND",
|
||||
"Message": "not found"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
{
|
||||
"Name": "simpleStateMachineWithRecoverStrategy",
|
||||
"Comment": "带自定义恢复策略的测试状态机定义",
|
||||
"StartState": "FirstState",
|
||||
"Version": "0.0.1",
|
||||
"RecoverStrategy": "Forward",
|
||||
"States": {
|
||||
"FirstState": {
|
||||
"Type": "ServiceTask",
|
||||
"ServiceName": "demoService",
|
||||
"ServiceMethod": "foo",
|
||||
"Input": [
|
||||
{
|
||||
"fooInput": "$.[a]",
|
||||
"throwExceptionRandomly": "$.[fooThrowExceptionRandomly]",
|
||||
"sleepTime": "$.[fooSleepTime]"
|
||||
}
|
||||
],
|
||||
"Output": {
|
||||
"fooResult": "$.#root"
|
||||
},
|
||||
"Status": {
|
||||
"$Exception{io.seata.saga.engine.mock.DemoException}": "UN",
|
||||
"#root != null": "SU",
|
||||
"#root == null": "FA"
|
||||
},
|
||||
"Catch": [
|
||||
{
|
||||
"Exceptions": [
|
||||
"java.lang.Throwable"
|
||||
],
|
||||
"Next": "Fail"
|
||||
}
|
||||
],
|
||||
"Next": "ChoiceState"
|
||||
},
|
||||
"ChoiceState":{
|
||||
"Type": "Choice",
|
||||
"Choices":[
|
||||
{
|
||||
"Expression":"[a] == 1",
|
||||
"Next":"SecondState"
|
||||
},
|
||||
{
|
||||
"Expression":"[a] == 2",
|
||||
"Next":"Fail"
|
||||
}
|
||||
],
|
||||
"Default":"Fail"
|
||||
},
|
||||
"SecondState": {
|
||||
"Type": "ServiceTask",
|
||||
"ServiceName": "demoService",
|
||||
"ServiceMethod": "bar",
|
||||
"Input": [
|
||||
{
|
||||
"barInput": "$.[fooResult]",
|
||||
"throwExceptionRandomly": "$.[barThrowExceptionRandomly]",
|
||||
"sleepTime": "$.[barSleepTime]"
|
||||
}
|
||||
],
|
||||
"Output": {
|
||||
"barResult": "$.#root"
|
||||
},
|
||||
"Status": {
|
||||
"$Exception{io.seata.saga.engine.mock.DemoException}": "UN",
|
||||
"#root != null": "SU",
|
||||
"#root == null": "FA"
|
||||
},
|
||||
"Catch": [
|
||||
{
|
||||
"Exceptions": [
|
||||
"java.lang.Throwable"
|
||||
],
|
||||
"Next": "Fail"
|
||||
}
|
||||
],
|
||||
"Next": "Succeed"
|
||||
},
|
||||
"Succeed": {
|
||||
"Type":"Succeed"
|
||||
},
|
||||
"Fail": {
|
||||
"Type":"Fail",
|
||||
"ErrorCode": "NOT_FOUND",
|
||||
"Message": "not found"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
{
|
||||
"Name": "simpleRetryStateMachine",
|
||||
"Comment": "带异常重试的测试状态机定义",
|
||||
"StartState": "FirstState",
|
||||
"Version": "0.0.1",
|
||||
"States": {
|
||||
"FirstState": {
|
||||
"Type": "ServiceTask",
|
||||
"ServiceName": "demoService",
|
||||
"ServiceMethod": "foo",
|
||||
"Next": "ChoiceState",
|
||||
"Input": [
|
||||
{
|
||||
"fooInput": "$.[a]"
|
||||
}
|
||||
],
|
||||
"Output": {
|
||||
"fooResult": "$.#root"
|
||||
}
|
||||
},
|
||||
"ChoiceState":{
|
||||
"Type": "Choice",
|
||||
"Choices":[
|
||||
{
|
||||
"Expression":"[a] == 1",
|
||||
"Next":"SecondState"
|
||||
},
|
||||
{
|
||||
"Expression":"[a] == 2",
|
||||
"Next":"ThirdState"
|
||||
}
|
||||
],
|
||||
"Default":"Fail"
|
||||
},
|
||||
"SecondState": {
|
||||
"Type": "ServiceTask",
|
||||
"ServiceName": "demoService",
|
||||
"ServiceMethod": "randomExceptionMethod",
|
||||
"Input": [
|
||||
{
|
||||
"barInput": "$.[fooResult]",
|
||||
"throwException": "$.[barThrowException]"
|
||||
}
|
||||
],
|
||||
"Output": {
|
||||
"barResult": "$.#root"
|
||||
},
|
||||
"Retry": [
|
||||
{
|
||||
"Exceptions": ["io.seata.saga.engine.mock.DemoException"],
|
||||
"IntervalSeconds": 1.5,
|
||||
"MaxAttempts": 3,
|
||||
"BackoffRate": 1.5
|
||||
},
|
||||
{
|
||||
"IntervalSeconds": 1,
|
||||
"MaxAttempts": 3,
|
||||
"BackoffRate": 1.5
|
||||
}
|
||||
],
|
||||
"Catch": [
|
||||
{
|
||||
"Exceptions": [
|
||||
"io.seata.saga.engine.mock.DemoException"
|
||||
],
|
||||
"Next": "Fail"
|
||||
}
|
||||
],
|
||||
"Next": "Succeed"
|
||||
},
|
||||
"ThirdState": {
|
||||
"Type": "ServiceTask",
|
||||
"ServiceName": "demoService",
|
||||
"ServiceMethod": "foo",
|
||||
"Input": [
|
||||
{
|
||||
"fooInput": "$.[fooResult]"
|
||||
}
|
||||
],
|
||||
"Output": {
|
||||
"fooResult": "$.#root"
|
||||
},
|
||||
"Next": "Succeed"
|
||||
},
|
||||
"Succeed": {
|
||||
"Type":"Succeed"
|
||||
},
|
||||
"Fail": {
|
||||
"Type":"Fail",
|
||||
"ErrorCode": "NOT_FOUND",
|
||||
"Message": "not found"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
{
|
||||
"Name": "simpleScriptTaskStateMachine",
|
||||
"Comment": "带ScriptTask的测试状态机定义",
|
||||
"StartState": "FirstState",
|
||||
"Version": "0.0.1",
|
||||
"States": {
|
||||
"FirstState": {
|
||||
"Type": "ServiceTask",
|
||||
"ServiceName": "demoService",
|
||||
"ServiceMethod": "foo",
|
||||
"Next": "ScriptState",
|
||||
"Input": [
|
||||
{
|
||||
"fooInput": "$.[a]"
|
||||
}
|
||||
],
|
||||
"Output": {
|
||||
"fooResult": "$.#root"
|
||||
}
|
||||
},
|
||||
"ScriptState": {
|
||||
"Type": "ScriptTask",
|
||||
"ScriptType": "groovy",
|
||||
"ScriptContent": "if(throwException){ throw new RuntimeException(\"test\") } else { 'hello ' + inputA }",
|
||||
"Input": [
|
||||
{
|
||||
"inputA": "$.[a]",
|
||||
"throwException": "$.[scriptThrowException]"
|
||||
}
|
||||
],
|
||||
"Output": {
|
||||
"scriptStateResult": "$.#root"
|
||||
},
|
||||
"Catch": [
|
||||
{
|
||||
"Exceptions": [
|
||||
"java.lang.Throwable"
|
||||
],
|
||||
"Next": "Fail"
|
||||
}
|
||||
],
|
||||
"Next": "ChoiceState"
|
||||
},
|
||||
"ChoiceState":{
|
||||
"Type": "Choice",
|
||||
"Choices":[
|
||||
{
|
||||
"Expression":"[a] == 1",
|
||||
"Next":"SecondState"
|
||||
},
|
||||
{
|
||||
"Expression":"[a] == 2",
|
||||
"Next":"ThirdState"
|
||||
}
|
||||
],
|
||||
"Default":"Fail"
|
||||
},
|
||||
"SecondState": {
|
||||
"Type": "ServiceTask",
|
||||
"ServiceName": "demoService",
|
||||
"ServiceMethod": "bar",
|
||||
"Input": [
|
||||
{
|
||||
"barInput": "$.[fooResult]",
|
||||
"throwException": "$.[barThrowException]"
|
||||
}
|
||||
],
|
||||
"Output": {
|
||||
"barResult": "$.#root"
|
||||
},
|
||||
"Catch": [
|
||||
{
|
||||
"Exceptions": [
|
||||
"io.seata.saga.engine.mock.DemoException"
|
||||
],
|
||||
"Next": "Fail"
|
||||
}
|
||||
],
|
||||
"Next": "Succeed"
|
||||
},
|
||||
"ThirdState": {
|
||||
"Type": "ServiceTask",
|
||||
"ServiceName": "demoService",
|
||||
"ServiceMethod": "foo",
|
||||
"Input": [
|
||||
{
|
||||
"fooInput": "$.[fooResult]"
|
||||
}
|
||||
],
|
||||
"Output": {
|
||||
"fooResult": "$.#root"
|
||||
},
|
||||
"Next": "Succeed"
|
||||
},
|
||||
"Succeed": {
|
||||
"Type":"Succeed"
|
||||
},
|
||||
"Fail": {
|
||||
"Type":"Fail",
|
||||
"ErrorCode": "NOT_FOUND",
|
||||
"Message": "not found"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
{
|
||||
"Name": "simpleStatusMatchingStateMachine",
|
||||
"Comment": "带Task执行状态匹配的测试状态机定义",
|
||||
"StartState": "FirstState",
|
||||
"Version": "0.0.1",
|
||||
"States": {
|
||||
"FirstState": {
|
||||
"Type": "ServiceTask",
|
||||
"ServiceName": "demoService",
|
||||
"ServiceMethod": "foo",
|
||||
"Next": "ReturnNullState",
|
||||
"Input": [
|
||||
{
|
||||
"fooInput": "$.[a]"
|
||||
}
|
||||
],
|
||||
"Output": {
|
||||
"fooResult": "$.#root"
|
||||
}
|
||||
},
|
||||
"ReturnNullState": {
|
||||
"Type": "ServiceTask",
|
||||
"ServiceName": "demoService",
|
||||
"ServiceMethod": "foo",
|
||||
"Next": "ChoiceState",
|
||||
"Status": {
|
||||
"$Exception{io.seata.saga.engine.mock.DemoException}": "UN",
|
||||
"$Exception{java.lang.Exception}": "FA",
|
||||
"#root != null && #root.size() > 0": "SU",
|
||||
"#root == null || #root.size() == 0": "FA"
|
||||
}
|
||||
},
|
||||
"ChoiceState":{
|
||||
"Type": "Choice",
|
||||
"Choices":[
|
||||
{
|
||||
"Expression":"[a] == 1",
|
||||
"Next":"SecondState"
|
||||
},
|
||||
{
|
||||
"Expression":"[a] == 2",
|
||||
"Next":"ThirdState"
|
||||
}
|
||||
],
|
||||
"Default":"Fail"
|
||||
},
|
||||
"SecondState": {
|
||||
"Type": "ServiceTask",
|
||||
"ServiceName": "demoService",
|
||||
"ServiceMethod": "bar",
|
||||
"Input": [
|
||||
{
|
||||
"barInput": "$.[fooResult]",
|
||||
"throwException": "$.[barThrowException]"
|
||||
}
|
||||
],
|
||||
"Output": {
|
||||
"barResult": "$.#root"
|
||||
},
|
||||
"Status": {
|
||||
"$Exception{io.seata.saga.engine.mock.DemoException}": "UN",
|
||||
"$Exception{java.lang.Exception}": "FA",
|
||||
"#root != null && #root.size() > 0": "SU",
|
||||
"#root == null || #root.size() == 0": "FA"
|
||||
},
|
||||
"Catch": [
|
||||
{
|
||||
"Exceptions": [
|
||||
"io.seata.saga.engine.mock.DemoException"
|
||||
],
|
||||
"Next": "Fail"
|
||||
}
|
||||
],
|
||||
"Next": "Succeed"
|
||||
},
|
||||
"ThirdState": {
|
||||
"Type": "ServiceTask",
|
||||
"ServiceName": "demoService",
|
||||
"ServiceMethod": "foo",
|
||||
"Input": [
|
||||
{
|
||||
"fooInput": "$.[fooResult]"
|
||||
}
|
||||
],
|
||||
"Output": {
|
||||
"fooResult": "$.#root"
|
||||
},
|
||||
"Next": "Succeed"
|
||||
},
|
||||
"Succeed": {
|
||||
"Type":"Succeed"
|
||||
},
|
||||
"Fail": {
|
||||
"Type":"Fail",
|
||||
"ErrorCode": "NOT_FOUND",
|
||||
"Message": "not found"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,118 @@
|
||||
{
|
||||
"Name": "simpleStateMachineWithUseDefCompensationSubMachine",
|
||||
"Comment": "自定义补偿子状态机",
|
||||
"StartState": "FirstState",
|
||||
"Version": "0.0.1",
|
||||
"States": {
|
||||
"FirstState": {
|
||||
"Type": "ServiceTask",
|
||||
"ServiceName": "demoService",
|
||||
"ServiceMethod": "foo",
|
||||
"CompensateState": "CompensateFirstState",
|
||||
"Next": "ChoiceState",
|
||||
"Input": [
|
||||
{
|
||||
"fooInput": "$.[a]"
|
||||
}
|
||||
],
|
||||
"Output": {
|
||||
"fooResult": "$.#root"
|
||||
},
|
||||
"Status": {
|
||||
"#root != null": "SU",
|
||||
"#root == null": "FA",
|
||||
"$Exception{io.seata.saga.engine.mock.DemoException}": "UN"
|
||||
}
|
||||
},
|
||||
"ChoiceState":{
|
||||
"Type": "Choice",
|
||||
"Choices":[
|
||||
{
|
||||
"Expression":"[a] == 1",
|
||||
"Next":"SecondState"
|
||||
},
|
||||
{
|
||||
"Expression":"[a] == 2",
|
||||
"Next":"CallSubStateMachine"
|
||||
}
|
||||
],
|
||||
"Default":"Fail"
|
||||
},
|
||||
"SecondState": {
|
||||
"Type": "ServiceTask",
|
||||
"ServiceName": "demoService",
|
||||
"ServiceMethod": "bar",
|
||||
"CompensateState": "CompensateSecondState",
|
||||
"Input": [
|
||||
{
|
||||
"barInput": "$.[fooResult]",
|
||||
"throwException": "$.[barThrowException]"
|
||||
}
|
||||
],
|
||||
"Output": {
|
||||
"barResult": "$.#root"
|
||||
},
|
||||
"Status": {
|
||||
"#root != null": "SU",
|
||||
"#root == null": "FA",
|
||||
"$Exception{io.seata.saga.engine.mock.DemoException}": "UN"
|
||||
},
|
||||
"Catch": [
|
||||
{
|
||||
"Exceptions": [
|
||||
"io.seata.saga.engine.mock.DemoException"
|
||||
],
|
||||
"Next": "CompensationTrigger"
|
||||
}
|
||||
],
|
||||
"Next": "Succeed"
|
||||
},
|
||||
"CallSubStateMachine": {
|
||||
"Type": "SubStateMachine",
|
||||
"StateMachineName": "simpleCompensationStateMachine",
|
||||
"CompensateState": "CompensateSubMachine",
|
||||
"Input": [
|
||||
{
|
||||
"a": "$.1",
|
||||
"barThrowException": "$.[barThrowException]",
|
||||
"fooThrowException": "$.[fooThrowException]",
|
||||
"compensateFooThrowException": "$.[compensateFooThrowException]"
|
||||
}
|
||||
],
|
||||
"Output": {
|
||||
"fooResult": "$.#root"
|
||||
},
|
||||
"Next": "Succeed"
|
||||
},
|
||||
"CompensateFirstState": {
|
||||
"Type": "ServiceTask",
|
||||
"ServiceName": "demoService",
|
||||
"ServiceMethod": "compensateFoo",
|
||||
"Input": [
|
||||
{
|
||||
"compensateFooInput": "$.[fooResult]"
|
||||
}
|
||||
]
|
||||
},
|
||||
"CompensateSubMachine": {
|
||||
"Type": "CompensateSubMachine",
|
||||
"Input": [
|
||||
{
|
||||
"compensateFooThrowException": "$.[compensateFooThrowException]"
|
||||
}
|
||||
]
|
||||
},
|
||||
"CompensationTrigger": {
|
||||
"Type": "CompensationTrigger",
|
||||
"Next": "Fail"
|
||||
},
|
||||
"Succeed": {
|
||||
"Type":"Succeed"
|
||||
},
|
||||
"Fail": {
|
||||
"Type":"Fail",
|
||||
"ErrorCode": "NOT_FOUND",
|
||||
"Message": "not found"
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user