chore(project): 添加项目配置文件和忽略规则

- 添加 Babel 配置文件支持 ES6+ 语法转换
- 添加 ESLint 忽略规则和配置文件
- 添加 Git 忽略规则文件
- 添加 Travis CI 配置文件
- 添加 1.4.2 版本变更日志文件
- 添加 Helm 图表辅助模板文件
- 添加 Helm 忽略规则文件
This commit is contained in:
2026-03-27 17:36:48 +08:00
commit c2453d6434
1703 changed files with 277582 additions and 0 deletions

View File

@@ -0,0 +1,41 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ Copyright 1999-2019 Seata.io Group.
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>seata-saga</artifactId>
<groupId>io.seata</groupId>
<version>${revision}</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<name>seata-saga-statelang ${project.version}</name>
<artifactId>seata-saga-statelang</artifactId>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,50 @@
/*
* Copyright 1999-2019 Seata.io Group.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.seata.saga.statelang.domain;
import java.util.List;
/**
* Choice State, We can choose only one choice
*
* @author lorne.cl
*/
public interface ChoiceState extends State {
/**
* get choices
*
* @return
*/
List<Choice> getChoices();
/**
* default choice
*
* @return
*/
String getDefault();
/**
* Choice
*/
static interface Choice {
String getExpression();
String getNext();
}
}

View File

@@ -0,0 +1,25 @@
/*
* Copyright 1999-2019 Seata.io Group.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.seata.saga.statelang.domain;
/**
* Compensate SubStateMachine State
*
* @author lorne.cl
*/
public interface CompensateSubStateMachineState extends ServiceTaskState {
}

View File

@@ -0,0 +1,25 @@
/*
* Copyright 1999-2019 Seata.io Group.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.seata.saga.statelang.domain;
/**
* Compensation trigger State
*
* @author lorne.cl
*/
public interface CompensationTriggerState extends State {
}

View File

@@ -0,0 +1,99 @@
/*
* Copyright 1999-2019 Seata.io Group.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.seata.saga.statelang.domain;
/**
* State Language Domain Constants
*
* @author lorne.cl
*/
public interface DomainConstants {
//region State Types
String STATE_TYPE_SERVICE_TASK = "ServiceTask";
String STATE_TYPE_CHOICE = "Choice";
String STATE_TYPE_FAIL = "Fail";
String STATE_TYPE_SUCCEED = "Succeed";
String STATE_TYPE_COMPENSATION_TRIGGER = "CompensationTrigger";
String STATE_TYPE_SUB_STATE_MACHINE = "SubStateMachine";
String STATE_TYPE_SUB_MACHINE_COMPENSATION = "CompensateSubMachine";
String STATE_TYPE_SCRIPT_TASK = "ScriptTask";
String STATE_TYPE_LOOP_START = "LoopStart";
//endregion
String COMPENSATE_SUB_MACHINE_STATE_NAME_PREFIX = "_compensate_sub_machine_state_";
//region Service Types
String SERVICE_TYPE_SPRING_BEAN = "SpringBean";
//endregion
//region System Variables
String VAR_NAME_STATEMACHINE_CONTEXT = "context";
String VAR_NAME_INPUT_PARAMS = "inputParams";
String VAR_NAME_OUTPUT_PARAMS = "outputParams";
String VAR_NAME_CURRENT_EXCEPTION = "currentException";//exception of current state
String VAR_NAME_BUSINESSKEY = "_business_key_";
String VAR_NAME_SUB_MACHINE_PARENT_ID = "_sub_machine_parent_id_";
String VAR_NAME_CURRENT_CHOICE = "_current_choice_";
String VAR_NAME_STATEMACHINE_ERROR_CODE = "_statemachine_error_code_";
String VAR_NAME_STATEMACHINE_ERROR_MSG = "_statemachine_error_message_";
String VAR_NAME_CURRENT_EXCEPTION_ROUTE = "_current_exception_route_";
String VAR_NAME_STATEMACHINE = "_current_statemachine_";
String VAR_NAME_STATEMACHINE_INST = "_current_statemachine_instance_";
String VAR_NAME_STATEMACHINE_ENGINE = "_current_statemachine_engine_";
String VAR_NAME_STATE_INST = "_current_state_instance_";
String VAR_NAME_STATEMACHINE_CONFIG = "_statemachine_config_";
String VAR_NAME_FAIL_END_STATE_FLAG = "_fail_end_state_flag_";
String VAR_NAME_CURRENT_COMPENSATION_HOLDER = "_current_compensation_holder_";
String VAR_NAME_RETRIED_STATE_INST_ID = "_retried_state_instance_id";
String VAR_NAME_OPERATION_NAME = "_operation_name_";
String VAR_NAME_ASYNC_CALLBACK = "_async_callback_";
String VAR_NAME_CURRENT_COMPEN_TRIGGER_STATE = "_is_compensating_";
String VAR_NAME_IS_EXCEPTION_NOT_CATCH = "_is_exception_not_catch_";
String VAR_NAME_PARENT_ID = "_parent_id_";
String VAR_NAME_SUB_STATEMACHINE_EXEC_STATUE = "_sub_statemachine_execution_status_";
String VAR_NAME_IS_FOR_SUB_STATMACHINE_FORWARD = "_is_for_sub_statemachine_forward_";
String VAR_NAME_FIRST_COMPENSATION_STATE_STARTED = "_first_compensation_state_started";
String VAR_NAME_GLOBAL_TX = "_global_transaction_";
String VAR_NAME_IS_ASYNC_EXECUTION = "_is_async_execution_";
String VAR_NAME_IS_LOOP_STATE = "_is_loop_state_";
String VAR_NAME_CURRENT_LOOP_CONTEXT_HOLDER = "_current_loop_context_holder_";
//endregion
// region of loop
String LOOP_COUNTER = "loopCounter";
String LOOP_SEMAPHORE = "loopSemaphore";
String LOOP_RESULT = "loopResult";
String NUMBER_OF_INSTANCES = "nrOfInstances";
String NUMBER_OF_ACTIVE_INSTANCES = "nrOfActiveInstances";
String NUMBER_OF_COMPLETED_INSTANCES = "nrOfCompletedInstances";
// endregion
String OPERATION_NAME_START = "start";
String OPERATION_NAME_FORWARD = "forward";
String OPERATION_NAME_COMPENSATE = "compensate";
String SEQ_ENTITY_STATE_MACHINE = "STATE_MACHINE";
String SEQ_ENTITY_STATE_MACHINE_INST = "STATE_MACHINE_INST";
String SEQ_ENTITY_STATE_INST = "STATE_INST";
String EXPRESSION_TYPE_SEQUENCE = "Sequence";
String EVALUATOR_TYPE_EXCEPTION = "Exception";
String SEPERATOR_PARENT_ID = ":";
String DEFAULT_JSON_PARSER = "fastjson";
}

View File

@@ -0,0 +1,25 @@
/*
* Copyright 1999-2019 Seata.io Group.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.seata.saga.statelang.domain;
/**
* End State
*
* @author lorne.cl
*/
public interface EndState extends State {
}

View File

@@ -0,0 +1,59 @@
/*
* Copyright 1999-2019 Seata.io Group.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.seata.saga.statelang.domain;
/**
* Execution Status
*
* @author lorne.cl
*/
public enum ExecutionStatus {
/**
* Running
*/
RU("Running"),
/**
* Succeed
*/
SU("Succeed"),
/**
* Failed
*/
FA("Failed"),
/**
* Unknown
*/
UN("Unknown"),
/**
* Skipped
*/
SK("Skipped");
private String statusString;
private ExecutionStatus(String statusString) {
this.statusString = statusString;
}
public String getStatusString() {
return statusString;
}
}

View File

@@ -0,0 +1,38 @@
/*
* Copyright 1999-2019 Seata.io Group.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.seata.saga.statelang.domain;
/**
* Fail End State
*
* @author lorne.cl
*/
public interface FailEndState extends EndState {
/**
* error code
*
* @return
*/
String getErrorCode();
/**
* error message
*
* @return
*/
String getMessage();
}

View File

@@ -0,0 +1,25 @@
/*
* Copyright 1999-2019 Seata.io Group.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.seata.saga.statelang.domain;
/**
* Loop Starter
*
* @author anselleeyy
*/
public interface LoopStartState extends State {
}

View File

@@ -0,0 +1,34 @@
/*
* Copyright 1999-2019 Seata.io Group.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.seata.saga.statelang.domain;
/**
* Recover Strategy
*
* @author lorne.cl
*/
public enum RecoverStrategy {
/**
* Compensate
*/
Compensate,
/**
* Forward
*/
Forward;
}

View File

@@ -0,0 +1,38 @@
/*
* Copyright 1999-2019 Seata.io Group.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.seata.saga.statelang.domain;
/**
* ScriptTask State, execute scripts
*
* @author lorne.cl
*/
public interface ScriptTaskState extends TaskState {
/**
* get ScriptType such as groovy
*
* @return
*/
String getScriptType();
/**
* get ScriptContent
*
* @return
*/
String getScriptContent();
}

View File

@@ -0,0 +1,75 @@
/*
* Copyright 1999-2019 Seata.io Group.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.seata.saga.statelang.domain;
import java.util.List;
/**
* ServiceTask State, be used to invoke a service
*
* @author lorne.cl
*/
public interface ServiceTaskState extends TaskState {
/**
* Service type: such as SpringBean, SOFA RPC, default is StringBean
*
* @return
*/
String getServiceType();
/**
* service name
*
* @return
*/
String getServiceName();
/**
* service method
*
* @return
*/
String getServiceMethod();
/**
* service method
*
* @return
*/
List<String> getParameterTypes();
/**
* Is it necessary to persist the service execution log? default is true
*
* @return
*/
boolean isPersist();
/**
* Is update last retry execution log, default append new
*
* @return
*/
Boolean isRetryPersistModeUpdate();
/**
* Is update last compensate execution log, default append new
*
* @return
*/
Boolean isCompensatePersistModeUpdate();
}

View File

@@ -0,0 +1,68 @@
/*
* Copyright 1999-2019 Seata.io Group.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.seata.saga.statelang.domain;
import java.util.Map;
/**
* A State in StateMachine
*
* @author lorne.cl
*/
public interface State {
/**
* name
*
* @return
*/
String getName();
/**
* comment
*
* @return
*/
String getComment();
/**
* type
*
* @return
*/
String getType();
/**
* next state name
*
* @return
*/
String getNext();
/**
* extension properties
*
* @return
*/
Map<String, Object> getExtensions();
/**
* state machine instance
*
* @return
*/
StateMachine getStateMachine();
}

View File

@@ -0,0 +1,376 @@
/*
* Copyright 1999-2019 Seata.io Group.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.seata.saga.statelang.domain;
import java.util.Date;
/**
* State execution instance
*
* @author lorne.cl
*/
public interface StateInstance {
/**
* id
*
* @return
*/
String getId();
/**
* set id
*
* @param id
*/
void setId(String id);
/**
* get Machine InstanceId
*
* @return
*/
String getMachineInstanceId();
/**
* set Machine InstanceId
*
* @param machineInstanceId
*/
void setMachineInstanceId(String machineInstanceId);
/**
* get name
*
* @return
*/
String getName();
/**
* set name
*
* @param name
*/
void setName(String name);
/**
* get type
*
* @return
*/
String getType();
/**
* set type
*
* @param type
*/
void setType(String type);
/**
* get service name
*
* @return
*/
String getServiceName();
/**
* set service name
*
* @param serviceName
*/
void setServiceName(String serviceName);
/**
* get service method
*
* @return
*/
String getServiceMethod();
/**
* set service method
*
* @param serviceMethod
*/
void setServiceMethod(String serviceMethod);
/**
* get service type
*
* @return
*/
String getServiceType();
/**
* get service type
*
* @param serviceType
*/
void setServiceType(String serviceType);
/**
* get businessKey
*
* @return
*/
String getBusinessKey();
/**
* set business key
*
* @param businessKey
*/
void setBusinessKey(String businessKey);
/**
* get start time
*
* @return
*/
Date getGmtStarted();
/**
* set start time
*
* @param gmtStarted
*/
void setGmtStarted(Date gmtStarted);
/**
* get update time
*
* @return
*/
Date getGmtUpdated();
/**
* set update time
*
* @param gmtUpdated
*/
void setGmtUpdated(Date gmtUpdated);
/**
* get end time
*
* @return
*/
Date getGmtEnd();
/**
* set end time
*
* @param gmtEnd
*/
void setGmtEnd(Date gmtEnd);
/**
* Is this state task will update data?
*
* @return
*/
boolean isForUpdate();
/**
* setForUpdate
*
* @param forUpdate
*/
void setForUpdate(boolean forUpdate);
/**
* get exception
*
* @return
*/
Exception getException();
/**
* set exception
*
* @param exception
*/
void setException(Exception exception);
/**
* get input params
*
* @return
*/
Object getInputParams();
/**
* set inout params
*
* @param inputParams
*/
void setInputParams(Object inputParams);
/**
* get output params
*
* @return
*/
Object getOutputParams();
/**
* Sets set output params.
*
* @param outputParams the output params
*/
void setOutputParams(Object outputParams);
/**
* Gets get status.
*
* @return the get status
*/
ExecutionStatus getStatus();
/**
* Sets set status.
*
* @param status the status
*/
void setStatus(ExecutionStatus status);
/**
* Gets get state id compensated for.
*
* @return the get state id compensated for
*/
String getStateIdCompensatedFor();
/**
* Sets set state id compensated for.
*
* @param stateIdCompensatedFor the state id compensated for
*/
void setStateIdCompensatedFor(String stateIdCompensatedFor);
/**
* Gets get state id retried for.
*
* @return the get state id retried for
*/
String getStateIdRetriedFor();
/**
* Sets set state id retried for.
*
* @param stateIdRetriedFor the state id retried for
*/
void setStateIdRetriedFor(String stateIdRetriedFor);
/**
* Gets get compensation state.
*
* @return the get compensation state
*/
StateInstance getCompensationState();
/**
* Sets set compensation state.
*
* @param compensationState the compensation state
*/
void setCompensationState(StateInstance compensationState);
/**
* Gets get state machine instance.
*
* @return the get state machine instance
*/
StateMachineInstance getStateMachineInstance();
/**
* Sets set state machine instance.
*
* @param stateMachineInstance the state machine instance
*/
void setStateMachineInstance(StateMachineInstance stateMachineInstance);
/**
* Is ignore status boolean.
*
* @return the boolean
*/
boolean isIgnoreStatus();
/**
* Sets set ignore status.
*
* @param ignoreStatus the ignore status
*/
void setIgnoreStatus(boolean ignoreStatus);
/**
* Is for compensation boolean.
*
* @return the boolean
*/
boolean isForCompensation();
/**
* Gets get serialized input params.
*
* @return the get serialized input params
*/
Object getSerializedInputParams();
/**
* Sets set serialized input params.
*
* @param serializedInputParams the serialized input params
*/
void setSerializedInputParams(Object serializedInputParams);
/**
* Gets get serialized output params.
*
* @return the get serialized output params
*/
Object getSerializedOutputParams();
/**
* Sets set serialized output params.
*
* @param serializedOutputParams the serialized output params
*/
void setSerializedOutputParams(Object serializedOutputParams);
/**
* Gets get serialized exception.
*
* @return the get serialized exception
*/
Object getSerializedException();
/**
* Sets set serialized exception.
*
* @param serializedException the serialized exception
*/
void setSerializedException(Object serializedException);
/**
* Gets get compensation status.
*
* @return the get compensation status
*/
ExecutionStatus getCompensationStatus();
}

View File

@@ -0,0 +1,202 @@
/*
* Copyright 1999-2019 Seata.io Group.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.seata.saga.statelang.domain;
import java.util.Date;
import java.util.Map;
/**
* StateMachine
*
* @author lorne.cl
*/
public interface StateMachine {
/**
* name
*
* @return
*/
String getName();
/**
* comment
*
* @return
*/
String getComment();
/**
* start state name
*
* @return
*/
String getStartState();
void setStartState(String startState);
/**
* version
*
* @return
*/
String getVersion();
/**
* set version
*
* @param version
*/
void setVersion(String version);
/**
* states
*
* @return
*/
Map<String/** state machine name **/, State> getStates();
/**
* get state
*
* @param name
* @return
*/
State getState(String name);
/**
* get id
*
* @return
*/
String getId();
void setId(String id);
/**
* get tenantId
*
* @return
*/
String getTenantId();
/**
* set tenantId
*
* @param tenantId
*/
void setTenantId(String tenantId);
/**
* app name
*
* @return
*/
String getAppName();
/**
* type, there is only one type: SSL(SEATA state language)
*
* @return
*/
String getType();
/**
* statue (Active|Inactive)
*
* @return
*/
Status getStatus();
/**
* recover strategy: prefer compensation or forward when error occurred
*
* @return
*/
RecoverStrategy getRecoverStrategy();
/**
* set RecoverStrategy
*
* @param recoverStrategy
*/
void setRecoverStrategy(RecoverStrategy recoverStrategy);
/**
* Is it persist execution log to storage?, default true
*
* @return
*/
boolean isPersist();
/**
* Is update last retry execution log, default append new
*
* @return
*/
Boolean isRetryPersistModeUpdate();
/**
* Is update last compensate execution log, default append new
*
* @return
*/
Boolean isCompensatePersistModeUpdate();
/**
* State language text
*
* @return
*/
String getContent();
void setContent(String content);
/**
* get create time
*
* @return
*/
Date getGmtCreate();
/**
* set create time
*
* @param date
*/
void setGmtCreate(Date date);
enum Status {
/**
* Active
*/
AC("Active"),
/**
* Inactive
*/
IN("Inactive");
private String statusString;
Status(String statusString) {
this.statusString = statusString;
}
public String getStatusString() {
return statusString;
}
}
}

View File

@@ -0,0 +1,330 @@
/*
* Copyright 1999-2019 Seata.io Group.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.seata.saga.statelang.domain;
import java.util.Date;
import java.util.List;
import java.util.Map;
/**
* StateMachine execution instance
*
* @author lorne.cl
*/
public interface StateMachineInstance {
/**
* Gets get id.
*
* @return the get id
*/
String getId();
/**
* Sets set id.
*
* @param id the id
*/
void setId(String id);
/**
* Gets get machine id.
*
* @return the get machine id
*/
String getMachineId();
/**
* Sets set machine id.
*
* @param machineId the machine id
*/
void setMachineId(String machineId);
/**
* Gets get tenant id.
*
* @return the tenant id
*/
String getTenantId();
/**
* Sets set tenant id.
*
* @param tenantId the tenant id
*/
void setTenantId(String tenantId);
/**
* Gets get parent id.
*
* @return the get parent id
*/
String getParentId();
/**
* Sets set parent id.
*
* @param parentId the parent id
*/
void setParentId(String parentId);
/**
* Gets get gmt started.
*
* @return the get gmt started
*/
Date getGmtStarted();
/**
* Sets set gmt started.
*
* @param gmtStarted the gmt started
*/
void setGmtStarted(Date gmtStarted);
/**
* Gets get gmt end.
*
* @return the get gmt end
*/
Date getGmtEnd();
/**
* Sets set gmt end.
*
* @param gmtEnd the gmt end
*/
void setGmtEnd(Date gmtEnd);
/**
* Put state instance.
*
* @param stateId the state id
* @param stateInstance the state instance
*/
void putStateInstance(String stateId, StateInstance stateInstance);
/**
* Gets get status.
*
* @return the get status
*/
ExecutionStatus getStatus();
/**
* Sets set status.
*
* @param status the status
*/
void setStatus(ExecutionStatus status);
/**
* Gets get compensation status.
*
* @return the get compensation status
*/
ExecutionStatus getCompensationStatus();
/**
* Sets set compensation status.
*
* @param compensationStatus the compensation status
*/
void setCompensationStatus(ExecutionStatus compensationStatus);
/**
* Is running boolean.
*
* @return the boolean
*/
boolean isRunning();
/**
* Sets set running.
*
* @param running the running
*/
void setRunning(boolean running);
/**
* Gets get gmt updated.
*
* @return the get gmt updated
*/
Date getGmtUpdated();
/**
* Sets set gmt updated.
*
* @param gmtUpdated the gmt updated
*/
void setGmtUpdated(Date gmtUpdated);
/**
* Gets get business key.
*
* @return the get business key
*/
String getBusinessKey();
/**
* Sets set business key.
*
* @param businessKey the business key
*/
void setBusinessKey(String businessKey);
/**
* Gets get exception.
*
* @return the get exception
*/
Exception getException();
/**
* Sets set exception.
*
* @param exception the exception
*/
void setException(Exception exception);
/**
* Gets get start params.
*
* @return the get start params
*/
Map<String, Object> getStartParams();
/**
* Sets set start params.
*
* @param startParams the start params
*/
void setStartParams(Map<String, Object> startParams);
/**
* Gets get end params.
*
* @return the get end params
*/
Map<String, Object> getEndParams();
/**
* Sets set end params.
*
* @param endParams the end params
*/
void setEndParams(Map<String, Object> endParams);
/**
* Gets get context.
*
* @return
*/
Map<String, Object> getContext();
/**
* Sets set context.
*
* @param context
*/
void setContext(Map<String, Object> context);
/**
* Gets get state machine.
*
* @return the get state machine
*/
StateMachine getStateMachine();
/**
* Sets set state machine.
*
* @param stateMachine the state machine
*/
void setStateMachine(StateMachine stateMachine);
/**
* Gets get state list.
*
* @return the get state list
*/
List<StateInstance> getStateList();
/**
* Sets set state list.
*
* @param stateList the state list
*/
void setStateList(List<StateInstance> stateList);
/**
* Gets get state map.
*
* @return the get state map
*/
Map<String, StateInstance> getStateMap();
/**
* Sets set state map.
*
* @param stateMap the state map
*/
void setStateMap(Map<String, StateInstance> stateMap);
/**
* Gets get serialized start params.
*
* @return the get serialized start params
*/
Object getSerializedStartParams();
/**
* Sets set serialized start params.
*
* @param serializedStartParams the serialized start params
*/
void setSerializedStartParams(Object serializedStartParams);
/**
* Gets get serialized end params.
*
* @return the get serialized end params
*/
Object getSerializedEndParams();
/**
* Sets set serialized end params.
*
* @param serializedEndParams the serialized end params
*/
void setSerializedEndParams(Object serializedEndParams);
/**
* Gets get serialized exception.
*
* @return the get serialized exception
*/
Object getSerializedException();
/**
* Sets set serialized exception.
*
* @param serializedException the serialized exception
*/
void setSerializedException(Object serializedException);
}

View File

@@ -0,0 +1,39 @@
/*
* Copyright 1999-2019 Seata.io Group.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.seata.saga.statelang.domain;
/**
* SubStateMachine
*
* @author lorne.cl
* @see TaskState
*/
public interface SubStateMachine extends TaskState {
/**
* state machine name
*
* @return
*/
String getStateMachineName();
/**
* Get compensate state object
*
* @return
*/
TaskState getCompensateStateObject();
}

View File

@@ -0,0 +1,25 @@
/*
* Copyright 1999-2019 Seata.io Group.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.seata.saga.statelang.domain;
/**
* SucceedEndState
*
* @author lorne.cl
*/
public interface SucceedEndState extends EndState {
}

View File

@@ -0,0 +1,224 @@
/*
* Copyright 1999-2019 Seata.io Group.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.seata.saga.statelang.domain;
import java.util.List;
import java.util.Map;
/**
* A state used to execute a task
*
* @author lorne.cl
*/
public interface TaskState extends State {
/**
* get compensate state
*
* @return
*/
String getCompensateState();
/**
* Is this state is used to compensate an other state, default false
*
* @return
*/
boolean isForCompensation();
/**
* Is this state will update data? default false
*
* @return
*/
boolean isForUpdate();
/**
* retry strategy
*
* @return
*/
List<Retry> getRetry();
/**
* exception handling strategy
*
* @return
*/
List<ExceptionMatch> getCatches();
/**
* Execution state determination rule
*
* @return
*/
Map<String, String> getStatus();
/**
* loop strategy
*
* @return
*/
Loop getLoop();
/**
* retry strategy
*/
interface Retry {
/**
* exceptions
*
* @return
*/
List<String> getExceptions();
/**
* exception classes
*
* @return
*/
List<Class<? extends Exception>> getExceptionClasses();
/**
* set exception classes
* @param exceptionClasses
*/
void setExceptionClasses(List<Class<? extends Exception>> exceptionClasses);
/**
* getIntervalSeconds
*
* @return
*/
double getIntervalSeconds();
/**
* getMaxAttempts
*
* @return
*/
int getMaxAttempts();
/**
* get BackoffRate, default 1
*
* @return
*/
double getBackoffRate();
}
/**
* exception match
*/
interface ExceptionMatch {
/**
* exceptions
*
* @return
*/
List<String> getExceptions();
/**
* exception classes
*
* @return
*/
List<Class<? extends Exception>> getExceptionClasses();
/**
* set exception classes
* @param exceptionClasses
*/
void setExceptionClasses(List<Class<? extends Exception>> exceptionClasses);
/**
* next state name
*
* @return
*/
String getNext();
}
/**
* status match
*/
interface StatusMatch {
/**
* status
*
* @return
*/
ExecutionStatus getStatus();
/**
* expression
*
* @return
*/
String getExpression();
/**
* expression type, default(SpringEL)|exception
*
* @return
*/
String getExpressionType();
}
/**
* loop strategy
*/
interface Loop {
/**
* parallel size, default 1
*
* @return
*/
int getParallel();
/**
* collection object name
*
* @return
*/
String getCollection();
/**
* element variable name
*
* @return
*/
String getElementVariableName();
/**
* element variable index name, default loopCounter
*
* @return
*/
String getElementIndexName();
/**
* completion condition, default nrOfInstances == nrOfCompletedInstances
*
* @return
*/
String getCompletionCondition();
}
}

View File

@@ -0,0 +1,312 @@
/*
* Copyright 1999-2019 Seata.io Group.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.seata.saga.statelang.domain.impl;
import java.util.List;
import java.util.Map;
import io.seata.common.util.StringUtils;
import io.seata.saga.statelang.domain.TaskState;
/**
* The state of the execution task (abstract class), the specific task to be executed is determined by the subclass
*
* @author lorne.cl
*/
public abstract class AbstractTaskState extends BaseState implements TaskState {
private String compensateState;
private boolean isForCompensation;
private boolean isForUpdate;
private List<Retry> retry;
private List<ExceptionMatch> catches;
private List<Object> input;
private Map<String, Object> output;
private Map<String, String> status;//Map<String/* expression */, String /* status */>
private List<Object> inputExpressions;
private Map<String, Object> outputExpressions;
private boolean isPersist = true;
private Boolean retryPersistModeUpdate;
private Boolean compensatePersistModeUpdate;
private Loop loop;
@Override
public String getCompensateState() {
return compensateState;
}
public void setCompensateState(String compensateState) {
this.compensateState = compensateState;
if (StringUtils.isNotBlank(this.compensateState)) {
setForUpdate(true);
}
}
@Override
public boolean isForCompensation() {
return isForCompensation;
}
public void setForCompensation(boolean isForCompensation) {
this.isForCompensation = isForCompensation;
}
@Override
public boolean isForUpdate() {
return this.isForUpdate;
}
public void setForUpdate(boolean isForUpdate) {
this.isForUpdate = isForUpdate;
}
@Override
public List<Retry> getRetry() {
return retry;
}
public void setRetry(List<Retry> retry) {
this.retry = retry;
}
@Override
public List<ExceptionMatch> getCatches() {
return catches;
}
public void setCatches(List<ExceptionMatch> catches) {
this.catches = catches;
}
public List<Object> getInput() {
return input;
}
public void setInput(List<Object> input) {
this.input = input;
}
public Map<String, Object> getOutput() {
return output;
}
public void setOutput(Map<String, Object> output) {
this.output = output;
}
public boolean isPersist() {
return isPersist;
}
public void setPersist(boolean persist) {
isPersist = persist;
}
public Boolean isRetryPersistModeUpdate() {
return retryPersistModeUpdate;
}
public void setRetryPersistModeUpdate(Boolean retryPersistModeUpdate) {
this.retryPersistModeUpdate = retryPersistModeUpdate;
}
public Boolean isCompensatePersistModeUpdate() {
return compensatePersistModeUpdate;
}
public void setCompensatePersistModeUpdate(Boolean compensatePersistModeUpdate) {
this.compensatePersistModeUpdate = compensatePersistModeUpdate;
}
public List<Object> getInputExpressions() {
return inputExpressions;
}
public void setInputExpressions(List<Object> inputExpressions) {
this.inputExpressions = inputExpressions;
}
public Map<String, Object> getOutputExpressions() {
return outputExpressions;
}
public void setOutputExpressions(Map<String, Object> outputExpressions) {
this.outputExpressions = outputExpressions;
}
@Override
public Map<String, String> getStatus() {
return status;
}
public void setStatus(Map<String, String> status) {
this.status = status;
}
@Override
public Loop getLoop() {
return loop;
}
public void setLoop(Loop loop) {
this.loop = loop;
}
public static class RetryImpl implements Retry {
private List<String> exceptions;
private List<Class<? extends Exception>> exceptionClasses;
private double intervalSeconds;
private int maxAttempts;
private double backoffRate;
@Override
public List<String> getExceptions() {
return exceptions;
}
public void setExceptions(List<String> exceptions) {
this.exceptions = exceptions;
}
@Override
public List<Class<? extends Exception>> getExceptionClasses() {
return exceptionClasses;
}
@Override
public void setExceptionClasses(List<Class<? extends Exception>> exceptionClasses) {
this.exceptionClasses = exceptionClasses;
}
@Override
public double getIntervalSeconds() {
return intervalSeconds;
}
public void setIntervalSeconds(double intervalSeconds) {
this.intervalSeconds = intervalSeconds;
}
@Override
public int getMaxAttempts() {
return maxAttempts;
}
public void setMaxAttempts(int maxAttempts) {
this.maxAttempts = maxAttempts;
}
@Override
public double getBackoffRate() {
return backoffRate;
}
public void setBackoffRate(double backoffRate) {
this.backoffRate = backoffRate;
}
}
public static class ExceptionMatchImpl implements ExceptionMatch {
List<String> exceptions;
List<Class<? extends Exception>> exceptionClasses;
String next;
@Override
public List<String> getExceptions() {
return exceptions;
}
public void setExceptions(List<String> exceptions) {
this.exceptions = exceptions;
}
@Override
public List<Class<? extends Exception>> getExceptionClasses() {
return exceptionClasses;
}
@Override
public void setExceptionClasses(List<Class<? extends Exception>> exceptionClasses) {
this.exceptionClasses = exceptionClasses;
}
@Override
public String getNext() {
return next;
}
public void setNext(String next) {
this.next = next;
}
}
public static class LoopImpl implements Loop {
private int parallel;
private String collection;
private String elementVariableName;
private String elementIndexName;
private String completionCondition;
@Override
public int getParallel() {
return parallel;
}
public void setParallel(int parallel) {
this.parallel = parallel;
}
@Override
public String getCollection() {
return collection;
}
public void setCollection(String collection) {
this.collection = collection;
}
@Override
public String getElementVariableName() {
return elementVariableName;
}
public void setElementVariableName(String elementVariableName) {
this.elementVariableName = elementVariableName;
}
@Override
public String getElementIndexName() {
return elementIndexName;
}
public void setElementIndexName(String elementIndexName) {
this.elementIndexName = elementIndexName;
}
@Override
public String getCompletionCondition() {
return completionCondition;
}
public void setCompletionCondition(String completionCondition) {
this.completionCondition = completionCondition;
}
}
}

View File

@@ -0,0 +1,90 @@
/*
* Copyright 1999-2019 Seata.io Group.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.seata.saga.statelang.domain.impl;
import java.util.Map;
import io.seata.saga.statelang.domain.State;
import io.seata.saga.statelang.domain.StateMachine;
/**
* BaseState
*
* @author lorne.cl
*/
public abstract class BaseState implements State {
private transient String name;
private String type;
private String comment;
private String next;
private Map<String, Object> extensions;
private transient StateMachine stateMachine;
@Override
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String getComment() {
return comment;
}
public void setComment(String comment) {
this.comment = comment;
}
@Override
public String getNext() {
return next;
}
public void setNext(String next) {
this.next = next;
}
@Override
public Map<String, Object> getExtensions() {
return extensions;
}
public void setExtensions(Map<String, Object> extensions) {
this.extensions = extensions;
}
@Override
public StateMachine getStateMachine() {
return stateMachine;
}
public void setStateMachine(StateMachine stateMachine) {
this.stateMachine = stateMachine;
}
@Override
public String getType() {
return type;
}
protected void setType(String type) {
this.type = type;
}
}

View File

@@ -0,0 +1,91 @@
/*
* Copyright 1999-2019 Seata.io Group.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.seata.saga.statelang.domain.impl;
import java.util.List;
import java.util.Map;
import io.seata.saga.statelang.domain.ChoiceState;
import io.seata.saga.statelang.domain.DomainConstants;
/**
* Single selection status
*
* @author lorne.cl
*/
public class ChoiceStateImpl extends BaseState implements ChoiceState {
private List<Choice> choices;
private String defaultChoice;
/**
* key: Evaluator, value: Next
**/
private Map<Object, String> choiceEvaluators;
public ChoiceStateImpl() {
setType(DomainConstants.STATE_TYPE_CHOICE);
}
@Override
public List<Choice> getChoices() {
return choices;
}
public void setChoices(List<Choice> choices) {
this.choices = choices;
}
@Override
public String getDefault() {
return defaultChoice;
}
public void setDefaultChoice(String defaultChoice) {
this.defaultChoice = defaultChoice;
}
public Map<Object, String> getChoiceEvaluators() {
return choiceEvaluators;
}
public void setChoiceEvaluators(Map<Object, String> choiceEvaluators) {
this.choiceEvaluators = choiceEvaluators;
}
public static class ChoiceImpl implements ChoiceState.Choice {
private String expression;
private String next;
@Override
public String getExpression() {
return expression;
}
public void setExpression(String expression) {
this.expression = expression;
}
@Override
public String getNext() {
return next;
}
public void setNext(String next) {
this.next = next;
}
}
}

View File

@@ -0,0 +1,30 @@
/*
* Copyright 1999-2019 Seata.io Group.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.seata.saga.statelang.domain.impl;
import io.seata.saga.statelang.domain.CompensateSubStateMachineState;
import io.seata.saga.statelang.domain.DomainConstants;
/**
* Used to compensate the state of the sub state machine, inherited from ServiceTaskState
*
* @author lorne.cl
*/
public class CompensateSubStateMachineStateImpl extends ServiceTaskStateImpl implements CompensateSubStateMachineState {
public CompensateSubStateMachineStateImpl() {
setType(DomainConstants.STATE_TYPE_SUB_MACHINE_COMPENSATION);
}
}

View File

@@ -0,0 +1,31 @@
/*
* Copyright 1999-2019 Seata.io Group.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.seata.saga.statelang.domain.impl;
import io.seata.saga.statelang.domain.CompensationTriggerState;
import io.seata.saga.statelang.domain.DomainConstants;
/**
* Triggering the "compensation" process for the state machine
*
* @author lorne.cl
*/
public class CompensationTriggerStateImpl extends BaseState implements CompensationTriggerState {
public CompensationTriggerStateImpl() {
setType(DomainConstants.STATE_TYPE_COMPENSATION_TRIGGER);
}
}

View File

@@ -0,0 +1,52 @@
/*
* Copyright 1999-2019 Seata.io Group.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.seata.saga.statelang.domain.impl;
import io.seata.saga.statelang.domain.DomainConstants;
import io.seata.saga.statelang.domain.FailEndState;
/**
* FailEndState
*
* @author lorne.cl
*/
public class FailEndStateImpl extends BaseState implements FailEndState {
private String errorCode;
private String message;
public FailEndStateImpl() {
setType(DomainConstants.STATE_TYPE_FAIL);
}
@Override
public String getErrorCode() {
return errorCode;
}
public void setErrorCode(String errorCode) {
this.errorCode = errorCode;
}
@Override
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
}

View File

@@ -0,0 +1,32 @@
/*
* Copyright 1999-2019 Seata.io Group.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.seata.saga.statelang.domain.impl;
import io.seata.saga.statelang.domain.DomainConstants;
import io.seata.saga.statelang.domain.LoopStartState;
/**
* Start the "loop" execution for the state with loop attribute
*
* @author anselleeyy
*/
public class LoopStartStateImpl extends BaseState implements LoopStartState {
public LoopStartStateImpl() {
setType(DomainConstants.STATE_TYPE_LOOP_START);
}
}

View File

@@ -0,0 +1,55 @@
/*
* Copyright 1999-2019 Seata.io Group.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.seata.saga.statelang.domain.impl;
import io.seata.saga.statelang.domain.DomainConstants;
import io.seata.saga.statelang.domain.ScriptTaskState;
/**
* A state used to execute script such as groovy
*
* @author lorne.cl
*/
public class ScriptTaskStateImpl extends AbstractTaskState implements ScriptTaskState {
private static final String DEFAULT_SCRIPT_TYPE = "groovy";
private String scriptType = DEFAULT_SCRIPT_TYPE;
private String scriptContent;
public ScriptTaskStateImpl() {
setType(DomainConstants.STATE_TYPE_SCRIPT_TASK);
}
@Override
public String getScriptType() {
return this.scriptType;
}
@Override
public String getScriptContent() {
return this.scriptContent;
}
public void setScriptType(String scriptType) {
this.scriptType = scriptType;
}
public void setScriptContent(String scriptContent) {
this.scriptContent = scriptContent;
}
}

View File

@@ -0,0 +1,103 @@
/*
* Copyright 1999-2019 Seata.io Group.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.seata.saga.statelang.domain.impl;
import java.lang.reflect.Method;
import java.util.List;
import java.util.Map;
import io.seata.saga.statelang.domain.DomainConstants;
import io.seata.saga.statelang.domain.ServiceTaskState;
/**
* A state used to invoke a service
*
* @author lorne.cl
*/
public class ServiceTaskStateImpl extends AbstractTaskState implements ServiceTaskState {
private String serviceType;
private String serviceName;
private String serviceMethod;
private List<String> parameterTypes;
private Method method;
private Map<Object, String> statusEvaluators;
private boolean isAsync;
public ServiceTaskStateImpl() {
setType(DomainConstants.STATE_TYPE_SERVICE_TASK);
}
@Override
public String getServiceType() {
return serviceType;
}
public void setServiceType(String serviceType) {
this.serviceType = serviceType;
}
@Override
public String getServiceName() {
return serviceName;
}
public void setServiceName(String serviceName) {
this.serviceName = serviceName;
}
@Override
public String getServiceMethod() {
return serviceMethod;
}
public void setServiceMethod(String serviceMethod) {
this.serviceMethod = serviceMethod;
}
@Override
public List<String> getParameterTypes() {
return parameterTypes;
}
public void setParameterTypes(List<String> parameterTypes) {
this.parameterTypes = parameterTypes;
}
public Method getMethod() {
return method;
}
public void setMethod(Method method) {
this.method = method;
}
public Map<Object, String> getStatusEvaluators() {
return statusEvaluators;
}
public void setStatusEvaluators(Map<Object, String> statusEvaluators) {
this.statusEvaluators = statusEvaluators;
}
public boolean isAsync() {
return isAsync;
}
public void setAsync(boolean async) {
isAsync = async;
}
}

View File

@@ -0,0 +1,310 @@
/*
* Copyright 1999-2019 Seata.io Group.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.seata.saga.statelang.domain.impl;
import java.util.Date;
import io.seata.common.util.StringUtils;
import io.seata.saga.statelang.domain.ExecutionStatus;
import io.seata.saga.statelang.domain.StateInstance;
import io.seata.saga.statelang.domain.StateMachineInstance;
/**
* state execution instance
*
* @author lorne.cl
*/
public class StateInstanceImpl implements StateInstance {
private String id;
private String machineInstanceId;
private String name;
private String type;
private String serviceName;
private String serviceMethod;
private String serviceType;
private String businessKey;
private Date gmtStarted;
private Date gmtUpdated;
private Date gmtEnd;
private boolean isForUpdate;
private Exception exception;
private Object serializedException;
private Object inputParams;
private Object serializedInputParams;
private Object outputParams;
private Object serializedOutputParams;
private ExecutionStatus status;
private String stateIdCompensatedFor;
private String stateIdRetriedFor;
private StateInstance compensationState;
private StateMachineInstance stateMachineInstance;
private boolean ignoreStatus;
@Override
public String getId() {
return id;
}
@Override
public void setId(String id) {
this.id = id;
}
@Override
public String getMachineInstanceId() {
return machineInstanceId;
}
@Override
public void setMachineInstanceId(String machineInstanceId) {
this.machineInstanceId = machineInstanceId;
}
@Override
public String getName() {
return name;
}
@Override
public void setName(String name) {
this.name = name;
}
@Override
public String getType() {
return type;
}
@Override
public void setType(String type) {
this.type = type;
}
@Override
public String getServiceName() {
return serviceName;
}
@Override
public void setServiceName(String serviceName) {
this.serviceName = serviceName;
}
@Override
public String getServiceMethod() {
return serviceMethod;
}
@Override
public void setServiceMethod(String serviceMethod) {
this.serviceMethod = serviceMethod;
}
@Override
public String getServiceType() {
return serviceType;
}
@Override
public void setServiceType(String serviceType) {
this.serviceType = serviceType;
}
@Override
public String getBusinessKey() {
return businessKey;
}
@Override
public void setBusinessKey(String businessKey) {
this.businessKey = businessKey;
}
@Override
public Date getGmtStarted() {
return gmtStarted;
}
@Override
public void setGmtStarted(Date gmtStarted) {
this.gmtStarted = gmtStarted;
}
@Override
public Date getGmtUpdated() {
return gmtUpdated;
}
@Override
public void setGmtUpdated(Date gmtUpdated) {
this.gmtUpdated = gmtUpdated;
}
@Override
public Date getGmtEnd() {
return gmtEnd;
}
@Override
public void setGmtEnd(Date gmtEnd) {
this.gmtEnd = gmtEnd;
}
@Override
public boolean isForUpdate() {
return isForUpdate;
}
@Override
public void setForUpdate(boolean forUpdate) {
isForUpdate = forUpdate;
}
@Override
public String getStateIdCompensatedFor() {
return stateIdCompensatedFor;
}
@Override
public void setStateIdCompensatedFor(String stateIdCompensatedFor) {
this.stateIdCompensatedFor = stateIdCompensatedFor;
}
@Override
public String getStateIdRetriedFor() {
return stateIdRetriedFor;
}
@Override
public void setStateIdRetriedFor(String stateIdRetriedFor) {
this.stateIdRetriedFor = stateIdRetriedFor;
}
@Override
public Exception getException() {
return exception;
}
@Override
public void setException(Exception exception) {
this.exception = exception;
}
@Override
public Object getInputParams() {
return inputParams;
}
@Override
public void setInputParams(Object inputParams) {
this.inputParams = inputParams;
}
@Override
public Object getOutputParams() {
return outputParams;
}
@Override
public void setOutputParams(Object outputParams) {
this.outputParams = outputParams;
}
@Override
public ExecutionStatus getStatus() {
return status;
}
@Override
public void setStatus(ExecutionStatus status) {
this.status = status;
}
@Override
public StateInstance getCompensationState() {
return compensationState;
}
@Override
public void setCompensationState(StateInstance compensationState) {
this.compensationState = compensationState;
}
@Override
public StateMachineInstance getStateMachineInstance() {
return stateMachineInstance;
}
@Override
public void setStateMachineInstance(StateMachineInstance stateMachineInstance) {
this.stateMachineInstance = stateMachineInstance;
}
@Override
public boolean isIgnoreStatus() {
return ignoreStatus;
}
@Override
public void setIgnoreStatus(boolean ignoreStatus) {
this.ignoreStatus = ignoreStatus;
}
@Override
public boolean isForCompensation() {
return StringUtils.isNotBlank(this.stateIdCompensatedFor);
}
@Override
public Object getSerializedInputParams() {
return serializedInputParams;
}
@Override
public void setSerializedInputParams(Object serializedInputParams) {
this.serializedInputParams = serializedInputParams;
}
@Override
public Object getSerializedOutputParams() {
return serializedOutputParams;
}
@Override
public void setSerializedOutputParams(Object serializedOutputParams) {
this.serializedOutputParams = serializedOutputParams;
}
@Override
public Object getSerializedException() {
return serializedException;
}
@Override
public void setSerializedException(Object serializedException) {
this.serializedException = serializedException;
}
@Override
public ExecutionStatus getCompensationStatus() {
if (this.compensationState != null) {
return this.compensationState.getStatus();
} else {
return null;
}
}
}

View File

@@ -0,0 +1,212 @@
/*
* Copyright 1999-2019 Seata.io Group.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.seata.saga.statelang.domain.impl;
import java.util.Date;
import java.util.LinkedHashMap;
import java.util.Map;
import io.seata.saga.statelang.domain.RecoverStrategy;
import io.seata.saga.statelang.domain.State;
import io.seata.saga.statelang.domain.StateMachine;
/**
* state machine
*
* @author lorne.cl
*/
public class StateMachineImpl implements StateMachine {
private String id;
private String tenantId;
private String appName = "SEATA";
private String name;
private String comment;
private String version;
private String startState;
private Status status = Status.AC;
private RecoverStrategy recoverStrategy;
private boolean isPersist = true;
private Boolean retryPersistModeUpdate;
private Boolean compensatePersistModeUpdate;
private String type = "STATE_LANG";
private transient String content;
private Date gmtCreate;
private Map<String, State> states = new LinkedHashMap<>();
@Override
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String getComment() {
return comment;
}
public void setComment(String comment) {
this.comment = comment;
}
@Override
public String getStartState() {
return startState;
}
@Override
public void setStartState(String startState) {
this.startState = startState;
}
@Override
public String getVersion() {
return version;
}
@Override
public void setVersion(String version) {
this.version = version;
}
@Override
public Map<String, State> getStates() {
return states;
}
public void setStates(Map<String, State> states) {
this.states = states;
}
@Override
public State getState(String name) {
return states.get(name);
}
public void putState(String stateName, State state) {
this.states.put(stateName, state);
if (state instanceof BaseState) {
((BaseState)state).setStateMachine(this);
}
}
@Override
public String getId() {
return id;
}
@Override
public void setId(String id) {
this.id = id;
}
@Override
public String getTenantId() {
return tenantId;
}
@Override
public void setTenantId(String tenantId) {
this.tenantId = tenantId;
}
@Override
public String getAppName() {
return appName;
}
public void setAppName(String appName) {
this.appName = appName;
}
@Override
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
@Override
public Status getStatus() {
return status;
}
public void setStatus(Status status) {
this.status = status;
}
@Override
public RecoverStrategy getRecoverStrategy() {
return recoverStrategy;
}
@Override
public void setRecoverStrategy(RecoverStrategy recoverStrategy) {
this.recoverStrategy = recoverStrategy;
}
@Override
public String getContent() {
return content;
}
@Override
public void setContent(String content) {
this.content = content;
}
@Override
public boolean isPersist() {
return isPersist;
}
public void setPersist(boolean persist) {
isPersist = persist;
}
@Override
public Date getGmtCreate() {
return gmtCreate;
}
@Override
public void setGmtCreate(Date gmtCreate) {
this.gmtCreate = gmtCreate;
}
@Override
public Boolean isRetryPersistModeUpdate() {
return retryPersistModeUpdate;
}
public void setRetryPersistModeUpdate(Boolean retryPersistModeUpdate) {
this.retryPersistModeUpdate = retryPersistModeUpdate;
}
@Override
public Boolean isCompensatePersistModeUpdate() {
return compensatePersistModeUpdate;
}
public void setCompensatePersistModeUpdate(Boolean compensatePersistModeUpdate) {
this.compensatePersistModeUpdate = compensatePersistModeUpdate;
}
}

View File

@@ -0,0 +1,277 @@
/*
* Copyright 1999-2019 Seata.io Group.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.seata.saga.statelang.domain.impl;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
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;
/**
* state machine execution instance
*
* @author lorne.cl
*/
public class StateMachineInstanceImpl implements StateMachineInstance {
private String id;
private String machineId;
private String tenantId;
private String parentId;
private Date gmtStarted;
private String businessKey;
private Map<String, Object> startParams = new HashMap<>();
private Object serializedStartParams;
private Date gmtEnd;
private Exception exception;
private Object serializedException;
private Map<String, Object> endParams = new HashMap<>();
private Object serializedEndParams;
private ExecutionStatus status;
private ExecutionStatus compensationStatus;
private boolean isRunning;
private Date gmtUpdated;
private Map<String, Object> context;
private StateMachine stateMachine;
private List<StateInstance> stateList = Collections.synchronizedList(new ArrayList<>());
private Map<String, StateInstance> stateMap = new ConcurrentHashMap<>();
@Override
public String getId() {
return id;
}
@Override
public void setId(String id) {
this.id = id;
}
@Override
public String getMachineId() {
return machineId;
}
@Override
public void setMachineId(String machineId) {
this.machineId = machineId;
}
@Override
public String getTenantId() {
return tenantId;
}
@Override
public void setTenantId(String tenantId) {
this.tenantId = tenantId;
}
@Override
public String getParentId() {
return parentId;
}
@Override
public void setParentId(String parentId) {
this.parentId = parentId;
}
@Override
public Date getGmtStarted() {
return gmtStarted;
}
@Override
public void setGmtStarted(Date gmtStarted) {
this.gmtStarted = gmtStarted;
}
@Override
public Date getGmtEnd() {
return gmtEnd;
}
@Override
public void setGmtEnd(Date gmtEnd) {
this.gmtEnd = gmtEnd;
}
@Override
public void putStateInstance(String stateId, StateInstance stateInstance) {
stateInstance.setStateMachineInstance(this);
stateMap.put(stateId, stateInstance);
stateList.add(stateInstance);
}
@Override
public ExecutionStatus getStatus() {
return status;
}
@Override
public void setStatus(ExecutionStatus status) {
this.status = status;
}
@Override
public ExecutionStatus getCompensationStatus() {
return compensationStatus;
}
@Override
public void setCompensationStatus(ExecutionStatus compensationStatus) {
this.compensationStatus = compensationStatus;
}
@Override
public boolean isRunning() {
return isRunning;
}
@Override
public void setRunning(boolean running) {
isRunning = running;
}
@Override
public Date getGmtUpdated() {
return gmtUpdated;
}
@Override
public void setGmtUpdated(Date gmtUpdated) {
this.gmtUpdated = gmtUpdated;
}
@Override
public String getBusinessKey() {
return businessKey;
}
@Override
public void setBusinessKey(String businessKey) {
this.businessKey = businessKey;
}
@Override
public Exception getException() {
return exception;
}
@Override
public void setException(Exception exception) {
this.exception = exception;
}
@Override
public Map<String, Object> getStartParams() {
return startParams;
}
@Override
public void setStartParams(Map<String, Object> startParams) {
this.startParams = startParams;
}
@Override
public Map<String, Object> getEndParams() {
return endParams;
}
@Override
public void setEndParams(Map<String, Object> endParams) {
this.endParams = endParams;
}
@Override
public Map<String, Object> getContext() {
return context;
}
@Override
public void setContext(Map<String, Object> context) {
this.context = context;
}
@Override
public StateMachine getStateMachine() {
return stateMachine;
}
@Override
public void setStateMachine(StateMachine stateMachine) {
this.stateMachine = stateMachine;
}
@Override
public List<StateInstance> getStateList() {
return stateList;
}
@Override
public void setStateList(List<StateInstance> stateList) {
this.stateList = stateList;
}
@Override
public Map<String, StateInstance> getStateMap() {
return stateMap;
}
@Override
public void setStateMap(Map<String, StateInstance> stateMap) {
this.stateMap = stateMap;
}
@Override
public Object getSerializedStartParams() {
return serializedStartParams;
}
@Override
public void setSerializedStartParams(Object serializedStartParams) {
this.serializedStartParams = serializedStartParams;
}
@Override
public Object getSerializedEndParams() {
return serializedEndParams;
}
@Override
public void setSerializedEndParams(Object serializedEndParams) {
this.serializedEndParams = serializedEndParams;
}
@Override
public Object getSerializedException() {
return serializedException;
}
@Override
public void setSerializedException(Object serializedException) {
this.serializedException = serializedException;
}
}

View File

@@ -0,0 +1,54 @@
/*
* Copyright 1999-2019 Seata.io Group.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.seata.saga.statelang.domain.impl;
import io.seata.saga.statelang.domain.DomainConstants;
import io.seata.saga.statelang.domain.SubStateMachine;
import io.seata.saga.statelang.domain.TaskState;
/**
* sub state machine
*
* @author lorne.cl
*/
public class SubStateMachineImpl extends ServiceTaskStateImpl implements SubStateMachine {
private String stateMachineName;
private TaskState compensateStateObject;
public SubStateMachineImpl() {
setType(DomainConstants.STATE_TYPE_SUB_STATE_MACHINE);
}
@Override
public String getStateMachineName() {
return stateMachineName;
}
public void setStateMachineName(String stateMachineName) {
this.stateMachineName = stateMachineName;
}
@Override
public TaskState getCompensateStateObject() {
return compensateStateObject;
}
public void setCompensateStateObject(TaskState compensateStateObject) {
this.compensateStateObject = compensateStateObject;
}
}

View File

@@ -0,0 +1,31 @@
/*
* Copyright 1999-2019 Seata.io Group.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.seata.saga.statelang.domain.impl;
import io.seata.saga.statelang.domain.DomainConstants;
import io.seata.saga.statelang.domain.SucceedEndState;
/**
* SucceedEndState
*
* @author lorne.cl
*/
public class SucceedEndStateImpl extends BaseState implements SucceedEndState {
public SucceedEndStateImpl() {
setType(DomainConstants.STATE_TYPE_SUCCEED);
}
}

View File

@@ -0,0 +1,60 @@
/*
* Copyright 1999-2019 Seata.io Group.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.seata.saga.statelang.parser;
/**
*
* Json Parser
*
* @author lorne.cl
*/
public interface JsonParser {
/**
* get Name
*
* @return
*/
String getName();
/**
* Object to Json string
*
* @param o
* @param prettyPrint
* @return
*/
String toJsonString(Object o, boolean prettyPrint);
/**
* Object to Json string
* @param o
* @param ignoreAutoType
* @param prettyPrint
* @return
*/
String toJsonString(Object o, boolean ignoreAutoType, boolean prettyPrint);
/**
* parse json string to Object
*
* @param json
* @param type
* @param <T>
* @return
*/
<T> T parse(String json, Class<T> type, boolean ignoreAutoType);
}

View File

@@ -0,0 +1,46 @@
/*
* Copyright 1999-2019 Seata.io Group.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.seata.saga.statelang.parser;
import io.seata.common.loader.EnhancedServiceLoader;
import io.seata.common.util.CollectionUtils;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
/**
* JsonParserFactory
*
* @author lorne.cl
*/
public class JsonParserFactory {
private JsonParserFactory() {
}
private static final ConcurrentMap<String, JsonParser> INSTANCES = new ConcurrentHashMap<>();
/**
* Gets JsonParser by name
*
* @param name parser name
* @return the JsonParser
*/
public static JsonParser getJsonParser(String name) {
return CollectionUtils.computeIfAbsent(INSTANCES, name,
key -> EnhancedServiceLoader.load(JsonParser.class, name, Thread.currentThread().getContextClassLoader()));
}
}

View File

@@ -0,0 +1,34 @@
/*
* Copyright 1999-2019 Seata.io Group.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.seata.saga.statelang.parser;
import io.seata.saga.statelang.domain.StateMachine;
/**
* State machine parser
*
* @author lorne.cl
*/
public interface StateMachineParser {
/**
* Parse an object (such as Json) into a State Machine model
*
* @param json
* @return
*/
StateMachine parse(String json);
}

View File

@@ -0,0 +1,30 @@
/*
* Copyright 1999-2019 Seata.io Group.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.seata.saga.statelang.parser;
import io.seata.saga.statelang.parser.impl.StateMachineParserImpl;
/**
* A simple factory of State machine language parser
*
* @author lorne.cl
*/
public class StateMachineParserFactory {
public static StateMachineParser getStateMachineParser(String jsonParserName) {
return new StateMachineParserImpl(jsonParserName);
}
}

View File

@@ -0,0 +1,34 @@
/*
* Copyright 1999-2019 Seata.io Group.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.seata.saga.statelang.parser;
import io.seata.saga.statelang.domain.State;
/**
* State Parser
*
* @author lorne.cl
*/
public interface StateParser<T extends State> {
/**
* Parse an object (such as Json) into a State model
*
* @param node
* @return
*/
T parse(Object node);
}

View File

@@ -0,0 +1,55 @@
/*
* Copyright 1999-2019 Seata.io Group.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.seata.saga.statelang.parser;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import io.seata.saga.statelang.domain.DomainConstants;
import io.seata.saga.statelang.parser.impl.ChoiceStateParser;
import io.seata.saga.statelang.parser.impl.CompensateSubStateMachineStateParser;
import io.seata.saga.statelang.parser.impl.CompensationTriggerStateParser;
import io.seata.saga.statelang.parser.impl.FailEndStateParser;
import io.seata.saga.statelang.parser.impl.ScriptTaskStateParser;
import io.seata.saga.statelang.parser.impl.ServiceTaskStateParser;
import io.seata.saga.statelang.parser.impl.SubStateMachineParser;
import io.seata.saga.statelang.parser.impl.SucceedEndStateParser;
/**
* A simple factory of state parser
*
* @author lorne.cl
*/
public class StateParserFactory {
protected static Map<String, StateParser> stateParserMap = new ConcurrentHashMap<>();
static {
stateParserMap.put(DomainConstants.STATE_TYPE_SERVICE_TASK, new ServiceTaskStateParser());
stateParserMap.put(DomainConstants.STATE_TYPE_CHOICE, new ChoiceStateParser());
stateParserMap.put(DomainConstants.STATE_TYPE_COMPENSATION_TRIGGER, new CompensationTriggerStateParser());
stateParserMap.put(DomainConstants.STATE_TYPE_FAIL, new FailEndStateParser());
stateParserMap.put(DomainConstants.STATE_TYPE_SUCCEED, new SucceedEndStateParser());
stateParserMap.put(DomainConstants.STATE_TYPE_SUB_STATE_MACHINE, new SubStateMachineParser());
stateParserMap.put(DomainConstants.STATE_TYPE_SUB_MACHINE_COMPENSATION,
new CompensateSubStateMachineStateParser());
stateParserMap.put(DomainConstants.STATE_TYPE_SCRIPT_TASK, new ScriptTaskStateParser());
}
public static StateParser getStateParser(String stateType) {
return stateParserMap.get(stateType);
}
}

View File

@@ -0,0 +1,151 @@
/*
* Copyright 1999-2019 Seata.io Group.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.seata.saga.statelang.parser.impl;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import io.seata.common.util.NumberUtils;
import io.seata.saga.statelang.domain.TaskState.ExceptionMatch;
import io.seata.saga.statelang.domain.TaskState.Loop;
import io.seata.saga.statelang.domain.TaskState.Retry;
import io.seata.saga.statelang.domain.impl.AbstractTaskState;
import io.seata.saga.statelang.domain.impl.AbstractTaskState.ExceptionMatchImpl;
import io.seata.saga.statelang.domain.impl.AbstractTaskState.LoopImpl;
import io.seata.saga.statelang.domain.impl.AbstractTaskState.RetryImpl;
/**
* AbstractTaskStateParser
*
* @author lorne.cl
*/
public abstract class AbstractTaskStateParser extends BaseStatePaser {
protected void parseTaskAttributes(AbstractTaskState state, Object node) {
parseBaseAttributes(state, node);
Map<String, Object> nodeMap = (Map<String, Object>) node;
state.setCompensateState((String) nodeMap.get("CompensateState"));
state.setForCompensation(Boolean.TRUE.equals(nodeMap.get("IsForCompensation")));
state.setForUpdate(Boolean.TRUE.equals(nodeMap.get("IsForUpdate")));
Object isPersist = nodeMap.get("IsPersist");
if (Boolean.FALSE.equals(isPersist)) {
state.setPersist(false);
}
// customize if update origin or append new retryStateInstLog
Object isRetryPersistModeUpdate = nodeMap.get("IsRetryPersistModeUpdate");
if (isRetryPersistModeUpdate instanceof Boolean) {
state.setRetryPersistModeUpdate(Boolean.TRUE.equals(isRetryPersistModeUpdate));
}
// customize if update last or append new compensateStateInstLog
Object isCompensatePersistModeUpdate = nodeMap.get("IsCompensatePersistModeUpdate");
if (isCompensatePersistModeUpdate instanceof Boolean) {
state.setCompensatePersistModeUpdate(Boolean.TRUE.equals(isCompensatePersistModeUpdate));
}
List<Object> retryList = (List<Object>) nodeMap.get("Retry");
if (retryList != null) {
state.setRetry(parseRetry(retryList));
}
List<Object> catchList = (List<Object>) nodeMap.get("Catch");
if (catchList != null) {
state.setCatches(parseCatch(catchList));
}
List<Object> inputList = (List<Object>) nodeMap.get("Input");
if (inputList != null) {
state.setInput(inputList);
}
Map<String, Object> outputMap = (Map<String, Object>) nodeMap.get("Output");
if (outputMap != null) {
state.setOutput(outputMap);
}
Map<String/* expression */, String /* status */> statusMap = (Map<String, String>) nodeMap.get("Status");
if (statusMap != null) {
state.setStatus(statusMap);
}
Object loopObj = nodeMap.get("Loop");
if (loopObj != null) {
state.setLoop(parseLoop(loopObj));
}
}
protected List<Retry> parseRetry(List<Object> retryList) {
if (retryList != null) {
List<Retry> retries = new ArrayList<>(retryList.size());
for (Object retryObj : retryList) {
Map<String, Object> retryMap = (Map<String, Object>) retryObj;
RetryImpl retry = new RetryImpl();
retry.setExceptions((List<String>) retryMap.get("Exceptions"));
Object intervalSeconds = retryMap.get("IntervalSeconds");
if (intervalSeconds != null && intervalSeconds instanceof Number) {
retry.setIntervalSeconds(((Number) intervalSeconds).doubleValue());
}
retry.setMaxAttempts((Integer) retryMap.get("MaxAttempts"));
Object backoffRate = retryMap.get("BackoffRate");
if (backoffRate != null && backoffRate instanceof Number) {
retry.setBackoffRate(((Number) backoffRate).doubleValue());
}
retries.add(retry);
}
return retries;
}
return new ArrayList<>(0);
}
protected List<ExceptionMatch> parseCatch(List<Object> catchList) {
List<ExceptionMatch> exceptionMatchList = new ArrayList<>(catchList.size());
for (Object exceptionMatchObj : catchList) {
Map<String, Object> exceptionMatchMap = (Map<String, Object>) exceptionMatchObj;
ExceptionMatchImpl exceptionMatch = new ExceptionMatchImpl();
exceptionMatch.setExceptions((List<String>) exceptionMatchMap.get("Exceptions"));
exceptionMatch.setNext((String) exceptionMatchMap.get("Next"));
exceptionMatchList.add(exceptionMatch);
}
return exceptionMatchList;
}
protected Loop parseLoop(Object loopObj) {
Map<String, Object> loopMap = (Map<String, Object>)loopObj;
LoopImpl loop = new LoopImpl();
Object parallel = loopMap.get("Parallel");
loop.setParallel(NumberUtils.toInt(parallel.toString(), 1));
loop.setCollection((String)loopMap.get("Collection"));
loop.setElementVariableName((String)loopMap.getOrDefault("ElementVariableName", "loopElement"));
loop.setElementIndexName((String)loopMap.getOrDefault("ElementIndexName", "loopCounter"));
loop.setCompletionCondition(
(String)loopMap.getOrDefault("CompletionCondition", "[nrOfInstances] == [nrOfCompletedInstances]"));
return loop;
}
}

View File

@@ -0,0 +1,36 @@
/*
* Copyright 1999-2019 Seata.io Group.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.seata.saga.statelang.parser.impl;
import java.util.Map;
import io.seata.saga.statelang.domain.impl.BaseState;
/**
* BaseStatePaser
*
* @author lorne.cl
*/
public abstract class BaseStatePaser {
protected void parseBaseAttributes(BaseState state, Object node) {
Map<String, Object> nodeMap = (Map<String, Object>)node;
state.setComment((String)nodeMap.get("Comment"));
state.setNext((String)nodeMap.get("Next"));
state.setExtensions((Map<String, Object>)nodeMap.get("Extensions"));
}
}

View File

@@ -0,0 +1,59 @@
/*
* Copyright 1999-2019 Seata.io Group.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.seata.saga.statelang.parser.impl;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import io.seata.saga.statelang.domain.ChoiceState;
import io.seata.saga.statelang.domain.ChoiceState.Choice;
import io.seata.saga.statelang.domain.impl.ChoiceStateImpl;
import io.seata.saga.statelang.domain.impl.ChoiceStateImpl.ChoiceImpl;
import io.seata.saga.statelang.parser.StateParser;
/**
* Single item selection state parser
*
* @author lorne.cl
*/
public class ChoiceStateParser extends BaseStatePaser implements StateParser<ChoiceState> {
@Override
public ChoiceState parse(Object node) {
ChoiceStateImpl choiceState = new ChoiceStateImpl();
parseBaseAttributes(choiceState, node);
Map<String, Object> nodeMap = (Map<String, Object>)node;
List<Object> choiceObjList = (List<Object>)nodeMap.get("Choices");
List<Choice> choiceStateList = new ArrayList<>(choiceObjList.size());
for (Object choiceObj : choiceObjList) {
Map<String, Object> choiceObjMap = (Map<String, Object>)choiceObj;
ChoiceImpl choice = new ChoiceImpl();
choice.setExpression((String)choiceObjMap.get("Expression"));
choice.setNext((String)choiceObjMap.get("Next"));
choiceStateList.add(choice);
}
choiceState.setChoices(choiceStateList);
choiceState.setDefaultChoice((String)nodeMap.get("Default"));
return choiceState;
}
}

View File

@@ -0,0 +1,46 @@
/*
* Copyright 1999-2019 Seata.io Group.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.seata.saga.statelang.parser.impl;
import io.seata.saga.statelang.domain.DomainConstants;
import io.seata.saga.statelang.domain.ServiceTaskState;
import io.seata.saga.statelang.domain.impl.CompensateSubStateMachineStateImpl;
import io.seata.saga.statelang.parser.StateParser;
import org.springframework.util.StringUtils;
/**
* CompensateSubStateMachineState Parser
*
* @author lorne.cl
*/
public class CompensateSubStateMachineStateParser extends AbstractTaskStateParser
implements StateParser<ServiceTaskState> {
@Override
public ServiceTaskState parse(Object node) {
CompensateSubStateMachineStateImpl compensateSubStateMachineState = new CompensateSubStateMachineStateImpl();
compensateSubStateMachineState.setForCompensation(true);
if (node != null) {
parseTaskAttributes(compensateSubStateMachineState, node);
}
if (StringUtils.isEmpty(compensateSubStateMachineState.getName())) {
compensateSubStateMachineState.setName(
DomainConstants.COMPENSATE_SUB_MACHINE_STATE_NAME_PREFIX + compensateSubStateMachineState.hashCode());
}
return compensateSubStateMachineState;
}
}

View File

@@ -0,0 +1,37 @@
/*
* Copyright 1999-2019 Seata.io Group.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.seata.saga.statelang.parser.impl;
import io.seata.saga.statelang.domain.CompensationTriggerState;
import io.seata.saga.statelang.domain.impl.CompensationTriggerStateImpl;
import io.seata.saga.statelang.parser.StateParser;
/**
* 'trigger compensation process' state parser
*
* @author lorne.cl
*/
public class CompensationTriggerStateParser extends BaseStatePaser implements StateParser<CompensationTriggerState> {
@Override
public CompensationTriggerState parse(Object node) {
CompensationTriggerStateImpl compensationTriggerState = new CompensationTriggerStateImpl();
parseBaseAttributes(compensationTriggerState, node);
return compensationTriggerState;
}
}

View File

@@ -0,0 +1,43 @@
/*
* Copyright 1999-2019 Seata.io Group.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.seata.saga.statelang.parser.impl;
import java.util.Map;
import io.seata.saga.statelang.domain.FailEndState;
import io.seata.saga.statelang.domain.impl.FailEndStateImpl;
import io.seata.saga.statelang.parser.StateParser;
/**
* Failed end state parser
*
* @author lorne.cl
*/
public class FailEndStateParser extends BaseStatePaser implements StateParser<FailEndState> {
@Override
public FailEndState parse(Object node) {
FailEndStateImpl failEndState = new FailEndStateImpl();
parseBaseAttributes(failEndState, node);
Map<String, Object> nodeMap = (Map<String, Object>)node;
failEndState.setErrorCode((String)nodeMap.get("ErrorCode"));
failEndState.setMessage((String)nodeMap.get("Message"));
return failEndState;
}
}

View File

@@ -0,0 +1,89 @@
/*
* Copyright 1999-2019 Seata.io Group.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.seata.saga.statelang.parser.impl;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.parser.Feature;
import com.alibaba.fastjson.serializer.SerializerFeature;
import io.seata.common.loader.LoadLevel;
import io.seata.saga.statelang.parser.JsonParser;
/**
* JsonParser implement by Fastjson
*
* @author lorne.cl
*/
@LoadLevel(name = FastjsonParser.NAME)
public class FastjsonParser implements JsonParser {
private static final SerializerFeature[] SERIALIZER_FEATURES = new SerializerFeature[] {
SerializerFeature.DisableCircularReferenceDetect,
SerializerFeature.WriteDateUseDateFormat,
SerializerFeature.WriteClassName };
private static final SerializerFeature[] SERIALIZER_FEATURES_PRETTY = new SerializerFeature[] {
SerializerFeature.DisableCircularReferenceDetect,
SerializerFeature.WriteDateUseDateFormat,
SerializerFeature.WriteClassName,
SerializerFeature.PrettyFormat };
private static final SerializerFeature[] FEATURES_PRETTY = new SerializerFeature[] {
SerializerFeature.DisableCircularReferenceDetect,
SerializerFeature.WriteDateUseDateFormat,
SerializerFeature.PrettyFormat };
public static final String NAME = "fastjson";
@Override
public String getName() {
return NAME;
}
@Override
public String toJsonString(Object o, boolean prettyPrint) {
return toJsonString(o, false, prettyPrint);
}
@Override
public String toJsonString(Object o, boolean ignoreAutoType, boolean prettyPrint) {
if (prettyPrint) {
if (ignoreAutoType) {
return JSON.toJSONString(o, FEATURES_PRETTY);
}
else {
return JSON.toJSONString(o, SERIALIZER_FEATURES_PRETTY);
}
}
else {
if (ignoreAutoType) {
return JSON.toJSONString(o);
}
else {
return JSON.toJSONString(o, SERIALIZER_FEATURES);
}
}
}
@Override
public <T> T parse(String json, Class<T> type, boolean ignoreAutoType) {
if (ignoreAutoType) {
return JSON.parseObject(json, type, Feature.IgnoreAutoType, Feature.OrderedField);
}
else {
return JSON.parseObject(json, type, Feature.SupportAutoType, Feature.OrderedField);
}
}
}

View File

@@ -0,0 +1,111 @@
/*
* Copyright 1999-2019 Seata.io Group.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.seata.saga.statelang.parser.impl;
import com.fasterxml.jackson.annotation.JsonInclude.Include;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.MapperFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.ObjectMapper.DefaultTyping;
import io.seata.common.loader.LoadLevel;
import io.seata.saga.statelang.parser.JsonParser;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
/**
* JsonParser implement by Jackson
*
* @author lorne.cl
*/
@LoadLevel(name = JacksonJsonParser.NAME)
public class JacksonJsonParser implements JsonParser {
private static final Logger LOGGER = LoggerFactory.getLogger(JacksonJsonParser.class);
private ObjectMapper objectMapperWithAutoType = new ObjectMapper()
.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
.enableDefaultTypingAsProperty(DefaultTyping.NON_FINAL, "@type")
.enable(MapperFeature.PROPAGATE_TRANSIENT_MARKER)
.setSerializationInclusion(Include.NON_NULL);
private ObjectMapper objectMapper = new ObjectMapper()
.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
.disableDefaultTyping()
.enable(MapperFeature.PROPAGATE_TRANSIENT_MARKER)
.setSerializationInclusion(Include.NON_NULL);
public static final String NAME = "jackson";
@Override
public String getName() {
return NAME;
}
@Override
public String toJsonString(Object o, boolean prettyPrint) {
return toJsonString(o, false, prettyPrint);
}
@Override
public String toJsonString(Object o, boolean ignoreAutoType, boolean prettyPrint) {
try {
if (o instanceof List && ((List) o).isEmpty()) {
return "[]";
}
if (prettyPrint) {
if (ignoreAutoType) {
return objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(o);
}
else {
return objectMapperWithAutoType.writerWithDefaultPrettyPrinter().writeValueAsString(o);
}
}
else {
if (ignoreAutoType) {
return objectMapper.writeValueAsString(o);
}
else {
return objectMapperWithAutoType.writeValueAsString(o);
}
}
} catch (JsonProcessingException e) {
throw new RuntimeException("Parse object to json error", e);
}
}
@Override
public <T> T parse(String json, Class<T> type, boolean ignoreAutoType) {
try {
if (json != null && "[]".equals(json)) {
return (T) (new ArrayList(0));
}
if (ignoreAutoType) {
return objectMapper.readValue(json, type);
}
else {
return objectMapperWithAutoType.readValue(json, type);
}
} catch (IOException e) {
throw new RuntimeException("Parse json to object error", e);
}
}
}

View File

@@ -0,0 +1,52 @@
/*
* Copyright 1999-2019 Seata.io Group.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.seata.saga.statelang.parser.impl;
import io.seata.common.util.StringUtils;
import io.seata.saga.statelang.domain.ScriptTaskState;
import io.seata.saga.statelang.domain.impl.ScriptTaskStateImpl;
import io.seata.saga.statelang.parser.StateParser;
import java.util.Map;
/**
* ScriptTaskState parser
*
* @author lorne.cl
*/
public class ScriptTaskStateParser extends AbstractTaskStateParser implements StateParser<ScriptTaskState> {
@Override
public ScriptTaskState parse(Object node) {
ScriptTaskStateImpl scriptTaskState = new ScriptTaskStateImpl();
parseTaskAttributes(scriptTaskState, node);
Map<String, Object> nodeMap = (Map<String, Object>)node;
String scriptType = (String) nodeMap.get("ScriptType");
if (StringUtils.isNotBlank(scriptType)) {
scriptTaskState.setScriptType(scriptType);
}
scriptTaskState.setScriptContent((String)nodeMap.get("ScriptContent"));
scriptTaskState.setForCompensation(false);
scriptTaskState.setForUpdate(false);
scriptTaskState.setPersist(false);
return scriptTaskState;
}
}

View File

@@ -0,0 +1,51 @@
/*
* Copyright 1999-2019 Seata.io Group.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.seata.saga.statelang.parser.impl;
import java.util.List;
import java.util.Map;
import io.seata.saga.statelang.domain.ServiceTaskState;
import io.seata.saga.statelang.domain.impl.ServiceTaskStateImpl;
import io.seata.saga.statelang.parser.StateParser;
/**
* ServcieTaskTask parser
*
* @author lorne.cl
*/
public class ServiceTaskStateParser extends AbstractTaskStateParser implements StateParser<ServiceTaskState> {
@Override
public ServiceTaskState parse(Object node) {
ServiceTaskStateImpl serviceTaskState = new ServiceTaskStateImpl();
parseTaskAttributes(serviceTaskState, node);
Map<String, Object> nodeMap = (Map<String, Object>)node;
serviceTaskState.setServiceName((String)nodeMap.get("ServiceName"));
serviceTaskState.setServiceMethod((String)nodeMap.get("ServiceMethod"));
serviceTaskState.setServiceType((String)nodeMap.get("ServiceType"));
serviceTaskState.setParameterTypes((List<String>)nodeMap.get("ParameterTypes"));
Object isAsync = nodeMap.get("IsAsync");
if (Boolean.TRUE.equals(isAsync)) {
serviceTaskState.setAsync(true);
}
return serviceTaskState;
}
}

View File

@@ -0,0 +1,137 @@
/*
* Copyright 1999-2019 Seata.io Group.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.seata.saga.statelang.parser.impl;
import java.util.Map;
import io.seata.common.util.StringUtils;
import io.seata.saga.statelang.domain.DomainConstants;
import io.seata.saga.statelang.domain.RecoverStrategy;
import io.seata.saga.statelang.domain.State;
import io.seata.saga.statelang.domain.StateMachine;
import io.seata.saga.statelang.domain.impl.AbstractTaskState;
import io.seata.saga.statelang.domain.impl.BaseState;
import io.seata.saga.statelang.domain.impl.StateMachineImpl;
import io.seata.saga.statelang.parser.JsonParser;
import io.seata.saga.statelang.parser.JsonParserFactory;
import io.seata.saga.statelang.parser.StateMachineParser;
import io.seata.saga.statelang.parser.StateParser;
import io.seata.saga.statelang.parser.StateParserFactory;
import io.seata.saga.statelang.parser.utils.DesignerJsonTransformer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* State machine language parser
*
* @author lorne.cl
*/
public class StateMachineParserImpl implements StateMachineParser {
private static final Logger LOGGER = LoggerFactory.getLogger(StateMachineParserImpl.class);
private String jsonParserName = DomainConstants.DEFAULT_JSON_PARSER;
public StateMachineParserImpl(String jsonParserName) {
if (StringUtils.isNotBlank(jsonParserName)) {
this.jsonParserName = jsonParserName;
}
}
@Override
public StateMachine parse(String json) {
JsonParser jsonParser = JsonParserFactory.getJsonParser(jsonParserName);
if (jsonParser == null) {
throw new RuntimeException("Cannot find JsonParer by name: " + jsonParserName);
}
Map<String, Object> node = jsonParser.parse(json, Map.class, true);
if (DesignerJsonTransformer.isDesignerJson(node)) {
node = DesignerJsonTransformer.toStandardJson(node);
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("===== Transformed standard state language:\n{}", jsonParser.toJsonString(node, true));
}
}
StateMachineImpl stateMachine = new StateMachineImpl();
stateMachine.setName((String) node.get("Name"));
stateMachine.setComment((String) node.get("Comment"));
stateMachine.setVersion((String) node.get("Version"));
stateMachine.setStartState((String) node.get("StartState"));
String recoverStrategy = (String) node.get("RecoverStrategy");
if (StringUtils.isNotBlank(recoverStrategy)) {
stateMachine.setRecoverStrategy(RecoverStrategy.valueOf(recoverStrategy));
}
Object isPersist = node.get("IsPersist");
if (Boolean.FALSE.equals(isPersist)) {
stateMachine.setPersist(false);
}
// customize if update origin or append new retryStateInstLog
Object isRetryPersistModeUpdate = node.get("IsRetryPersistModeUpdate");
if (isRetryPersistModeUpdate instanceof Boolean) {
stateMachine.setRetryPersistModeUpdate(Boolean.TRUE.equals(isRetryPersistModeUpdate));
}
// customize if update last or append new compensateStateInstLog
Object isCompensatePersistModeUpdate = node.get("IsCompensatePersistModeUpdate");
if (isCompensatePersistModeUpdate instanceof Boolean) {
stateMachine.setCompensatePersistModeUpdate(Boolean.TRUE.equals(isCompensatePersistModeUpdate));
}
Map<String, Object> statesNode = (Map<String, Object>) node.get("States");
statesNode.forEach((stateName, value) -> {
Map<String, Object> stateNode = (Map<String, Object>) value;
String stateType = (String) stateNode.get("Type");
StateParser<?> stateParser = StateParserFactory.getStateParser(stateType);
if (stateParser == null) {
throw new IllegalArgumentException("State Type [" + stateType + "] is not support");
}
State state = stateParser.parse(stateNode);
if (state instanceof BaseState) {
((BaseState) state).setName(stateName);
}
if (stateMachine.getState(stateName) != null) {
throw new IllegalArgumentException("State[name:" + stateName + "] is already exists");
}
stateMachine.putState(stateName, state);
});
Map<String, State> stateMap = stateMachine.getStates();
for (State state : stateMap.values()) {
if (state instanceof AbstractTaskState) {
AbstractTaskState taskState = (AbstractTaskState) state;
if (StringUtils.isNotBlank(taskState.getCompensateState())) {
taskState.setForUpdate(true);
State compState = stateMap.get(taskState.getCompensateState());
if (compState instanceof AbstractTaskState) {
((AbstractTaskState) compState).setForCompensation(true);
}
}
}
}
return stateMachine;
}
public String getJsonParserName() {
return jsonParserName;
}
public void setJsonParserName(String jsonParserName) {
this.jsonParserName = jsonParserName;
}
}

View File

@@ -0,0 +1,54 @@
/*
* Copyright 1999-2019 Seata.io Group.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.seata.saga.statelang.parser.impl;
import java.util.Map;
import io.seata.saga.statelang.domain.ServiceTaskState;
import io.seata.saga.statelang.domain.SubStateMachine;
import io.seata.saga.statelang.domain.impl.SubStateMachineImpl;
import io.seata.saga.statelang.parser.StateParser;
import org.springframework.util.StringUtils;
/**
* SubStateMachineParser
*
* @author lorne.cl
*/
public class SubStateMachineParser extends AbstractTaskStateParser implements StateParser<SubStateMachine> {
@Override
public SubStateMachine parse(Object node) {
SubStateMachineImpl subStateMachine = new SubStateMachineImpl();
parseTaskAttributes(subStateMachine, node);
Map<String, Object> nodeMap = (Map<String, Object>)node;
subStateMachine.setStateMachineName((String)nodeMap.get("StateMachineName"));
if (StringUtils.isEmpty(subStateMachine.getCompensateState())) {
//build default SubStateMachine compensate state
CompensateSubStateMachineStateParser compensateSubStateMachineStateParser
= new CompensateSubStateMachineStateParser();
ServiceTaskState subStateMachineCompenState = compensateSubStateMachineStateParser.parse(null);
subStateMachine.setCompensateStateObject(subStateMachineCompenState);
subStateMachine.setCompensateState(subStateMachineCompenState.getName());
}
return subStateMachine;
}
}

View File

@@ -0,0 +1,37 @@
/*
* Copyright 1999-2019 Seata.io Group.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.seata.saga.statelang.parser.impl;
import io.seata.saga.statelang.domain.SucceedEndState;
import io.seata.saga.statelang.domain.impl.SucceedEndStateImpl;
import io.seata.saga.statelang.parser.StateParser;
/**
* Succeed end state parser
*
* @author lorne.cl
*/
public class SucceedEndStateParser extends BaseStatePaser implements StateParser<SucceedEndState> {
@Override
public SucceedEndState parse(Object node) {
SucceedEndStateImpl succeedEndState = new SucceedEndStateImpl();
parseBaseAttributes(succeedEndState, node);
return succeedEndState;
}
}

View File

@@ -0,0 +1,284 @@
/*
* Copyright 1999-2019 Seata.io Group.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.seata.saga.statelang.parser.utils;
import io.seata.common.exception.FrameworkErrorCode;
import io.seata.common.exception.FrameworkException;
import io.seata.common.util.CollectionUtils;
import io.seata.saga.statelang.domain.ExecutionStatus;
import io.seata.saga.statelang.domain.StateInstance;
import io.seata.saga.statelang.domain.StateMachineInstance;
import io.seata.saga.statelang.parser.JsonParser;
import org.springframework.util.StringUtils;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
/**
* Transform designer json to standard Saga State language json
*
* @author lorne.cl
*/
public class DesignerJsonTransformer {
public static Map<String, Object> toStandardJson(Map<String, Object> designerJsonObject) {
if (!isDesignerJson(designerJsonObject)) {
return designerJsonObject;
}
Map<String, Object> machineJsonObject = new LinkedHashMap<>();
List<Object> nodes = (List) designerJsonObject.get("nodes");
if (CollectionUtils.isNotEmpty(nodes)) {
Map<String, Object> nodeMap = new LinkedHashMap<>(nodes.size());
for (Object node : nodes) {
Map<String, Object> nodeObj = (Map<String, Object>) node;
transformNode(machineJsonObject, nodeMap, nodeObj);
}
List<Object> edges = (List) designerJsonObject.get("edges");
if (CollectionUtils.isNotEmpty(edges)) {
for (Object edge : edges) {
Map<String, Object> edgeObj = (Map<String, Object>) edge;
transformEdge(machineJsonObject, nodes, nodeMap, edgeObj);
}
}
}
return machineJsonObject;
}
private static void transformNode(Map<String, Object> machineJsonObject, Map<String, Object> nodeMap, Map<String, Object> nodeObj) {
nodeMap.put((String) nodeObj.get("id"), nodeObj);
String type = (String) nodeObj.get("stateType");
Map<String, Object> propsObj = (Map<String, Object>) nodeObj.get("stateProps");
if ("Start".equals(type)) {
if (propsObj != null && propsObj.containsKey("StateMachine")) {
machineJsonObject.putAll((Map<String, Object>) propsObj.get("StateMachine"));
}
} else if (!"Catch".equals(type)) {
Map<String, Object> states = (Map<String, Object>) CollectionUtils.computeIfAbsent(machineJsonObject, "States",
key -> new LinkedHashMap<>());
Map<String, Object> stateJsonObject = new LinkedHashMap<>();
String stateId = (String) nodeObj.get("stateId");
if (states.containsKey(stateId)) {
throw new RuntimeException(
"Transform designer json to standard json failed, stateId[" + stateId + "] already exists, pls rename it.");
}
String comment = (String) nodeObj.get("label");
if (StringUtils.hasLength(comment)) {
stateJsonObject.put("Comment", comment);
}
if (propsObj != null) {
stateJsonObject.putAll(propsObj);
}
states.put(stateId, stateJsonObject);
String stateType = (String) nodeObj.get("stateType");
if ("Compensation".equals(stateType)) {
stateJsonObject.put("Type", "ServiceTask");
} else {
stateJsonObject.put("Type", stateType);
}
}
}
private static void transformEdge(Map<String, Object> machineJsonObject, List<Object> nodes, Map<String, Object> nodeMap, Map<String, Object> edgeObj) {
String sourceId = (String) edgeObj.get("source");
String targetId = (String) edgeObj.get("target");
if (StringUtils.hasLength(sourceId)) {
Map<String, Object> sourceNode = (Map<String, Object>) nodeMap.get(sourceId);
Map<String, Object> targetNode = (Map<String, Object>) nodeMap.get(targetId);
if (sourceNode != null) {
Map<String, Object> states = (Map<String, Object>) machineJsonObject.get("States");
Map<String, Object> sourceState = (Map<String, Object>) states.get((String) sourceNode.get("stateId"));
String targetStateId = (String) targetNode.get("stateId");
String sourceType = (String) sourceNode.get("stateType");
if ("Start".equals(sourceType)) {
machineJsonObject.put("StartState", targetStateId);
//Make sure 'StartState' is before 'States'
machineJsonObject.put("States", machineJsonObject.remove("States"));
} else if ("ServiceTask".equals(sourceType)) {
if (targetNode != null && "Compensation".equals(targetNode.get("stateType"))) {
sourceState.put("CompensateState", targetStateId);
} else {
sourceState.put("Next", targetStateId);
}
} else if ("Catch".equals(sourceType)) {
Map<String, Object> catchAttachedNode = getCatchAttachedNode(sourceNode, nodes);
if (catchAttachedNode == null) {
throw new RuntimeException("'Catch' node[" + sourceNode.get("id") + "] is not attached on a 'ServiceTask' or 'ScriptTask'");
}
Map<String, Object> catchAttachedState = (Map<String, Object>) states.get(catchAttachedNode.get("stateId"));
List<Object> catches = (List<Object>) CollectionUtils.computeIfAbsent(catchAttachedState, "Catch",
key -> new ArrayList<>());
Map<String, Object> edgeProps = (Map<String, Object>) edgeObj.get("stateProps");
if (edgeProps != null) {
Map<String, Object> catchObj = new LinkedHashMap<>();
catchObj.put("Exceptions", edgeProps.get("Exceptions"));
catchObj.put("Next", targetStateId);
catches.add(catchObj);
}
} else if ("Choice".equals(sourceType)) {
List<Object> choices = (List<Object>) CollectionUtils.computeIfAbsent(sourceState, "Choices",
key -> new ArrayList<>());
Map<String, Object> edgeProps = (Map<String, Object>) edgeObj.get("stateProps");
if (edgeProps != null) {
if (Boolean.TRUE.equals(edgeProps.get("Default"))) {
sourceState.put("Default", targetStateId);
} else {
Map<String, Object> choiceObj = new LinkedHashMap<>();
choiceObj.put("Expression", edgeProps.get("Expression"));
choiceObj.put("Next", targetStateId);
choices.add(choiceObj);
}
}
} else {
sourceState.put("Next", targetStateId);
}
}
}
}
public static boolean isDesignerJson(Map<String, Object> jsonObject) {
return jsonObject != null && jsonObject.containsKey("nodes") && jsonObject.containsKey("edges");
}
private static Map<String, Object> getCatchAttachedNode(Map<String, Object> catchNode, List<Object> nodes) {
Number catchNodeX = (Number) catchNode.get("x");
Number catchNodeY = (Number) catchNode.get("y");
String catchSize = (String) catchNode.get("size");
String[] catchSizes = catchSize.split("\\*");
int catchWidth = Integer.parseInt(catchSizes[0]);
int catchHeight = Integer.parseInt(catchSizes[1]);
for (Object node : nodes) {
Map<String, Object> nodeObj = (Map<String, Object>) node;
if (catchNode != nodeObj &&
("ServiceTask".equals(nodeObj.get("stateType"))
|| "ScriptTask".equals(nodeObj.get("stateType")))) {
Number nodeX = (Number) nodeObj.get("x");
Number nodeY = (Number) nodeObj.get("y");
String nodeSize = (String) nodeObj.get("size");
String[] nodeSizes = nodeSize.split("\\*");
int nodeWidth = Integer.parseInt(nodeSizes[0]);
int nodeHeight = Integer.parseInt(nodeSizes[1]);
if (isBordersCoincided(catchNodeX, nodeX, catchWidth, nodeWidth)
&& isBordersCoincided(catchNodeY, nodeY, catchHeight, nodeHeight)) {
return nodeObj;
}
}
}
return null;
}
private static boolean isBordersCoincided(Number xyA, Number xyB, Number lengthA, Number lengthB) {
double centerPointLength = xyA.doubleValue() > xyB.doubleValue() ? xyA.doubleValue() - xyB.doubleValue() : xyB.doubleValue() - xyA.doubleValue();
return ((lengthA.doubleValue() + lengthB.doubleValue()) / 2) > centerPointLength;
}
/**
* Generate tracing graph json
* @param stateMachineInstance
* @return
*/
public static String generateTracingGraphJson(StateMachineInstance stateMachineInstance, JsonParser jsonParser) {
if (stateMachineInstance == null) {
throw new FrameworkException("StateMachineInstance is not exits",
FrameworkErrorCode.StateMachineInstanceNotExists);
}
String stateMachineJson = stateMachineInstance.getStateMachine().getContent();
if (StringUtils.isEmpty(stateMachineJson)) {
throw new FrameworkException("Cannot get StateMachine Json",
FrameworkErrorCode.ObjectNotExists);
}
Map<String, Object> stateMachineJsonObj = jsonParser.parse(stateMachineJson, Map.class, true);
if (!DesignerJsonTransformer.isDesignerJson(stateMachineJsonObj)) {
throw new FrameworkException("StateMachine Json is not generated by Designer",
FrameworkErrorCode.InvalidConfiguration);
}
Map<String, List<StateInstance>> stateInstanceMapGroupByName = new HashMap<>(stateMachineInstance.getStateMap().size());
for (StateInstance stateInstance : stateMachineInstance.getStateMap().values()) {
CollectionUtils.computeIfAbsent(stateInstanceMapGroupByName, stateInstance.getName(), key -> new ArrayList<>())
.add(stateInstance);
}
List<Object> nodesArray = (List<Object>) stateMachineJsonObj.get("nodes");
for (Object nodeObj : nodesArray) {
Map<String, Object> node = (Map<String, Object>) nodeObj;
String stateId = (String) node.get("stateId");
String stateType = (String) node.get("stateType");
if ("ServiceTask".equals(stateType)
|| "SubStateMachine".equals(stateType)
|| "Compensation".equals(stateType)) {
node.remove("color");
}
List<StateInstance> stateInstanceList = stateInstanceMapGroupByName.get(stateId);
if (CollectionUtils.isNotEmpty(stateInstanceList)) {
StateInstance stateInstance = null;
if (stateInstanceList.size() == 1) {
stateInstance = stateInstanceList.get(0);
} else {
//find out latest stateInstance
for (StateInstance stateInst : stateInstanceList) {
if (stateInstance == null
|| stateInst.getGmtStarted().after(stateInstance.getGmtStarted())) {
stateInstance = stateInst;
}
}
}
node.put("stateInstanceId", stateInstance.getId());
node.put("stateInstanceStatus", stateInstance.getStatus());
if (ExecutionStatus.SU.equals(stateInstance.getStatus())) {
node.put("color", "green");
Map<String, Object> style = new LinkedHashMap<>();
style.put("fill", "#00D73E");
style.put("lineWidth", 2);
node.put("style", style);
} else {
node.put("color", "red");
Map<String, Object> style = new LinkedHashMap<>();
style.put("fill", "#FF7777");
style.put("lineWidth", 2);
node.put("style", style);
}
}
}
if (stateMachineJsonObj != null) {
return jsonParser.toJsonString(stateMachineJsonObj, true);
}
return "";
}
}

View File

@@ -0,0 +1,65 @@
/*
* Copyright 1999-2019 Seata.io Group.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.seata.saga.statelang.parser.utils;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.io.StringWriter;
import java.io.Writer;
/**
* IOUtils
* copy from commons-io
*
* @author lorne.cl
*/
public class IOUtils {
private static final int DEFAULT_BUFFER_SIZE = 1024 * 4;
public static String toString(InputStream input, String encoding) throws IOException {
StringWriter sw = new StringWriter();
copy(input, sw, encoding);
return sw.toString();
}
public static void copy(InputStream input, Writer output, String encoding) throws IOException {
if (encoding == null) {
copy(input, output);
} else {
InputStreamReader in = new InputStreamReader(input, encoding);
copy(in, output);
}
}
public static void copy(InputStream input, Writer output) throws IOException {
InputStreamReader in = new InputStreamReader(input);
copy(in, output);
}
public static int copy(Reader input, Writer output) throws IOException {
char[] buffer = new char[DEFAULT_BUFFER_SIZE];
int count = 0;
int n = 0;
while (-1 != (n = input.read(buffer))) {
output.write(buffer, 0, n);
count += n;
}
return count;
}
}

View File

@@ -0,0 +1,2 @@
io.seata.saga.statelang.parser.impl.FastjsonParser
io.seata.saga.statelang.parser.impl.JacksonJsonParser

View File

@@ -0,0 +1,77 @@
/*
* Copyright 1999-2019 Seata.io Group.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.seata.saga.statelang.parser;
import java.io.IOException;
import java.util.Date;
import java.util.Map;
import io.seata.saga.statelang.domain.StateMachine;
import io.seata.saga.statelang.parser.utils.DesignerJsonTransformer;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.springframework.core.io.ClassPathResource;
/**
* StateParser tests
*
* @author lorne.cl
*/
public class StateParserTests {
@Test
public void testParser() throws IOException {
ClassPathResource resource = new ClassPathResource("statelang/simple_statemachine.json");
String json = io.seata.saga.statelang.parser.utils.IOUtils.toString(resource.getInputStream(), "UTF-8");
StateMachine stateMachine = StateMachineParserFactory.getStateMachineParser(null).parse(json);
stateMachine.setGmtCreate(new Date());
Assertions.assertNotNull(stateMachine);
JsonParser jsonParser = JsonParserFactory.getJsonParser("jackson");
String outputJson = jsonParser.toJsonString(stateMachine, true);
System.out.println(outputJson);
JsonParser fastjsonParser = JsonParserFactory.getJsonParser("fastjson");
String fastjsonOutputJson = fastjsonParser.toJsonString(stateMachine, true);
System.out.println(fastjsonOutputJson);
Assertions.assertEquals(stateMachine.getName(), "simpleTestStateMachine");
Assertions.assertTrue(stateMachine.getStates().size() > 0);
}
@Test
public void testDesignerJsonTransformer() throws IOException {
ClassPathResource resource = new ClassPathResource("statelang/simple_statemachine_with_layout.json");
String json = io.seata.saga.statelang.parser.utils.IOUtils.toString(resource.getInputStream(), "UTF-8");
JsonParser jsonParser = JsonParserFactory.getJsonParser("jackson");
Map<String, Object> parsedObj = DesignerJsonTransformer.toStandardJson(jsonParser.parse(json, Map.class, true));
Assertions.assertNotNull(parsedObj);
String outputJson = jsonParser.toJsonString(parsedObj, true);
System.out.println(outputJson);
JsonParser fastjsonParser = JsonParserFactory.getJsonParser("fastjson");
Map<String, Object> fastjsonParsedObj = DesignerJsonTransformer.toStandardJson(fastjsonParser.parse(json, Map.class, true));
Assertions.assertNotNull(fastjsonParsedObj);
String fastjsonOutputJson = fastjsonParser.toJsonString(fastjsonParsedObj, true);
System.out.println(fastjsonOutputJson);
}
}

View File

@@ -0,0 +1,15 @@
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<Pattern>%d{HH:mm:ss.SSS} %-5level %logger{80} - %msg%n</Pattern>
</encoder>
</appender>
<root level="DEBUG">
<appender-ref ref="STDOUT"/>
</root>
</configuration>

View File

@@ -0,0 +1,138 @@
{
"Name": "simpleTestStateMachine",
"Comment": "测试状态机定义",
"StartState": "FirstState",
"Version": "0.0.1",
"States": {
"FirstState": {
"Type": "ServiceTask",
"ServiceName": "is.seata.saga.DemoService",
"ServiceMethod": "foo",
"IsPersist": false,
"Next": "ScriptState"
},
"ScriptState": {
"Type": "ScriptTask",
"ScriptType": "groovy",
"ScriptContent": "return 'hello ' + inputA",
"Input": [
{
"inputA": "$.data1"
}
],
"Output": {
"scriptStateResult": "$.#root"
},
"Next": "ChoiceState"
},
"ChoiceState": {
"Type": "Choice",
"Choices": [
{
"Expression": "foo == 1",
"Next": "FirstMatchState"
},
{
"Expression": "foo == 2",
"Next": "SecondMatchState"
}
],
"Default": "FailState"
},
"FirstMatchState": {
"Type": "ServiceTask",
"ServiceName": "is.seata.saga.DemoService",
"ServiceMethod": "bar",
"CompensateState": "CompensateFirst",
"Status": {
"return.code == 'S'": "SU",
"return.code == 'F'": "FA",
"$exception{java.lang.Throwable}": "UN"
},
"Input": [
{
"inputA1": "$.data1",
"inputA2": {
"a": "$.data2.a"
}
},
{
"inputB": "$.header"
}
],
"Output": {
"firstMatchStateResult": "$.#root"
},
"Retry": [
{
"Exceptions": ["java.lang.Exception"],
"IntervalSeconds": 2,
"MaxAttempts": 3,
"BackoffRate": 1.5
}
],
"Catch": [
{
"Exceptions": [
"java.lang.Exception"
],
"Next": "CompensationTrigger"
}
],
"Next": "SuccessState"
},
"CompensateFirst": {
"Type": "ServiceTask",
"ServiceName": "is.seata.saga.DemoService",
"ServiceMethod": "compensateBar",
"IsForCompensation": true,
"IsForUpdate": true,
"Input": [
{
"input": "$.data"
}
],
"Output": {
"firstMatchStateResult": "$.#root"
},
"Status": {
"return.code == 'S'": "SU",
"return.code == 'F'": "FA",
"$exception{java.lang.Throwable}": "UN"
}
},
"CompensationTrigger": {
"Type": "CompensationTrigger",
"Next": "CompensateEndState"
},
"CompensateEndState": {
"Type": "Fail",
"ErrorCode": "StateCompensated",
"Message": "State Compensated!"
},
"SecondMatchState": {
"Type": "SubStateMachine",
"StateMachineName": "simpleTestStateMachine",
"Input": [
{
"input": "$.data"
},
{
"header": "$.header"
}
],
"Output": {
"firstMatchStateResult": "$.#root"
},
"Next": "SuccessState"
},
"FailState": {
"Type": "Fail",
"ErrorCode": "DefaultStateError",
"Message": "No Matches!"
},
"SuccessState": {
"Type": "Succeed"
}
}
}

View File

@@ -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"
}
]
}