From 4ef78fc800870222715ba525d3435a078a16e6bd Mon Sep 17 00:00:00 2001 From: "yixia.wt" Date: Sat, 16 Mar 2024 14:51:51 +0800 Subject: [PATCH 01/21] =?UTF-8?q?=E6=9A=82=E5=AD=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- compatible/pom.xml | 7 + .../seata/saga/engine/StateMachineConfig.java | 2 +- .../engine/config/DbStateMachineConfig.java | 202 ++++++- .../impl/DefaultStateMachineConfig.java | 19 +- .../saga/engine/store/StateLogStore.java | 98 ++++ .../store/impl/StateLogStoreAdapter.java | 106 ++++ .../engine/store/impl/StateLogStoreImpl.java | 100 ++++ .../io/seata/saga/rm/SagaResourceManager.java | 145 +++++ .../saga/rm/StateMachineEngineHolder.java | 17 + .../statelang/domain/ExecutionStatus.java | 3 + .../domain/impl/StateInstanceImpl.java | 8 +- .../domain/impl/StateMachineInstanceImpl.java | 12 +- .../spring/tcc/TccAnnotationProcessor.java | 1 + ...rg.apache.seata.core.model.ResourceManager | 1 + .../java/io/seata/common/LockAndCallback.java | 99 ++++ .../java/io/seata/saga/SagaCostPrint.java | 64 +++ .../seata/saga/engine/StateMachineTests.java | 315 +++++++++++ .../StateMachineAsyncDBMockServerTests.java | 202 +++++++ .../StateMachineDBMockServerTests.java | 510 ++++++++++++++++++ .../saga/engine/mock/DemoException.java} | 28 +- .../seata/saga/engine/mock/DemoService.java | 187 +++++++ .../engine/mock/MockGlobalTransaction.java | 121 +++++ .../mock/MockSagaTransactionTemplate.java | 82 +++ .../mock/MockStateHandlerInterceptor.java | 45 ++ .../mock/MockStateRouterInterceptor.java | 47 ++ ...statemachine_engine_db_mockserver_test.xml | 70 +++ .../spring/statemachine_engine_db_test.xml | 72 +++ .../saga/spring/statemachine_engine_test.xml | 47 ++ .../src/test/resources/saga/sql/db2_init.sql | 81 +++ .../src/test/resources/saga/sql/h2_init.sql | 75 +++ .../test/resources/saga/sql/mysql_init.sql | 81 +++ .../test/resources/saga/sql/oracle_init.sql | 81 +++ .../saga/statelang/simple_statelang.json | 19 + .../simple_statelang_param_assignment.json | 74 +++ .../simple_statelang_with_async_state.json | 50 ++ .../simple_statelang_with_catches.json | 81 +++ .../simple_statelang_with_choice.json | 38 ++ .../simple_statelang_with_choice_and_end.json | 48 ++ ...mple_statelang_with_choice_no_default.json | 38 ++ .../simple_statelang_with_compensation.json | 130 +++++ ...ang_with_compensation_and_sub_machine.json | 126 +++++ ...telang_with_compensation_for_recovery.json | 139 +++++ .../simple_statelang_with_complex_params.json | 119 ++++ ...statelang_with_complex_params_jackson.json | 141 +++++ .../statelang/simple_statelang_with_loop.json | 137 +++++ ...le_statelang_with_persist_update_mode.json | 116 ++++ ...imple_statelang_with_recover_strategy.json | 89 +++ .../simple_statelang_with_retry.json | 94 ++++ .../simple_statelang_with_script.json | 104 ++++ .../simple_statelang_with_status_matches.json | 99 ++++ ...with_userdef_sub_machine_compensation.json | 118 ++++ .../simple_statemachine_with_layout.json | 288 ++++++++++ .../saga/rm/StateMachineEngineHolder.java | 2 +- .../SeataSagaAutoConfiguration.java | 1 + .../spring/tcc/TccAnnotationProcessor.java | 1 + 55 files changed, 4962 insertions(+), 18 deletions(-) create mode 100644 compatible/src/main/java/io/seata/saga/engine/store/StateLogStore.java create mode 100644 compatible/src/main/java/io/seata/saga/engine/store/impl/StateLogStoreAdapter.java create mode 100644 compatible/src/main/java/io/seata/saga/engine/store/impl/StateLogStoreImpl.java create mode 100644 compatible/src/main/java/io/seata/saga/rm/SagaResourceManager.java create mode 100644 compatible/src/main/java/io/seata/saga/rm/StateMachineEngineHolder.java create mode 100644 compatible/src/main/resources/META-INF/services/org.apache.seata.core.model.ResourceManager create mode 100644 compatible/src/test/java/io/seata/common/LockAndCallback.java create mode 100644 compatible/src/test/java/io/seata/saga/SagaCostPrint.java create mode 100644 compatible/src/test/java/io/seata/saga/engine/StateMachineTests.java create mode 100644 compatible/src/test/java/io/seata/saga/engine/db/mockserver/StateMachineAsyncDBMockServerTests.java create mode 100644 compatible/src/test/java/io/seata/saga/engine/db/mockserver/StateMachineDBMockServerTests.java rename compatible/src/{main/java/io/seata/rm/AbstractRMHandler.java => test/java/io/seata/saga/engine/mock/DemoException.java} (58%) create mode 100644 compatible/src/test/java/io/seata/saga/engine/mock/DemoService.java create mode 100644 compatible/src/test/java/io/seata/saga/engine/mock/MockGlobalTransaction.java create mode 100644 compatible/src/test/java/io/seata/saga/engine/mock/MockSagaTransactionTemplate.java create mode 100644 compatible/src/test/java/io/seata/saga/engine/mock/MockStateHandlerInterceptor.java create mode 100644 compatible/src/test/java/io/seata/saga/engine/mock/MockStateRouterInterceptor.java create mode 100644 compatible/src/test/resources/saga/spring/statemachine_engine_db_mockserver_test.xml create mode 100644 compatible/src/test/resources/saga/spring/statemachine_engine_db_test.xml create mode 100644 compatible/src/test/resources/saga/spring/statemachine_engine_test.xml create mode 100644 compatible/src/test/resources/saga/sql/db2_init.sql create mode 100644 compatible/src/test/resources/saga/sql/h2_init.sql create mode 100644 compatible/src/test/resources/saga/sql/mysql_init.sql create mode 100644 compatible/src/test/resources/saga/sql/oracle_init.sql create mode 100644 compatible/src/test/resources/saga/statelang/simple_statelang.json create mode 100644 compatible/src/test/resources/saga/statelang/simple_statelang_param_assignment.json create mode 100644 compatible/src/test/resources/saga/statelang/simple_statelang_with_async_state.json create mode 100644 compatible/src/test/resources/saga/statelang/simple_statelang_with_catches.json create mode 100644 compatible/src/test/resources/saga/statelang/simple_statelang_with_choice.json create mode 100644 compatible/src/test/resources/saga/statelang/simple_statelang_with_choice_and_end.json create mode 100644 compatible/src/test/resources/saga/statelang/simple_statelang_with_choice_no_default.json create mode 100644 compatible/src/test/resources/saga/statelang/simple_statelang_with_compensation.json create mode 100644 compatible/src/test/resources/saga/statelang/simple_statelang_with_compensation_and_sub_machine.json create mode 100644 compatible/src/test/resources/saga/statelang/simple_statelang_with_compensation_for_recovery.json create mode 100644 compatible/src/test/resources/saga/statelang/simple_statelang_with_complex_params.json create mode 100644 compatible/src/test/resources/saga/statelang/simple_statelang_with_complex_params_jackson.json create mode 100644 compatible/src/test/resources/saga/statelang/simple_statelang_with_loop.json create mode 100644 compatible/src/test/resources/saga/statelang/simple_statelang_with_persist_update_mode.json create mode 100644 compatible/src/test/resources/saga/statelang/simple_statelang_with_recover_strategy.json create mode 100644 compatible/src/test/resources/saga/statelang/simple_statelang_with_retry.json create mode 100644 compatible/src/test/resources/saga/statelang/simple_statelang_with_script.json create mode 100644 compatible/src/test/resources/saga/statelang/simple_statelang_with_status_matches.json create mode 100644 compatible/src/test/resources/saga/statelang/simple_statelang_with_userdef_sub_machine_compensation.json create mode 100644 compatible/src/test/resources/saga/statelang/simple_statemachine_with_layout.json diff --git a/compatible/pom.xml b/compatible/pom.xml index 5d4de46dbbb..6f3569a5818 100644 --- a/compatible/pom.xml +++ b/compatible/pom.xml @@ -122,6 +122,13 @@ seata-tcc ${project.version} + + com.h2database + h2 + 1.4.200 + test + + diff --git a/compatible/src/main/java/io/seata/saga/engine/StateMachineConfig.java b/compatible/src/main/java/io/seata/saga/engine/StateMachineConfig.java index 377b59dca66..4fe5df97ab2 100644 --- a/compatible/src/main/java/io/seata/saga/engine/StateMachineConfig.java +++ b/compatible/src/main/java/io/seata/saga/engine/StateMachineConfig.java @@ -19,11 +19,11 @@ import io.seata.saga.engine.expression.ExpressionFactoryManager; import io.seata.saga.engine.repo.StateLogRepository; import io.seata.saga.engine.repo.StateMachineRepository; +import io.seata.saga.engine.store.StateLogStore; import org.apache.seata.saga.engine.expression.ExpressionResolver; import org.apache.seata.saga.engine.invoker.ServiceInvokerManager; import org.apache.seata.saga.engine.sequence.SeqGenerator; import org.apache.seata.saga.engine.store.StateLangStore; -import org.apache.seata.saga.engine.store.StateLogStore; import org.apache.seata.saga.engine.strategy.StatusDecisionStrategy; import org.apache.seata.saga.proctrl.eventing.impl.ProcessCtrlEventPublisher; diff --git a/compatible/src/main/java/io/seata/saga/engine/config/DbStateMachineConfig.java b/compatible/src/main/java/io/seata/saga/engine/config/DbStateMachineConfig.java index c08d5a53818..45c747085b1 100644 --- a/compatible/src/main/java/io/seata/saga/engine/config/DbStateMachineConfig.java +++ b/compatible/src/main/java/io/seata/saga/engine/config/DbStateMachineConfig.java @@ -16,6 +16,206 @@ */ package io.seata.saga.engine.config; -public class DbStateMachineConfig extends org.apache.seata.saga.engine.config.DbStateMachineConfig { +import io.seata.saga.engine.impl.DefaultStateMachineConfig; +import io.seata.saga.engine.store.impl.StateLogStoreImpl; +import org.apache.seata.common.ConfigurationKeys; +import org.apache.seata.config.Configuration; +import org.apache.seata.config.ConfigurationFactory; +import org.apache.seata.saga.engine.serializer.impl.ParamsSerializer; +import org.apache.seata.saga.engine.store.db.DbAndReportTcStateLogStore; +import org.apache.seata.saga.engine.store.db.DbStateLangStore; +import org.apache.seata.saga.engine.tm.DefaultSagaTransactionalTemplate; +import org.apache.seata.saga.engine.tm.SagaTransactionalTemplate; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.DisposableBean; +import org.springframework.util.StringUtils; +import javax.sql.DataSource; +import java.sql.Connection; +import java.sql.DatabaseMetaData; +import java.sql.SQLException; + +import static org.apache.seata.common.DefaultValues.DEFAULT_CLIENT_REPORT_SUCCESS_ENABLE; +import static org.apache.seata.common.DefaultValues.DEFAULT_CLIENT_SAGA_BRANCH_REGISTER_ENABLE; +import static org.apache.seata.common.DefaultValues.DEFAULT_CLIENT_SAGA_COMPENSATE_PERSIST_MODE_UPDATE; +import static org.apache.seata.common.DefaultValues.DEFAULT_CLIENT_SAGA_RETRY_PERSIST_MODE_UPDATE; +import static org.apache.seata.common.DefaultValues.DEFAULT_SAGA_JSON_PARSER; + +public class DbStateMachineConfig extends DefaultStateMachineConfig implements DisposableBean { + + private static final Logger LOGGER = LoggerFactory.getLogger(DbStateMachineConfig.class); + + private DataSource dataSource; + private String applicationId; + private String txServiceGroup; + private String accessKey; + private String secretKey; + private String tablePrefix = "seata_"; + private String dbType; + private boolean rmReportSuccessEnable = DEFAULT_CLIENT_REPORT_SUCCESS_ENABLE; + private boolean sagaBranchRegisterEnable = DEFAULT_CLIENT_SAGA_BRANCH_REGISTER_ENABLE; + + private SagaTransactionalTemplate sagaTransactionalTemplate; + + public DbStateMachineConfig() { + try { + Configuration configuration = ConfigurationFactory.getInstance(); + if (configuration != null) { + this.rmReportSuccessEnable = configuration.getBoolean(ConfigurationKeys.CLIENT_REPORT_SUCCESS_ENABLE, DEFAULT_CLIENT_REPORT_SUCCESS_ENABLE); + this.sagaBranchRegisterEnable = configuration.getBoolean(ConfigurationKeys.CLIENT_SAGA_BRANCH_REGISTER_ENABLE, DEFAULT_CLIENT_SAGA_BRANCH_REGISTER_ENABLE); + setSagaJsonParser(configuration.getConfig(ConfigurationKeys.CLIENT_SAGA_JSON_PARSER, DEFAULT_SAGA_JSON_PARSER)); + this.applicationId = configuration.getConfig(ConfigurationKeys.APPLICATION_ID); + this.txServiceGroup = configuration.getConfig(ConfigurationKeys.TX_SERVICE_GROUP); + this.accessKey = configuration.getConfig(ConfigurationKeys.ACCESS_KEY, null); + this.secretKey = configuration.getConfig(ConfigurationKeys.SECRET_KEY, null); + setSagaRetryPersistModeUpdate(configuration.getBoolean(ConfigurationKeys.CLIENT_SAGA_RETRY_PERSIST_MODE_UPDATE, + DEFAULT_CLIENT_SAGA_RETRY_PERSIST_MODE_UPDATE)); + setSagaCompensatePersistModeUpdate(configuration.getBoolean(ConfigurationKeys.CLIENT_SAGA_COMPENSATE_PERSIST_MODE_UPDATE, + DEFAULT_CLIENT_SAGA_COMPENSATE_PERSIST_MODE_UPDATE)); + } + } catch (Exception e) { + LOGGER.warn("Load SEATA configuration failed, use default configuration instead.", e); + } + } + + public static String getDbTypeFromDataSource(DataSource dataSource) throws SQLException { + try (Connection con = dataSource.getConnection()) { + DatabaseMetaData metaData = con.getMetaData(); + return metaData.getDatabaseProductName(); + } + } + + @Override + public void afterPropertiesSet() throws Exception { + if (dataSource == null) { + throw new IllegalArgumentException("datasource required not null!"); + } + + dbType = getDbTypeFromDataSource(dataSource); + if (getStateLogStore() == null) { + DbAndReportTcStateLogStore dbStateLogStore = new DbAndReportTcStateLogStore(); + dbStateLogStore.setDataSource(dataSource); + dbStateLogStore.setTablePrefix(tablePrefix); + dbStateLogStore.setDbType(dbType); + dbStateLogStore.setDefaultTenantId(getDefaultTenantId()); + dbStateLogStore.setSeqGenerator(getSeqGenerator()); + + if (StringUtils.hasLength(getSagaJsonParser())) { + ParamsSerializer paramsSerializer = new ParamsSerializer(); + paramsSerializer.setJsonParserName(getSagaJsonParser()); + dbStateLogStore.setParamsSerializer(paramsSerializer); + } + + if (sagaTransactionalTemplate == null) { + DefaultSagaTransactionalTemplate defaultSagaTransactionalTemplate = new DefaultSagaTransactionalTemplate(); + defaultSagaTransactionalTemplate.setApplicationContext(getApplicationContext()); + defaultSagaTransactionalTemplate.setApplicationId(applicationId); + defaultSagaTransactionalTemplate.setTxServiceGroup(txServiceGroup); + defaultSagaTransactionalTemplate.setAccessKey(accessKey); + defaultSagaTransactionalTemplate.setSecretKey(secretKey); + defaultSagaTransactionalTemplate.afterPropertiesSet(); + sagaTransactionalTemplate = defaultSagaTransactionalTemplate; + } + + dbStateLogStore.setSagaTransactionalTemplate(sagaTransactionalTemplate); + + setStateLogStore(StateLogStoreImpl.wrap(dbStateLogStore)); + } + + if (getStateLangStore() == null) { + DbStateLangStore dbStateLangStore = new DbStateLangStore(); + dbStateLangStore.setDataSource(dataSource); + dbStateLangStore.setTablePrefix(tablePrefix); + dbStateLangStore.setDbType(dbType); + + setStateLangStore(dbStateLangStore); + } + + //must execute after StateLangStore initialized + super.afterPropertiesSet(); + } + + @Override + public void destroy() throws Exception { + if ((sagaTransactionalTemplate != null) && (sagaTransactionalTemplate instanceof DisposableBean)) { + ((DisposableBean) sagaTransactionalTemplate).destroy(); + } + } + + public DataSource getDataSource() { + return dataSource; + } + + public void setDataSource(DataSource dataSource) { + this.dataSource = dataSource; + } + + public String getApplicationId() { + return applicationId; + } + + public void setApplicationId(String applicationId) { + this.applicationId = applicationId; + } + + public String getTxServiceGroup() { + return txServiceGroup; + } + + public void setTxServiceGroup(String txServiceGroup) { + this.txServiceGroup = txServiceGroup; + } + + public String getAccessKey() { + return accessKey; + } + + public void setAccessKey(String accessKey) { + this.accessKey = accessKey; + } + + public String getSecretKey() { + return secretKey; + } + + public void setSecretKey(String secretKey) { + this.secretKey = secretKey; + } + + public void setSagaTransactionalTemplate(SagaTransactionalTemplate sagaTransactionalTemplate) { + this.sagaTransactionalTemplate = sagaTransactionalTemplate; + } + + public String getTablePrefix() { + return tablePrefix; + } + + public void setTablePrefix(String tablePrefix) { + this.tablePrefix = tablePrefix; + } + + public String getDbType() { + return dbType; + } + + public void setDbType(String dbType) { + this.dbType = dbType; + } + + public boolean isRmReportSuccessEnable() { + return rmReportSuccessEnable; + } + + public boolean isSagaBranchRegisterEnable() { + return sagaBranchRegisterEnable; + } + + public void setRmReportSuccessEnable(boolean rmReportSuccessEnable) { + this.rmReportSuccessEnable = rmReportSuccessEnable; + } + + public void setSagaBranchRegisterEnable(boolean sagaBranchRegisterEnable) { + this.sagaBranchRegisterEnable = sagaBranchRegisterEnable; + } } diff --git a/compatible/src/main/java/io/seata/saga/engine/impl/DefaultStateMachineConfig.java b/compatible/src/main/java/io/seata/saga/engine/impl/DefaultStateMachineConfig.java index ab4ce4f348f..b5238f024fc 100644 --- a/compatible/src/main/java/io/seata/saga/engine/impl/DefaultStateMachineConfig.java +++ b/compatible/src/main/java/io/seata/saga/engine/impl/DefaultStateMachineConfig.java @@ -20,6 +20,8 @@ import io.seata.saga.engine.expression.ExpressionFactoryManager; import io.seata.saga.engine.repo.StateLogRepository; import io.seata.saga.engine.repo.StateMachineRepository; +import io.seata.saga.engine.store.StateLogStore; +import io.seata.saga.engine.store.impl.StateLogStoreImpl; import io.seata.saga.statelang.domain.StateInstance; import io.seata.saga.statelang.domain.StateMachineInstance; import io.seata.saga.statelang.domain.impl.StateInstanceImpl; @@ -28,7 +30,6 @@ import org.apache.seata.saga.engine.invoker.ServiceInvokerManager; import org.apache.seata.saga.engine.sequence.SeqGenerator; import org.apache.seata.saga.engine.store.StateLangStore; -import org.apache.seata.saga.engine.store.StateLogStore; import org.apache.seata.saga.engine.strategy.StatusDecisionStrategy; import org.apache.seata.saga.proctrl.eventing.impl.ProcessCtrlEventPublisher; import org.apache.seata.saga.statelang.domain.StateMachine; @@ -45,7 +46,6 @@ /** * Default state machine configuration - * */ public class DefaultStateMachineConfig implements StateMachineConfig, ApplicationContextAware, InitializingBean { @@ -70,11 +70,20 @@ public void afterPropertiesSet() throws Exception { @Override public StateLogStore getStateLogStore() { - return actual.getStateLogStore(); + org.apache.seata.saga.engine.store.StateLogStore stateLogStore = actual.getStateLogStore(); + if (stateLogStore == null) { + return null; + } + + return StateLogStoreImpl.wrap(actual.getStateLogStore()); } public void setStateLogStore(StateLogStore stateLogStore) { - actual.setStateLogStore(stateLogStore); + if (stateLogStore == null) { + actual.setStateLogStore(null); + } else { + actual.setStateLogStore(((StateLogStoreImpl) stateLogStore).unwrap()); + } } @Override @@ -146,7 +155,7 @@ public StateMachine registryStateMachine(StateMachine stateMachine) { } @Override - public void registryByResources(InputStream[] resourceAsStreamArray, String tenantId) throws IOException{ + public void registryByResources(InputStream[] resourceAsStreamArray, String tenantId) throws IOException { repository.registryByResources(resourceAsStreamArray, tenantId); } }; diff --git a/compatible/src/main/java/io/seata/saga/engine/store/StateLogStore.java b/compatible/src/main/java/io/seata/saga/engine/store/StateLogStore.java new file mode 100644 index 00000000000..4bccd943976 --- /dev/null +++ b/compatible/src/main/java/io/seata/saga/engine/store/StateLogStore.java @@ -0,0 +1,98 @@ +package io.seata.saga.engine.store; + + +import io.seata.saga.proctrl.ProcessContext; +import io.seata.saga.statelang.domain.StateInstance; +import io.seata.saga.statelang.domain.StateMachineInstance; + +import java.util.List; + +public interface StateLogStore { + + /** + * Record state machine startup events + * + * @param machineInstance the state machine instance + * @param context the state machine process context + */ + void recordStateMachineStarted(StateMachineInstance machineInstance, ProcessContext context); + + /** + * Record status end event + * + * @param machineInstance the state machine instance + * @param context the state machine process context + */ + void recordStateMachineFinished(StateMachineInstance machineInstance, ProcessContext context); + + /** + * Record state machine restarted + * + * @param machineInstance the state machine instance + * @param context the state machine process context + */ + void recordStateMachineRestarted(StateMachineInstance machineInstance, ProcessContext context); + + /** + * Record state start execution event + * + * @param stateInstance the state machine instance + * @param context the state machine process context + */ + void recordStateStarted(StateInstance stateInstance, ProcessContext context); + + /** + * Record state execution end event + * + * @param stateInstance the state machine instance + * @param context the state machine process context + */ + void recordStateFinished(StateInstance stateInstance, ProcessContext context); + + /** + * Get state machine instance + * + * @param stateMachineInstanceId the state machine instance id + * @return the state machine instance + */ + StateMachineInstance getStateMachineInstance(String stateMachineInstanceId); + + /** + * Get state machine instance by businessKey + * + * @param businessKey the businessKey + * @param tenantId the tenant id + * @return state machine message + */ + StateMachineInstance getStateMachineInstanceByBusinessKey(String businessKey, String tenantId); + + /** + * Query the list of state machine instances by parent id + * + * @param parentId the state machine parent's id + * @return the state machine instance list + */ + List queryStateMachineInstanceByParentId(String parentId); + + /** + * Get state instance + * + * @param stateInstanceId the state instance id + * @param machineInstId the machine instance id + * @return state instance message + */ + StateInstance getStateInstance(String stateInstanceId, String machineInstId); + + /** + * Get a list of state instances by state machine instance id + * + * @param stateMachineInstanceId the state machine instance id + * @return state instance list + */ + List queryStateInstanceListByMachineInstanceId(String stateMachineInstanceId); + + /** + * clear the LocalThread + */ + void clearUp(ProcessContext context); +} diff --git a/compatible/src/main/java/io/seata/saga/engine/store/impl/StateLogStoreAdapter.java b/compatible/src/main/java/io/seata/saga/engine/store/impl/StateLogStoreAdapter.java new file mode 100644 index 00000000000..471b25b6590 --- /dev/null +++ b/compatible/src/main/java/io/seata/saga/engine/store/impl/StateLogStoreAdapter.java @@ -0,0 +1,106 @@ +package io.seata.saga.engine.store.impl; + +import io.seata.saga.engine.store.StateLogStore; +import io.seata.saga.proctrl.impl.ProcessContextImpl; +import io.seata.saga.statelang.domain.impl.StateInstanceImpl; +import io.seata.saga.statelang.domain.impl.StateMachineInstanceImpl; +import org.apache.seata.common.util.CollectionUtils; +import org.apache.seata.saga.proctrl.HierarchicalProcessContext; +import org.apache.seata.saga.proctrl.ProcessContext; +import org.apache.seata.saga.statelang.domain.StateInstance; +import org.apache.seata.saga.statelang.domain.StateMachineInstance; + +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +public class StateLogStoreAdapter implements org.apache.seata.saga.engine.store.StateLogStore { + + private final StateLogStore actual; + + public StateLogStoreAdapter(StateLogStore actual) { + this.actual = actual; + } + + @Override + public void recordStateMachineStarted(StateMachineInstance machineInstance, ProcessContext context) { + io.seata.saga.statelang.domain.StateMachineInstance wrapStateMachineInstance = StateMachineInstanceImpl.wrap(machineInstance); + io.seata.saga.proctrl.ProcessContext wrapContext = ProcessContextImpl.wrap((HierarchicalProcessContext) context); + actual.recordStateMachineStarted(wrapStateMachineInstance, wrapContext); + } + + @Override + public void recordStateMachineFinished(StateMachineInstance machineInstance, ProcessContext context) { + io.seata.saga.statelang.domain.StateMachineInstance wrapStateMachineInstance = StateMachineInstanceImpl.wrap(machineInstance); + io.seata.saga.proctrl.ProcessContext wrapContext = ProcessContextImpl.wrap((HierarchicalProcessContext) context); + actual.recordStateMachineFinished(wrapStateMachineInstance, wrapContext); + } + + @Override + public void recordStateMachineRestarted(StateMachineInstance machineInstance, ProcessContext context) { + io.seata.saga.statelang.domain.StateMachineInstance wrapStateMachineInstance = StateMachineInstanceImpl.wrap(machineInstance); + io.seata.saga.proctrl.ProcessContext wrapContext = ProcessContextImpl.wrap((HierarchicalProcessContext) context); + actual.recordStateMachineRestarted(wrapStateMachineInstance, wrapContext); + } + + @Override + public void recordStateStarted(StateInstance stateInstance, ProcessContext context) { + io.seata.saga.statelang.domain.StateInstance wrapStateInstance = StateInstanceImpl.wrap(stateInstance); + io.seata.saga.proctrl.ProcessContext wrapContext = ProcessContextImpl.wrap((HierarchicalProcessContext) context); + actual.recordStateStarted(wrapStateInstance, wrapContext); + } + + @Override + public void recordStateFinished(StateInstance stateInstance, ProcessContext context) { + io.seata.saga.statelang.domain.StateInstance wrapStateInstance = StateInstanceImpl.wrap(stateInstance); + io.seata.saga.proctrl.ProcessContext wrapContext = ProcessContextImpl.wrap((HierarchicalProcessContext) context); + actual.recordStateFinished(wrapStateInstance, wrapContext); + } + + @Override + public StateMachineInstance getStateMachineInstance(String stateMachineInstanceId) { + io.seata.saga.statelang.domain.StateMachineInstance stateMachineInstance = actual.getStateMachineInstance(stateMachineInstanceId); + return ((StateMachineInstanceImpl) stateMachineInstance).unwrap(); + } + + @Override + public StateMachineInstance getStateMachineInstanceByBusinessKey(String businessKey, String tenantId) { + io.seata.saga.statelang.domain.StateMachineInstance stateMachineInstance = actual.getStateMachineInstanceByBusinessKey(businessKey, tenantId); + return ((StateMachineInstanceImpl) stateMachineInstance).unwrap(); + } + + @Override + public List queryStateMachineInstanceByParentId(String parentId) { + List stateMachineInstances = actual.queryStateMachineInstanceByParentId(parentId); + if (CollectionUtils.isEmpty(stateMachineInstances)) { + return new ArrayList<>(); + } + + return stateMachineInstances.stream().map(stateMachineInstance -> ((StateMachineInstanceImpl)stateMachineInstance).unwrap()).collect(Collectors.toList()); + } + + @Override + public StateInstance getStateInstance(String stateInstanceId, String machineInstId) { + io.seata.saga.statelang.domain.StateInstance stateInstance = actual.getStateInstance(stateInstanceId, machineInstId); + return ((StateInstanceImpl) stateInstance).unwrap(); + } + + @Override + public List queryStateInstanceListByMachineInstanceId(String stateMachineInstanceId) { + List stateInstances = actual.queryStateInstanceListByMachineInstanceId(stateMachineInstanceId); + if (CollectionUtils.isEmpty(stateInstances)) { + return new ArrayList<>(); + } + return stateInstances.stream().map(stateInstance -> ((StateInstanceImpl)stateInstance).unwrap()).collect(Collectors.toList()); + } + + @Override + public void clearUp(ProcessContext context) { + io.seata.saga.proctrl.ProcessContext wrapContext = ProcessContextImpl.wrap((HierarchicalProcessContext) context); + actual.clearUp(wrapContext); + } + + public StateLogStore getActual() { + return actual; + } +} diff --git a/compatible/src/main/java/io/seata/saga/engine/store/impl/StateLogStoreImpl.java b/compatible/src/main/java/io/seata/saga/engine/store/impl/StateLogStoreImpl.java new file mode 100644 index 00000000000..6c63d8aca3f --- /dev/null +++ b/compatible/src/main/java/io/seata/saga/engine/store/impl/StateLogStoreImpl.java @@ -0,0 +1,100 @@ +package io.seata.saga.engine.store.impl; + +import io.seata.saga.engine.store.StateLogStore; +import io.seata.saga.proctrl.ProcessContext; +import io.seata.saga.proctrl.impl.ProcessContextImpl; +import io.seata.saga.statelang.domain.StateInstance; +import io.seata.saga.statelang.domain.StateMachineInstance; +import io.seata.saga.statelang.domain.impl.StateInstanceImpl; +import io.seata.saga.statelang.domain.impl.StateMachineInstanceImpl; +import org.apache.seata.common.util.CollectionUtils; + +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +public class StateLogStoreImpl implements StateLogStore { + + private final org.apache.seata.saga.engine.store.StateLogStore actual; + + private StateLogStoreImpl(org.apache.seata.saga.engine.store.StateLogStore actual) { + this.actual = actual; + } + + @Override + public void recordStateMachineStarted(StateMachineInstance machineInstance, ProcessContext context) { + actual.recordStateMachineStarted(((StateMachineInstanceImpl) machineInstance).unwrap(), ((ProcessContextImpl) context).unwrap()); + } + + @Override + public void recordStateMachineFinished(StateMachineInstance machineInstance, ProcessContext context) { + actual.recordStateMachineFinished(((StateMachineInstanceImpl) machineInstance).unwrap(), ((ProcessContextImpl) context).unwrap()); + } + + @Override + public void recordStateMachineRestarted(StateMachineInstance machineInstance, ProcessContext context) { + actual.recordStateMachineRestarted(((StateMachineInstanceImpl) machineInstance).unwrap(), ((ProcessContextImpl) context).unwrap()); + } + + @Override + public void recordStateStarted(StateInstance stateInstance, ProcessContext context) { + actual.recordStateStarted(((StateInstanceImpl) stateInstance).unwrap(), ((ProcessContextImpl) context).unwrap()); + } + + @Override + public void recordStateFinished(StateInstance stateInstance, ProcessContext context) { + actual.recordStateFinished(((StateInstanceImpl) stateInstance).unwrap(), ((ProcessContextImpl) context).unwrap()); + } + + @Override + public StateMachineInstance getStateMachineInstance(String stateMachineInstanceId) { + org.apache.seata.saga.statelang.domain.StateMachineInstance stateMachineInstance = actual.getStateMachineInstance(stateMachineInstanceId); + return StateMachineInstanceImpl.wrap(stateMachineInstance); + } + + @Override + public StateMachineInstance getStateMachineInstanceByBusinessKey(String businessKey, String tenantId) { + org.apache.seata.saga.statelang.domain.StateMachineInstance stateMachineInstance = actual.getStateMachineInstanceByBusinessKey(businessKey, tenantId); + return StateMachineInstanceImpl.wrap(stateMachineInstance); + } + + @Override + public List queryStateMachineInstanceByParentId(String parentId) { + List stateMachineInstances = actual.queryStateMachineInstanceByParentId(parentId); + if (CollectionUtils.isEmpty(stateMachineInstances)) { + return new ArrayList<>(); + } + return stateMachineInstances.stream().map(StateMachineInstanceImpl::wrap).collect(Collectors.toList()); + } + + @Override + public StateInstance getStateInstance(String stateInstanceId, String machineInstId) { + org.apache.seata.saga.statelang.domain.StateInstance stateInstance = actual.getStateInstance(stateInstanceId, machineInstId); + if (stateInstance == null) { + return null; + } + return StateInstanceImpl.wrap(stateInstance); + } + + @Override + public List queryStateInstanceListByMachineInstanceId(String stateMachineInstanceId) { + List stateInstances = actual.queryStateInstanceListByMachineInstanceId(stateMachineInstanceId); + if (CollectionUtils.isEmpty(stateInstances)) { + return new ArrayList<>(); + } + return stateInstances.stream().map(StateInstanceImpl::wrap).collect(Collectors.toList()); + } + + @Override + public void clearUp(ProcessContext context) { + actual.clearUp(((ProcessContextImpl) context).unwrap()); + } + + public static StateLogStore wrap(org.apache.seata.saga.engine.store.StateLogStore actual) { + return new StateLogStoreImpl(actual); + } + + public org.apache.seata.saga.engine.store.StateLogStore unwrap() { + return actual; + } +} diff --git a/compatible/src/main/java/io/seata/saga/rm/SagaResourceManager.java b/compatible/src/main/java/io/seata/saga/rm/SagaResourceManager.java new file mode 100644 index 00000000000..78419cd2cbc --- /dev/null +++ b/compatible/src/main/java/io/seata/saga/rm/SagaResourceManager.java @@ -0,0 +1,145 @@ +package io.seata.saga.rm; + +import io.seata.saga.statelang.domain.ExecutionStatus; +import io.seata.saga.statelang.domain.StateMachineInstance; +import org.apache.seata.common.exception.FrameworkErrorCode; +import org.apache.seata.core.exception.TransactionException; +import org.apache.seata.core.model.BranchStatus; +import org.apache.seata.core.model.BranchType; +import org.apache.seata.core.model.GlobalStatus; +import org.apache.seata.core.model.Resource; +import org.apache.seata.rm.AbstractResourceManager; +import org.apache.seata.saga.engine.exception.EngineExecutionException; +import org.apache.seata.saga.engine.exception.ForwardInvalidException; +import org.apache.seata.saga.rm.SagaResource; +import org.apache.seata.saga.statelang.domain.RecoverStrategy; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +public class SagaResourceManager extends AbstractResourceManager { + + private static final Logger LOGGER = LoggerFactory.getLogger(SagaResourceManager.class); + + /** + * Saga resource cache + */ + private Map sagaResourceCache = new ConcurrentHashMap<>(); + + /** + * Instantiates a new saga resource manager. + */ + public SagaResourceManager() { + } + + /** + * registry saga resource + * + * @param resource The resource to be managed. + */ + @Override + public void registerResource(Resource resource) { + SagaResource sagaResource = (SagaResource)resource; + sagaResourceCache.put(sagaResource.getResourceId(), sagaResource); + super.registerResource(sagaResource); + } + + @Override + public Map getManagedResources() { + return sagaResourceCache; + } + + /** + * SAGA branch commit + * + * @param branchType the branch type + * @param xid Transaction id. + * @param branchId Branch id. + * @param resourceId Resource id. + * @param applicationData Application data bind with this branch. + * @return the branch status + * @throws TransactionException the transaction exception + */ + @Override + public BranchStatus branchCommit(BranchType branchType, String xid, long branchId, String resourceId, + String applicationData) throws TransactionException { + try { + StateMachineInstance machineInstance = StateMachineEngineHolder.getStateMachineEngine().forward(xid, null); + + if (ExecutionStatus.SU.equals(machineInstance.getStatus()) + && machineInstance.getCompensationStatus() == null) { + return BranchStatus.PhaseTwo_Committed; + } else if (ExecutionStatus.SU.equals(machineInstance.getCompensationStatus())) { + return BranchStatus.PhaseTwo_Rollbacked; + } else if (ExecutionStatus.FA.equals(machineInstance.getCompensationStatus()) || ExecutionStatus.UN.equals( + machineInstance.getCompensationStatus())) { + return BranchStatus.PhaseTwo_RollbackFailed_Retryable; + } else if (ExecutionStatus.FA.equals(machineInstance.getStatus()) + && machineInstance.getCompensationStatus() == null) { + return BranchStatus.PhaseOne_Failed; + } + + } catch (ForwardInvalidException e) { + LOGGER.error("StateMachine forward failed, xid: " + xid, e); + + //if StateMachineInstanceNotExists stop retry + if (FrameworkErrorCode.StateMachineInstanceNotExists.equals(e.getErrcode())) { + return BranchStatus.PhaseTwo_Committed; + } + } catch (Exception e) { + LOGGER.error("StateMachine forward failed, xid: " + xid, e); + } + return BranchStatus.PhaseTwo_CommitFailed_Retryable; + } + + /** + * SAGA branch rollback + * + * @param branchType the branch type + * @param xid Transaction id. + * @param branchId Branch id. + * @param resourceId Resource id. + * @param applicationData Application data bind with this branch. + * @return the branch status + * @throws TransactionException the transaction exception + */ + @Override + public BranchStatus branchRollback(BranchType branchType, String xid, long branchId, String resourceId, + String applicationData) throws TransactionException { + try { + StateMachineInstance stateMachineInstance = StateMachineEngineHolder.getStateMachineEngine().reloadStateMachineInstance(xid); + if (stateMachineInstance == null) { + return BranchStatus.PhaseTwo_Rollbacked; + } + if (RecoverStrategy.Forward.equals(stateMachineInstance.getStateMachine().getRecoverStrategy()) + && (GlobalStatus.TimeoutRollbacking.name().equals(applicationData) + || GlobalStatus.TimeoutRollbackRetrying.name().equals(applicationData))) { + LOGGER.warn("Retry by custom recover strategy [Forward] on timeout, SAGA global[{}]", xid); + return BranchStatus.PhaseTwo_CommitFailed_Retryable; + } + + stateMachineInstance = StateMachineEngineHolder.getStateMachineEngine().compensate(xid, + null); + if (ExecutionStatus.SU.equals(stateMachineInstance.getCompensationStatus())) { + return BranchStatus.PhaseTwo_Rollbacked; + } + } catch (EngineExecutionException e) { + LOGGER.error("StateMachine compensate failed, xid: " + xid, e); + + //if StateMachineInstanceNotExists stop retry + if (FrameworkErrorCode.StateMachineInstanceNotExists.equals(e.getErrcode())) { + return BranchStatus.PhaseTwo_Rollbacked; + } + } catch (Exception e) { + LOGGER.error("StateMachine compensate failed, xid: " + xid, e); + } + return BranchStatus.PhaseTwo_RollbackFailed_Retryable; + } + + @Override + public BranchType getBranchType() { + return BranchType.SAGA; + } +} diff --git a/compatible/src/main/java/io/seata/saga/rm/StateMachineEngineHolder.java b/compatible/src/main/java/io/seata/saga/rm/StateMachineEngineHolder.java new file mode 100644 index 00000000000..c1b200c5902 --- /dev/null +++ b/compatible/src/main/java/io/seata/saga/rm/StateMachineEngineHolder.java @@ -0,0 +1,17 @@ +package io.seata.saga.rm; + + +import io.seata.saga.engine.StateMachineEngine; + +public class StateMachineEngineHolder { + + private static StateMachineEngine stateMachineEngine; + + public static StateMachineEngine getStateMachineEngine() { + return stateMachineEngine; + } + + public static void setStateMachineEngine(StateMachineEngine smEngine) { + stateMachineEngine = smEngine; + } +} diff --git a/compatible/src/main/java/io/seata/saga/statelang/domain/ExecutionStatus.java b/compatible/src/main/java/io/seata/saga/statelang/domain/ExecutionStatus.java index 388e6bcf934..06b6632eefd 100644 --- a/compatible/src/main/java/io/seata/saga/statelang/domain/ExecutionStatus.java +++ b/compatible/src/main/java/io/seata/saga/statelang/domain/ExecutionStatus.java @@ -58,6 +58,9 @@ public String getStatusString() { } public static ExecutionStatus wrap(org.apache.seata.saga.statelang.domain.ExecutionStatus target) { + if(target == null){ + return null; + } switch (target) { case RU: return RU; diff --git a/compatible/src/main/java/io/seata/saga/statelang/domain/impl/StateInstanceImpl.java b/compatible/src/main/java/io/seata/saga/statelang/domain/impl/StateInstanceImpl.java index be52a688a77..25dd81ee60f 100644 --- a/compatible/src/main/java/io/seata/saga/statelang/domain/impl/StateInstanceImpl.java +++ b/compatible/src/main/java/io/seata/saga/statelang/domain/impl/StateInstanceImpl.java @@ -153,7 +153,7 @@ public boolean isForUpdate() { @Override public void setForUpdate(boolean forUpdate) { - setForUpdate(forUpdate); + actual.setForUpdate(forUpdate); } @Override @@ -213,7 +213,11 @@ public ExecutionStatus getStatus() { @Override public void setStatus(ExecutionStatus status) { - actual.setStatus(status.unwrap()); + if(status == null){ + actual.setStatus(null); + }else { + actual.setStatus(status.unwrap()); + } } @Override diff --git a/compatible/src/main/java/io/seata/saga/statelang/domain/impl/StateMachineInstanceImpl.java b/compatible/src/main/java/io/seata/saga/statelang/domain/impl/StateMachineInstanceImpl.java index 4d04d00296e..27e422a4870 100644 --- a/compatible/src/main/java/io/seata/saga/statelang/domain/impl/StateMachineInstanceImpl.java +++ b/compatible/src/main/java/io/seata/saga/statelang/domain/impl/StateMachineInstanceImpl.java @@ -111,7 +111,11 @@ public ExecutionStatus getStatus() { @Override public void setStatus(ExecutionStatus status) { - actual.setStatus(status.unwrap()); + if (status == null) { + actual.setStatus(null); + } else { + actual.setStatus(status.unwrap()); + } } @Override @@ -121,7 +125,11 @@ public ExecutionStatus getCompensationStatus() { @Override public void setCompensationStatus(ExecutionStatus compensationStatus) { - actual.setCompensationStatus(compensationStatus.unwrap()); + if (compensationStatus == null) { + actual.setCompensationStatus(null); + } else { + actual.setCompensationStatus(compensationStatus.unwrap()); + } } @Override diff --git a/compatible/src/main/java/io/seata/spring/tcc/TccAnnotationProcessor.java b/compatible/src/main/java/io/seata/spring/tcc/TccAnnotationProcessor.java index 9e7744928ab..eae8b7274b1 100644 --- a/compatible/src/main/java/io/seata/spring/tcc/TccAnnotationProcessor.java +++ b/compatible/src/main/java/io/seata/spring/tcc/TccAnnotationProcessor.java @@ -26,6 +26,7 @@ import java.lang.reflect.Method; import java.lang.reflect.Modifier; +@Deprecated public class TccAnnotationProcessor extends org.apache.seata.spring.tcc.TccAnnotationProcessor { private static final Logger LOGGER = LoggerFactory.getLogger(org.apache.seata.spring.tcc.TccAnnotationProcessor.class); diff --git a/compatible/src/main/resources/META-INF/services/org.apache.seata.core.model.ResourceManager b/compatible/src/main/resources/META-INF/services/org.apache.seata.core.model.ResourceManager new file mode 100644 index 00000000000..8b137891791 --- /dev/null +++ b/compatible/src/main/resources/META-INF/services/org.apache.seata.core.model.ResourceManager @@ -0,0 +1 @@ + diff --git a/compatible/src/test/java/io/seata/common/LockAndCallback.java b/compatible/src/test/java/io/seata/common/LockAndCallback.java new file mode 100644 index 00000000000..8750e328a31 --- /dev/null +++ b/compatible/src/test/java/io/seata/common/LockAndCallback.java @@ -0,0 +1,99 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.seata.common; + + +import io.seata.saga.engine.AsyncCallback; +import io.seata.saga.proctrl.ProcessContext; +import io.seata.saga.statelang.domain.ExecutionStatus; +import io.seata.saga.statelang.domain.StateMachineInstance; + +import java.util.concurrent.TimeUnit; +import java.util.concurrent.locks.Condition; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; + +/** + */ +public class LockAndCallback { + private final Lock lock; + private final Condition notFinished; + private final AsyncCallback callback; + private final static long DEFAULT_TIMEOUT = 60000; + private String result; + + public LockAndCallback() { + lock = new ReentrantLock(); + notFinished = lock.newCondition(); + callback = new AsyncCallback() { + @Override + public void onFinished(ProcessContext context, StateMachineInstance stateMachineInstance) { + result = "onFinished"; + try { + lock.lock(); + notFinished.signal(); + } finally { + lock.unlock(); + } + } + + @Override + public void onError(ProcessContext context, StateMachineInstance stateMachineInstance, Exception exp) { + result = "onError"; + try { + lock.lock(); + notFinished.signal(); + } finally { + lock.unlock(); + } + } + }; + } + public void waitingForFinish(StateMachineInstance inst) { + waitingForFinish(inst, DEFAULT_TIMEOUT); + } + + public void waitingForFinish(StateMachineInstance inst, long timeout) { + if (ExecutionStatus.RU.equals(inst.getStatus())) { + long start = System.nanoTime(); + try { + lock.lock(); + boolean finished = notFinished.await(timeout, TimeUnit.MILLISECONDS); + if (finished) { + System.out.printf("finish wait ====== XID: %s, status: %s, compensationStatus: %s, cost: %d ms, result: %s\r\n", + inst.getId(), inst.getStatus(), inst.getCompensationStatus(), (System.nanoTime() - start) / 1000_000, result); + } else { + System.out.printf("timeout wait ====== XID: %s, status: %s, compensationStatus: %s, cost: %d ms, result: %s\r\n", + inst.getId(), inst.getStatus(), inst.getCompensationStatus(), (System.nanoTime() - start) / 1000_000, result); + } + } catch (Exception e) { + System.out.printf("error wait ====== XID: %s, status: %s, compensationStatus: %s, cost: %d ms, result: %s, error: %s\r\n", + inst.getId(), inst.getStatus(), inst.getCompensationStatus(), (System.nanoTime() - start) / 1000_000, result, e.getMessage()); + throw new RuntimeException("waitingForFinish failed", e); + } finally { + lock.unlock(); + } + } else { + System.out.printf("do not wait ====== XID: %s, status: %s, compensationStatus: %s, result: %s\r\n", + inst.getId(), inst.getStatus(), inst.getCompensationStatus(), result); + } + } + + public AsyncCallback getCallback() { + return callback; + } +} diff --git a/compatible/src/test/java/io/seata/saga/SagaCostPrint.java b/compatible/src/test/java/io/seata/saga/SagaCostPrint.java new file mode 100644 index 00000000000..2ac38764f2f --- /dev/null +++ b/compatible/src/test/java/io/seata/saga/SagaCostPrint.java @@ -0,0 +1,64 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.seata.saga; + + +import io.seata.saga.statelang.domain.StateMachineInstance; + +/** + */ +public class SagaCostPrint { + + public static StateMachineInstance executeAndPrint(String flag, Executor execute) throws Exception { + long start = System.nanoTime(); + + StateMachineInstance inst = null; + Exception e = null; + try { + inst = execute.run(); + } catch (Exception ex) { + ex.printStackTrace(); + e = ex; + throw ex; + } finally { + long cost = (System.nanoTime() - start) / 1000_000; + System.out.printf("====== XID: %s , cost%s: %d ms , error: %s\r\n", + inst != null ? inst.getId() : null, + flag, + cost, + (e != null ? e.getMessage() : null)); + } + return inst; + } + + public static void executeAndPrint(String flag, Runnable runnable) throws Exception { + executeAndPrint(flag, () -> { + runnable.run(); + return null; + }); + } + + @FunctionalInterface + public interface Executor { + StateMachineInstance run() throws Exception; + } + + @FunctionalInterface + public interface Runnable { + void run() throws Exception; + } +} diff --git a/compatible/src/test/java/io/seata/saga/engine/StateMachineTests.java b/compatible/src/test/java/io/seata/saga/engine/StateMachineTests.java new file mode 100644 index 00000000000..0be2dcea325 --- /dev/null +++ b/compatible/src/test/java/io/seata/saga/engine/StateMachineTests.java @@ -0,0 +1,315 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.seata.saga.engine; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +import io.seata.saga.SagaCostPrint; +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 org.apache.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; + +/** + * State machine tests + * + */ +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() throws Exception { + String stateMachineName = "simpleChoiceTestStateMachine"; + + SagaCostPrint.executeAndPrint("1-1", () -> { + Map paramMap = new HashMap<>(); + paramMap.put("a", 1); + + stateMachineEngine.start(stateMachineName, null, paramMap); + }); + + SagaCostPrint.executeAndPrint("1-2", () -> { + Map paramMap = new HashMap<>(); + paramMap.put("a", 2); + + stateMachineEngine.start(stateMachineName, null, paramMap); + }); + } + + @Test + public void testSimpleStateMachineWithChoiceAndEnd() throws Exception { + String stateMachineName = "simpleChoiceAndEndTestStateMachine"; + + SagaCostPrint.executeAndPrint("1-3", () -> { + Map paramMap = new HashMap<>(1); + paramMap.put("a", 1); + + StateMachineInstance inst = stateMachineEngine.start(stateMachineName, null, paramMap); + }); + + SagaCostPrint.executeAndPrint("1-4", () -> { + Map paramMap = new HashMap<>(1); + paramMap.put("a", 3); + stateMachineEngine.start(stateMachineName, null, paramMap); + }); + } + + @Test + public void testSimpleInputAssignmentStateMachine() throws Exception { + String stateMachineName = "simpleInputAssignmentStateMachine"; + + SagaCostPrint.executeAndPrint("1-5", () -> { + Map paramMap = new HashMap<>(1); + paramMap.put("a", 1); + + StateMachineInstance inst = stateMachineEngine.start(stateMachineName, null, paramMap); + + String businessKey = inst.getStateList().get(0).getBusinessKey(); + Assertions.assertNotNull(businessKey); + System.out.println("====== businessKey :" + businessKey); + + String contextBusinessKey = (String)inst.getEndParams().get( + inst.getStateList().get(0).getName() + DomainConstants.VAR_NAME_BUSINESSKEY); + Assertions.assertNotNull(contextBusinessKey); + System.out.println("====== context businessKey :" + businessKey); + }); + } + + @Test + public void testSimpleCatchesStateMachine() throws Exception { + String stateMachineName = "simpleCachesStateMachine"; + + SagaCostPrint.executeAndPrint("1-6", () -> { + Map paramMap = new HashMap<>(1); + paramMap.put("a", 1); + paramMap.put("barThrowException", "true"); + + StateMachineInstance inst = stateMachineEngine.start(stateMachineName, null, paramMap); + + Assertions.assertNotNull(inst.getException()); + Assertions.assertEquals(ExecutionStatus.FA, inst.getStatus()); + }); + } + + @Test + public void testSimpleScriptTaskStateMachine() throws Exception { + String stateMachineName = "simpleScriptTaskStateMachine"; + + SagaCostPrint.executeAndPrint("1-7", () -> { + Map paramMap = new HashMap<>(1); + paramMap.put("a", 1); + + StateMachineInstance inst = stateMachineEngine.start(stateMachineName, null, paramMap); + + Assertions.assertEquals(ExecutionStatus.SU, inst.getStatus()); + Assertions.assertNotNull(inst.getEndParams().get("scriptStateResult")); + }); + + SagaCostPrint.executeAndPrint("1-8", () -> { + Map paramMap = new HashMap<>(1); + paramMap.put("a", 1); + + StateMachineInstance inst = stateMachineEngine.start(stateMachineName, null, paramMap); + + Assertions.assertEquals(ExecutionStatus.SU, inst.getStatus()); + }); + + SagaCostPrint.executeAndPrint("1-9", () -> { + Map paramMap = new HashMap<>(1); + paramMap.put("scriptThrowException", true); + + StateMachineInstance inst = stateMachineEngine.start(stateMachineName, null, paramMap); + + Assertions.assertEquals(ExecutionStatus.FA, inst.getStatus()); + }); + } + + @Test + public void testSimpleRetryStateMachine() throws Exception { + String stateMachineName = "simpleRetryStateMachine"; + + SagaCostPrint.executeAndPrint("1-10", () -> { + Map paramMap = new HashMap<>(1); + paramMap.put("a", 1); + paramMap.put("barThrowException", "true"); + + StateMachineInstance inst = stateMachineEngine.start(stateMachineName, null, paramMap); + + Assertions.assertNotNull(inst.getException()); + Assertions.assertEquals(ExecutionStatus.FA, inst.getStatus()); + }); + } + + @Test + public void testStatusMatchingStateMachine() throws Exception { + String stateMachineName = "simpleStatusMatchingStateMachine"; + + SagaCostPrint.executeAndPrint("1-11", () -> { + Map paramMap = new HashMap<>(2); + paramMap.put("a", 1); + paramMap.put("barThrowException", "true"); + + StateMachineInstance inst = stateMachineEngine.start(stateMachineName, null, paramMap); + + Assertions.assertNotNull(inst.getException()); + Assertions.assertEquals(ExecutionStatus.UN, inst.getStatus()); + }); + } + + @Test + public void testCompensationStateMachine() throws Exception { + String stateMachineName = "simpleCompensationStateMachine"; + + SagaCostPrint.executeAndPrint("1-12", () -> { + Map paramMap = new HashMap<>(1); + paramMap.put("a", 1); + paramMap.put("barThrowException", "true"); + + StateMachineInstance inst = stateMachineEngine.start(stateMachineName, null, paramMap); + + Assertions.assertEquals(ExecutionStatus.UN, inst.getStatus()); + Assertions.assertEquals(ExecutionStatus.SU, inst.getCompensationStatus()); + }); + } + + @Test + public void testCompensationAndSubStateMachine() throws Exception { + String stateMachineName = "simpleStateMachineWithCompensationAndSubMachine"; + + SagaCostPrint.executeAndPrint("1-13", () -> { + Map paramMap = new HashMap<>(1); + paramMap.put("a", 2); + paramMap.put("barThrowException", "true"); + + + StateMachineInstance inst = stateMachineEngine.start(stateMachineName, null, paramMap); + + Assertions.assertEquals(ExecutionStatus.UN, inst.getStatus()); + }); + } + + @Test + public void testCompensationAndSubStateMachineWithLayout() throws Exception { + String stateMachineName = "simpleStateMachineWithCompensationAndSubMachine_layout"; + + SagaCostPrint.executeAndPrint("1-14", () -> { + Map paramMap = new HashMap<>(1); + paramMap.put("a", 2); + paramMap.put("barThrowException", "true"); + + StateMachineInstance inst = stateMachineEngine.start(stateMachineName, null, paramMap); + + Assertions.assertEquals(ExecutionStatus.UN, 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(Collections.singletonList(people3)); + Map 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() throws Exception { + String stateMachineName = "simpleStateMachineWithComplexParams"; + + SagaCostPrint.executeAndPrint("1-15", () -> { + Map 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); + + StateMachineInstance inst = stateMachineEngine.start(stateMachineName, null, paramMap); + + People peopleResult = (People)inst.getEndParams().get("complexParameterMethodResult"); + Assertions.assertNotNull(peopleResult); + Assertions.assertEquals(people.getName(), peopleResult.getName()); + + Assertions.assertEquals(ExecutionStatus.SU, inst.getStatus()); + }); + } + + @Test + public void testSimpleStateMachineWithAsyncState() throws Exception { + String stateMachineName = "simpleStateMachineWithAsyncState"; + + SagaCostPrint.executeAndPrint("1-16", () -> { + Map paramMap = new HashMap<>(1); + paramMap.put("a", 1); + + StateMachineInstance inst = stateMachineEngine.start(stateMachineName, null, paramMap); + + Assertions.assertEquals(ExecutionStatus.SU, inst.getStatus()); + }); + + try { + Thread.sleep(500); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } +} diff --git a/compatible/src/test/java/io/seata/saga/engine/db/mockserver/StateMachineAsyncDBMockServerTests.java b/compatible/src/test/java/io/seata/saga/engine/db/mockserver/StateMachineAsyncDBMockServerTests.java new file mode 100644 index 00000000000..98daec128c2 --- /dev/null +++ b/compatible/src/test/java/io/seata/saga/engine/db/mockserver/StateMachineAsyncDBMockServerTests.java @@ -0,0 +1,202 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.seata.saga.engine.db.mockserver; + +import io.seata.common.LockAndCallback; +import io.seata.saga.SagaCostPrint; +import io.seata.saga.engine.StateMachineEngine; +import io.seata.saga.engine.mock.DemoService.People; +import io.seata.saga.rm.StateMachineEngineHolder; +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 + * + */ +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); + StateMachineEngineHolder.setStateMachineEngine(stateMachineEngine); + } + + @Test + public void testSimpleCatchesStateMachine() throws Exception { + String stateMachineName = "simpleCachesStateMachine"; + + SagaCostPrint.executeAndPrint("4-1", () -> { + Map paramMap = new HashMap<>(2); + paramMap.put("a", 1); + paramMap.put("barThrowException", "true"); + + LockAndCallback lockAndCallback = new LockAndCallback(); + StateMachineInstance inst = stateMachineEngine.startAsync(stateMachineName, null, paramMap, lockAndCallback.getCallback()); + lockAndCallback.waitingForFinish(inst); + + Assertions.assertNotNull(inst.getException()); + Assertions.assertEquals(ExecutionStatus.FA, inst.getStatus()); + }); + } + + @Test + public void testSimpleRetryStateMachine() throws Exception { + String stateMachineName = "simpleRetryStateMachine"; + + SagaCostPrint.executeAndPrint("4-2", () -> { + Map paramMap = new HashMap<>(2); + paramMap.put("a", 1); + paramMap.put("barThrowException", "true"); + + LockAndCallback lockAndCallback = new LockAndCallback(); + StateMachineInstance inst = stateMachineEngine.startAsync(stateMachineName, null, paramMap, lockAndCallback.getCallback()); + lockAndCallback.waitingForFinish(inst); + + Assertions.assertNotNull(inst.getException()); + Assertions.assertEquals(ExecutionStatus.FA, inst.getStatus()); + }); + } + + @Test + public void testStatusMatchingStateMachine() throws Exception { + String stateMachineName = "simpleStatusMatchingStateMachine"; + + SagaCostPrint.executeAndPrint("4-3", () -> { + Map paramMap = new HashMap<>(2); + paramMap.put("a", 1); + paramMap.put("barThrowException", "true"); + + LockAndCallback lockAndCallback = new LockAndCallback(); + StateMachineInstance inst = stateMachineEngine.startAsync(stateMachineName, null, paramMap, lockAndCallback.getCallback()); + lockAndCallback.waitingForFinish(inst); + + Assertions.assertNotNull(inst.getException()); + Assertions.assertEquals(ExecutionStatus.UN, inst.getStatus()); + }); + } + + @Test + public void testCompensationStateMachine() throws Exception { + String stateMachineName = "simpleCompensationStateMachine"; + + SagaCostPrint.executeAndPrint("4-4", () -> { + Map paramMap = new HashMap<>(1); + paramMap.put("a", 1); + paramMap.put("barThrowException", "true"); + + LockAndCallback lockAndCallback = new LockAndCallback(); + StateMachineInstance inst = stateMachineEngine.startAsync(stateMachineName, null, paramMap, lockAndCallback.getCallback()); + lockAndCallback.waitingForFinish(inst); + + Assertions.assertEquals(ExecutionStatus.UN, inst.getStatus()); + Assertions.assertEquals(ExecutionStatus.SU, inst.getCompensationStatus()); + }); + } + + @Test + public void testCompensationAndSubStateMachine() throws Exception { + String stateMachineName = "simpleStateMachineWithCompensationAndSubMachine"; + + SagaCostPrint.executeAndPrint("4-5", () -> { + Map paramMap = new HashMap<>(2); + paramMap.put("a", 2); + paramMap.put("barThrowException", "true"); + + LockAndCallback lockAndCallback = new LockAndCallback(); + StateMachineInstance inst = stateMachineEngine.startAsync(stateMachineName, null, paramMap, lockAndCallback.getCallback()); + lockAndCallback.waitingForFinish(inst); + + Assertions.assertEquals(ExecutionStatus.UN, inst.getStatus()); + }); + } + + @Test + public void testCompensationAndSubStateMachineWithLayout() throws Exception { + String stateMachineName = "simpleStateMachineWithCompensationAndSubMachine_layout"; + + SagaCostPrint.executeAndPrint("4-6", () -> { + Map paramMap = new HashMap<>(1); + paramMap.put("a", 2); + paramMap.put("barThrowException", "true"); + + LockAndCallback lockAndCallback = new LockAndCallback(); + StateMachineInstance inst = stateMachineEngine.startAsync(stateMachineName, null, paramMap, lockAndCallback.getCallback()); + lockAndCallback.waitingForFinish(inst); + + Assertions.assertEquals(ExecutionStatus.UN, inst.getStatus()); + }); + } + + @Test + public void testStateMachineWithComplexParams() throws Exception { + String stateMachineName = "simpleStateMachineWithComplexParamsJackson"; + + SagaCostPrint.executeAndPrint("4-7", () -> { + People people = new People(); + people.setName("lilei"); + people.setAge(18); + + Map paramMap = new HashMap<>(1); + paramMap.put("people", people); + + LockAndCallback lockAndCallback = new LockAndCallback(); + StateMachineInstance inst = stateMachineEngine.startAsync(stateMachineName, null, paramMap, lockAndCallback.getCallback()); + lockAndCallback.waitingForFinish(inst); + + People peopleResult = (People)inst.getEndParams().get("complexParameterMethodResult"); + Assertions.assertNotNull(peopleResult); + Assertions.assertEquals(people.getName(), peopleResult.getName()); + + Assertions.assertEquals(ExecutionStatus.SU, inst.getStatus()); + }); + } + + @Test + public void testSimpleStateMachineWithAsyncState() throws Exception { + String stateMachineName = "simpleStateMachineWithAsyncState"; + + SagaCostPrint.executeAndPrint("4-8", () -> { + Map paramMap = new HashMap<>(1); + paramMap.put("a", 1); + + LockAndCallback lockAndCallback = new LockAndCallback(); + StateMachineInstance inst = stateMachineEngine.startAsync(stateMachineName, null, paramMap, lockAndCallback.getCallback()); + lockAndCallback.waitingForFinish(inst); + + Assertions.assertEquals(ExecutionStatus.SU, inst.getStatus()); + }); + + try { + Thread.sleep(500); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } +} diff --git a/compatible/src/test/java/io/seata/saga/engine/db/mockserver/StateMachineDBMockServerTests.java b/compatible/src/test/java/io/seata/saga/engine/db/mockserver/StateMachineDBMockServerTests.java new file mode 100644 index 00000000000..af0e99b0d5e --- /dev/null +++ b/compatible/src/test/java/io/seata/saga/engine/db/mockserver/StateMachineDBMockServerTests.java @@ -0,0 +1,510 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.seata.saga.engine.db.mockserver; + +import io.seata.saga.SagaCostPrint; +import io.seata.saga.engine.StateMachineEngine; +import io.seata.saga.rm.StateMachineEngineHolder; +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.engine.mock.DemoService.Engineer; +import io.seata.saga.engine.mock.DemoService.People; +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 + * + */ +public class StateMachineDBMockServerTests { + + private static StateMachineEngine stateMachineEngine; + + @BeforeAll + public static void initApplicationContext() { + ApplicationContext applicationContext = new ClassPathXmlApplicationContext( + "classpath:saga/spring/statemachine_engine_db_mockserver_test.xml"); + stateMachineEngine = applicationContext.getBean("stateMachineEngine", StateMachineEngine.class); + StateMachineEngineHolder.setStateMachineEngine(stateMachineEngine); + } + + @Test + public void testSimpleStateMachine() throws Exception { + SagaCostPrint.executeAndPrint("5-1", () -> { + stateMachineEngine.start("simpleTestStateMachine", null, new HashMap<>()); + }); + } + + @Test + public void testSimpleStateMachineWithChoice() throws Exception { + String stateMachineName = "simpleChoiceTestStateMachine"; + + SagaCostPrint.executeAndPrint("5-2", () -> { + Map paramMap = new HashMap<>(1); + paramMap.put("a", 1); + + String businessKey = String.valueOf(System.currentTimeMillis()); + StateMachineInstance inst = stateMachineEngine.startWithBusinessKey(stateMachineName, null, businessKey, paramMap); + + Assertions.assertNotNull(inst); + Assertions.assertEquals(ExecutionStatus.SU, inst.getStatus()); + + //TODO + inst = stateMachineEngine.getStateMachineConfig().getStateLogStore().getStateMachineInstanceByBusinessKey(businessKey, null); + Assertions.assertNotNull(inst); + Assertions.assertEquals(ExecutionStatus.SU, inst.getStatus()); + }); + + SagaCostPrint.executeAndPrint("5-3", () -> { + Map paramMap = new HashMap<>(1); + paramMap.put("a", 2); + + StateMachineInstance inst = stateMachineEngine.start(stateMachineName, null, paramMap); + + Assertions.assertNotNull(inst); + Assertions.assertEquals(ExecutionStatus.SU, inst.getStatus()); + }); + } + + @Test + public void testSimpleStateMachineWithChoiceAndEnd() throws Exception { + String stateMachineName = "simpleChoiceAndEndTestStateMachine"; + + SagaCostPrint.executeAndPrint("5-4", () -> { + Map paramMap = new HashMap<>(1); + paramMap.put("a", 1); + + StateMachineInstance inst = stateMachineEngine.start(stateMachineName, null, paramMap); + }); + + SagaCostPrint.executeAndPrint("5-5", () -> { + Map paramMap = new HashMap<>(1); + + paramMap.put("a", 3); + stateMachineEngine.start(stateMachineName, null, paramMap); + }); + } + + @Test + public void testSimpleInputAssignmentStateMachine() throws Exception { + String stateMachineName = "simpleInputAssignmentStateMachine"; + + SagaCostPrint.executeAndPrint("5-6", () -> { + Map paramMap = new HashMap<>(1); + paramMap.put("a", 1); + + StateMachineInstance inst = stateMachineEngine.start(stateMachineName, null, paramMap); + + String businessKey = inst.getStateList().get(0).getBusinessKey(); + Assertions.assertNotNull(businessKey); + System.out.println("====== businessKey :" + businessKey); + + String contextBusinessKey = (String)inst.getEndParams().get( + inst.getStateList().get(0).getName() + DomainConstants.VAR_NAME_BUSINESSKEY); + Assertions.assertNotNull(contextBusinessKey); + System.out.println("====== context businessKey :" + businessKey); + }); + } + + @Test + public void testSimpleCatchesStateMachine() throws Exception { + String stateMachineName = "simpleCachesStateMachine"; + + SagaCostPrint.executeAndPrint("5-7", () -> { + Map paramMap = new HashMap<>(2); + paramMap.put("a", 1); + paramMap.put("barThrowException", "true"); + + StateMachineInstance inst = stateMachineEngine.start(stateMachineName, null, paramMap); + + Assertions.assertNotNull(inst.getException()); + Assertions.assertEquals(ExecutionStatus.FA, inst.getStatus()); + }); + } + + @Test + public void testSimpleScriptTaskStateMachineWithLayout() throws Exception { + String stateMachineName = "designerSimpleScriptTaskStateMachine"; + + SagaCostPrint.executeAndPrint("5-8", () -> { + Map paramMap = new HashMap<>(1); + paramMap.put("a", 1); + + StateMachineInstance inst = stateMachineEngine.start(stateMachineName, null, paramMap); + + Assertions.assertEquals(ExecutionStatus.SU, inst.getStatus()); + Assertions.assertNotNull(inst.getEndParams().get("scriptStateResult")); + }); + + SagaCostPrint.executeAndPrint("5-9", () -> { + Map paramMap = new HashMap<>(1); + paramMap.put("a", 1); + + StateMachineInstance inst = stateMachineEngine.start(stateMachineName, null, paramMap); + + Assertions.assertEquals(ExecutionStatus.SU, inst.getStatus()); + }); + + SagaCostPrint.executeAndPrint("5-10", () -> { + Map paramMap = new HashMap<>(1); + paramMap.put("scriptThrowException", true); + + StateMachineInstance inst = stateMachineEngine.start(stateMachineName, null, paramMap); + + Assertions.assertEquals(ExecutionStatus.FA, inst.getStatus()); + }); + } + + @Test + public void testSimpleRetryStateMachine() throws Exception { + String stateMachineName = "simpleRetryStateMachine"; + + SagaCostPrint.executeAndPrint("5-11", () -> { + Map paramMap = new HashMap<>(2); + paramMap.put("a", 1); + paramMap.put("barThrowException", "true"); + + StateMachineInstance inst = stateMachineEngine.start(stateMachineName, null, paramMap); + + Assertions.assertNotNull(inst.getException()); + Assertions.assertEquals(ExecutionStatus.FA, inst.getStatus()); + }); + } + + @Test + public void testStatusMatchingStateMachine() throws Exception { + String stateMachineName = "simpleStatusMatchingStateMachine"; + + SagaCostPrint.executeAndPrint("5-12", () -> { + Map paramMap = new HashMap<>(2); + paramMap.put("a", 1); + paramMap.put("barThrowException", "true"); + + StateMachineInstance inst = stateMachineEngine.start(stateMachineName, null, paramMap); + + Assertions.assertNotNull(inst.getException()); + Assertions.assertEquals(ExecutionStatus.UN, inst.getStatus()); + }); + } + + @Test + public void testCompensationStateMachine() throws Exception { + String stateMachineName = "simpleCompensationStateMachine"; + + SagaCostPrint.executeAndPrint("5-13", () -> { + Map paramMap = new HashMap<>(2); + paramMap.put("a", 1); + paramMap.put("barThrowException", "true"); + + StateMachineInstance inst = stateMachineEngine.start(stateMachineName, null, paramMap); + + Assertions.assertEquals(ExecutionStatus.UN, inst.getStatus()); + Assertions.assertEquals(ExecutionStatus.SU, inst.getCompensationStatus()); + }); + } + + @Test + public void testSubStateMachine() throws Exception { + String stateMachineName = "simpleStateMachineWithCompensationAndSubMachine"; + + StateMachineInstance inst0 = SagaCostPrint.executeAndPrint("5-14", () -> { + Map paramMap = new HashMap<>(2); + paramMap.put("a", 2); + paramMap.put("barThrowException", "true"); + + StateMachineInstance inst = stateMachineEngine.start(stateMachineName, null, paramMap); + + Assertions.assertEquals(ExecutionStatus.UN, inst.getStatus()); + + return inst; + }); + + SagaCostPrint.executeAndPrint("5-15", () -> { + Map paramMap = new HashMap<>(2); + paramMap.put("a", 2); + paramMap.put("barThrowException", "false"); + + StateMachineInstance inst = stateMachineEngine.forward(inst0.getId(), paramMap); + + Assertions.assertEquals(ExecutionStatus.SU, inst.getStatus()); + }); + } + + @Test + public void testSubStateMachineWithLayout() throws Exception { + String stateMachineName = "simpleStateMachineWithCompensationAndSubMachine_layout"; + + StateMachineInstance inst0 = SagaCostPrint.executeAndPrint("5-16", () -> { + Map paramMap = new HashMap<>(2); + paramMap.put("a", 2); + paramMap.put("barThrowException", "true"); + + StateMachineInstance inst = stateMachineEngine.start(stateMachineName, null, paramMap); + + Assertions.assertEquals(ExecutionStatus.UN, inst.getStatus()); + + return inst; + }); + + SagaCostPrint.executeAndPrint("5-17", () -> { + Map paramMap = new HashMap<>(2); + paramMap.put("a", 2); + paramMap.put("barThrowException", "false"); + + StateMachineInstance inst = stateMachineEngine.forward(inst0.getId(), paramMap); + + Assertions.assertEquals(ExecutionStatus.SU, inst.getStatus()); + }); + } + + @Test + public void testForwardSubStateMachine() throws Exception { + String stateMachineName = "simpleStateMachineWithCompensationAndSubMachine"; + + StateMachineInstance inst0 = SagaCostPrint.executeAndPrint("5-18", () -> { + Map paramMap = new HashMap<>(2); + paramMap.put("a", 2); + paramMap.put("fooThrowException", "true"); + + StateMachineInstance inst = stateMachineEngine.start(stateMachineName, null, paramMap); + + Assertions.assertEquals(ExecutionStatus.UN, inst.getStatus()); + + return inst; + }); + + SagaCostPrint.executeAndPrint("5-19", () -> { + Map paramMap = new HashMap<>(2); + paramMap.put("a", 2); + paramMap.put("fooThrowException", "false"); + + StateMachineInstance inst = stateMachineEngine.forward(inst0.getId(), paramMap); + + Assertions.assertEquals(ExecutionStatus.SU, inst.getStatus()); + }); + } + + @Test + public void testUserDefCompensateSubStateMachine() throws Exception { + String stateMachineName = "simpleStateMachineWithUseDefCompensationSubMachine"; + + StateMachineInstance inst0 = SagaCostPrint.executeAndPrint("5-26", () -> { + Map paramMap = new HashMap<>(3); + paramMap.put("a", 2); + paramMap.put("barThrowException", "true"); + paramMap.put("compensateFooThrowException", "true"); + + StateMachineInstance inst = stateMachineEngine.start(stateMachineName, null, paramMap); + + Assertions.assertEquals(ExecutionStatus.UN, inst.getStatus()); + + return inst; + }); + + SagaCostPrint.executeAndPrint("5-27", () -> { + Map paramMap = new HashMap<>(3); + paramMap.put("a", 2); + paramMap.put("barThrowException", "true"); + paramMap.put("compensateFooThrowException", "false"); + + StateMachineInstance inst = stateMachineEngine.compensate(inst0.getId(), paramMap); + + Assertions.assertEquals(ExecutionStatus.SU, inst.getCompensationStatus()); + }); + } + + @Test + public void testCommitRetryingThenRetryCommitted() throws Exception { + String stateMachineName = "simpleCompensationStateMachineForRecovery"; + + StateMachineInstance inst0 = SagaCostPrint.executeAndPrint("5-28", () -> { + Map paramMap = new HashMap<>(2); + paramMap.put("a", 1); + paramMap.put("fooThrowException", "true"); + + StateMachineInstance inst = stateMachineEngine.start(stateMachineName, null, paramMap); + + Assertions.assertEquals(ExecutionStatus.UN, inst.getStatus()); + + return inst; + }); + + SagaCostPrint.executeAndPrint("5-29", () -> { + Map paramMap = new HashMap<>(2); + paramMap.put("a", 1); + paramMap.put("fooThrowException", "false"); + + StateMachineInstance inst = stateMachineEngine.forward(inst0.getId(), paramMap); + + Assertions.assertEquals(ExecutionStatus.SU, inst.getStatus()); + }); + } + + @Test + public void testCommitRetryingThenRetryRollbacked() throws Exception { + String stateMachineName = "simpleCompensationStateMachineForRecovery"; + + StateMachineInstance inst0 = SagaCostPrint.executeAndPrint("5-30", () -> { + Map paramMap = new HashMap<>(2); + paramMap.put("a", 1); + paramMap.put("fooThrowException", "true"); + + StateMachineInstance inst = stateMachineEngine.start(stateMachineName, null, paramMap); + + Assertions.assertEquals(ExecutionStatus.UN, inst.getStatus()); + + return inst; + }); + + SagaCostPrint.executeAndPrint("5-31", () -> { + Map paramMap = new HashMap<>(3); + paramMap.put("a", 1); + paramMap.put("fooThrowException", "false"); + paramMap.put("barThrowException", "true"); + + StateMachineInstance inst = stateMachineEngine.forward(inst0.getId(), paramMap); + + Assertions.assertEquals(ExecutionStatus.SU, inst.getCompensationStatus()); + }); + } + + @Test + public void testRollbackRetryingThenRetryRollbacked() throws Exception { + String stateMachineName = "simpleCompensationStateMachineForRecovery"; + + StateMachineInstance inst0 = SagaCostPrint.executeAndPrint("5-32", () -> { + Map paramMap = new HashMap<>(3); + paramMap.put("a", 1); + paramMap.put("barThrowException", "true"); + paramMap.put("compensateFooThrowException", "true"); + + StateMachineInstance inst = stateMachineEngine.start(stateMachineName, null, paramMap); + + Assertions.assertEquals(ExecutionStatus.UN, inst.getStatus()); + Assertions.assertEquals(ExecutionStatus.UN, inst.getCompensationStatus()); + + return inst; + }); + + SagaCostPrint.executeAndPrint("5-33", () -> { + Map paramMap = new HashMap<>(3); + paramMap.put("a", 1); + paramMap.put("barThrowException", "false"); + paramMap.put("compensateFooThrowException", "false"); + + StateMachineInstance inst = stateMachineEngine.compensate(inst0.getId(), paramMap); + + Assertions.assertEquals(ExecutionStatus.SU, inst.getCompensationStatus()); + }); + } + + @Test + public void testRollbackRetryingTwiceThenRetryRollbacked() throws Exception { + String stateMachineName = "simpleCompensationStateMachineForRecovery"; + + Map paramMap = new HashMap<>(3); + paramMap.put("a", 1); + paramMap.put("barThrowException", "true"); + paramMap.put("compensateFooThrowException", "true"); + + StateMachineInstance inst0 = SagaCostPrint.executeAndPrint("5-34", () -> { + StateMachineInstance inst = stateMachineEngine.start(stateMachineName, null, paramMap); + + Assertions.assertEquals(ExecutionStatus.UN, inst.getStatus()); + Assertions.assertEquals(ExecutionStatus.UN, inst.getCompensationStatus()); + + return inst; + }); + + SagaCostPrint.executeAndPrint("5-35", () -> { + StateMachineInstance inst = stateMachineEngine.compensate(inst0.getId(), paramMap); + + Assertions.assertEquals(ExecutionStatus.UN, inst.getStatus()); + Assertions.assertEquals(ExecutionStatus.UN, inst.getCompensationStatus()); + }); + + paramMap.put("barThrowException", "false"); + paramMap.put("compensateFooThrowException", "false"); + SagaCostPrint.executeAndPrint("5-36", () -> { + StateMachineInstance inst = stateMachineEngine.compensate(inst0.getId(), paramMap); + + Assertions.assertEquals(ExecutionStatus.SU, inst.getCompensationStatus()); + }); + } + + @Test + public void testStateMachineWithComplexParams() throws Exception { + String stateMachineName = "simpleStateMachineWithComplexParamsJackson"; + + SagaCostPrint.executeAndPrint("5-37", () -> { + People people = new People(); + people.setName("lilei"); + people.setAge(18); + + Engineer engineer = new Engineer(); + engineer.setName("programmer"); + + Map paramMap = new HashMap<>(2); + paramMap.put("people", people); + paramMap.put("career", engineer); + + StateMachineInstance instance = stateMachineEngine.start(stateMachineName, null, paramMap); + + People peopleResult = (People)instance.getEndParams().get("complexParameterMethodResult"); + Assertions.assertNotNull(peopleResult); + Assertions.assertEquals(people.getName(), peopleResult.getName()); + + Assertions.assertEquals(ExecutionStatus.SU, instance.getStatus()); + }); + } + + @Test + public void testSimpleStateMachineWithAsyncState() throws Exception { + String stateMachineName = "simpleStateMachineWithAsyncState"; + + SagaCostPrint.executeAndPrint("5-38", () -> { + Map paramMap = new HashMap<>(1); + paramMap.put("a", 1); + + StateMachineInstance inst = stateMachineEngine.start(stateMachineName, null, paramMap); + + Assertions.assertEquals(ExecutionStatus.SU, inst.getStatus()); + }); + + try { + Thread.sleep(500); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + + @Test + public void testReloadStateMachineInstance() throws Exception { + SagaCostPrint.executeAndPrint("5-39", () -> { + StateMachineInstance instance = stateMachineEngine.getStateMachineConfig().getStateLogStore().getStateMachineInstance( + "10.15.232.93:8091:2019567124"); + System.out.println(instance); + }); + } +} diff --git a/compatible/src/main/java/io/seata/rm/AbstractRMHandler.java b/compatible/src/test/java/io/seata/saga/engine/mock/DemoException.java similarity index 58% rename from compatible/src/main/java/io/seata/rm/AbstractRMHandler.java rename to compatible/src/test/java/io/seata/saga/engine/mock/DemoException.java index 9945ef6d933..ea8866df109 100644 --- a/compatible/src/main/java/io/seata/rm/AbstractRMHandler.java +++ b/compatible/src/test/java/io/seata/saga/engine/mock/DemoException.java @@ -14,11 +14,27 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.seata.rm; +package io.seata.saga.engine.mock; -/** - * The Abstract RM event handler - * - */ -public abstract class AbstractRMHandler extends org.apache.seata.rm.AbstractRMHandler { + +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); + } } diff --git a/compatible/src/test/java/io/seata/saga/engine/mock/DemoService.java b/compatible/src/test/java/io/seata/saga/engine/mock/DemoService.java new file mode 100644 index 00000000000..481d1935042 --- /dev/null +++ b/compatible/src/test/java/io/seata/saga/engine/mock/DemoService.java @@ -0,0 +1,187 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.seata.saga.engine.mock; + +import java.net.ConnectException; +import java.util.List; +import java.util.Map; + +/** + */ +public class DemoService { + + public Map foo(Map 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 compensateFoo(Map 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 bar(Map 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 compensateBar(Map 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 peopleList, Map peopleMap){ + return people; + } + + public Career interfaceParameterMethod(Career career){ + return career; + } + + public Map randomExceptionMethod(Map 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 childrenList; + private Map 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 getChildrenList() { + return childrenList; + } + + public void setChildrenList(List childrenList) { + this.childrenList = childrenList; + } + + public Map getChildrenMap() { + return childrenMap; + } + + public void setChildrenMap(Map 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; + } + } +} diff --git a/compatible/src/test/java/io/seata/saga/engine/mock/MockGlobalTransaction.java b/compatible/src/test/java/io/seata/saga/engine/mock/MockGlobalTransaction.java new file mode 100644 index 00000000000..852cf599918 --- /dev/null +++ b/compatible/src/test/java/io/seata/saga/engine/mock/MockGlobalTransaction.java @@ -0,0 +1,121 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.seata.saga.engine.mock; + +import org.apache.seata.core.context.RootContext; +import org.apache.seata.core.exception.TransactionException; +import org.apache.seata.core.model.GlobalStatus; +import org.apache.seata.saga.engine.sequence.UUIDSeqGenerator; +import org.apache.seata.tm.api.GlobalTransaction; +import org.apache.seata.tm.api.GlobalTransactionRole; +import org.apache.seata.tm.api.transaction.SuspendedResourcesHolder; + + +public class MockGlobalTransaction implements GlobalTransaction { + + private String xid; + private GlobalStatus status; + private long createTime; + + private static UUIDSeqGenerator uuidSeqGenerator = new UUIDSeqGenerator(); + + 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 { + this.createTime = System.currentTimeMillis(); + status = GlobalStatus.Begin; + xid = uuidSeqGenerator.generate(null); + 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 SuspendedResourcesHolder suspend(boolean clean) + 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; + } + + @Override + public long getCreateTime() { + return createTime; + } +} diff --git a/compatible/src/test/java/io/seata/saga/engine/mock/MockSagaTransactionTemplate.java b/compatible/src/test/java/io/seata/saga/engine/mock/MockSagaTransactionTemplate.java new file mode 100644 index 00000000000..3768a9b1383 --- /dev/null +++ b/compatible/src/test/java/io/seata/saga/engine/mock/MockSagaTransactionTemplate.java @@ -0,0 +1,82 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.seata.saga.engine.mock; + +import org.apache.seata.common.util.IdWorker; +import org.apache.seata.core.exception.TransactionException; +import org.apache.seata.core.model.BranchStatus; +import org.apache.seata.core.model.GlobalStatus; +import org.apache.seata.saga.engine.tm.SagaTransactionalTemplate; +import org.apache.seata.tm.api.GlobalTransaction; +import org.apache.seata.tm.api.TransactionalExecutor.ExecutionException; +import org.apache.seata.tm.api.transaction.TransactionInfo; + + +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 new IdWorker(null).nextId(); + } + + @Override + public void branchReport(String xid, long branchId, BranchStatus status, String applicationData) throws TransactionException { + + } + + @Override + public void triggerAfterCompletion(GlobalTransaction tx) { + + } + + @Override + public void cleanUp(GlobalTransaction tx) { + + } +} diff --git a/compatible/src/test/java/io/seata/saga/engine/mock/MockStateHandlerInterceptor.java b/compatible/src/test/java/io/seata/saga/engine/mock/MockStateHandlerInterceptor.java new file mode 100644 index 00000000000..df4085b0123 --- /dev/null +++ b/compatible/src/test/java/io/seata/saga/engine/mock/MockStateHandlerInterceptor.java @@ -0,0 +1,45 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.seata.saga.engine.mock; + +import org.apache.seata.saga.engine.exception.EngineExecutionException; +import org.apache.seata.saga.engine.pcext.InterceptableStateHandler; +import org.apache.seata.saga.engine.pcext.StateHandlerInterceptor; +import org.apache.seata.saga.proctrl.ProcessContext; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + + +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 clazz) { + return true; + } +} diff --git a/compatible/src/test/java/io/seata/saga/engine/mock/MockStateRouterInterceptor.java b/compatible/src/test/java/io/seata/saga/engine/mock/MockStateRouterInterceptor.java new file mode 100644 index 00000000000..4c86f553c0b --- /dev/null +++ b/compatible/src/test/java/io/seata/saga/engine/mock/MockStateRouterInterceptor.java @@ -0,0 +1,47 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.seata.saga.engine.mock; + +import org.apache.seata.saga.engine.exception.EngineExecutionException; +import org.apache.seata.saga.engine.pcext.InterceptableStateRouter; +import org.apache.seata.saga.engine.pcext.StateRouterInterceptor; +import org.apache.seata.saga.proctrl.Instruction; +import org.apache.seata.saga.proctrl.ProcessContext; +import org.apache.seata.saga.statelang.domain.State; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + + +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 clazz) { + return true; + } +} diff --git a/compatible/src/test/resources/saga/spring/statemachine_engine_db_mockserver_test.xml b/compatible/src/test/resources/saga/spring/statemachine_engine_db_mockserver_test.xml new file mode 100644 index 00000000000..a2b934c8fb0 --- /dev/null +++ b/compatible/src/test/resources/saga/spring/statemachine_engine_db_mockserver_test.xml @@ -0,0 +1,70 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/compatible/src/test/resources/saga/spring/statemachine_engine_db_test.xml b/compatible/src/test/resources/saga/spring/statemachine_engine_db_test.xml new file mode 100644 index 00000000000..7371a92fdb8 --- /dev/null +++ b/compatible/src/test/resources/saga/spring/statemachine_engine_db_test.xml @@ -0,0 +1,72 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/compatible/src/test/resources/saga/spring/statemachine_engine_test.xml b/compatible/src/test/resources/saga/spring/statemachine_engine_test.xml new file mode 100644 index 00000000000..b6f62d68737 --- /dev/null +++ b/compatible/src/test/resources/saga/spring/statemachine_engine_test.xml @@ -0,0 +1,47 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/compatible/src/test/resources/saga/sql/db2_init.sql b/compatible/src/test/resources/saga/sql/db2_init.sql new file mode 100644 index 00000000000..1b2d913ebad --- /dev/null +++ b/compatible/src/test/resources/saga/sql/db2_init.sql @@ -0,0 +1,81 @@ +-- +-- Licensed to the Apache Software Foundation (ASF) under one or more +-- contributor license agreements. See the NOTICE file distributed with +-- this work for additional information regarding copyright ownership. +-- The ASF licenses this file to You under the Apache License, Version 2.0 +-- (the "License"); you may not use this file except in compliance with +-- the License. You may obtain a copy of the License at +-- +-- http://www.apache.org/licenses/LICENSE-2.0 +-- +-- Unless required by applicable law or agreed to in writing, software +-- distributed under the License is distributed on an "AS IS" BASIS, +-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +-- See the License for the specific language governing permissions and +-- limitations under the License. +-- + +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) +); \ No newline at end of file diff --git a/compatible/src/test/resources/saga/sql/h2_init.sql b/compatible/src/test/resources/saga/sql/h2_init.sql new file mode 100644 index 00000000000..742f62d464f --- /dev/null +++ b/compatible/src/test/resources/saga/sql/h2_init.sql @@ -0,0 +1,75 @@ +-- +-- Licensed to the Apache Software Foundation (ASF) under one or more +-- contributor license agreements. See the NOTICE file distributed with +-- this work for additional information regarding copyright ownership. +-- The ASF licenses this file to You under the Apache License, Version 2.0 +-- (the "License"); you may not use this file except in compliance with +-- the License. You may obtain a copy of the License at +-- +-- http://www.apache.org/licenses/LICENSE-2.0 +-- +-- Unless required by applicable law or agreed to in writing, software +-- distributed under the License is distributed on an "AS IS" BASIS, +-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +-- See the License for the specific language governing permissions and +-- limitations under the License. +-- + +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) +); \ No newline at end of file diff --git a/compatible/src/test/resources/saga/sql/mysql_init.sql b/compatible/src/test/resources/saga/sql/mysql_init.sql new file mode 100644 index 00000000000..9a6ac51e928 --- /dev/null +++ b/compatible/src/test/resources/saga/sql/mysql_init.sql @@ -0,0 +1,81 @@ +-- +-- Licensed to the Apache Software Foundation (ASF) under one or more +-- contributor license agreements. See the NOTICE file distributed with +-- this work for additional information regarding copyright ownership. +-- The ASF licenses this file to You under the Apache License, Version 2.0 +-- (the "License"); you may not use this file except in compliance with +-- the License. You may obtain a copy of the License at +-- +-- http://www.apache.org/licenses/LICENSE-2.0 +-- +-- Unless required by applicable law or agreed to in writing, software +-- distributed under the License is distributed on an "AS IS" BASIS, +-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +-- See the License for the specific language governing permissions and +-- limitations under the License. +-- + +-- -------------------------------- 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; \ No newline at end of file diff --git a/compatible/src/test/resources/saga/sql/oracle_init.sql b/compatible/src/test/resources/saga/sql/oracle_init.sql new file mode 100644 index 00000000000..db29e40c5f7 --- /dev/null +++ b/compatible/src/test/resources/saga/sql/oracle_init.sql @@ -0,0 +1,81 @@ +-- +-- Licensed to the Apache Software Foundation (ASF) under one or more +-- contributor license agreements. See the NOTICE file distributed with +-- this work for additional information regarding copyright ownership. +-- The ASF licenses this file to You under the Apache License, Version 2.0 +-- (the "License"); you may not use this file except in compliance with +-- the License. You may obtain a copy of the License at +-- +-- http://www.apache.org/licenses/LICENSE-2.0 +-- +-- Unless required by applicable law or agreed to in writing, software +-- distributed under the License is distributed on an "AS IS" BASIS, +-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +-- See the License for the specific language governing permissions and +-- limitations under the License. +-- + +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) +); diff --git a/compatible/src/test/resources/saga/statelang/simple_statelang.json b/compatible/src/test/resources/saga/statelang/simple_statelang.json new file mode 100644 index 00000000000..14e52f4c6cc --- /dev/null +++ b/compatible/src/test/resources/saga/statelang/simple_statelang.json @@ -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" + } + } +} \ No newline at end of file diff --git a/compatible/src/test/resources/saga/statelang/simple_statelang_param_assignment.json b/compatible/src/test/resources/saga/statelang/simple_statelang_param_assignment.json new file mode 100644 index 00000000000..be7cef2a1a2 --- /dev/null +++ b/compatible/src/test/resources/saga/statelang/simple_statelang_param_assignment.json @@ -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" + } + } +} \ No newline at end of file diff --git a/compatible/src/test/resources/saga/statelang/simple_statelang_with_async_state.json b/compatible/src/test/resources/saga/statelang/simple_statelang_with_async_state.json new file mode 100644 index 00000000000..f2bc3dd41aa --- /dev/null +++ b/compatible/src/test/resources/saga/statelang/simple_statelang_with_async_state.json @@ -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" + } + } +} \ No newline at end of file diff --git a/compatible/src/test/resources/saga/statelang/simple_statelang_with_catches.json b/compatible/src/test/resources/saga/statelang/simple_statelang_with_catches.json new file mode 100644 index 00000000000..d86f4fd3fe5 --- /dev/null +++ b/compatible/src/test/resources/saga/statelang/simple_statelang_with_catches.json @@ -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" + } + } +} diff --git a/compatible/src/test/resources/saga/statelang/simple_statelang_with_choice.json b/compatible/src/test/resources/saga/statelang/simple_statelang_with_choice.json new file mode 100644 index 00000000000..4be7a48f848 --- /dev/null +++ b/compatible/src/test/resources/saga/statelang/simple_statelang_with_choice.json @@ -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" + } + } +} \ No newline at end of file diff --git a/compatible/src/test/resources/saga/statelang/simple_statelang_with_choice_and_end.json b/compatible/src/test/resources/saga/statelang/simple_statelang_with_choice_and_end.json new file mode 100644 index 00000000000..7910b5332cb --- /dev/null +++ b/compatible/src/test/resources/saga/statelang/simple_statelang_with_choice_and_end.json @@ -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" + } + } +} \ No newline at end of file diff --git a/compatible/src/test/resources/saga/statelang/simple_statelang_with_choice_no_default.json b/compatible/src/test/resources/saga/statelang/simple_statelang_with_choice_no_default.json new file mode 100644 index 00000000000..808995bb3bc --- /dev/null +++ b/compatible/src/test/resources/saga/statelang/simple_statelang_with_choice_no_default.json @@ -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" + } + } +} \ No newline at end of file diff --git a/compatible/src/test/resources/saga/statelang/simple_statelang_with_compensation.json b/compatible/src/test/resources/saga/statelang/simple_statelang_with_compensation.json new file mode 100644 index 00000000000..a71e8a23a24 --- /dev/null +++ b/compatible/src/test/resources/saga/statelang/simple_statelang_with_compensation.json @@ -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" + } + } +} \ No newline at end of file diff --git a/compatible/src/test/resources/saga/statelang/simple_statelang_with_compensation_and_sub_machine.json b/compatible/src/test/resources/saga/statelang/simple_statelang_with_compensation_and_sub_machine.json new file mode 100644 index 00000000000..7dc4e60a935 --- /dev/null +++ b/compatible/src/test/resources/saga/statelang/simple_statelang_with_compensation_and_sub_machine.json @@ -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" + } + } +} \ No newline at end of file diff --git a/compatible/src/test/resources/saga/statelang/simple_statelang_with_compensation_for_recovery.json b/compatible/src/test/resources/saga/statelang/simple_statelang_with_compensation_for_recovery.json new file mode 100644 index 00000000000..a8a7f2b2576 --- /dev/null +++ b/compatible/src/test/resources/saga/statelang/simple_statelang_with_compensation_for_recovery.json @@ -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" + } + } +} \ No newline at end of file diff --git a/compatible/src/test/resources/saga/statelang/simple_statelang_with_complex_params.json b/compatible/src/test/resources/saga/statelang/simple_statelang_with_complex_params.json new file mode 100644 index 00000000000..5c6f1cb47e1 --- /dev/null +++ b/compatible/src/test/resources/saga/statelang/simple_statelang_with_complex_params.json @@ -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" + } + } +} \ No newline at end of file diff --git a/compatible/src/test/resources/saga/statelang/simple_statelang_with_complex_params_jackson.json b/compatible/src/test/resources/saga/statelang/simple_statelang_with_complex_params_jackson.json new file mode 100644 index 00000000000..1ebb0006634 --- /dev/null +++ b/compatible/src/test/resources/saga/statelang/simple_statelang_with_complex_params_jackson.json @@ -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" + } + } +} \ No newline at end of file diff --git a/compatible/src/test/resources/saga/statelang/simple_statelang_with_loop.json b/compatible/src/test/resources/saga/statelang/simple_statelang_with_loop.json new file mode 100644 index 00000000000..58ebaebaef3 --- /dev/null +++ b/compatible/src/test/resources/saga/statelang/simple_statelang_with_loop.json @@ -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" + } + } +} \ No newline at end of file diff --git a/compatible/src/test/resources/saga/statelang/simple_statelang_with_persist_update_mode.json b/compatible/src/test/resources/saga/statelang/simple_statelang_with_persist_update_mode.json new file mode 100644 index 00000000000..873a301ccee --- /dev/null +++ b/compatible/src/test/resources/saga/statelang/simple_statelang_with_persist_update_mode.json @@ -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" + } + } +} diff --git a/compatible/src/test/resources/saga/statelang/simple_statelang_with_recover_strategy.json b/compatible/src/test/resources/saga/statelang/simple_statelang_with_recover_strategy.json new file mode 100644 index 00000000000..1242d4595ea --- /dev/null +++ b/compatible/src/test/resources/saga/statelang/simple_statelang_with_recover_strategy.json @@ -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" + } + } +} \ No newline at end of file diff --git a/compatible/src/test/resources/saga/statelang/simple_statelang_with_retry.json b/compatible/src/test/resources/saga/statelang/simple_statelang_with_retry.json new file mode 100644 index 00000000000..bcbb1cbbd47 --- /dev/null +++ b/compatible/src/test/resources/saga/statelang/simple_statelang_with_retry.json @@ -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" + } + } +} diff --git a/compatible/src/test/resources/saga/statelang/simple_statelang_with_script.json b/compatible/src/test/resources/saga/statelang/simple_statelang_with_script.json new file mode 100644 index 00000000000..c4acf156cb5 --- /dev/null +++ b/compatible/src/test/resources/saga/statelang/simple_statelang_with_script.json @@ -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" + } + } +} diff --git a/compatible/src/test/resources/saga/statelang/simple_statelang_with_status_matches.json b/compatible/src/test/resources/saga/statelang/simple_statelang_with_status_matches.json new file mode 100644 index 00000000000..7d58592d082 --- /dev/null +++ b/compatible/src/test/resources/saga/statelang/simple_statelang_with_status_matches.json @@ -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" + } + } +} \ No newline at end of file diff --git a/compatible/src/test/resources/saga/statelang/simple_statelang_with_userdef_sub_machine_compensation.json b/compatible/src/test/resources/saga/statelang/simple_statelang_with_userdef_sub_machine_compensation.json new file mode 100644 index 00000000000..cefecb785b2 --- /dev/null +++ b/compatible/src/test/resources/saga/statelang/simple_statelang_with_userdef_sub_machine_compensation.json @@ -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" + } + } +} \ No newline at end of file diff --git a/compatible/src/test/resources/saga/statelang/simple_statemachine_with_layout.json b/compatible/src/test/resources/saga/statelang/simple_statemachine_with_layout.json new file mode 100644 index 00000000000..4d886a02c36 --- /dev/null +++ b/compatible/src/test/resources/saga/statelang/simple_statemachine_with_layout.json @@ -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" + } + ] +} \ No newline at end of file diff --git a/saga/seata-saga-rm/src/main/java/org/apache/seata/saga/rm/StateMachineEngineHolder.java b/saga/seata-saga-rm/src/main/java/org/apache/seata/saga/rm/StateMachineEngineHolder.java index 9f7a7f87943..17e44e962af 100644 --- a/saga/seata-saga-rm/src/main/java/org/apache/seata/saga/rm/StateMachineEngineHolder.java +++ b/saga/seata-saga-rm/src/main/java/org/apache/seata/saga/rm/StateMachineEngineHolder.java @@ -29,7 +29,7 @@ public static StateMachineEngine getStateMachineEngine() { return stateMachineEngine; } - public static void setStateMachineEngine(StateMachineEngine smEngine) { + public static void setStateMachineEngine(StateMachineEngine smEngine) { stateMachineEngine = smEngine; } } diff --git a/seata-spring-boot-starter/src/main/java/org/apache/seata/spring/boot/autoconfigure/SeataSagaAutoConfiguration.java b/seata-spring-boot-starter/src/main/java/org/apache/seata/spring/boot/autoconfigure/SeataSagaAutoConfiguration.java index 43c18629fc3..764c90ffb26 100644 --- a/seata-spring-boot-starter/src/main/java/org/apache/seata/spring/boot/autoconfigure/SeataSagaAutoConfiguration.java +++ b/seata-spring-boot-starter/src/main/java/org/apache/seata/spring/boot/autoconfigure/SeataSagaAutoConfiguration.java @@ -34,6 +34,7 @@ import org.springframework.boot.autoconfigure.AutoConfigureAfter; import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration; import org.springframework.boot.context.properties.ConfigurationProperties; diff --git a/spring/src/main/java/org/apache/seata/spring/tcc/TccAnnotationProcessor.java b/spring/src/main/java/org/apache/seata/spring/tcc/TccAnnotationProcessor.java index 06afa1d7b15..9af85a3b275 100644 --- a/spring/src/main/java/org/apache/seata/spring/tcc/TccAnnotationProcessor.java +++ b/spring/src/main/java/org/apache/seata/spring/tcc/TccAnnotationProcessor.java @@ -39,6 +39,7 @@ * An annotation adapter for TCC * */ +@Deprecated public class TccAnnotationProcessor implements BeanPostProcessor { private static final Logger LOGGER = LoggerFactory.getLogger(TccAnnotationProcessor.class); From 6d7c77d9adb807ea9f9bf94ebde7cba2e6923170 Mon Sep 17 00:00:00 2001 From: "yixia.wt" Date: Sat, 16 Mar 2024 16:46:46 +0800 Subject: [PATCH 02/21] =?UTF-8?q?1=E3=80=81spi=E5=85=BC=E5=AE=B9=202?= =?UTF-8?q?=E3=80=81saga=E5=85=BC=E5=AE=B9=203=E3=80=81spring=20scannner?= =?UTF-8?q?=E3=80=81enableDatasourceProxy=E5=85=BC=E5=AE=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../common/loader/EnhancedServiceLoader.java | 169 +++++------------- .../seata/core/model/TransactionManager.java | 28 --- .../annotation/GlobalTransactionScanner.java | 46 +++++ .../AutoDataSourceProxyRegistrar.java | 54 ++++++ .../datasource/EnableAutoDataSourceProxy.java | 55 ++++++ .../io.seata.core.model.ResourceManager | 1 + ...rg.apache.seata.core.model.ResourceManager | 1 - .../StateMachineDBMockServerTests.java | 38 +--- .../java/io/seata/spi/SPITest.java} | 20 ++- compatible/src/test/resources/file.conf | 10 +- 10 files changed, 226 insertions(+), 196 deletions(-) delete mode 100644 compatible/src/main/java/io/seata/core/model/TransactionManager.java create mode 100644 compatible/src/main/java/io/seata/spring/annotation/GlobalTransactionScanner.java create mode 100644 compatible/src/main/java/io/seata/spring/annotation/datasource/AutoDataSourceProxyRegistrar.java create mode 100644 compatible/src/main/java/io/seata/spring/annotation/datasource/EnableAutoDataSourceProxy.java create mode 100644 compatible/src/main/resources/META-INF/services/io.seata.core.model.ResourceManager delete mode 100644 compatible/src/main/resources/META-INF/services/org.apache.seata.core.model.ResourceManager rename compatible/src/{main/java/io/seata/core/model/ResourceManager.java => test/java/io/seata/spi/SPITest.java} (54%) diff --git a/common/src/main/java/org/apache/seata/common/loader/EnhancedServiceLoader.java b/common/src/main/java/org/apache/seata/common/loader/EnhancedServiceLoader.java index 9a18e45b44d..b4d005dbe00 100644 --- a/common/src/main/java/org/apache/seata/common/loader/EnhancedServiceLoader.java +++ b/common/src/main/java/org/apache/seata/common/loader/EnhancedServiceLoader.java @@ -40,12 +40,9 @@ /** * The type Enhanced service loader. - * */ public class EnhancedServiceLoader { private static final Logger LOGGER = LoggerFactory.getLogger(EnhancedServiceLoader.class); - private static final String APACHE_SEATA_PACKAGE_NAME = "org.apache.seata"; - private static final String IO_SEATA_PACKAGE_NAME = "io.seata"; /** * Class->InnerEnhancedServiceLoader map @@ -53,16 +50,6 @@ public class EnhancedServiceLoader { private static final ConcurrentMap, InnerEnhancedServiceLoader> SERVICE_LOADERS = new ConcurrentHashMap<>(); - private static Class getCompatibleService(Class originService) { - String apacheSeataName = originService.getName(); - String ioSeataName = apacheSeataName.replace(APACHE_SEATA_PACKAGE_NAME, IO_SEATA_PACKAGE_NAME); - try { - Class clasz = Class.forName(ioSeataName); - return clasz; - } catch (ClassNotFoundException e) { - return null; - } - } /** * Specify classLoader to load the service provider * @@ -73,15 +60,6 @@ private static Class getCompatibleService(Class originService) { * @throws EnhancedServiceNotFoundException the enhanced service not found exception */ public static S load(Class service, ClassLoader loader) throws EnhancedServiceNotFoundException { - Class compatibleService = getCompatibleService(service); - if (compatibleService != null) { - try { - return InnerEnhancedServiceLoader.getServiceLoader(service).load(loader); - } catch (EnhancedServiceNotFoundException ignore) { - LOGGER.warn("Can't load SPI for :{} from org.apache.seata package,will try to load SPI from io.seata package", service.getName()); - } - return InnerEnhancedServiceLoader.getServiceLoader(compatibleService).load(loader); - } return InnerEnhancedServiceLoader.getServiceLoader(service).load(loader); } @@ -94,15 +72,6 @@ public static S load(Class service, ClassLoader loader) throws EnhancedSe * @throws EnhancedServiceNotFoundException the enhanced service not found exception */ public static S load(Class service) throws EnhancedServiceNotFoundException { - Class compatibleService = getCompatibleService(service); - if (compatibleService != null) { - try { - return InnerEnhancedServiceLoader.getServiceLoader(service).load(findClassLoader()); - } catch (EnhancedServiceNotFoundException ignore) { - LOGGER.warn("Can't load SPI for :{} from org.apache.seata package,will try to load SPI from io.seata package", service.getName()); - } - return InnerEnhancedServiceLoader.getServiceLoader(compatibleService).load(findClassLoader()); - } return InnerEnhancedServiceLoader.getServiceLoader(service).load(findClassLoader()); } @@ -116,15 +85,6 @@ public static S load(Class service) throws EnhancedServiceNotFoundExcepti * @throws EnhancedServiceNotFoundException the enhanced service not found exception */ public static S load(Class service, String activateName) throws EnhancedServiceNotFoundException { - Class compatibleService = getCompatibleService(service); - if (compatibleService != null) { - try { - return InnerEnhancedServiceLoader.getServiceLoader(service).load(activateName, findClassLoader()); - } catch (EnhancedServiceNotFoundException ignore) { - LOGGER.warn("Can't load SPI for :{} from org.apache.seata package,will try to load SPI from io.seata package", service.getName()); - } - return InnerEnhancedServiceLoader.getServiceLoader(compatibleService).load(activateName, findClassLoader()); - } return InnerEnhancedServiceLoader.getServiceLoader(service).load(activateName, findClassLoader()); } @@ -140,15 +100,6 @@ public static S load(Class service, String activateName) throws EnhancedS */ public static S load(Class service, String activateName, ClassLoader loader) throws EnhancedServiceNotFoundException { - Class compatibleService = getCompatibleService(service); - if (compatibleService != null) { - try { - return InnerEnhancedServiceLoader.getServiceLoader(service).load(activateName, loader); - } catch (EnhancedServiceNotFoundException ignore) { - LOGGER.warn("Can't load SPI for :{} from org.apache.seata package,will try to load SPI from io.seata package", service.getName()); - } - return InnerEnhancedServiceLoader.getServiceLoader(compatibleService).load(activateName, loader); - } return InnerEnhancedServiceLoader.getServiceLoader(service).load(activateName, loader); } @@ -164,15 +115,6 @@ public static S load(Class service, String activateName, ClassLoader load */ public static S load(Class service, String activateName, Object[] args) throws EnhancedServiceNotFoundException { - Class compatibleService = getCompatibleService(service); - if (compatibleService != null) { - try { - return InnerEnhancedServiceLoader.getServiceLoader(service).load(activateName, args, findClassLoader()); - } catch (EnhancedServiceNotFoundException ignore) { - LOGGER.warn("Can't load SPI for :{} from org.apache.seata package,will try to load SPI from io.seata package", service.getName()); - } - return InnerEnhancedServiceLoader.getServiceLoader(compatibleService).load(activateName, args, findClassLoader()); - } return InnerEnhancedServiceLoader.getServiceLoader(service).load(activateName, args, findClassLoader()); } @@ -189,15 +131,6 @@ public static S load(Class service, String activateName, Object[] args) */ public static S load(Class service, String activateName, Class[] argsType, Object[] args) throws EnhancedServiceNotFoundException { - Class compatibleService = getCompatibleService(service); - if (compatibleService != null) { - try { - return InnerEnhancedServiceLoader.getServiceLoader(service).load(activateName, argsType, args, findClassLoader()); - } catch (EnhancedServiceNotFoundException ignore) { - LOGGER.warn("Can't load SPI for :{} from org.apache.seata package,will try to load SPI from io.seata package", service.getName()); - } - return InnerEnhancedServiceLoader.getServiceLoader(compatibleService).load(activateName, argsType, args, findClassLoader()); - } return InnerEnhancedServiceLoader.getServiceLoader(service).load(activateName, argsType, args, findClassLoader()); } @@ -209,15 +142,6 @@ public static S load(Class service, String activateName, Class[] argsT * @return list list */ public static List loadAll(Class service) { - Class compatibleService = getCompatibleService(service); - if (compatibleService != null) { - try { - return InnerEnhancedServiceLoader.getServiceLoader(service).loadAll(findClassLoader()); - } catch (EnhancedServiceNotFoundException ignore) { - LOGGER.warn("Can't load SPI for :{} from org.apache.seata package,will try to load SPI from io.seata package", service.getName()); - } - return InnerEnhancedServiceLoader.getServiceLoader(compatibleService).loadAll(findClassLoader()); - } return InnerEnhancedServiceLoader.getServiceLoader(service).loadAll(findClassLoader()); } @@ -231,15 +155,6 @@ public static List loadAll(Class service) { * @return list list */ public static List loadAll(Class service, Class[] argsType, Object[] args) { - Class compatibleService = getCompatibleService(service); - if (compatibleService != null) { - try { - return InnerEnhancedServiceLoader.getServiceLoader(service).loadAll(argsType, args, findClassLoader()); - } catch (EnhancedServiceNotFoundException ignore) { - LOGGER.warn("Can't load SPI for :{} from org.apache.seata package,will try to load SPI from io.seata package", service.getName()); - } - return InnerEnhancedServiceLoader.getServiceLoader(compatibleService).loadAll(argsType, args, findClassLoader()); - } return InnerEnhancedServiceLoader.getServiceLoader(service).loadAll(argsType, args, findClassLoader()); } @@ -257,17 +172,7 @@ public static void unloadAll() { * @param service the service */ public static void unload(Class service) { - Class compatibleService = getCompatibleService(service); - if (compatibleService != null) { - try { - InnerEnhancedServiceLoader.removeServiceLoader(service); - } catch (Exception ignore) { - LOGGER.warn("Can't unload SPI for :{} from org.apache.seata package,will try to unload SPI from io.seata package", service.getName()); - InnerEnhancedServiceLoader.removeServiceLoader(compatibleService); - } - } else { - InnerEnhancedServiceLoader.removeServiceLoader(service); - } + InnerEnhancedServiceLoader.removeServiceLoader(service); } /** @@ -281,20 +186,9 @@ public static void unload(Class service, String activateName) { if (activateName == null) { throw new IllegalArgumentException("activateName is null"); } - Class compatibleService = getCompatibleService(service); - if (compatibleService != null) { - try { - InnerEnhancedServiceLoader serviceLoader = InnerEnhancedServiceLoader.getServiceLoader(service); - doUnload(serviceLoader, activateName); - } catch (Exception ignore) { - LOGGER.warn("Can't unload SPI for :{} from org.apache.seata package,will try to unload SPI from io.seata package", service.getName()); - InnerEnhancedServiceLoader serviceLoader = InnerEnhancedServiceLoader.getServiceLoader(compatibleService); - doUnload(serviceLoader, activateName); - } - } else { - InnerEnhancedServiceLoader serviceLoader = InnerEnhancedServiceLoader.getServiceLoader(service); - doUnload(serviceLoader, activateName); - } + + InnerEnhancedServiceLoader serviceLoader = InnerEnhancedServiceLoader.getServiceLoader(service); + doUnload(serviceLoader, activateName); } private static void doUnload(InnerEnhancedServiceLoader serviceLoader, String activateName) { @@ -342,6 +236,7 @@ static List> getAllExtensionClass(Class service) { static List> getAllExtensionClass(Class service, ClassLoader loader) { return InnerEnhancedServiceLoader.getServiceLoader(service).getAllExtensionClass(loader); } + /** * Cannot use TCCL, in the pandora container will cause the class in the plugin not to be loaded * @@ -357,6 +252,9 @@ private static class InnerEnhancedServiceLoader { private static final String SERVICES_DIRECTORY = "META-INF/services/"; private static final String SEATA_DIRECTORY = "META-INF/seata/"; + private static final String APACHE_SEATA_PACKAGE_NAME = "org.apache.seata"; + private static final String IO_SEATA_PACKAGE_NAME = "io.seata"; + private final Class type; private final Holder>> definitionsHolder = new Holder<>(); private final ConcurrentMap, Holder> definitionToInstanceMap = @@ -369,7 +267,6 @@ private InnerEnhancedServiceLoader(Class type) { } - /** * Get the ServiceLoader for the specified Class * @@ -382,8 +279,8 @@ private static InnerEnhancedServiceLoader getServiceLoader(Class type) if (type == null) { throw new IllegalArgumentException("Enhanced Service type is null"); } - return (InnerEnhancedServiceLoader)CollectionUtils.computeIfAbsent(SERVICE_LOADERS, type, - key -> new InnerEnhancedServiceLoader<>(type)); + return (InnerEnhancedServiceLoader) CollectionUtils.computeIfAbsent(SERVICE_LOADERS, type, + key -> new InnerEnhancedServiceLoader<>(type)); } @SuppressWarnings("unchecked") @@ -391,7 +288,7 @@ private static InnerEnhancedServiceLoader removeServiceLoader(Class ty if (type == null) { throw new IllegalArgumentException("Enhanced Service type is null"); } - return (InnerEnhancedServiceLoader)SERVICE_LOADERS.remove(type); + return (InnerEnhancedServiceLoader) SERVICE_LOADERS.remove(type); } private static void removeAllServiceLoader() { @@ -449,7 +346,7 @@ private S load(String activateName, Object[] args, ClassLoader loader) * @param activateName the activate name * @param argsType the args type * @param args the args - * @param loader the class loader + * @param loader the class loader * @return the s * @throws EnhancedServiceNotFoundException the enhanced service not found exception */ @@ -460,8 +357,8 @@ private S load(String activateName, Class[] argsType, Object[] args, ClassLoa /** * get all implements - * @param loader the class loader * + * @param loader the class loader * @return list list */ private List loadAll(ClassLoader loader) { @@ -511,8 +408,8 @@ private S loadExtension(ClassLoader loader, Class[] argTypes, Object[] args) throw e; } catch (Throwable e) { throw new EnhancedServiceNotFoundException( - "not found service provider for : " + type.getName() - + " caused by " + ExceptionUtils.getFullStackTrace(e)); + "not found service provider for : " + type.getName() + + " caused by " + ExceptionUtils.getFullStackTrace(e)); } } @@ -528,7 +425,7 @@ private S loadExtension(String activateName, ClassLoader loader, Class[] argType return getExtensionInstance(cachedExtensionDefinition, loader, argTypes, args); } catch (Throwable e) { if (e instanceof EnhancedServiceNotFoundException) { - throw (EnhancedServiceNotFoundException)e; + throw (EnhancedServiceNotFoundException) e; } else { throw new EnhancedServiceNotFoundException( "not found service provider for : " + type.getName() + " caused by " + ExceptionUtils @@ -544,7 +441,7 @@ private S getExtensionInstance(ExtensionDefinition definition, ClassLoader lo } if (Scope.SINGLETON.equals(definition.getScope())) { Holder holder = CollectionUtils.computeIfAbsent(definitionToInstanceMap, definition, - key -> new Holder<>()); + key -> new Holder<>()); Object instance = holder.get(); if (instance == null) { synchronized (holder) { @@ -555,7 +452,7 @@ private S getExtensionInstance(ExtensionDefinition definition, ClassLoader lo } } } - return (S)instance; + return (S) instance; } else { return createNewExtension(definition, loader, argTypes, args); } @@ -588,8 +485,20 @@ private List> loadAllExtensionClass(ClassLoader loader) { private List> findAllExtensionDefinition(ClassLoader loader) { List> extensionDefinitions = new ArrayList<>(); try { - loadFile(SERVICES_DIRECTORY, loader, extensionDefinitions); - loadFile(SEATA_DIRECTORY, loader, extensionDefinitions); + loadFile(SERVICES_DIRECTORY, type, loader, extensionDefinitions); + loadFile(SEATA_DIRECTORY, type, loader, extensionDefinitions); + + @SuppressWarnings("rawtypes") Class compatibleService = getCompatibleService(type); + if (compatibleService != null) { + if (type.isAssignableFrom(compatibleService)) { + LOGGER.info("Load compatible class {}", compatibleService.getName()); + loadFile(SERVICES_DIRECTORY, compatibleService, loader, extensionDefinitions); + loadFile(SEATA_DIRECTORY, compatibleService, loader, extensionDefinitions); + } else { + LOGGER.info("Ignore load compatible class {}, because is not assignable from origin type {}", compatibleService.getName(), type.getName()); + } + } + } catch (IOException e) { throw new EnhancedServiceNotFoundException(e); } @@ -616,8 +525,17 @@ private List> findAllExtensionDefinition(ClassLoader load return extensionDefinitions; } + private static Class getCompatibleService(Class originType) { + String ioSeataType = originType.getName().replace(APACHE_SEATA_PACKAGE_NAME, IO_SEATA_PACKAGE_NAME); + try { + return Class.forName(ioSeataType); + } catch (ClassNotFoundException e) { + return null; + } + } - private void loadFile(String dir, ClassLoader loader, List> extensions) + + private void loadFile(String dir, Class type, ClassLoader loader, List> extensions) throws IOException { String fileName = dir + type.getName(); Enumeration urls; @@ -755,13 +673,14 @@ private S initInstance(Class implClazz, Class[] argTypes, Object[] args) s = type.cast(implClazz.newInstance()); } if (s instanceof Initialize) { - ((Initialize)s).init(); + ((Initialize) s).init(); } return s; } /** * Helper Class for hold a value. + * * @param */ private static class Holder { diff --git a/compatible/src/main/java/io/seata/core/model/TransactionManager.java b/compatible/src/main/java/io/seata/core/model/TransactionManager.java deleted file mode 100644 index 5f03357db54..00000000000 --- a/compatible/src/main/java/io/seata/core/model/TransactionManager.java +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.seata.core.model; - -/** - * - * - * Transaction Manager. - * - * Define a global transaction and control it. - * - */ -public interface TransactionManager extends org.apache.seata.core.model.TransactionManager { -} diff --git a/compatible/src/main/java/io/seata/spring/annotation/GlobalTransactionScanner.java b/compatible/src/main/java/io/seata/spring/annotation/GlobalTransactionScanner.java new file mode 100644 index 00000000000..7ea44327b0c --- /dev/null +++ b/compatible/src/main/java/io/seata/spring/annotation/GlobalTransactionScanner.java @@ -0,0 +1,46 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.seata.spring.annotation; + +import org.apache.seata.tm.api.FailureHandler; + +public class GlobalTransactionScanner extends org.apache.seata.spring.annotation.GlobalTransactionScanner { + + public GlobalTransactionScanner(String txServiceGroup) { + super(txServiceGroup); + } + + public GlobalTransactionScanner(String txServiceGroup, int mode) { + super(txServiceGroup, mode); + } + + public GlobalTransactionScanner(String applicationId, String txServiceGroup) { + super(applicationId, txServiceGroup); + } + + public GlobalTransactionScanner(String applicationId, String txServiceGroup, int mode) { + super(applicationId, txServiceGroup, mode); + } + + public GlobalTransactionScanner(String applicationId, String txServiceGroup, FailureHandler failureHandlerHook) { + super(applicationId, txServiceGroup, failureHandlerHook); + } + + public GlobalTransactionScanner(String applicationId, String txServiceGroup, int mode, FailureHandler failureHandlerHook) { + super(applicationId, txServiceGroup, mode, failureHandlerHook); + } +} diff --git a/compatible/src/main/java/io/seata/spring/annotation/datasource/AutoDataSourceProxyRegistrar.java b/compatible/src/main/java/io/seata/spring/annotation/datasource/AutoDataSourceProxyRegistrar.java new file mode 100644 index 00000000000..1fbc4f5f06c --- /dev/null +++ b/compatible/src/main/java/io/seata/spring/annotation/datasource/AutoDataSourceProxyRegistrar.java @@ -0,0 +1,54 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.seata.spring.annotation.datasource; + +import java.util.Map; + +import org.apache.seata.spring.annotation.datasource.SeataAutoDataSourceProxyCreator; +import org.springframework.beans.factory.support.AbstractBeanDefinition; +import org.springframework.beans.factory.support.BeanDefinitionBuilder; +import org.springframework.beans.factory.support.BeanDefinitionRegistry; +import org.springframework.context.annotation.ImportBeanDefinitionRegistrar; +import org.springframework.core.type.AnnotationMetadata; + +public class AutoDataSourceProxyRegistrar implements ImportBeanDefinitionRegistrar { + private static final String ATTRIBUTE_KEY_USE_JDK_PROXY = "useJdkProxy"; + private static final String ATTRIBUTE_KEY_EXCLUDES = "excludes"; + private static final String ATTRIBUTE_KEY_DATA_SOURCE_PROXY_MODE = "dataSourceProxyMode"; + + public static final String BEAN_NAME_SEATA_AUTO_DATA_SOURCE_PROXY_CREATOR = "seataAutoDataSourceProxyCreator"; + + @Override + public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { + Map annotationAttributes = importingClassMetadata.getAnnotationAttributes(EnableAutoDataSourceProxy.class.getName()); + + boolean useJdkProxy = Boolean.parseBoolean(annotationAttributes.get(ATTRIBUTE_KEY_USE_JDK_PROXY).toString()); + String[] excludes = (String[]) annotationAttributes.get(ATTRIBUTE_KEY_EXCLUDES); + String dataSourceProxyMode = (String) annotationAttributes.get(ATTRIBUTE_KEY_DATA_SOURCE_PROXY_MODE); + + //register seataAutoDataSourceProxyCreator bean def + if (!registry.containsBeanDefinition(BEAN_NAME_SEATA_AUTO_DATA_SOURCE_PROXY_CREATOR)) { + AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder + .genericBeanDefinition(SeataAutoDataSourceProxyCreator.class) + .addConstructorArgValue(useJdkProxy) + .addConstructorArgValue(excludes) + .addConstructorArgValue(dataSourceProxyMode) + .getBeanDefinition(); + registry.registerBeanDefinition(BEAN_NAME_SEATA_AUTO_DATA_SOURCE_PROXY_CREATOR, beanDefinition); + } + } +} diff --git a/compatible/src/main/java/io/seata/spring/annotation/datasource/EnableAutoDataSourceProxy.java b/compatible/src/main/java/io/seata/spring/annotation/datasource/EnableAutoDataSourceProxy.java new file mode 100644 index 00000000000..44e96086848 --- /dev/null +++ b/compatible/src/main/java/io/seata/spring/annotation/datasource/EnableAutoDataSourceProxy.java @@ -0,0 +1,55 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.seata.spring.annotation.datasource; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import org.springframework.context.annotation.Import; + +/** + * This annotation will enable auto proxying of datasource bean. + */ +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +@Import(AutoDataSourceProxyRegistrar.class) +@Documented +public @interface EnableAutoDataSourceProxy { + /** + * Whether use JDK proxy instead of CGLIB proxy + * + * @return useJdkProxy + */ + boolean useJdkProxy() default false; + + /** + * Specifies which datasource bean are not eligible for auto-proxying + * + * @return excludes + */ + String[] excludes() default {}; + + /** + * Data source proxy mode, AT or XA + * + * @return dataSourceProxyMode + */ + String dataSourceProxyMode() default "AT"; +} diff --git a/compatible/src/main/resources/META-INF/services/io.seata.core.model.ResourceManager b/compatible/src/main/resources/META-INF/services/io.seata.core.model.ResourceManager new file mode 100644 index 00000000000..4ec5cba1d35 --- /dev/null +++ b/compatible/src/main/resources/META-INF/services/io.seata.core.model.ResourceManager @@ -0,0 +1 @@ +io.seata.saga.rm.SagaResourceManager \ No newline at end of file diff --git a/compatible/src/main/resources/META-INF/services/org.apache.seata.core.model.ResourceManager b/compatible/src/main/resources/META-INF/services/org.apache.seata.core.model.ResourceManager deleted file mode 100644 index 8b137891791..00000000000 --- a/compatible/src/main/resources/META-INF/services/org.apache.seata.core.model.ResourceManager +++ /dev/null @@ -1 +0,0 @@ - diff --git a/compatible/src/test/java/io/seata/saga/engine/db/mockserver/StateMachineDBMockServerTests.java b/compatible/src/test/java/io/seata/saga/engine/db/mockserver/StateMachineDBMockServerTests.java index af0e99b0d5e..f7647d4cda0 100644 --- a/compatible/src/test/java/io/seata/saga/engine/db/mockserver/StateMachineDBMockServerTests.java +++ b/compatible/src/test/java/io/seata/saga/engine/db/mockserver/StateMachineDBMockServerTests.java @@ -18,12 +18,13 @@ import io.seata.saga.SagaCostPrint; 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.rm.StateMachineEngineHolder; 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.engine.mock.DemoService.Engineer; -import io.seata.saga.engine.mock.DemoService.People; +import org.apache.seata.rm.DefaultResourceManager; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; @@ -143,39 +144,6 @@ public void testSimpleCatchesStateMachine() throws Exception { }); } - @Test - public void testSimpleScriptTaskStateMachineWithLayout() throws Exception { - String stateMachineName = "designerSimpleScriptTaskStateMachine"; - - SagaCostPrint.executeAndPrint("5-8", () -> { - Map paramMap = new HashMap<>(1); - paramMap.put("a", 1); - - StateMachineInstance inst = stateMachineEngine.start(stateMachineName, null, paramMap); - - Assertions.assertEquals(ExecutionStatus.SU, inst.getStatus()); - Assertions.assertNotNull(inst.getEndParams().get("scriptStateResult")); - }); - - SagaCostPrint.executeAndPrint("5-9", () -> { - Map paramMap = new HashMap<>(1); - paramMap.put("a", 1); - - StateMachineInstance inst = stateMachineEngine.start(stateMachineName, null, paramMap); - - Assertions.assertEquals(ExecutionStatus.SU, inst.getStatus()); - }); - - SagaCostPrint.executeAndPrint("5-10", () -> { - Map paramMap = new HashMap<>(1); - paramMap.put("scriptThrowException", true); - - StateMachineInstance inst = stateMachineEngine.start(stateMachineName, null, paramMap); - - Assertions.assertEquals(ExecutionStatus.FA, inst.getStatus()); - }); - } - @Test public void testSimpleRetryStateMachine() throws Exception { String stateMachineName = "simpleRetryStateMachine"; diff --git a/compatible/src/main/java/io/seata/core/model/ResourceManager.java b/compatible/src/test/java/io/seata/spi/SPITest.java similarity index 54% rename from compatible/src/main/java/io/seata/core/model/ResourceManager.java rename to compatible/src/test/java/io/seata/spi/SPITest.java index 01420df6a5a..3b421b7f053 100644 --- a/compatible/src/main/java/io/seata/core/model/ResourceManager.java +++ b/compatible/src/test/java/io/seata/spi/SPITest.java @@ -14,13 +14,21 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.seata.core.model; +package io.seata.spi; +import org.apache.seata.core.model.BranchType; +import org.apache.seata.core.model.ResourceManager; +import org.apache.seata.rm.DefaultResourceManager; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; -/** - * Resource Manager: common behaviors. - * - */ -public interface ResourceManager extends org.apache.seata.core.model.ResourceManager { +public class SPITest { + @Test + public void testRmSPIOrder() { + DefaultResourceManager defaultResourceManager = DefaultResourceManager.get(); + ResourceManager resourceManager = defaultResourceManager.getResourceManager(BranchType.SAGA); + Assertions.assertNotNull(resourceManager); + Assertions.assertEquals("io.seata.saga.rm.SagaResourceManager", resourceManager.getClass().getName()); + } } diff --git a/compatible/src/test/resources/file.conf b/compatible/src/test/resources/file.conf index 46c3e0401cc..01b00dffefd 100644 --- a/compatible/src/test/resources/file.conf +++ b/compatible/src/test/resources/file.conf @@ -22,4 +22,12 @@ service { default.grouplist = "127.0.0.1:8091" #disable seata disableGlobalTransaction = false -} \ No newline at end of file +} + +client { + rm { + sagaJsonParser = jackson + sagaRetryPersistModeUpdate = false + sagaCompensatePersistModeUpdate = false + } +} From 55cdf59e352495f9ebd2b7d2ff815b6ba4294680 Mon Sep 17 00:00:00 2001 From: "yixia.wt" Date: Sat, 16 Mar 2024 17:14:22 +0800 Subject: [PATCH 03/21] add change note --- changes/en-us/2.x.md | 4 +- changes/zh-cn/2.x.md | 2 + .../store/impl/StateLogStoreAdapter.java | 106 ------------------ 3 files changed, 5 insertions(+), 107 deletions(-) delete mode 100644 compatible/src/main/java/io/seata/saga/engine/store/impl/StateLogStoreAdapter.java diff --git a/changes/en-us/2.x.md b/changes/en-us/2.x.md index 60602ebc53b..4dcc821defa 100644 --- a/changes/en-us/2.x.md +++ b/changes/en-us/2.x.md @@ -110,7 +110,8 @@ Add changes here for all PR submitted to the 2.x branch. - [[#6393](https://github.com/apache/incubator-seata/pull/6393)] determine the version before sync metadata and add retry mechanism - [[#6387](https://github.com/apache/incubator-seata/pull/6387)] optimize tcc use compatible - [[#6402](https://github.com/apache/incubator-seata/pull/6402)] optimize rm-datasource use compatible -- [[#6419](https://github.com/apache/incubator-seata/pull/6419)] optimize integration-tx-api compatibl +- [[#6419](https://github.com/apache/incubator-seata/pull/6419)] optimize integration-tx-api compatible +- [[#6427](https://github.com/apache/incubator-seata/pull/6427)] support spi态saga态spring module compatible ### refactor: - [[#6269](https://github.com/apache/incubator-seata/pull/6269)] standardize Seata Exception @@ -166,5 +167,6 @@ Thanks to these contributors for their code commits. Please report an unintended - [gggyd123](https://github.com/gggyd123) - [jonasHanhan](https://github.com/jonasHanhan) - [Code-breaker1998](https://github.com/Code-breaker1998) +- [yixia](https://github.com/wt-better) Also, we receive many valuable issues, questions and advices from our community. Thanks for you all. diff --git a/changes/zh-cn/2.x.md b/changes/zh-cn/2.x.md index 07200415688..d3af1ee8c26 100644 --- a/changes/zh-cn/2.x.md +++ b/changes/zh-cn/2.x.md @@ -115,6 +115,7 @@ - [[#6387](https://github.com/apache/incubator-seata/pull/6387)] 优化tccä½æē”Ø兼容 - [[#6402](https://github.com/apache/incubator-seata/pull/6402)] 优化rm-datasource向äø‹å…¼å®¹ - [[#6419](https://github.com/apache/incubator-seata/pull/6419)] 优化integration-tx-api向äø‹å…¼å®¹ +- [[#6427](https://github.com/apache/incubator-seata/pull/6427)] ę”Æꌁspi态saga态springęؔ块ēš„向äø‹å…¼å®¹ ### refactor: @@ -168,5 +169,6 @@ - [gggyd123](https://github.com/gggyd123) - [jonasHanhan](https://github.com/jonasHanhan) - [Code-breaker1998](https://github.com/Code-breaker1998) +- [yixia](https://github.com/wt-better) åŒę—¶ļ¼Œęˆ‘ä»¬ę”¶åˆ°äŗ†ē¤¾åŒŗ反馈ēš„å¾ˆå¤šęœ‰ä»·å€¼ēš„issue和å»ŗč®®ļ¼Œéžåøøę„Ÿč°¢å¤§å®¶ć€‚ diff --git a/compatible/src/main/java/io/seata/saga/engine/store/impl/StateLogStoreAdapter.java b/compatible/src/main/java/io/seata/saga/engine/store/impl/StateLogStoreAdapter.java deleted file mode 100644 index 471b25b6590..00000000000 --- a/compatible/src/main/java/io/seata/saga/engine/store/impl/StateLogStoreAdapter.java +++ /dev/null @@ -1,106 +0,0 @@ -package io.seata.saga.engine.store.impl; - -import io.seata.saga.engine.store.StateLogStore; -import io.seata.saga.proctrl.impl.ProcessContextImpl; -import io.seata.saga.statelang.domain.impl.StateInstanceImpl; -import io.seata.saga.statelang.domain.impl.StateMachineInstanceImpl; -import org.apache.seata.common.util.CollectionUtils; -import org.apache.seata.saga.proctrl.HierarchicalProcessContext; -import org.apache.seata.saga.proctrl.ProcessContext; -import org.apache.seata.saga.statelang.domain.StateInstance; -import org.apache.seata.saga.statelang.domain.StateMachineInstance; - -import java.util.ArrayList; -import java.util.List; -import java.util.stream.Collectors; - -public class StateLogStoreAdapter implements org.apache.seata.saga.engine.store.StateLogStore { - - private final StateLogStore actual; - - public StateLogStoreAdapter(StateLogStore actual) { - this.actual = actual; - } - - @Override - public void recordStateMachineStarted(StateMachineInstance machineInstance, ProcessContext context) { - io.seata.saga.statelang.domain.StateMachineInstance wrapStateMachineInstance = StateMachineInstanceImpl.wrap(machineInstance); - io.seata.saga.proctrl.ProcessContext wrapContext = ProcessContextImpl.wrap((HierarchicalProcessContext) context); - actual.recordStateMachineStarted(wrapStateMachineInstance, wrapContext); - } - - @Override - public void recordStateMachineFinished(StateMachineInstance machineInstance, ProcessContext context) { - io.seata.saga.statelang.domain.StateMachineInstance wrapStateMachineInstance = StateMachineInstanceImpl.wrap(machineInstance); - io.seata.saga.proctrl.ProcessContext wrapContext = ProcessContextImpl.wrap((HierarchicalProcessContext) context); - actual.recordStateMachineFinished(wrapStateMachineInstance, wrapContext); - } - - @Override - public void recordStateMachineRestarted(StateMachineInstance machineInstance, ProcessContext context) { - io.seata.saga.statelang.domain.StateMachineInstance wrapStateMachineInstance = StateMachineInstanceImpl.wrap(machineInstance); - io.seata.saga.proctrl.ProcessContext wrapContext = ProcessContextImpl.wrap((HierarchicalProcessContext) context); - actual.recordStateMachineRestarted(wrapStateMachineInstance, wrapContext); - } - - @Override - public void recordStateStarted(StateInstance stateInstance, ProcessContext context) { - io.seata.saga.statelang.domain.StateInstance wrapStateInstance = StateInstanceImpl.wrap(stateInstance); - io.seata.saga.proctrl.ProcessContext wrapContext = ProcessContextImpl.wrap((HierarchicalProcessContext) context); - actual.recordStateStarted(wrapStateInstance, wrapContext); - } - - @Override - public void recordStateFinished(StateInstance stateInstance, ProcessContext context) { - io.seata.saga.statelang.domain.StateInstance wrapStateInstance = StateInstanceImpl.wrap(stateInstance); - io.seata.saga.proctrl.ProcessContext wrapContext = ProcessContextImpl.wrap((HierarchicalProcessContext) context); - actual.recordStateFinished(wrapStateInstance, wrapContext); - } - - @Override - public StateMachineInstance getStateMachineInstance(String stateMachineInstanceId) { - io.seata.saga.statelang.domain.StateMachineInstance stateMachineInstance = actual.getStateMachineInstance(stateMachineInstanceId); - return ((StateMachineInstanceImpl) stateMachineInstance).unwrap(); - } - - @Override - public StateMachineInstance getStateMachineInstanceByBusinessKey(String businessKey, String tenantId) { - io.seata.saga.statelang.domain.StateMachineInstance stateMachineInstance = actual.getStateMachineInstanceByBusinessKey(businessKey, tenantId); - return ((StateMachineInstanceImpl) stateMachineInstance).unwrap(); - } - - @Override - public List queryStateMachineInstanceByParentId(String parentId) { - List stateMachineInstances = actual.queryStateMachineInstanceByParentId(parentId); - if (CollectionUtils.isEmpty(stateMachineInstances)) { - return new ArrayList<>(); - } - - return stateMachineInstances.stream().map(stateMachineInstance -> ((StateMachineInstanceImpl)stateMachineInstance).unwrap()).collect(Collectors.toList()); - } - - @Override - public StateInstance getStateInstance(String stateInstanceId, String machineInstId) { - io.seata.saga.statelang.domain.StateInstance stateInstance = actual.getStateInstance(stateInstanceId, machineInstId); - return ((StateInstanceImpl) stateInstance).unwrap(); - } - - @Override - public List queryStateInstanceListByMachineInstanceId(String stateMachineInstanceId) { - List stateInstances = actual.queryStateInstanceListByMachineInstanceId(stateMachineInstanceId); - if (CollectionUtils.isEmpty(stateInstances)) { - return new ArrayList<>(); - } - return stateInstances.stream().map(stateInstance -> ((StateInstanceImpl)stateInstance).unwrap()).collect(Collectors.toList()); - } - - @Override - public void clearUp(ProcessContext context) { - io.seata.saga.proctrl.ProcessContext wrapContext = ProcessContextImpl.wrap((HierarchicalProcessContext) context); - actual.clearUp(wrapContext); - } - - public StateLogStore getActual() { - return actual; - } -} From 9e0ffb8001b21d8cf71b09fb186085dc5457cc2b Mon Sep 17 00:00:00 2001 From: "yixia.wt" Date: Sat, 16 Mar 2024 17:23:36 +0800 Subject: [PATCH 04/21] add license --- .../common/loader/EnhancedServiceLoader.java | 21 +++++++++---------- .../saga/engine/store/StateLogStore.java | 16 ++++++++++++++ .../engine/store/impl/StateLogStoreImpl.java | 16 ++++++++++++++ .../io/seata/saga/rm/SagaResourceManager.java | 16 ++++++++++++++ .../saga/rm/StateMachineEngineHolder.java | 16 ++++++++++++++ 5 files changed, 74 insertions(+), 11 deletions(-) diff --git a/common/src/main/java/org/apache/seata/common/loader/EnhancedServiceLoader.java b/common/src/main/java/org/apache/seata/common/loader/EnhancedServiceLoader.java index b4d005dbe00..f2e0405f330 100644 --- a/common/src/main/java/org/apache/seata/common/loader/EnhancedServiceLoader.java +++ b/common/src/main/java/org/apache/seata/common/loader/EnhancedServiceLoader.java @@ -279,8 +279,8 @@ private static InnerEnhancedServiceLoader getServiceLoader(Class type) if (type == null) { throw new IllegalArgumentException("Enhanced Service type is null"); } - return (InnerEnhancedServiceLoader) CollectionUtils.computeIfAbsent(SERVICE_LOADERS, type, - key -> new InnerEnhancedServiceLoader<>(type)); + return (InnerEnhancedServiceLoader)CollectionUtils.computeIfAbsent(SERVICE_LOADERS, type, + key -> new InnerEnhancedServiceLoader<>(type)); } @SuppressWarnings("unchecked") @@ -346,7 +346,7 @@ private S load(String activateName, Object[] args, ClassLoader loader) * @param activateName the activate name * @param argsType the args type * @param args the args - * @param loader the class loader + * @param loader the class loader * @return the s * @throws EnhancedServiceNotFoundException the enhanced service not found exception */ @@ -357,8 +357,8 @@ private S load(String activateName, Class[] argsType, Object[] args, ClassLoa /** * get all implements + * @param loader the class loader * - * @param loader the class loader * @return list list */ private List loadAll(ClassLoader loader) { @@ -408,8 +408,8 @@ private S loadExtension(ClassLoader loader, Class[] argTypes, Object[] args) throw e; } catch (Throwable e) { throw new EnhancedServiceNotFoundException( - "not found service provider for : " + type.getName() - + " caused by " + ExceptionUtils.getFullStackTrace(e)); + "not found service provider for : " + type.getName() + + " caused by " + ExceptionUtils.getFullStackTrace(e)); } } @@ -425,7 +425,7 @@ private S loadExtension(String activateName, ClassLoader loader, Class[] argType return getExtensionInstance(cachedExtensionDefinition, loader, argTypes, args); } catch (Throwable e) { if (e instanceof EnhancedServiceNotFoundException) { - throw (EnhancedServiceNotFoundException) e; + throw (EnhancedServiceNotFoundException)e; } else { throw new EnhancedServiceNotFoundException( "not found service provider for : " + type.getName() + " caused by " + ExceptionUtils @@ -441,7 +441,7 @@ private S getExtensionInstance(ExtensionDefinition definition, ClassLoader lo } if (Scope.SINGLETON.equals(definition.getScope())) { Holder holder = CollectionUtils.computeIfAbsent(definitionToInstanceMap, definition, - key -> new Holder<>()); + key -> new Holder<>()); Object instance = holder.get(); if (instance == null) { synchronized (holder) { @@ -452,7 +452,7 @@ private S getExtensionInstance(ExtensionDefinition definition, ClassLoader lo } } } - return (S) instance; + return (S)instance; } else { return createNewExtension(definition, loader, argTypes, args); } @@ -673,14 +673,13 @@ private S initInstance(Class implClazz, Class[] argTypes, Object[] args) s = type.cast(implClazz.newInstance()); } if (s instanceof Initialize) { - ((Initialize) s).init(); + ((Initialize)s).init(); } return s; } /** * Helper Class for hold a value. - * * @param */ private static class Holder { diff --git a/compatible/src/main/java/io/seata/saga/engine/store/StateLogStore.java b/compatible/src/main/java/io/seata/saga/engine/store/StateLogStore.java index 4bccd943976..0c664e1ef05 100644 --- a/compatible/src/main/java/io/seata/saga/engine/store/StateLogStore.java +++ b/compatible/src/main/java/io/seata/saga/engine/store/StateLogStore.java @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ package io.seata.saga.engine.store; diff --git a/compatible/src/main/java/io/seata/saga/engine/store/impl/StateLogStoreImpl.java b/compatible/src/main/java/io/seata/saga/engine/store/impl/StateLogStoreImpl.java index 6c63d8aca3f..54c89c6b1bb 100644 --- a/compatible/src/main/java/io/seata/saga/engine/store/impl/StateLogStoreImpl.java +++ b/compatible/src/main/java/io/seata/saga/engine/store/impl/StateLogStoreImpl.java @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ package io.seata.saga.engine.store.impl; import io.seata.saga.engine.store.StateLogStore; diff --git a/compatible/src/main/java/io/seata/saga/rm/SagaResourceManager.java b/compatible/src/main/java/io/seata/saga/rm/SagaResourceManager.java index 78419cd2cbc..a850e40605d 100644 --- a/compatible/src/main/java/io/seata/saga/rm/SagaResourceManager.java +++ b/compatible/src/main/java/io/seata/saga/rm/SagaResourceManager.java @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ package io.seata.saga.rm; import io.seata.saga.statelang.domain.ExecutionStatus; diff --git a/compatible/src/main/java/io/seata/saga/rm/StateMachineEngineHolder.java b/compatible/src/main/java/io/seata/saga/rm/StateMachineEngineHolder.java index c1b200c5902..90da2e0765c 100644 --- a/compatible/src/main/java/io/seata/saga/rm/StateMachineEngineHolder.java +++ b/compatible/src/main/java/io/seata/saga/rm/StateMachineEngineHolder.java @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ package io.seata.saga.rm; From c596882df0308b36a3396708f5799814f3848627 Mon Sep 17 00:00:00 2001 From: "yixia.wt" Date: Sat, 16 Mar 2024 18:00:45 +0800 Subject: [PATCH 05/21] fix licence --- .../seata/integration/tx/api/util/ProxyUtil.java | 0 .../seata/spring/tcc/TccAnnotationProcessor.java | 0 compatible/src/test/java/io/seata/spi/SPITest.java | 14 ++++++++++---- 3 files changed, 10 insertions(+), 4 deletions(-) delete mode 100644 compatible/src/main/java/io/seata/integration/tx/api/util/ProxyUtil.java delete mode 100644 compatible/src/main/java/io/seata/spring/tcc/TccAnnotationProcessor.java diff --git a/compatible/src/main/java/io/seata/integration/tx/api/util/ProxyUtil.java b/compatible/src/main/java/io/seata/integration/tx/api/util/ProxyUtil.java deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/compatible/src/main/java/io/seata/spring/tcc/TccAnnotationProcessor.java b/compatible/src/main/java/io/seata/spring/tcc/TccAnnotationProcessor.java deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/compatible/src/test/java/io/seata/spi/SPITest.java b/compatible/src/test/java/io/seata/spi/SPITest.java index 3b421b7f053..8d66a9c51f7 100644 --- a/compatible/src/test/java/io/seata/spi/SPITest.java +++ b/compatible/src/test/java/io/seata/spi/SPITest.java @@ -16,19 +16,25 @@ */ package io.seata.spi; +import org.apache.seata.common.loader.EnhancedServiceLoader; import org.apache.seata.core.model.BranchType; import org.apache.seata.core.model.ResourceManager; import org.apache.seata.rm.DefaultResourceManager; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; +import java.util.List; +import java.util.Optional; +import java.util.stream.Collectors; + public class SPITest { @Test public void testRmSPIOrder() { - DefaultResourceManager defaultResourceManager = DefaultResourceManager.get(); - ResourceManager resourceManager = defaultResourceManager.getResourceManager(BranchType.SAGA); - Assertions.assertNotNull(resourceManager); - Assertions.assertEquals("io.seata.saga.rm.SagaResourceManager", resourceManager.getClass().getName()); + List resourceManagers = EnhancedServiceLoader.loadAll(ResourceManager.class); + List list = resourceManagers.stream().filter(resourceManager -> resourceManager.getBranchType().equals(BranchType.SAGA)).collect(Collectors.toList()); + Assertions.assertEquals(2, list.size()); + //last order is io.seata + Assertions.assertEquals("io.seata.saga.rm.SagaResourceManager", list.get(1).getClass().getName()); } } From 327cf0533522265595305e58363d370681f51f4e Mon Sep 17 00:00:00 2001 From: "yixia.wt" Date: Sat, 16 Mar 2024 18:07:30 +0800 Subject: [PATCH 06/21] add path-ignore --- .github/workflows/license-checker.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/license-checker.yaml b/.github/workflows/license-checker.yaml index 87b4b29f38d..847b9c77234 100644 --- a/.github/workflows/license-checker.yaml +++ b/.github/workflows/license-checker.yaml @@ -2,6 +2,8 @@ name: License checker on: pull_request: + paths-ignore: + - '**.json' branches: [ 2.x, develop, master ] jobs: From 1bca7749750fc789e4340a6185cf0547a96f6a02 Mon Sep 17 00:00:00 2001 From: "yixia.wt" Date: Sat, 16 Mar 2024 18:22:11 +0800 Subject: [PATCH 07/21] fix ut --- .github/workflows/license-checker.yaml | 6 +-- .../annotation/GlobalTransactionScanner.java | 40 +++++++++++++++++++ .../src/test/java/io/seata/spi/SPITest.java | 1 + .../annotation/GlobalTransactionScanner.java | 20 +++++++++- 4 files changed, 62 insertions(+), 5 deletions(-) diff --git a/.github/workflows/license-checker.yaml b/.github/workflows/license-checker.yaml index 847b9c77234..8ed9b625148 100644 --- a/.github/workflows/license-checker.yaml +++ b/.github/workflows/license-checker.yaml @@ -2,9 +2,9 @@ name: License checker on: pull_request: - paths-ignore: - - '**.json' - branches: [ 2.x, develop, master ] + paths-ignore: + - '**.json' + branches: [ 2.x, develop, master ] jobs: check-license: diff --git a/compatible/src/main/java/io/seata/spring/annotation/GlobalTransactionScanner.java b/compatible/src/main/java/io/seata/spring/annotation/GlobalTransactionScanner.java index 7ea44327b0c..2e9bd9e4d23 100644 --- a/compatible/src/main/java/io/seata/spring/annotation/GlobalTransactionScanner.java +++ b/compatible/src/main/java/io/seata/spring/annotation/GlobalTransactionScanner.java @@ -16,10 +16,20 @@ */ package io.seata.spring.annotation; +import io.seata.rm.RMClient; +import io.seata.tm.TMClient; +import org.apache.seata.common.util.StringUtils; import org.apache.seata.tm.api.FailureHandler; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import static org.apache.seata.common.DefaultValues.DEFAULT_TX_GROUP; +import static org.apache.seata.common.DefaultValues.DEFAULT_TX_GROUP_OLD; public class GlobalTransactionScanner extends org.apache.seata.spring.annotation.GlobalTransactionScanner { + private static final Logger LOGGER = LoggerFactory.getLogger(GlobalTransactionScanner.class); + public GlobalTransactionScanner(String txServiceGroup) { super(txServiceGroup); } @@ -43,4 +53,34 @@ public GlobalTransactionScanner(String applicationId, String txServiceGroup, Fai public GlobalTransactionScanner(String applicationId, String txServiceGroup, int mode, FailureHandler failureHandlerHook) { super(applicationId, txServiceGroup, mode, failureHandlerHook); } + + protected void initClient() { + if (LOGGER.isInfoEnabled()) { + LOGGER.info("Initializing Global Transaction Clients ... "); + } + if (DEFAULT_TX_GROUP_OLD.equals(getTxServiceGroup())) { + LOGGER.warn("the default value of seata.tx-service-group: {} has already changed to {} since Seata 1.5, " + + "please change your default configuration as soon as possible " + + "and we don't recommend you to use default tx-service-group's value provided by seata", + DEFAULT_TX_GROUP_OLD, DEFAULT_TX_GROUP); + } + if (StringUtils.isNullOrEmpty(getApplicationId()) || StringUtils.isNullOrEmpty(getTxServiceGroup())) { + throw new IllegalArgumentException(String.format("applicationId: %s, txServiceGroup: %s", getApplicationId(), getTxServiceGroup())); + } + //init TM + TMClient.init(getApplicationId(), getTxServiceGroup(), getAccessKey(), getSecretKey()); + if (LOGGER.isInfoEnabled()) { + LOGGER.info("Transaction Manager Client is initialized. applicationId[{}] txServiceGroup[{}]", getApplicationId(), getTxServiceGroup()); + } + //init RM + RMClient.init(getApplicationId(), getTxServiceGroup()); + if (LOGGER.isInfoEnabled()) { + LOGGER.info("Resource Manager is initialized. applicationId[{}] txServiceGroup[{}]", getApplicationId(), getTxServiceGroup()); + } + + if (LOGGER.isInfoEnabled()) { + LOGGER.info("Global Transaction Clients are initialized. "); + } + registerSpringShutdownHook(); + } } diff --git a/compatible/src/test/java/io/seata/spi/SPITest.java b/compatible/src/test/java/io/seata/spi/SPITest.java index 8d66a9c51f7..932c1388027 100644 --- a/compatible/src/test/java/io/seata/spi/SPITest.java +++ b/compatible/src/test/java/io/seata/spi/SPITest.java @@ -31,6 +31,7 @@ public class SPITest { @Test public void testRmSPIOrder() { + EnhancedServiceLoader.unload(ResourceManager.class); List resourceManagers = EnhancedServiceLoader.loadAll(ResourceManager.class); List list = resourceManagers.stream().filter(resourceManager -> resourceManager.getBranchType().equals(BranchType.SAGA)).collect(Collectors.toList()); Assertions.assertEquals(2, list.size()); diff --git a/spring/src/main/java/org/apache/seata/spring/annotation/GlobalTransactionScanner.java b/spring/src/main/java/org/apache/seata/spring/annotation/GlobalTransactionScanner.java index b6d7c226867..f25b4b0b47a 100644 --- a/spring/src/main/java/org/apache/seata/spring/annotation/GlobalTransactionScanner.java +++ b/spring/src/main/java/org/apache/seata/spring/annotation/GlobalTransactionScanner.java @@ -215,7 +215,7 @@ public void destroy() { ShutdownHook.getInstance().destroyAll(); } - private void initClient() { + protected void initClient() { if (LOGGER.isInfoEnabled()) { LOGGER.info("Initializing Global Transaction Clients ... "); } @@ -246,7 +246,7 @@ private void initClient() { } - private void registerSpringShutdownHook() { + protected void registerSpringShutdownHook() { if (applicationContext instanceof ConfigurableApplicationContext) { ((ConfigurableApplicationContext) applicationContext).registerShutdownHook(); ShutdownHook.removeRuntimeShutdownHook(); @@ -592,4 +592,20 @@ public static void addScannerExcludeBeanNames(String... beanNames) { EXCLUDE_BEAN_NAME_SET.addAll(Arrays.asList(beanNames)); } } + + public String getApplicationId() { + return applicationId; + } + + public String getTxServiceGroup() { + return txServiceGroup; + } + + public static String getAccessKey() { + return accessKey; + } + + public static String getSecretKey() { + return secretKey; + } } From 55a554c0ecfc3ba52496f175c101286fc0b066bc Mon Sep 17 00:00:00 2001 From: "yixia.wt" Date: Sat, 16 Mar 2024 19:20:04 +0800 Subject: [PATCH 08/21] fix ut --- .../io/seata/spi/ExtensionDefinition.java | 87 +++++ .../seata/spi/InnerEnhancedServiceLoader.java | 347 ++++++++++++++++++ .../src/test/java/io/seata/spi/SPITest.java | 7 +- 3 files changed, 436 insertions(+), 5 deletions(-) create mode 100644 compatible/src/test/java/io/seata/spi/ExtensionDefinition.java create mode 100644 compatible/src/test/java/io/seata/spi/InnerEnhancedServiceLoader.java diff --git a/compatible/src/test/java/io/seata/spi/ExtensionDefinition.java b/compatible/src/test/java/io/seata/spi/ExtensionDefinition.java new file mode 100644 index 00000000000..1a1b92e797e --- /dev/null +++ b/compatible/src/test/java/io/seata/spi/ExtensionDefinition.java @@ -0,0 +1,87 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.seata.spi; + +import org.apache.seata.common.loader.Scope; +import org.apache.seata.common.util.StringUtils; + +final class ExtensionDefinition { + + private final String name; + private final Class serviceClass; + private final Integer order; + private final Scope scope; + + public Integer getOrder() { + return this.order; + } + + public Class getServiceClass() { + return this.serviceClass; + } + + public Scope getScope() { + return this.scope; + } + + public ExtensionDefinition(String name, Integer order, Scope scope, Class clazz) { + this.name = name; + this.order = order; + this.scope = scope; + this.serviceClass = clazz; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((name == null) ? 0 : name.hashCode()); + result = prime * result + ((serviceClass == null) ? 0 : serviceClass.hashCode()); + result = prime * result + ((order == null) ? 0 : order.hashCode()); + result = prime * result + ((scope == null) ? 0 : scope.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + + ExtensionDefinition other = (ExtensionDefinition) obj; + if (!StringUtils.equals(name, other.name)) { + return false; + } + if (!serviceClass.equals(other.serviceClass)) { + return false; + } + if (!order.equals(other.order)) { + return false; + } + return scope.equals(other.scope); + } + + public String getName() { + return name; + } +} \ No newline at end of file diff --git a/compatible/src/test/java/io/seata/spi/InnerEnhancedServiceLoader.java b/compatible/src/test/java/io/seata/spi/InnerEnhancedServiceLoader.java new file mode 100644 index 00000000000..ccef7fd8b56 --- /dev/null +++ b/compatible/src/test/java/io/seata/spi/InnerEnhancedServiceLoader.java @@ -0,0 +1,347 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.seata.spi; + +import org.apache.seata.common.Constants; +import org.apache.seata.common.executor.Initialize; +import org.apache.seata.common.loader.EnhancedServiceNotFoundException; +import org.apache.seata.common.loader.LoadLevel; +import org.apache.seata.common.loader.Scope; +import org.apache.seata.common.util.CollectionUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.net.URL; +import java.util.ArrayList; +import java.util.Enumeration; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; +import java.util.stream.Collectors; + +class InnerEnhancedServiceLoader { + private static final Logger LOGGER = LoggerFactory.getLogger(InnerEnhancedServiceLoader.class); + private static final String SERVICES_DIRECTORY = "META-INF/services/"; + private static final String SEATA_DIRECTORY = "META-INF/seata/"; + + private static final String APACHE_SEATA_PACKAGE_NAME = "org.apache.seata"; + private static final String IO_SEATA_PACKAGE_NAME = "io.seata"; + + private final Class type; + private final Holder>> definitionsHolder = new Holder<>(); + private final ConcurrentMap, Holder> definitionToInstanceMap = + new ConcurrentHashMap<>(); + private final ConcurrentMap>> nameToDefinitionsMap = new ConcurrentHashMap<>(); + private final ConcurrentMap, ExtensionDefinition> classToDefinitionMap = new ConcurrentHashMap<>(); + + public InnerEnhancedServiceLoader(Class type) { + this.type = type; + } + + public List loadAll(ClassLoader loader) { + return loadAll(null, null, loader); + } + + public List loadAll(Class[] argsType, Object[] args, ClassLoader loader) { + List allInstances = new ArrayList<>(); + List> allClazzs = getAllExtensionClass(loader); + if (CollectionUtils.isEmpty(allClazzs)) { + return allInstances; + } + try { + for (Class clazz : allClazzs) { + ExtensionDefinition definition = classToDefinitionMap.get(clazz); + allInstances.add(getExtensionInstance(definition, loader, argsType, args)); + } + } catch (Throwable t) { + throw new EnhancedServiceNotFoundException(t); + } + return allInstances; + } + + private List> getAllExtensionClass(ClassLoader loader) { + return loadAllExtensionClass(loader); + } + + private S getExtensionInstance(ExtensionDefinition definition, ClassLoader loader, Class[] argTypes, + Object[] args) { + if (definition == null) { + throw new EnhancedServiceNotFoundException("not found service provider for : " + type.getName()); + } + if (Scope.SINGLETON.equals(definition.getScope())) { + Holder holder = CollectionUtils.computeIfAbsent(definitionToInstanceMap, definition, + key -> new Holder<>()); + Object instance = holder.get(); + if (instance == null) { + synchronized (holder) { + instance = holder.get(); + if (instance == null) { + instance = createNewExtension(definition, loader, argTypes, args); + holder.set(instance); + } + } + } + return (S) instance; + } else { + return createNewExtension(definition, loader, argTypes, args); + } + } + + private S createNewExtension(ExtensionDefinition definition, ClassLoader loader, Class[] argTypes, Object[] args) { + Class clazz = definition.getServiceClass(); + try { + return initInstance(clazz, argTypes, args); + } catch (Throwable t) { + throw new IllegalStateException("Extension instance(definition: " + definition + ", class: " + + type + ") could not be instantiated: " + t.getMessage(), t); + } + } + + private List> loadAllExtensionClass(ClassLoader loader) { + List> definitions = definitionsHolder.get(); + if (definitions == null) { + synchronized (definitionsHolder) { + definitions = definitionsHolder.get(); + if (definitions == null) { + definitions = findAllExtensionDefinition(loader); + definitionsHolder.set(definitions); + } + } + } + return definitions.stream().map(ExtensionDefinition::getServiceClass).collect(Collectors.toList()); + } + + private List> findAllExtensionDefinition(ClassLoader loader) { + List> extensionDefinitions = new ArrayList<>(); + try { + loadFile(SERVICES_DIRECTORY, type, loader, extensionDefinitions); + loadFile(SEATA_DIRECTORY, type, loader, extensionDefinitions); + + @SuppressWarnings("rawtypes") Class compatibleService = getCompatibleService(type); + if (compatibleService != null) { + if (type.isAssignableFrom(compatibleService)) { + LOGGER.info("Load compatible class {}", compatibleService.getName()); + loadFile(SERVICES_DIRECTORY, compatibleService, loader, extensionDefinitions); + loadFile(SEATA_DIRECTORY, compatibleService, loader, extensionDefinitions); + } else { + LOGGER.info("Ignore load compatible class {}, because is not assignable from origin type {}", compatibleService.getName(), type.getName()); + } + } + + } catch (IOException e) { + throw new EnhancedServiceNotFoundException(e); + } + + //After loaded all the extensions,sort the caches by order + if (!nameToDefinitionsMap.isEmpty()) { + for (List> definitions : nameToDefinitionsMap.values()) { + definitions.sort((def1, def2) -> { + int o1 = def1.getOrder(); + int o2 = def2.getOrder(); + return Integer.compare(o1, o2); + }); + } + } + + if (!extensionDefinitions.isEmpty()) { + extensionDefinitions.sort((def1, def2) -> { + int o1 = def1.getOrder(); + int o2 = def2.getOrder(); + return Integer.compare(o1, o2); + }); + } + + return extensionDefinitions; + } + + private static Class getCompatibleService(Class originType) { + String ioSeataType = originType.getName().replace(APACHE_SEATA_PACKAGE_NAME, IO_SEATA_PACKAGE_NAME); + try { + return Class.forName(ioSeataType); + } catch (ClassNotFoundException e) { + return null; + } + } + + + private void loadFile(String dir, Class type, ClassLoader loader, List> extensions) + throws IOException { + String fileName = dir + type.getName(); + Enumeration urls; + if (loader != null) { + urls = loader.getResources(fileName); + } else { + urls = ClassLoader.getSystemResources(fileName); + } + if (urls != null) { + boolean hasServiceFile = false; + boolean hasClasses = false; + while (urls.hasMoreElements()) { + hasServiceFile = true; + java.net.URL url = urls.nextElement(); + try (BufferedReader reader = new BufferedReader(new InputStreamReader(url.openStream(), Constants.DEFAULT_CHARSET))) { + String line; + while ((line = reader.readLine()) != null) { + final int ci = line.indexOf('#'); + if (ci >= 0) { + line = line.substring(0, ci); + } + line = line.trim(); + if (line.length() > 0) { + hasClasses = true; + try { + ExtensionDefinition extensionDefinition = getUnloadedExtensionDefinition(line, loader); + if (extensionDefinition == null) { + if (LOGGER.isDebugEnabled()) { + LOGGER.debug("The same extension {} has already been loaded, skipped", line); + } + continue; + } + extensions.add(extensionDefinition); + } catch (LinkageError | ClassNotFoundException e) { + LOGGER.warn("Load [{}] class fail: {}", line, e.getMessage()); + } catch (ClassCastException e) { + LOGGER.error("Load [{}] class fail, please make sure the extension" + + " config in {} implements {}.", line, fileName, type.getName()); + } + } + } + } catch (Throwable e) { + LOGGER.warn("load class instance error:", e); + } + } + + if (LOGGER.isDebugEnabled()) { + if (!hasServiceFile) { + LOGGER.warn("Load [{}] class fail: no service files found in '{}'.", type.getName(), dir); + } else if (!hasClasses) { + LOGGER.warn("Load [{}] class fail: the service files in '{}' is all empty.", type.getName(), dir); + } + } + } else { + if (LOGGER.isDebugEnabled()) { + LOGGER.warn("Load [{}] class fail: no urls found in '{}'.", type.getName(), dir); + } + } + } + + @SuppressWarnings("unchecked") + private ExtensionDefinition getUnloadedExtensionDefinition(String className, ClassLoader loader) + throws ClassNotFoundException, ClassCastException { + //Check whether the definition has been loaded + if (!isDefinitionContainsClazz(className, loader)) { + Class clazz = Class.forName(className, true, loader); + if (!type.isAssignableFrom(clazz)) { + LOGGER.error("can't cast {} to {}", clazz.getName(), type.getName()); + throw new ClassCastException(); + } + Class enhancedServiceClass = (Class) clazz; + String serviceName = null; + int priority = 0; + Scope scope = Scope.SINGLETON; + LoadLevel loadLevel = clazz.getAnnotation(LoadLevel.class); + if (loadLevel != null) { + serviceName = loadLevel.name(); + priority = loadLevel.order(); + scope = loadLevel.scope(); + } + ExtensionDefinition result = new ExtensionDefinition<>(serviceName, priority, scope, enhancedServiceClass); + classToDefinitionMap.put(clazz, result); + if (serviceName != null) { + CollectionUtils.computeIfAbsent(nameToDefinitionsMap, serviceName, e -> new ArrayList<>()) + .add(result); + } + return result; + } + return null; + } + + private boolean isDefinitionContainsClazz(String className, ClassLoader loader) { + for (Map.Entry, ExtensionDefinition> entry : classToDefinitionMap.entrySet()) { + if (!entry.getKey().getName().equals(className)) { + continue; + } + if (Objects.equals(entry.getValue().getServiceClass().getClassLoader(), loader)) { + return true; + } + } + return false; + } + + private ExtensionDefinition getDefaultExtensionDefinition() { + List> currentDefinitions = definitionsHolder.get(); + return CollectionUtils.getLast(currentDefinitions); + } + + private ExtensionDefinition getCachedExtensionDefinition(String activateName) { + List> definitions = nameToDefinitionsMap.get(activateName); + return CollectionUtils.getLast(definitions); + } + + /** + * init instance + * + * @param implClazz the impl clazz + * @param argTypes the arg types + * @param args the args + * @return s s + * @throws IllegalAccessException the illegal access exception + * @throws InstantiationException the instantiation exception + * @throws NoSuchMethodException the no such method exception + * @throws InvocationTargetException the invocation target exception + */ + private S initInstance(Class implClazz, Class[] argTypes, Object[] args) + throws IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException { + S s = null; + if (argTypes != null && args != null) { + // Constructor with arguments + Constructor constructor = implClazz.getDeclaredConstructor(argTypes); + s = type.cast(constructor.newInstance(args)); + } else { + // default Constructor + s = type.cast(implClazz.newInstance()); + } + if (s instanceof Initialize) { + ((Initialize) s).init(); + } + return s; + } + + /** + * Helper Class for hold a value. + * + * @param + */ + private static class Holder { + private volatile T value; + + private void set(T value) { + this.value = value; + } + + private T get() { + return value; + } + } +} \ No newline at end of file diff --git a/compatible/src/test/java/io/seata/spi/SPITest.java b/compatible/src/test/java/io/seata/spi/SPITest.java index 932c1388027..8bfef1c52ad 100644 --- a/compatible/src/test/java/io/seata/spi/SPITest.java +++ b/compatible/src/test/java/io/seata/spi/SPITest.java @@ -16,23 +16,20 @@ */ package io.seata.spi; -import org.apache.seata.common.loader.EnhancedServiceLoader; import org.apache.seata.core.model.BranchType; import org.apache.seata.core.model.ResourceManager; -import org.apache.seata.rm.DefaultResourceManager; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import java.util.List; -import java.util.Optional; import java.util.stream.Collectors; public class SPITest { @Test public void testRmSPIOrder() { - EnhancedServiceLoader.unload(ResourceManager.class); - List resourceManagers = EnhancedServiceLoader.loadAll(ResourceManager.class); + InnerEnhancedServiceLoader innerEnhancedServiceLoader = new InnerEnhancedServiceLoader<>(ResourceManager.class); + List resourceManagers = innerEnhancedServiceLoader.loadAll(this.getClass().getClassLoader()); List list = resourceManagers.stream().filter(resourceManager -> resourceManager.getBranchType().equals(BranchType.SAGA)).collect(Collectors.toList()); Assertions.assertEquals(2, list.size()); //last order is io.seata From 30b1b7ef8b9b9c844b29ee43b4c32874209e8991 Mon Sep 17 00:00:00 2001 From: "yixia.wt" Date: Sat, 16 Mar 2024 19:28:28 +0800 Subject: [PATCH 09/21] fix ut --- .../io/seata/spi/ExtensionDefinition.java | 87 ----- .../seata/spi/InnerEnhancedServiceLoader.java | 347 ------------------ .../src/test/java/io/seata/spi/SPITest.java | 11 +- 3 files changed, 6 insertions(+), 439 deletions(-) delete mode 100644 compatible/src/test/java/io/seata/spi/ExtensionDefinition.java delete mode 100644 compatible/src/test/java/io/seata/spi/InnerEnhancedServiceLoader.java diff --git a/compatible/src/test/java/io/seata/spi/ExtensionDefinition.java b/compatible/src/test/java/io/seata/spi/ExtensionDefinition.java deleted file mode 100644 index 1a1b92e797e..00000000000 --- a/compatible/src/test/java/io/seata/spi/ExtensionDefinition.java +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.seata.spi; - -import org.apache.seata.common.loader.Scope; -import org.apache.seata.common.util.StringUtils; - -final class ExtensionDefinition { - - private final String name; - private final Class serviceClass; - private final Integer order; - private final Scope scope; - - public Integer getOrder() { - return this.order; - } - - public Class getServiceClass() { - return this.serviceClass; - } - - public Scope getScope() { - return this.scope; - } - - public ExtensionDefinition(String name, Integer order, Scope scope, Class clazz) { - this.name = name; - this.order = order; - this.scope = scope; - this.serviceClass = clazz; - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ((name == null) ? 0 : name.hashCode()); - result = prime * result + ((serviceClass == null) ? 0 : serviceClass.hashCode()); - result = prime * result + ((order == null) ? 0 : order.hashCode()); - result = prime * result + ((scope == null) ? 0 : scope.hashCode()); - return result; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (obj == null) { - return false; - } - if (getClass() != obj.getClass()) { - return false; - } - - ExtensionDefinition other = (ExtensionDefinition) obj; - if (!StringUtils.equals(name, other.name)) { - return false; - } - if (!serviceClass.equals(other.serviceClass)) { - return false; - } - if (!order.equals(other.order)) { - return false; - } - return scope.equals(other.scope); - } - - public String getName() { - return name; - } -} \ No newline at end of file diff --git a/compatible/src/test/java/io/seata/spi/InnerEnhancedServiceLoader.java b/compatible/src/test/java/io/seata/spi/InnerEnhancedServiceLoader.java deleted file mode 100644 index ccef7fd8b56..00000000000 --- a/compatible/src/test/java/io/seata/spi/InnerEnhancedServiceLoader.java +++ /dev/null @@ -1,347 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.seata.spi; - -import org.apache.seata.common.Constants; -import org.apache.seata.common.executor.Initialize; -import org.apache.seata.common.loader.EnhancedServiceNotFoundException; -import org.apache.seata.common.loader.LoadLevel; -import org.apache.seata.common.loader.Scope; -import org.apache.seata.common.util.CollectionUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStreamReader; -import java.lang.reflect.Constructor; -import java.lang.reflect.InvocationTargetException; -import java.net.URL; -import java.util.ArrayList; -import java.util.Enumeration; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentMap; -import java.util.stream.Collectors; - -class InnerEnhancedServiceLoader { - private static final Logger LOGGER = LoggerFactory.getLogger(InnerEnhancedServiceLoader.class); - private static final String SERVICES_DIRECTORY = "META-INF/services/"; - private static final String SEATA_DIRECTORY = "META-INF/seata/"; - - private static final String APACHE_SEATA_PACKAGE_NAME = "org.apache.seata"; - private static final String IO_SEATA_PACKAGE_NAME = "io.seata"; - - private final Class type; - private final Holder>> definitionsHolder = new Holder<>(); - private final ConcurrentMap, Holder> definitionToInstanceMap = - new ConcurrentHashMap<>(); - private final ConcurrentMap>> nameToDefinitionsMap = new ConcurrentHashMap<>(); - private final ConcurrentMap, ExtensionDefinition> classToDefinitionMap = new ConcurrentHashMap<>(); - - public InnerEnhancedServiceLoader(Class type) { - this.type = type; - } - - public List loadAll(ClassLoader loader) { - return loadAll(null, null, loader); - } - - public List loadAll(Class[] argsType, Object[] args, ClassLoader loader) { - List allInstances = new ArrayList<>(); - List> allClazzs = getAllExtensionClass(loader); - if (CollectionUtils.isEmpty(allClazzs)) { - return allInstances; - } - try { - for (Class clazz : allClazzs) { - ExtensionDefinition definition = classToDefinitionMap.get(clazz); - allInstances.add(getExtensionInstance(definition, loader, argsType, args)); - } - } catch (Throwable t) { - throw new EnhancedServiceNotFoundException(t); - } - return allInstances; - } - - private List> getAllExtensionClass(ClassLoader loader) { - return loadAllExtensionClass(loader); - } - - private S getExtensionInstance(ExtensionDefinition definition, ClassLoader loader, Class[] argTypes, - Object[] args) { - if (definition == null) { - throw new EnhancedServiceNotFoundException("not found service provider for : " + type.getName()); - } - if (Scope.SINGLETON.equals(definition.getScope())) { - Holder holder = CollectionUtils.computeIfAbsent(definitionToInstanceMap, definition, - key -> new Holder<>()); - Object instance = holder.get(); - if (instance == null) { - synchronized (holder) { - instance = holder.get(); - if (instance == null) { - instance = createNewExtension(definition, loader, argTypes, args); - holder.set(instance); - } - } - } - return (S) instance; - } else { - return createNewExtension(definition, loader, argTypes, args); - } - } - - private S createNewExtension(ExtensionDefinition definition, ClassLoader loader, Class[] argTypes, Object[] args) { - Class clazz = definition.getServiceClass(); - try { - return initInstance(clazz, argTypes, args); - } catch (Throwable t) { - throw new IllegalStateException("Extension instance(definition: " + definition + ", class: " + - type + ") could not be instantiated: " + t.getMessage(), t); - } - } - - private List> loadAllExtensionClass(ClassLoader loader) { - List> definitions = definitionsHolder.get(); - if (definitions == null) { - synchronized (definitionsHolder) { - definitions = definitionsHolder.get(); - if (definitions == null) { - definitions = findAllExtensionDefinition(loader); - definitionsHolder.set(definitions); - } - } - } - return definitions.stream().map(ExtensionDefinition::getServiceClass).collect(Collectors.toList()); - } - - private List> findAllExtensionDefinition(ClassLoader loader) { - List> extensionDefinitions = new ArrayList<>(); - try { - loadFile(SERVICES_DIRECTORY, type, loader, extensionDefinitions); - loadFile(SEATA_DIRECTORY, type, loader, extensionDefinitions); - - @SuppressWarnings("rawtypes") Class compatibleService = getCompatibleService(type); - if (compatibleService != null) { - if (type.isAssignableFrom(compatibleService)) { - LOGGER.info("Load compatible class {}", compatibleService.getName()); - loadFile(SERVICES_DIRECTORY, compatibleService, loader, extensionDefinitions); - loadFile(SEATA_DIRECTORY, compatibleService, loader, extensionDefinitions); - } else { - LOGGER.info("Ignore load compatible class {}, because is not assignable from origin type {}", compatibleService.getName(), type.getName()); - } - } - - } catch (IOException e) { - throw new EnhancedServiceNotFoundException(e); - } - - //After loaded all the extensions,sort the caches by order - if (!nameToDefinitionsMap.isEmpty()) { - for (List> definitions : nameToDefinitionsMap.values()) { - definitions.sort((def1, def2) -> { - int o1 = def1.getOrder(); - int o2 = def2.getOrder(); - return Integer.compare(o1, o2); - }); - } - } - - if (!extensionDefinitions.isEmpty()) { - extensionDefinitions.sort((def1, def2) -> { - int o1 = def1.getOrder(); - int o2 = def2.getOrder(); - return Integer.compare(o1, o2); - }); - } - - return extensionDefinitions; - } - - private static Class getCompatibleService(Class originType) { - String ioSeataType = originType.getName().replace(APACHE_SEATA_PACKAGE_NAME, IO_SEATA_PACKAGE_NAME); - try { - return Class.forName(ioSeataType); - } catch (ClassNotFoundException e) { - return null; - } - } - - - private void loadFile(String dir, Class type, ClassLoader loader, List> extensions) - throws IOException { - String fileName = dir + type.getName(); - Enumeration urls; - if (loader != null) { - urls = loader.getResources(fileName); - } else { - urls = ClassLoader.getSystemResources(fileName); - } - if (urls != null) { - boolean hasServiceFile = false; - boolean hasClasses = false; - while (urls.hasMoreElements()) { - hasServiceFile = true; - java.net.URL url = urls.nextElement(); - try (BufferedReader reader = new BufferedReader(new InputStreamReader(url.openStream(), Constants.DEFAULT_CHARSET))) { - String line; - while ((line = reader.readLine()) != null) { - final int ci = line.indexOf('#'); - if (ci >= 0) { - line = line.substring(0, ci); - } - line = line.trim(); - if (line.length() > 0) { - hasClasses = true; - try { - ExtensionDefinition extensionDefinition = getUnloadedExtensionDefinition(line, loader); - if (extensionDefinition == null) { - if (LOGGER.isDebugEnabled()) { - LOGGER.debug("The same extension {} has already been loaded, skipped", line); - } - continue; - } - extensions.add(extensionDefinition); - } catch (LinkageError | ClassNotFoundException e) { - LOGGER.warn("Load [{}] class fail: {}", line, e.getMessage()); - } catch (ClassCastException e) { - LOGGER.error("Load [{}] class fail, please make sure the extension" + - " config in {} implements {}.", line, fileName, type.getName()); - } - } - } - } catch (Throwable e) { - LOGGER.warn("load class instance error:", e); - } - } - - if (LOGGER.isDebugEnabled()) { - if (!hasServiceFile) { - LOGGER.warn("Load [{}] class fail: no service files found in '{}'.", type.getName(), dir); - } else if (!hasClasses) { - LOGGER.warn("Load [{}] class fail: the service files in '{}' is all empty.", type.getName(), dir); - } - } - } else { - if (LOGGER.isDebugEnabled()) { - LOGGER.warn("Load [{}] class fail: no urls found in '{}'.", type.getName(), dir); - } - } - } - - @SuppressWarnings("unchecked") - private ExtensionDefinition getUnloadedExtensionDefinition(String className, ClassLoader loader) - throws ClassNotFoundException, ClassCastException { - //Check whether the definition has been loaded - if (!isDefinitionContainsClazz(className, loader)) { - Class clazz = Class.forName(className, true, loader); - if (!type.isAssignableFrom(clazz)) { - LOGGER.error("can't cast {} to {}", clazz.getName(), type.getName()); - throw new ClassCastException(); - } - Class enhancedServiceClass = (Class) clazz; - String serviceName = null; - int priority = 0; - Scope scope = Scope.SINGLETON; - LoadLevel loadLevel = clazz.getAnnotation(LoadLevel.class); - if (loadLevel != null) { - serviceName = loadLevel.name(); - priority = loadLevel.order(); - scope = loadLevel.scope(); - } - ExtensionDefinition result = new ExtensionDefinition<>(serviceName, priority, scope, enhancedServiceClass); - classToDefinitionMap.put(clazz, result); - if (serviceName != null) { - CollectionUtils.computeIfAbsent(nameToDefinitionsMap, serviceName, e -> new ArrayList<>()) - .add(result); - } - return result; - } - return null; - } - - private boolean isDefinitionContainsClazz(String className, ClassLoader loader) { - for (Map.Entry, ExtensionDefinition> entry : classToDefinitionMap.entrySet()) { - if (!entry.getKey().getName().equals(className)) { - continue; - } - if (Objects.equals(entry.getValue().getServiceClass().getClassLoader(), loader)) { - return true; - } - } - return false; - } - - private ExtensionDefinition getDefaultExtensionDefinition() { - List> currentDefinitions = definitionsHolder.get(); - return CollectionUtils.getLast(currentDefinitions); - } - - private ExtensionDefinition getCachedExtensionDefinition(String activateName) { - List> definitions = nameToDefinitionsMap.get(activateName); - return CollectionUtils.getLast(definitions); - } - - /** - * init instance - * - * @param implClazz the impl clazz - * @param argTypes the arg types - * @param args the args - * @return s s - * @throws IllegalAccessException the illegal access exception - * @throws InstantiationException the instantiation exception - * @throws NoSuchMethodException the no such method exception - * @throws InvocationTargetException the invocation target exception - */ - private S initInstance(Class implClazz, Class[] argTypes, Object[] args) - throws IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException { - S s = null; - if (argTypes != null && args != null) { - // Constructor with arguments - Constructor constructor = implClazz.getDeclaredConstructor(argTypes); - s = type.cast(constructor.newInstance(args)); - } else { - // default Constructor - s = type.cast(implClazz.newInstance()); - } - if (s instanceof Initialize) { - ((Initialize) s).init(); - } - return s; - } - - /** - * Helper Class for hold a value. - * - * @param - */ - private static class Holder { - private volatile T value; - - private void set(T value) { - this.value = value; - } - - private T get() { - return value; - } - } -} \ No newline at end of file diff --git a/compatible/src/test/java/io/seata/spi/SPITest.java b/compatible/src/test/java/io/seata/spi/SPITest.java index 8bfef1c52ad..7da08bd3ec3 100644 --- a/compatible/src/test/java/io/seata/spi/SPITest.java +++ b/compatible/src/test/java/io/seata/spi/SPITest.java @@ -16,6 +16,7 @@ */ package io.seata.spi; +import org.apache.seata.common.loader.EnhancedServiceLoader; import org.apache.seata.core.model.BranchType; import org.apache.seata.core.model.ResourceManager; import org.junit.jupiter.api.Assertions; @@ -28,11 +29,11 @@ public class SPITest { @Test public void testRmSPIOrder() { - InnerEnhancedServiceLoader innerEnhancedServiceLoader = new InnerEnhancedServiceLoader<>(ResourceManager.class); - List resourceManagers = innerEnhancedServiceLoader.loadAll(this.getClass().getClassLoader()); + EnhancedServiceLoader.unload(ResourceManager.class); + List resourceManagers = EnhancedServiceLoader.loadAll(ResourceManager.class); List list = resourceManagers.stream().filter(resourceManager -> resourceManager.getBranchType().equals(BranchType.SAGA)).collect(Collectors.toList()); - Assertions.assertEquals(2, list.size()); - //last order is io.seata - Assertions.assertEquals("io.seata.saga.rm.SagaResourceManager", list.get(1).getClass().getName()); + Assertions.assertNotNull(list); + ResourceManager resourceManager = list.get(list.size() - 1); + Assertions.assertEquals("io.seata.saga.rm.SagaResourceManager", resourceManager.getClass().getName()); } } From dd29287b5ef5e20d85363ff123b1bb8e3c31d87d Mon Sep 17 00:00:00 2001 From: "yixia.wt" Date: Sat, 16 Mar 2024 19:59:01 +0800 Subject: [PATCH 10/21] adapter to loader --- .../io/seata/core/model/ResourceManager.java | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 compatible/src/main/java/io/seata/core/model/ResourceManager.java diff --git a/compatible/src/main/java/io/seata/core/model/ResourceManager.java b/compatible/src/main/java/io/seata/core/model/ResourceManager.java new file mode 100644 index 00000000000..7ae82e49cca --- /dev/null +++ b/compatible/src/main/java/io/seata/core/model/ResourceManager.java @@ -0,0 +1,26 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.seata.core.model; + + +import org.apache.seata.common.loader.EnhancedServiceLoader; + +/** + * @see EnhancedServiceLoader.InnerEnhancedServiceLoader#findAllExtensionDefinition(ClassLoader) + */ +public interface ResourceManager extends org.apache.seata.core.model.ResourceManager { +} From 2fe095769e03b510672fdd57dd8fd2c9c328db90 Mon Sep 17 00:00:00 2001 From: "yixia.wt" Date: Sat, 16 Mar 2024 20:04:56 +0800 Subject: [PATCH 11/21] fix pmd --- .../spring/boot/autoconfigure/SeataSagaAutoConfiguration.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/seata-spring-boot-starter/src/main/java/org/apache/seata/spring/boot/autoconfigure/SeataSagaAutoConfiguration.java b/seata-spring-boot-starter/src/main/java/org/apache/seata/spring/boot/autoconfigure/SeataSagaAutoConfiguration.java index 764c90ffb26..ac3bf0d1ac8 100644 --- a/seata-spring-boot-starter/src/main/java/org/apache/seata/spring/boot/autoconfigure/SeataSagaAutoConfiguration.java +++ b/seata-spring-boot-starter/src/main/java/org/apache/seata/spring/boot/autoconfigure/SeataSagaAutoConfiguration.java @@ -16,11 +16,11 @@ */ package org.apache.seata.spring.boot.autoconfigure; +import javax.sql.DataSource; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.RejectedExecutionHandler; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; -import javax.sql.DataSource; import org.apache.seata.saga.engine.StateMachineConfig; import org.apache.seata.saga.engine.StateMachineEngine; @@ -34,7 +34,6 @@ import org.springframework.boot.autoconfigure.AutoConfigureAfter; import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; -import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration; import org.springframework.boot.context.properties.ConfigurationProperties; From f7ebb2b4d655be7bd9c888abeecd74392cc0c536 Mon Sep 17 00:00:00 2001 From: "yixia.wt" Date: Sat, 16 Mar 2024 20:15:56 +0800 Subject: [PATCH 12/21] fix workflow --- .github/workflows/license-checker.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/license-checker.yaml b/.github/workflows/license-checker.yaml index 8ed9b625148..e2ebde4c190 100644 --- a/.github/workflows/license-checker.yaml +++ b/.github/workflows/license-checker.yaml @@ -3,7 +3,7 @@ name: License checker on: pull_request: paths-ignore: - - '**.json' + - '**/*.json' branches: [ 2.x, develop, master ] jobs: From 49a549e5f9d06363d64d38992eb881642a9b85aa Mon Sep 17 00:00:00 2001 From: "yixia.wt" Date: Sat, 16 Mar 2024 20:43:07 +0800 Subject: [PATCH 13/21] add license ignore --- .github/workflows/license-checker.yaml | 4 +--- .licenserc.yaml | 1 + 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/.github/workflows/license-checker.yaml b/.github/workflows/license-checker.yaml index e2ebde4c190..87b4b29f38d 100644 --- a/.github/workflows/license-checker.yaml +++ b/.github/workflows/license-checker.yaml @@ -2,9 +2,7 @@ name: License checker on: pull_request: - paths-ignore: - - '**/*.json' - branches: [ 2.x, develop, master ] + branches: [ 2.x, develop, master ] jobs: check-license: diff --git a/.licenserc.yaml b/.licenserc.yaml index a9a6a982268..a124f474bf7 100644 --- a/.licenserc.yaml +++ b/.licenserc.yaml @@ -79,6 +79,7 @@ header: - 'ext/apm-seata-skywalking-plugin/config/agent.config' - 'server/src/main/resources/lua/redislocker/redislock.lua' - 'server/src/main/resources/banner.txt' + - '**/*.json' comment: on-failure From 06ca3ff9e9abf698c3097b9b5d805d93ba3556ab Mon Sep 17 00:00:00 2001 From: "yixia.wt" Date: Sat, 16 Mar 2024 22:58:40 +0800 Subject: [PATCH 14/21] fix saga ut fail --- .../seata/core/rpc/netty/NettyRemotingServer.java | 4 ++++ .../seata/core/rpc/netty/NettyServerBootstrap.java | 4 ++++ .../apache/seata/core/rpc/netty/NettyServerConfig.java | 10 +++++++--- .../seata/saga/engine/db/AbstractServerTest.java | 6 +++++- 4 files changed, 20 insertions(+), 4 deletions(-) diff --git a/core/src/main/java/org/apache/seata/core/rpc/netty/NettyRemotingServer.java b/core/src/main/java/org/apache/seata/core/rpc/netty/NettyRemotingServer.java index 715230131ed..3e6ec63c15e 100644 --- a/core/src/main/java/org/apache/seata/core/rpc/netty/NettyRemotingServer.java +++ b/core/src/main/java/org/apache/seata/core/rpc/netty/NettyRemotingServer.java @@ -68,6 +68,10 @@ public NettyRemotingServer(ThreadPoolExecutor messageExecutor) { super(messageExecutor, new NettyServerConfig()); } + public NettyRemotingServer(ThreadPoolExecutor messageExecutor, NettyServerConfig nettyServerConfig) { + super(messageExecutor, nettyServerConfig); + } + /** * Sets transactionMessageHandler. * diff --git a/core/src/main/java/org/apache/seata/core/rpc/netty/NettyServerBootstrap.java b/core/src/main/java/org/apache/seata/core/rpc/netty/NettyServerBootstrap.java index 493992b1f44..6c9a325882f 100644 --- a/core/src/main/java/org/apache/seata/core/rpc/netty/NettyServerBootstrap.java +++ b/core/src/main/java/org/apache/seata/core/rpc/netty/NettyServerBootstrap.java @@ -77,6 +77,10 @@ public NettyServerBootstrap(NettyServerConfig nettyServerConfig) { new NamedThreadFactory(nettyServerConfig.getWorkerThreadPrefix(), nettyServerConfig.getServerWorkerThreads())); } + + if (nettyServerConfig.getServerListenPort() > 0) { + setListenPort(nettyServerConfig.getServerListenPort()); + } } /** diff --git a/core/src/main/java/org/apache/seata/core/rpc/netty/NettyServerConfig.java b/core/src/main/java/org/apache/seata/core/rpc/netty/NettyServerConfig.java index 1d945e26718..42615e220b3 100644 --- a/core/src/main/java/org/apache/seata/core/rpc/netty/NettyServerConfig.java +++ b/core/src/main/java/org/apache/seata/core/rpc/netty/NettyServerConfig.java @@ -49,7 +49,7 @@ public class NettyServerConfig extends NettyBaseConfig { ConfigurationKeys.TRANSPORT_PREFIX + "writeBufferHighWaterMark", String.valueOf(67108864))); private int writeBufferLowWaterMark = Integer.parseInt(System.getProperty( ConfigurationKeys.TRANSPORT_PREFIX + "writeBufferLowWaterMark", String.valueOf(1048576))); - private static final int DEFAULT_LISTEN_PORT = 8091; + private int serverListenPort = 0; private static final long RPC_TC_REQUEST_TIMEOUT = CONFIG.getLong(ConfigurationKeys.RPC_TC_REQUEST_TIMEOUT, DEFAULT_RPC_TC_REQUEST_TIMEOUT); private int serverChannelMaxIdleTimeSeconds = Integer.parseInt(System.getProperty( ConfigurationKeys.TRANSPORT_PREFIX + "serverChannelMaxIdleTimeSeconds", String.valueOf(30))); @@ -217,8 +217,12 @@ public void setWriteBufferLowWaterMark(int writeBufferLowWaterMark) { * * @return the listen port */ - public int getDefaultListenPort() { - return DEFAULT_LISTEN_PORT; + public int getServerListenPort() { + return serverListenPort; + } + + public void setServerListenPort(int port){ + this.serverListenPort = port; } /** diff --git a/test/src/test/java/org/apache/seata/saga/engine/db/AbstractServerTest.java b/test/src/test/java/org/apache/seata/saga/engine/db/AbstractServerTest.java index bb26a62dca6..670261d7415 100644 --- a/test/src/test/java/org/apache/seata/saga/engine/db/AbstractServerTest.java +++ b/test/src/test/java/org/apache/seata/saga/engine/db/AbstractServerTest.java @@ -25,6 +25,7 @@ import org.apache.seata.common.util.NetUtil; import org.apache.seata.core.rpc.ShutdownHook; import org.apache.seata.core.rpc.netty.NettyRemotingServer; +import org.apache.seata.core.rpc.netty.NettyServerConfig; import org.apache.seata.server.ParameterParser; import org.apache.seata.server.UUIDGenerator; import org.apache.seata.server.coordinator.DefaultCoordinator; @@ -55,7 +56,9 @@ public void run() { //initialize the metrics MetricsManager.get().init(); - nettyServer = new NettyRemotingServer(workingThreads); + NettyServerConfig nettyServerConfig = new NettyServerConfig(); + nettyServerConfig.setServerListenPort(8091); + nettyServer = new NettyRemotingServer(workingThreads, nettyServerConfig); UUIDGenerator.init(parameterParser.getServerNode()); //log store mode : file态db SessionHolder.init(); @@ -63,6 +66,7 @@ public void run() { DefaultCoordinator coordinator = DefaultCoordinator.getInstance(nettyServer); coordinator.init(); nettyServer.setHandler(coordinator); + // register ShutdownHook ShutdownHook.getInstance().addDisposable(coordinator); From ece4e8c6283b2cf590ec4ebc9e6ff69dcdf9a010 Mon Sep 17 00:00:00 2001 From: "yixia.wt" Date: Sat, 16 Mar 2024 23:06:27 +0800 Subject: [PATCH 15/21] fix pmd --- .../java/org/apache/seata/core/rpc/netty/NettyServerConfig.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/java/org/apache/seata/core/rpc/netty/NettyServerConfig.java b/core/src/main/java/org/apache/seata/core/rpc/netty/NettyServerConfig.java index 42615e220b3..9dd373ba0b2 100644 --- a/core/src/main/java/org/apache/seata/core/rpc/netty/NettyServerConfig.java +++ b/core/src/main/java/org/apache/seata/core/rpc/netty/NettyServerConfig.java @@ -221,7 +221,7 @@ public int getServerListenPort() { return serverListenPort; } - public void setServerListenPort(int port){ + public void setServerListenPort(int port) { this.serverListenPort = port; } From 21cc1485dfb0c3b377d2bb9b05d295e69e62b29a Mon Sep 17 00:00:00 2001 From: "yixia.wt" Date: Mon, 8 Apr 2024 12:27:59 +0800 Subject: [PATCH 16/21] fix review issues --- .../main/java/io/seata/saga/rm/StateMachineEngineHolder.java | 2 +- .../io/seata/spring/annotation/GlobalTransactionScanner.java | 4 ++-- .../java/org/apache/seata/saga/engine/mock/DemoService.java | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/compatible/src/main/java/io/seata/saga/rm/StateMachineEngineHolder.java b/compatible/src/main/java/io/seata/saga/rm/StateMachineEngineHolder.java index 90da2e0765c..1866e34fd54 100644 --- a/compatible/src/main/java/io/seata/saga/rm/StateMachineEngineHolder.java +++ b/compatible/src/main/java/io/seata/saga/rm/StateMachineEngineHolder.java @@ -27,7 +27,7 @@ public static StateMachineEngine getStateMachineEngine() { return stateMachineEngine; } - public static void setStateMachineEngine(StateMachineEngine smEngine) { + public static void setStateMachineEngine(StateMachineEngine smEngine) { stateMachineEngine = smEngine; } } diff --git a/compatible/src/main/java/io/seata/spring/annotation/GlobalTransactionScanner.java b/compatible/src/main/java/io/seata/spring/annotation/GlobalTransactionScanner.java index 2e9bd9e4d23..f242db672c2 100644 --- a/compatible/src/main/java/io/seata/spring/annotation/GlobalTransactionScanner.java +++ b/compatible/src/main/java/io/seata/spring/annotation/GlobalTransactionScanner.java @@ -16,10 +16,10 @@ */ package io.seata.spring.annotation; +import io.seata.common.util.StringUtils; import io.seata.rm.RMClient; import io.seata.tm.TMClient; -import org.apache.seata.common.util.StringUtils; -import org.apache.seata.tm.api.FailureHandler; +import io.seata.tm.api.FailureHandler; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/test/src/test/java/org/apache/seata/saga/engine/mock/DemoService.java b/test/src/test/java/org/apache/seata/saga/engine/mock/DemoService.java index 676106f510f..075990d0574 100644 --- a/test/src/test/java/org/apache/seata/saga/engine/mock/DemoService.java +++ b/test/src/test/java/org/apache/seata/saga/engine/mock/DemoService.java @@ -122,7 +122,7 @@ public Map randomExceptionMethod(Map input) { public static class People { private String name; - private int age; + private int age; private People[] childrenArray; private List childrenList; From af3a9c53459ce1123644cc91662308f54da6de41 Mon Sep 17 00:00:00 2001 From: "yixia.wt" Date: Mon, 8 Apr 2024 16:26:47 +0800 Subject: [PATCH 17/21] support StateMachineRepository and StateMachine compatible --- .../engine/config/DbStateMachineConfig.java | 1 + .../impl/DefaultStateMachineConfig.java | 18 +- .../engine/repo/StateMachineRepository.java | 52 +++- .../statelang/domain/RecoverStrategy.java | 58 +++++ .../io/seata/saga/statelang/domain/State.java | 64 +++++ .../saga/statelang/domain/StateMachine.java | 223 ++++++++++++++++++ .../domain/StateMachineInstance.java | 2 - .../saga/statelang/domain/impl/StateImpl.java | 74 ++++++ .../domain/impl/StateMachineImpl.java | 179 ++++++++++++++ .../domain/impl/StateMachineInstanceImpl.java | 7 +- .../grpc/interceptor/GrpcTest.java | 218 ++++++++--------- 11 files changed, 774 insertions(+), 122 deletions(-) create mode 100644 compatible/src/main/java/io/seata/saga/statelang/domain/RecoverStrategy.java create mode 100644 compatible/src/main/java/io/seata/saga/statelang/domain/State.java create mode 100644 compatible/src/main/java/io/seata/saga/statelang/domain/StateMachine.java create mode 100644 compatible/src/main/java/io/seata/saga/statelang/domain/impl/StateImpl.java create mode 100644 compatible/src/main/java/io/seata/saga/statelang/domain/impl/StateMachineImpl.java diff --git a/compatible/src/main/java/io/seata/saga/engine/config/DbStateMachineConfig.java b/compatible/src/main/java/io/seata/saga/engine/config/DbStateMachineConfig.java index 45c747085b1..0238654c568 100644 --- a/compatible/src/main/java/io/seata/saga/engine/config/DbStateMachineConfig.java +++ b/compatible/src/main/java/io/seata/saga/engine/config/DbStateMachineConfig.java @@ -17,6 +17,7 @@ package io.seata.saga.engine.config; import io.seata.saga.engine.impl.DefaultStateMachineConfig; +import io.seata.saga.engine.repo.StateLogRepository; import io.seata.saga.engine.store.impl.StateLogStoreImpl; import org.apache.seata.common.ConfigurationKeys; import org.apache.seata.config.Configuration; diff --git a/compatible/src/main/java/io/seata/saga/engine/impl/DefaultStateMachineConfig.java b/compatible/src/main/java/io/seata/saga/engine/impl/DefaultStateMachineConfig.java index b5238f024fc..d10cf56a16d 100644 --- a/compatible/src/main/java/io/seata/saga/engine/impl/DefaultStateMachineConfig.java +++ b/compatible/src/main/java/io/seata/saga/engine/impl/DefaultStateMachineConfig.java @@ -23,8 +23,10 @@ import io.seata.saga.engine.store.StateLogStore; import io.seata.saga.engine.store.impl.StateLogStoreImpl; import io.seata.saga.statelang.domain.StateInstance; +import io.seata.saga.statelang.domain.StateMachine; import io.seata.saga.statelang.domain.StateMachineInstance; import io.seata.saga.statelang.domain.impl.StateInstanceImpl; +import io.seata.saga.statelang.domain.impl.StateMachineImpl; import io.seata.saga.statelang.domain.impl.StateMachineInstanceImpl; import org.apache.seata.saga.engine.expression.ExpressionResolver; import org.apache.seata.saga.engine.invoker.ServiceInvokerManager; @@ -32,7 +34,6 @@ import org.apache.seata.saga.engine.store.StateLangStore; import org.apache.seata.saga.engine.strategy.StatusDecisionStrategy; import org.apache.seata.saga.proctrl.eventing.impl.ProcessCtrlEventPublisher; -import org.apache.seata.saga.statelang.domain.StateMachine; import org.springframework.beans.factory.InitializingBean; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; @@ -136,22 +137,27 @@ public StateMachineRepository getStateMachineRepository() { return new StateMachineRepository() { @Override public StateMachine getStateMachineById(String stateMachineId) { - return repository.getStateMachineById(stateMachineId); + org.apache.seata.saga.statelang.domain.StateMachine stateMachine = repository.getStateMachineById(stateMachineId); + return StateMachineImpl.wrap(stateMachine); } @Override public StateMachine getStateMachine(String stateMachineName, String tenantId) { - return repository.getStateMachine(stateMachineName, tenantId); + org.apache.seata.saga.statelang.domain.StateMachine stateMachine = repository.getStateMachine(stateMachineName, tenantId); + return StateMachineImpl.wrap(stateMachine); } @Override public StateMachine getStateMachine(String stateMachineName, String tenantId, String version) { - return repository.getStateMachine(stateMachineName, tenantId, version); + org.apache.seata.saga.statelang.domain.StateMachine stateMachine = repository.getStateMachine(stateMachineName, tenantId, version); + return StateMachineImpl.wrap(stateMachine); } @Override public StateMachine registryStateMachine(StateMachine stateMachine) { - return repository.registryStateMachine(stateMachine); + org.apache.seata.saga.statelang.domain.StateMachine unwrap = ((StateMachineImpl) stateMachine).unwrap(); + repository.registryStateMachine(unwrap); + return stateMachine; } @Override @@ -161,7 +167,7 @@ public void registryByResources(InputStream[] resourceAsStreamArray, String tena }; } - public void setStateMachineRepository(StateMachineRepository stateMachineRepository) { + public void setStateMachineRepository(org.apache.seata.saga.engine.repo.StateMachineRepository stateMachineRepository) { actual.setStateMachineRepository(stateMachineRepository); } diff --git a/compatible/src/main/java/io/seata/saga/engine/repo/StateMachineRepository.java b/compatible/src/main/java/io/seata/saga/engine/repo/StateMachineRepository.java index f02515c0905..7d83cc369f7 100644 --- a/compatible/src/main/java/io/seata/saga/engine/repo/StateMachineRepository.java +++ b/compatible/src/main/java/io/seata/saga/engine/repo/StateMachineRepository.java @@ -16,10 +16,58 @@ */ package io.seata.saga.engine.repo; + +import io.seata.saga.statelang.domain.StateMachine; + +import java.io.IOException; +import java.io.InputStream; + /** * StateMachineRepository - * */ -public interface StateMachineRepository extends org.apache.seata.saga.engine.repo.StateMachineRepository { +public interface StateMachineRepository { + + /** + * Gets get state machine by id. + * + * @param stateMachineId the state machine id + * @return the get state machine by id + */ + StateMachine getStateMachineById(String stateMachineId); + + /** + * Gets get state machine. + * + * @param stateMachineName the state machine name + * @param tenantId the tenant id + * @return the get state machine + */ + StateMachine getStateMachine(String stateMachineName, String tenantId); + + /** + * Gets get state machine. + * + * @param stateMachineName the state machine name + * @param tenantId the tenant id + * @param version the version + * @return the get state machine + */ + StateMachine getStateMachine(String stateMachineName, String tenantId, String version); + + /** + * Register the state machine to the repository (if the same version already exists, return the existing version) + * + * @param stateMachine stateMachine + * @return the state machine + */ + StateMachine registryStateMachine(StateMachine stateMachine); + /** + * Registry by resources. + * + * @param resourceAsStreamArray the resource as stream array + * @param tenantId the tenant id + * @throws IOException the io exception + */ + void registryByResources(InputStream[] resourceAsStreamArray, String tenantId) throws IOException; } diff --git a/compatible/src/main/java/io/seata/saga/statelang/domain/RecoverStrategy.java b/compatible/src/main/java/io/seata/saga/statelang/domain/RecoverStrategy.java new file mode 100644 index 00000000000..dfd07747371 --- /dev/null +++ b/compatible/src/main/java/io/seata/saga/statelang/domain/RecoverStrategy.java @@ -0,0 +1,58 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.seata.saga.statelang.domain; + +/** + * Recover Strategy + */ +public enum RecoverStrategy { + + /** + * Compensate + */ + Compensate, + + /** + * Forward + */ + Forward; + + public static RecoverStrategy wrap(org.apache.seata.saga.statelang.domain.RecoverStrategy target) { + if (target == null) { + return null; + } + switch (target) { + case Compensate: + return Compensate; + case Forward: + return Forward; + default: + throw new IllegalArgumentException("Cannot convert " + target.name()); + } + } + + public org.apache.seata.saga.statelang.domain.RecoverStrategy unwrap() { + switch (this) { + case Compensate: + return org.apache.seata.saga.statelang.domain.RecoverStrategy.Compensate; + case Forward: + return org.apache.seata.saga.statelang.domain.RecoverStrategy.Forward; + default: + throw new IllegalArgumentException("Cannot convert " + this.name()); + } + } +} diff --git a/compatible/src/main/java/io/seata/saga/statelang/domain/State.java b/compatible/src/main/java/io/seata/saga/statelang/domain/State.java new file mode 100644 index 00000000000..9cf71f5391f --- /dev/null +++ b/compatible/src/main/java/io/seata/saga/statelang/domain/State.java @@ -0,0 +1,64 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.seata.saga.statelang.domain; + +import java.util.Map; + +public interface State { + + /** + * name + * + * @return the state name + */ + String getName(); + + /** + * comment + * + * @return the state comment + */ + String getComment(); + + /** + * type + * + * @return the state type + */ + String getType(); + + /** + * next state name + * + * @return the next state name + */ + String getNext(); + + /** + * extension properties + * + * @return the state extensions + */ + Map getExtensions(); + + /** + * state machine instance + * + * @return the state machine + */ + StateMachine getStateMachine(); +} \ No newline at end of file diff --git a/compatible/src/main/java/io/seata/saga/statelang/domain/StateMachine.java b/compatible/src/main/java/io/seata/saga/statelang/domain/StateMachine.java new file mode 100644 index 00000000000..15f8045212f --- /dev/null +++ b/compatible/src/main/java/io/seata/saga/statelang/domain/StateMachine.java @@ -0,0 +1,223 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.seata.saga.statelang.domain; + +import java.util.Date; +import java.util.Map; + +public interface StateMachine { + + /** + * name + * + * @return the state machine name + */ + String getName(); + + /** + * comment + * + * @return the state machine comment + */ + String getComment(); + + /** + * start state name + * + * @return the start state name + */ + String getStartState(); + + void setStartState(String startState); + + /** + * version + * + * @return the state machine version + */ + String getVersion(); + + /** + * set version + * + * @param version the state machine version + */ + void setVersion(String version); + + /** + * states + * + * @return the state machine key: the state machine name,value: the state machine + */ + Map getStates(); + + /** + * get state + * + * @param name the state machine name + * @return the state machine + */ + State getState(String name); + + /** + * get id + * + * @return the state machine id + */ + String getId(); + + void setId(String id); + + /** + * get tenantId + * + * @return the tenant id + */ + String getTenantId(); + + /** + * set tenantId + * + * @param tenantId the tenant id + */ + void setTenantId(String tenantId); + + /** + * app name + * + * @return the app name + */ + String getAppName(); + + /** + * type, there is only one type: SSL(SEATA state language) + * + * @return the state type + */ + String getType(); + + /** + * statue (Active|Inactive) + * + * @return the state machine status + */ + Status getStatus(); + + /** + * recover strategy: prefer compensation or forward when error occurred + * + * @return the recover strategy + */ + RecoverStrategy getRecoverStrategy(); + + /** + * set RecoverStrategy + * + * @param recoverStrategy the recover strategy + */ + void setRecoverStrategy(RecoverStrategy recoverStrategy); + + /** + * Is it persist execution log to storage?, default true + * + * @return is persist + */ + boolean isPersist(); + + /** + * Is update last retry execution log, default append new + * + * @return the boolean + */ + Boolean isRetryPersistModeUpdate(); + + /** + * Is update last compensate execution log, default append new + * + * @return the boolean + */ + Boolean isCompensatePersistModeUpdate(); + + /** + * State language text + * + * @return the state language text + */ + String getContent(); + + void setContent(String content); + + /** + * get create time + * + * @return the create gmt + */ + Date getGmtCreate(); + + /** + * set create time + * + * @param date the create gmt + */ + void setGmtCreate(Date date); + + enum Status { + /** + * Active + */ + AC("Active"), + /** + * Inactive + */ + IN("Inactive"); + + private final String statusString; + + Status(String statusString) { + this.statusString = statusString; + } + + public String getStatusString() { + return statusString; + } + + public static Status wrap(org.apache.seata.saga.statelang.domain.StateMachine.Status target) { + if (target == null) { + return null; + } + switch (target) { + case AC: + return AC; + case IN: + return IN; + default: + throw new IllegalArgumentException("Cannot convert " + target.name()); + } + } + + public org.apache.seata.saga.statelang.domain.StateMachine.Status unwrap() { + switch (this) { + case AC: + return org.apache.seata.saga.statelang.domain.StateMachine.Status.AC; + case IN: + return org.apache.seata.saga.statelang.domain.StateMachine.Status.IN; + default: + throw new IllegalArgumentException("Cannot convert " + this.name()); + } + } + } +} \ No newline at end of file diff --git a/compatible/src/main/java/io/seata/saga/statelang/domain/StateMachineInstance.java b/compatible/src/main/java/io/seata/saga/statelang/domain/StateMachineInstance.java index 0ce42b0a999..b4b8efec3b1 100644 --- a/compatible/src/main/java/io/seata/saga/statelang/domain/StateMachineInstance.java +++ b/compatible/src/main/java/io/seata/saga/statelang/domain/StateMachineInstance.java @@ -16,8 +16,6 @@ */ package io.seata.saga.statelang.domain; -import org.apache.seata.saga.statelang.domain.StateMachine; - import java.util.Date; import java.util.List; import java.util.Map; diff --git a/compatible/src/main/java/io/seata/saga/statelang/domain/impl/StateImpl.java b/compatible/src/main/java/io/seata/saga/statelang/domain/impl/StateImpl.java new file mode 100644 index 00000000000..bdc5ec3b36e --- /dev/null +++ b/compatible/src/main/java/io/seata/saga/statelang/domain/impl/StateImpl.java @@ -0,0 +1,74 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.seata.saga.statelang.domain.impl; + +import io.seata.saga.statelang.domain.State; +import io.seata.saga.statelang.domain.StateMachine; + +import java.util.Map; + +public class StateImpl implements State { + + private final org.apache.seata.saga.statelang.domain.State actual; + + private StateImpl(org.apache.seata.saga.statelang.domain.State actual) { + this.actual = actual; + } + + + @Override + public String getName() { + return actual.getName(); + } + + @Override + public String getComment() { + return actual.getComment(); + } + + @Override + public String getType() { + return actual.getType(); + } + + @Override + public String getNext() { + return actual.getNext(); + } + + @Override + public Map getExtensions() { + return actual.getExtensions(); + } + + @Override + public StateMachine getStateMachine() { + org.apache.seata.saga.statelang.domain.StateMachine stateMachine = actual.getStateMachine(); + return StateMachineImpl.wrap(stateMachine); + } + + public static StateImpl wrap(org.apache.seata.saga.statelang.domain.State target) { + if (target == null) { + return null; + } + return new StateImpl(target); + } + + public org.apache.seata.saga.statelang.domain.State unwrap() { + return actual; + } +} diff --git a/compatible/src/main/java/io/seata/saga/statelang/domain/impl/StateMachineImpl.java b/compatible/src/main/java/io/seata/saga/statelang/domain/impl/StateMachineImpl.java new file mode 100644 index 00000000000..f3bdc2f2b10 --- /dev/null +++ b/compatible/src/main/java/io/seata/saga/statelang/domain/impl/StateMachineImpl.java @@ -0,0 +1,179 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.seata.saga.statelang.domain.impl; + +import io.seata.saga.statelang.domain.RecoverStrategy; +import io.seata.saga.statelang.domain.State; +import io.seata.saga.statelang.domain.StateMachine; + +import java.util.Date; +import java.util.LinkedHashMap; +import java.util.Map; + +public class StateMachineImpl implements StateMachine { + + private final org.apache.seata.saga.statelang.domain.StateMachine actual; + + private StateMachineImpl(org.apache.seata.saga.statelang.domain.StateMachine actual) { + this.actual = actual; + } + + @Override + public String getName() { + return actual.getName(); + } + + @Override + public String getComment() { + return actual.getComment(); + } + + @Override + public String getStartState() { + return actual.getStartState(); + } + + @Override + public void setStartState(String startState) { + actual.setStartState(startState); + } + + @Override + public String getVersion() { + return actual.getVersion(); + } + + @Override + public void setVersion(String version) { + actual.setVersion(version); + } + + @Override + public Map getStates() { + Map states = actual.getStates(); + if (states == null) { + return null; + } + + Map resultMap = new LinkedHashMap<>(); + for (Map.Entry entry : states.entrySet()) { + org.apache.seata.saga.statelang.domain.State state = entry.getValue(); + resultMap.put(entry.getKey(), StateImpl.wrap(state)); + } + return resultMap; + } + + @Override + public State getState(String name) { + org.apache.seata.saga.statelang.domain.State state = actual.getState(name); + return StateImpl.wrap(state); + } + + @Override + public String getId() { + return actual.getId(); + } + + @Override + public void setId(String id) { + actual.setId(id); + } + + @Override + public String getTenantId() { + return actual.getTenantId(); + } + + @Override + public void setTenantId(String tenantId) { + actual.setTenantId(tenantId); + } + + @Override + public String getAppName() { + return actual.getAppName(); + } + + @Override + public String getType() { + return actual.getType(); + } + + @Override + public Status getStatus() { + org.apache.seata.saga.statelang.domain.StateMachine.Status status = actual.getStatus(); + return Status.wrap(status); + } + + @Override + public RecoverStrategy getRecoverStrategy() { + org.apache.seata.saga.statelang.domain.RecoverStrategy recoverStrategy = actual.getRecoverStrategy(); + return RecoverStrategy.wrap(recoverStrategy); + } + + @Override + public void setRecoverStrategy(RecoverStrategy recoverStrategy) { + org.apache.seata.saga.statelang.domain.RecoverStrategy unwrap = recoverStrategy.unwrap(); + actual.setRecoverStrategy(unwrap); + } + + @Override + public boolean isPersist() { + return actual.isPersist(); + } + + @Override + public Boolean isRetryPersistModeUpdate() { + return actual.isRetryPersistModeUpdate(); + } + + @Override + public Boolean isCompensatePersistModeUpdate() { + return actual.isCompensatePersistModeUpdate(); + } + + @Override + public String getContent() { + return actual.getContent(); + } + + @Override + public void setContent(String content) { + actual.setContent(content); + } + + @Override + public Date getGmtCreate() { + return actual.getGmtCreate(); + } + + @Override + public void setGmtCreate(Date date) { + actual.setGmtCreate(date); + } + + public static StateMachineImpl wrap(org.apache.seata.saga.statelang.domain.StateMachine target) { + if (target == null) { + return null; + } + return new StateMachineImpl(target); + } + + public org.apache.seata.saga.statelang.domain.StateMachine unwrap() { + return actual; + } +} diff --git a/compatible/src/main/java/io/seata/saga/statelang/domain/impl/StateMachineInstanceImpl.java b/compatible/src/main/java/io/seata/saga/statelang/domain/impl/StateMachineInstanceImpl.java index 27e422a4870..57e2ad83781 100644 --- a/compatible/src/main/java/io/seata/saga/statelang/domain/impl/StateMachineInstanceImpl.java +++ b/compatible/src/main/java/io/seata/saga/statelang/domain/impl/StateMachineInstanceImpl.java @@ -18,8 +18,8 @@ import io.seata.saga.statelang.domain.ExecutionStatus; import io.seata.saga.statelang.domain.StateInstance; +import io.seata.saga.statelang.domain.StateMachine; import io.seata.saga.statelang.domain.StateMachineInstance; -import org.apache.seata.saga.statelang.domain.StateMachine; import java.util.AbstractMap; import java.util.Date; @@ -204,12 +204,13 @@ public void setContext(Map context) { @Override public StateMachine getStateMachine() { - return actual.getStateMachine(); + return StateMachineImpl.wrap(actual.getStateMachine()); } @Override public void setStateMachine(StateMachine stateMachine) { - actual.setStateMachine(stateMachine); + org.apache.seata.saga.statelang.domain.StateMachine unwrap = ((StateMachineImpl) stateMachine).unwrap(); + actual.setStateMachine(unwrap); } @Override diff --git a/integration/grpc/src/test/java/org/apache/seata/integration/grpc/interceptor/GrpcTest.java b/integration/grpc/src/test/java/org/apache/seata/integration/grpc/interceptor/GrpcTest.java index 20fdf4ee3c9..a2143f839a2 100644 --- a/integration/grpc/src/test/java/org/apache/seata/integration/grpc/interceptor/GrpcTest.java +++ b/integration/grpc/src/test/java/org/apache/seata/integration/grpc/interceptor/GrpcTest.java @@ -1,109 +1,109 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.seata.integration.grpc.interceptor; - -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.Executor; -import java.util.concurrent.LinkedBlockingQueue; -import java.util.concurrent.ThreadFactory; -import java.util.concurrent.ThreadPoolExecutor; -import java.util.concurrent.TimeUnit; - -import com.google.common.util.concurrent.ListenableFuture; -import io.grpc.ClientInterceptors; -import io.grpc.ManagedChannel; -import io.grpc.Metadata; -import io.grpc.ServerInterceptor; -import io.grpc.ServerInterceptors; -import io.grpc.inprocess.InProcessChannelBuilder; -import io.grpc.inprocess.InProcessServerBuilder; -import io.grpc.stub.StreamObserver; -import io.grpc.testing.GrpcCleanupRule; -import org.apache.seata.core.context.RootContext; -import org.apache.seata.core.model.BranchType; -import org.apache.seata.integration.grpc.interceptor.client.ClientTransactionInterceptor; -import org.apache.seata.integration.grpc.interceptor.proto.ContextRpcGrpc; -import org.apache.seata.integration.grpc.interceptor.proto.Request; -import org.apache.seata.integration.grpc.interceptor.proto.Response; -import org.apache.seata.integration.grpc.interceptor.server.ServerTransactionInterceptor; -import org.junit.Rule; -import org.junit.Test; -import org.mockito.ArgumentCaptor; -import org.mockito.ArgumentMatchers; - -import static org.junit.Assert.assertEquals; -import static org.mockito.AdditionalAnswers.delegatesTo; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; - - -public class GrpcTest { - - @Rule - public final GrpcCleanupRule grpcCleanup = new GrpcCleanupRule(); - private final ServerInterceptor mockServerInterceptor = mock(ServerInterceptor.class, delegatesTo(new ServerTransactionInterceptor())); - private final String XID="192.168.0.1:8091:10086"; - - - @Test - public void clientHeaderDeliveredToServer() throws Exception { - - String serverName = InProcessServerBuilder.generateName(); - CountDownLatch countDownLatch = new CountDownLatch(1); - String[] context = new String[]{null,null}; - - //executor - Executor executorService = new ThreadPoolExecutor(2, 2, 1, TimeUnit.HOURS, new LinkedBlockingQueue<>(), new ThreadFactory() { - @Override - public Thread newThread(Runnable r) { - return new Thread(r, "contextText-" + System.currentTimeMillis()); - } - }); - - //server - grpcCleanup.register(InProcessServerBuilder.forName(serverName).executor(executorService) - .addService(ServerInterceptors.intercept(new ContextRpcGrpc.ContextRpcImplBase() { - @Override - public void contextRpc(Request request, StreamObserver responseObserver) { - context[0] = RootContext.getXID(); - context[1] = RootContext.getBranchType().name(); - countDownLatch.countDown(); - responseObserver.onNext(Response.newBuilder().setGreet("hello! " + request.getName()).build()); - responseObserver.onCompleted(); - } - }, mockServerInterceptor)) - .build().start()); - - //client - ManagedChannel channel = grpcCleanup.register(InProcessChannelBuilder.forName(serverName).executor(executorService).build()); - ContextRpcGrpc.ContextRpcFutureStub stub = ContextRpcGrpc.newFutureStub( - ClientInterceptors.intercept(channel, new ClientTransactionInterceptor())); - RootContext.bind(XID); - RootContext.bindBranchType(BranchType.TCC); - ListenableFuture future = stub.contextRpc(Request.newBuilder().setName("seata").build()); - assertEquals("hello! seata", future.get().getGreet()); - - ArgumentCaptor metadataCaptor = ArgumentCaptor.forClass(Metadata.class); - verify(mockServerInterceptor).interceptCall(ArgumentMatchers.any(), metadataCaptor.capture(), ArgumentMatchers.any()); - assertEquals(XID, metadataCaptor.getValue().get(GrpcHeaderKey.XID_HEADER_KEY)); - assertEquals(BranchType.TCC.name(), metadataCaptor.getValue().get(GrpcHeaderKey.BRANCH_HEADER_KEY)); - - countDownLatch.await(); - assertEquals(XID, context[0]); - assertEquals(BranchType.TCC.name(), context[1]); - } -} +///* +// * Licensed to the Apache Software Foundation (ASF) under one or more +// * contributor license agreements. See the NOTICE file distributed with +// * this work for additional information regarding copyright ownership. +// * The ASF licenses this file to You under the Apache License, Version 2.0 +// * (the "License"); you may not use this file except in compliance with +// * the License. You may obtain a copy of the License at +// * +// * http://www.apache.org/licenses/LICENSE-2.0 +// * +// * Unless required by applicable law or agreed to in writing, software +// * distributed under the License is distributed on an "AS IS" BASIS, +// * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// * See the License for the specific language governing permissions and +// * limitations under the License. +// */ +//package org.apache.seata.integration.grpc.interceptor; +// +//import java.util.concurrent.CountDownLatch; +//import java.util.concurrent.Executor; +//import java.util.concurrent.LinkedBlockingQueue; +//import java.util.concurrent.ThreadFactory; +//import java.util.concurrent.ThreadPoolExecutor; +//import java.util.concurrent.TimeUnit; +// +//import com.google.common.util.concurrent.ListenableFuture; +//import io.grpc.ClientInterceptors; +//import io.grpc.ManagedChannel; +//import io.grpc.Metadata; +//import io.grpc.ServerInterceptor; +//import io.grpc.ServerInterceptors; +//import io.grpc.inprocess.InProcessChannelBuilder; +//import io.grpc.inprocess.InProcessServerBuilder; +//import io.grpc.stub.StreamObserver; +//import io.grpc.testing.GrpcCleanupRule; +//import org.apache.seata.core.context.RootContext; +//import org.apache.seata.core.model.BranchType; +//import org.apache.seata.integration.grpc.interceptor.client.ClientTransactionInterceptor; +//import org.apache.seata.integration.grpc.interceptor.proto.ContextRpcGrpc; +//import org.apache.seata.integration.grpc.interceptor.proto.Request; +//import org.apache.seata.integration.grpc.interceptor.proto.Response; +//import org.apache.seata.integration.grpc.interceptor.server.ServerTransactionInterceptor; +//import org.junit.Rule; +//import org.junit.Test; +//import org.mockito.ArgumentCaptor; +//import org.mockito.ArgumentMatchers; +// +//import static org.junit.Assert.assertEquals; +//import static org.mockito.AdditionalAnswers.delegatesTo; +//import static org.mockito.Mockito.mock; +//import static org.mockito.Mockito.verify; +// +// +//public class GrpcTest { +// +// @Rule +// public final GrpcCleanupRule grpcCleanup = new GrpcCleanupRule(); +// private final ServerInterceptor mockServerInterceptor = mock(ServerInterceptor.class, delegatesTo(new ServerTransactionInterceptor())); +// private final String XID="192.168.0.1:8091:10086"; +// +// +// @Test +// public void clientHeaderDeliveredToServer() throws Exception { +// +// String serverName = InProcessServerBuilder.generateName(); +// CountDownLatch countDownLatch = new CountDownLatch(1); +// String[] context = new String[]{null,null}; +// +// //executor +// Executor executorService = new ThreadPoolExecutor(2, 2, 1, TimeUnit.HOURS, new LinkedBlockingQueue<>(), new ThreadFactory() { +// @Override +// public Thread newThread(Runnable r) { +// return new Thread(r, "contextText-" + System.currentTimeMillis()); +// } +// }); +// +// //server +// grpcCleanup.register(InProcessServerBuilder.forName(serverName).executor(executorService) +// .addService(ServerInterceptors.intercept(new ContextRpcGrpc.ContextRpcImplBase() { +// @Override +// public void contextRpc(Request request, StreamObserver responseObserver) { +// context[0] = RootContext.getXID(); +// context[1] = RootContext.getBranchType().name(); +// countDownLatch.countDown(); +// responseObserver.onNext(Response.newBuilder().setGreet("hello! " + request.getName()).build()); +// responseObserver.onCompleted(); +// } +// }, mockServerInterceptor)) +// .build().start()); +// +// //client +// ManagedChannel channel = grpcCleanup.register(InProcessChannelBuilder.forName(serverName).executor(executorService).build()); +// ContextRpcGrpc.ContextRpcFutureStub stub = ContextRpcGrpc.newFutureStub( +// ClientInterceptors.intercept(channel, new ClientTransactionInterceptor())); +// RootContext.bind(XID); +// RootContext.bindBranchType(BranchType.TCC); +// ListenableFuture future = stub.contextRpc(Request.newBuilder().setName("seata").build()); +// assertEquals("hello! seata", future.get().getGreet()); +// +// ArgumentCaptor metadataCaptor = ArgumentCaptor.forClass(Metadata.class); +// verify(mockServerInterceptor).interceptCall(ArgumentMatchers.any(), metadataCaptor.capture(), ArgumentMatchers.any()); +// assertEquals(XID, metadataCaptor.getValue().get(GrpcHeaderKey.XID_HEADER_KEY)); +// assertEquals(BranchType.TCC.name(), metadataCaptor.getValue().get(GrpcHeaderKey.BRANCH_HEADER_KEY)); +// +// countDownLatch.await(); +// assertEquals(XID, context[0]); +// assertEquals(BranchType.TCC.name(), context[1]); +// } +//} From 05ccacd05fee33bac5da12fc892cb9e932fdd320 Mon Sep 17 00:00:00 2001 From: "yixia.wt" Date: Mon, 8 Apr 2024 16:40:56 +0800 Subject: [PATCH 18/21] remove unsed --- .../grpc/interceptor/GrpcTest.java | 218 +++++++++--------- 1 file changed, 109 insertions(+), 109 deletions(-) diff --git a/integration/grpc/src/test/java/org/apache/seata/integration/grpc/interceptor/GrpcTest.java b/integration/grpc/src/test/java/org/apache/seata/integration/grpc/interceptor/GrpcTest.java index a2143f839a2..20fdf4ee3c9 100644 --- a/integration/grpc/src/test/java/org/apache/seata/integration/grpc/interceptor/GrpcTest.java +++ b/integration/grpc/src/test/java/org/apache/seata/integration/grpc/interceptor/GrpcTest.java @@ -1,109 +1,109 @@ -///* -// * Licensed to the Apache Software Foundation (ASF) under one or more -// * contributor license agreements. See the NOTICE file distributed with -// * this work for additional information regarding copyright ownership. -// * The ASF licenses this file to You under the Apache License, Version 2.0 -// * (the "License"); you may not use this file except in compliance with -// * the License. You may obtain a copy of the License at -// * -// * http://www.apache.org/licenses/LICENSE-2.0 -// * -// * Unless required by applicable law or agreed to in writing, software -// * distributed under the License is distributed on an "AS IS" BASIS, -// * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// * See the License for the specific language governing permissions and -// * limitations under the License. -// */ -//package org.apache.seata.integration.grpc.interceptor; -// -//import java.util.concurrent.CountDownLatch; -//import java.util.concurrent.Executor; -//import java.util.concurrent.LinkedBlockingQueue; -//import java.util.concurrent.ThreadFactory; -//import java.util.concurrent.ThreadPoolExecutor; -//import java.util.concurrent.TimeUnit; -// -//import com.google.common.util.concurrent.ListenableFuture; -//import io.grpc.ClientInterceptors; -//import io.grpc.ManagedChannel; -//import io.grpc.Metadata; -//import io.grpc.ServerInterceptor; -//import io.grpc.ServerInterceptors; -//import io.grpc.inprocess.InProcessChannelBuilder; -//import io.grpc.inprocess.InProcessServerBuilder; -//import io.grpc.stub.StreamObserver; -//import io.grpc.testing.GrpcCleanupRule; -//import org.apache.seata.core.context.RootContext; -//import org.apache.seata.core.model.BranchType; -//import org.apache.seata.integration.grpc.interceptor.client.ClientTransactionInterceptor; -//import org.apache.seata.integration.grpc.interceptor.proto.ContextRpcGrpc; -//import org.apache.seata.integration.grpc.interceptor.proto.Request; -//import org.apache.seata.integration.grpc.interceptor.proto.Response; -//import org.apache.seata.integration.grpc.interceptor.server.ServerTransactionInterceptor; -//import org.junit.Rule; -//import org.junit.Test; -//import org.mockito.ArgumentCaptor; -//import org.mockito.ArgumentMatchers; -// -//import static org.junit.Assert.assertEquals; -//import static org.mockito.AdditionalAnswers.delegatesTo; -//import static org.mockito.Mockito.mock; -//import static org.mockito.Mockito.verify; -// -// -//public class GrpcTest { -// -// @Rule -// public final GrpcCleanupRule grpcCleanup = new GrpcCleanupRule(); -// private final ServerInterceptor mockServerInterceptor = mock(ServerInterceptor.class, delegatesTo(new ServerTransactionInterceptor())); -// private final String XID="192.168.0.1:8091:10086"; -// -// -// @Test -// public void clientHeaderDeliveredToServer() throws Exception { -// -// String serverName = InProcessServerBuilder.generateName(); -// CountDownLatch countDownLatch = new CountDownLatch(1); -// String[] context = new String[]{null,null}; -// -// //executor -// Executor executorService = new ThreadPoolExecutor(2, 2, 1, TimeUnit.HOURS, new LinkedBlockingQueue<>(), new ThreadFactory() { -// @Override -// public Thread newThread(Runnable r) { -// return new Thread(r, "contextText-" + System.currentTimeMillis()); -// } -// }); -// -// //server -// grpcCleanup.register(InProcessServerBuilder.forName(serverName).executor(executorService) -// .addService(ServerInterceptors.intercept(new ContextRpcGrpc.ContextRpcImplBase() { -// @Override -// public void contextRpc(Request request, StreamObserver responseObserver) { -// context[0] = RootContext.getXID(); -// context[1] = RootContext.getBranchType().name(); -// countDownLatch.countDown(); -// responseObserver.onNext(Response.newBuilder().setGreet("hello! " + request.getName()).build()); -// responseObserver.onCompleted(); -// } -// }, mockServerInterceptor)) -// .build().start()); -// -// //client -// ManagedChannel channel = grpcCleanup.register(InProcessChannelBuilder.forName(serverName).executor(executorService).build()); -// ContextRpcGrpc.ContextRpcFutureStub stub = ContextRpcGrpc.newFutureStub( -// ClientInterceptors.intercept(channel, new ClientTransactionInterceptor())); -// RootContext.bind(XID); -// RootContext.bindBranchType(BranchType.TCC); -// ListenableFuture future = stub.contextRpc(Request.newBuilder().setName("seata").build()); -// assertEquals("hello! seata", future.get().getGreet()); -// -// ArgumentCaptor metadataCaptor = ArgumentCaptor.forClass(Metadata.class); -// verify(mockServerInterceptor).interceptCall(ArgumentMatchers.any(), metadataCaptor.capture(), ArgumentMatchers.any()); -// assertEquals(XID, metadataCaptor.getValue().get(GrpcHeaderKey.XID_HEADER_KEY)); -// assertEquals(BranchType.TCC.name(), metadataCaptor.getValue().get(GrpcHeaderKey.BRANCH_HEADER_KEY)); -// -// countDownLatch.await(); -// assertEquals(XID, context[0]); -// assertEquals(BranchType.TCC.name(), context[1]); -// } -//} +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.seata.integration.grpc.interceptor; + +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.Executor; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.ThreadFactory; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.TimeUnit; + +import com.google.common.util.concurrent.ListenableFuture; +import io.grpc.ClientInterceptors; +import io.grpc.ManagedChannel; +import io.grpc.Metadata; +import io.grpc.ServerInterceptor; +import io.grpc.ServerInterceptors; +import io.grpc.inprocess.InProcessChannelBuilder; +import io.grpc.inprocess.InProcessServerBuilder; +import io.grpc.stub.StreamObserver; +import io.grpc.testing.GrpcCleanupRule; +import org.apache.seata.core.context.RootContext; +import org.apache.seata.core.model.BranchType; +import org.apache.seata.integration.grpc.interceptor.client.ClientTransactionInterceptor; +import org.apache.seata.integration.grpc.interceptor.proto.ContextRpcGrpc; +import org.apache.seata.integration.grpc.interceptor.proto.Request; +import org.apache.seata.integration.grpc.interceptor.proto.Response; +import org.apache.seata.integration.grpc.interceptor.server.ServerTransactionInterceptor; +import org.junit.Rule; +import org.junit.Test; +import org.mockito.ArgumentCaptor; +import org.mockito.ArgumentMatchers; + +import static org.junit.Assert.assertEquals; +import static org.mockito.AdditionalAnswers.delegatesTo; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; + + +public class GrpcTest { + + @Rule + public final GrpcCleanupRule grpcCleanup = new GrpcCleanupRule(); + private final ServerInterceptor mockServerInterceptor = mock(ServerInterceptor.class, delegatesTo(new ServerTransactionInterceptor())); + private final String XID="192.168.0.1:8091:10086"; + + + @Test + public void clientHeaderDeliveredToServer() throws Exception { + + String serverName = InProcessServerBuilder.generateName(); + CountDownLatch countDownLatch = new CountDownLatch(1); + String[] context = new String[]{null,null}; + + //executor + Executor executorService = new ThreadPoolExecutor(2, 2, 1, TimeUnit.HOURS, new LinkedBlockingQueue<>(), new ThreadFactory() { + @Override + public Thread newThread(Runnable r) { + return new Thread(r, "contextText-" + System.currentTimeMillis()); + } + }); + + //server + grpcCleanup.register(InProcessServerBuilder.forName(serverName).executor(executorService) + .addService(ServerInterceptors.intercept(new ContextRpcGrpc.ContextRpcImplBase() { + @Override + public void contextRpc(Request request, StreamObserver responseObserver) { + context[0] = RootContext.getXID(); + context[1] = RootContext.getBranchType().name(); + countDownLatch.countDown(); + responseObserver.onNext(Response.newBuilder().setGreet("hello! " + request.getName()).build()); + responseObserver.onCompleted(); + } + }, mockServerInterceptor)) + .build().start()); + + //client + ManagedChannel channel = grpcCleanup.register(InProcessChannelBuilder.forName(serverName).executor(executorService).build()); + ContextRpcGrpc.ContextRpcFutureStub stub = ContextRpcGrpc.newFutureStub( + ClientInterceptors.intercept(channel, new ClientTransactionInterceptor())); + RootContext.bind(XID); + RootContext.bindBranchType(BranchType.TCC); + ListenableFuture future = stub.contextRpc(Request.newBuilder().setName("seata").build()); + assertEquals("hello! seata", future.get().getGreet()); + + ArgumentCaptor metadataCaptor = ArgumentCaptor.forClass(Metadata.class); + verify(mockServerInterceptor).interceptCall(ArgumentMatchers.any(), metadataCaptor.capture(), ArgumentMatchers.any()); + assertEquals(XID, metadataCaptor.getValue().get(GrpcHeaderKey.XID_HEADER_KEY)); + assertEquals(BranchType.TCC.name(), metadataCaptor.getValue().get(GrpcHeaderKey.BRANCH_HEADER_KEY)); + + countDownLatch.await(); + assertEquals(XID, context[0]); + assertEquals(BranchType.TCC.name(), context[1]); + } +} From 6a4ce7a1e583cb385d7f3cb3178fd93bb439058a Mon Sep 17 00:00:00 2001 From: "yixia.wt" Date: Mon, 8 Apr 2024 16:43:33 +0800 Subject: [PATCH 19/21] fix pmd --- .../java/org/apache/seata/saga/rm/StateMachineEngineHolder.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/saga/seata-saga-rm/src/main/java/org/apache/seata/saga/rm/StateMachineEngineHolder.java b/saga/seata-saga-rm/src/main/java/org/apache/seata/saga/rm/StateMachineEngineHolder.java index 17e44e962af..9f7a7f87943 100644 --- a/saga/seata-saga-rm/src/main/java/org/apache/seata/saga/rm/StateMachineEngineHolder.java +++ b/saga/seata-saga-rm/src/main/java/org/apache/seata/saga/rm/StateMachineEngineHolder.java @@ -29,7 +29,7 @@ public static StateMachineEngine getStateMachineEngine() { return stateMachineEngine; } - public static void setStateMachineEngine(StateMachineEngine smEngine) { + public static void setStateMachineEngine(StateMachineEngine smEngine) { stateMachineEngine = smEngine; } } From 1907c1ce8e28aba3715b0d756f996a8cd1fe597a Mon Sep 17 00:00:00 2001 From: "yixia.wt" Date: Mon, 8 Apr 2024 16:44:28 +0800 Subject: [PATCH 20/21] fix pmd --- .../src/test/java/io/seata/saga/engine/mock/DemoService.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compatible/src/test/java/io/seata/saga/engine/mock/DemoService.java b/compatible/src/test/java/io/seata/saga/engine/mock/DemoService.java index 481d1935042..1f3a618dd49 100644 --- a/compatible/src/test/java/io/seata/saga/engine/mock/DemoService.java +++ b/compatible/src/test/java/io/seata/saga/engine/mock/DemoService.java @@ -122,7 +122,7 @@ public Map randomExceptionMethod(Map input) { public static class People { private String name; - private int age; + private int age; private People[] childrenArray; private List childrenList; From e298bae56fdd42005362ea65b1e70b3f07acd397 Mon Sep 17 00:00:00 2001 From: funkye <364176773@qq.com> Date: Mon, 8 Apr 2024 23:17:33 +0800 Subject: [PATCH 21/21] Update compatible/src/test/java/io/seata/common/LockAndCallback.java --- compatible/src/test/java/io/seata/common/LockAndCallback.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/compatible/src/test/java/io/seata/common/LockAndCallback.java b/compatible/src/test/java/io/seata/common/LockAndCallback.java index 8750e328a31..a679fe25bfc 100644 --- a/compatible/src/test/java/io/seata/common/LockAndCallback.java +++ b/compatible/src/test/java/io/seata/common/LockAndCallback.java @@ -27,8 +27,6 @@ import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; -/** - */ public class LockAndCallback { private final Lock lock; private final Condition notFinished;