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,31 @@
<?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-processctrl ${project.version}</name>
<artifactId>seata-saga-processctrl</artifactId>
<packaging>jar</packaging>
</project>

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.proctrl;
import java.util.Map;
/**
* Hierarchical process context
*
* @author lorne.cl
*/
public interface HierarchicalProcessContext extends ProcessContext {
/**
* Gets get variable locally.
*
* @param name the name
* @return the get variable locally
*/
Object getVariableLocally(String name);
/**
* Sets set variable locally.
*
* @param name the name
* @param value the value
*/
void setVariableLocally(String name, Object value);
/**
* Gets get variables locally.
*
* @return the get variables locally
*/
Map<String, Object> getVariablesLocally();
/**
* Sets set variables locally.
*
* @param variables the variables
*/
void setVariablesLocally(Map<String, Object> variables);
/**
* Has variable local boolean.
*
* @param name the name
* @return the boolean
*/
boolean hasVariableLocal(String name);
/**
* Remove variable locally.
*
* @param name the name
* @return the removed variable or null
*/
Object removeVariableLocally(String name);
/**
* Clear locally.
*/
void clearLocally();
}

View File

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

View File

@@ -0,0 +1,98 @@
/*
* 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.proctrl;
import java.util.Map;
/**
* Process Context
*
* @author jin.xie
* @author lorne.cl
*/
public interface ProcessContext {
String VAR_NAME_PROCESS_TYPE = "_ProcessType_";
/**
* Gets get variable.
*
* @param name the name
* @return the get variable
*/
Object getVariable(String name);
/**
* Sets set variable.
*
* @param name the name
* @param value the value
*/
void setVariable(String name, Object value);
/**
* Gets get variables.
*
* @return the get variables
*/
Map<String, Object> getVariables();
/**
* Sets set variables.
*
* @param variables the variables
*/
void setVariables(Map<String, Object> variables);
/**
* Remove variable.
*
* @param name the name
* @return the removed variable or null
*/
Object removeVariable(String name);
/**
* Has variable boolean.
*
* @param name the name
* @return the boolean
*/
boolean hasVariable(String name);
/**
* Gets get instruction.
*
* @return the get instruction
*/
Instruction getInstruction();
/**
* Sets set instruction.
*
* @param instruction the instruction
*/
void setInstruction(Instruction instruction);
/**
* Gets get instruction.
*
* @param <T> the type parameter
* @param clazz the clazz
* @return the get instruction
*/
<T extends Instruction> T getInstruction(Class<T> clazz);
}

View File

@@ -0,0 +1,35 @@
/*
* Copyright 1999-2019 Seata.io Group.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.seata.saga.proctrl;
import io.seata.common.exception.FrameworkException;
/**
* Process Controller
*
* @author jin.xie
* @author lorne.cl
*/
public interface ProcessController {
/**
* process business logic
*
* @param context
* @throws FrameworkException
*/
void process(ProcessContext context) throws FrameworkException;
}

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.proctrl;
import io.seata.common.exception.FrameworkException;
/**
* Process Router
*
* @author jin.xie
* @author lorne.cl
*/
public interface ProcessRouter {
/**
* route
*
* @param context
* @return
* @throws FrameworkException
*/
Instruction route(ProcessContext context) throws FrameworkException;
}

View File

@@ -0,0 +1,72 @@
/*
* Copyright 1999-2019 Seata.io Group.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.seata.saga.proctrl;
/**
* Process type
*
* @author jin.xie
* @author lorne.cl
*/
public enum ProcessType {
/**
* SEATA State Language
*/
STATE_LANG("STATE_LANG", "SEATA State Language");
private String code;
private String message;
ProcessType(String code, String message) {
this.code = code;
this.message = message;
}
/**
* get enum by code
*
* @param code
* @return
*/
public static ProcessType getEnumByCode(String code) {
for (ProcessType codetmp : ProcessType.values()) {
if (codetmp.getCode().equalsIgnoreCase(code)) {
return codetmp;
}
}
return null;
}
/**
* get code
*
* @return
*/
public String getCode() {
return code;
}
/**
* get message
*
* @return
*/
public String getMessage() {
return message;
}
}

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.proctrl.eventing;
import java.util.List;
import io.seata.common.exception.FrameworkException;
/**
* Event bus
*
* @author lorne.cl
*/
public interface EventBus<E> {
/**
* insert add element into bus
*
* @param e
* @return
* @throws FrameworkException
*/
boolean offer(E e) throws FrameworkException;
/**
* get event consumers
*
* @param clazz
* @return
*/
List<EventConsumer> getEventConsumers(Class<?> clazz);
/**
* register event consumer
*
* @param eventConsumer
*/
void registerEventConsumer(EventConsumer eventConsumer);
}

View File

@@ -0,0 +1,42 @@
/*
* Copyright 1999-2019 Seata.io Group.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.seata.saga.proctrl.eventing;
import io.seata.common.exception.FrameworkException;
/**
* Event Consumer
*
* @author lorne.cl
*/
public interface EventConsumer<E> {
/**
* process
*
* @param event
* @throws FrameworkException
*/
void process(E event) throws FrameworkException;
/**
* if thd handler can handle this class return true otherwise return false
*
* @param clazz
* @return
*/
boolean accept(Class<E> clazz);
}

View File

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

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.proctrl.eventing.impl;
import java.util.ArrayList;
import java.util.List;
import io.seata.saga.proctrl.eventing.EventBus;
import io.seata.saga.proctrl.eventing.EventConsumer;
/**
* Abstract Event Bus
*
* @author jin.xie
* @author lorne.cl
*/
public abstract class AbstractEventBus<E> implements EventBus<E> {
private List<EventConsumer> eventConsumerList = new ArrayList<>();
@Override
public List<EventConsumer> getEventConsumers(Class clazz) {
List<EventConsumer> acceptedConsumers = new ArrayList<>();
for (EventConsumer eventConsumer : eventConsumerList) {
if (eventConsumer.accept(clazz)) {
acceptedConsumers.add(eventConsumer);
}
}
return acceptedConsumers;
}
@Override
public void registerEventConsumer(EventConsumer eventConsumer) {
eventConsumerList.add(eventConsumer);
}
}

View File

@@ -0,0 +1,58 @@
/*
* 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.proctrl.eventing.impl;
import java.util.List;
import java.util.concurrent.ThreadPoolExecutor;
import io.seata.common.exception.FrameworkException;
import io.seata.common.util.CollectionUtils;
import io.seata.saga.proctrl.ProcessContext;
import io.seata.saga.proctrl.eventing.EventConsumer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Asynchronized EventBus
*
* @author lorne.cl
*/
public class AsyncEventBus extends AbstractEventBus<ProcessContext> {
private static final Logger LOGGER = LoggerFactory.getLogger(AsyncEventBus.class);
private ThreadPoolExecutor threadPoolExecutor;
@Override
public boolean offer(ProcessContext context) throws FrameworkException {
List<EventConsumer> eventConsumers = getEventConsumers(context.getClass());
if (CollectionUtils.isEmpty(eventConsumers)) {
if (LOGGER.isWarnEnabled()) {
LOGGER.warn("cannot find event handler by class: " + context.getClass());
}
return false;
}
for (EventConsumer eventConsumer : eventConsumers) {
threadPoolExecutor.execute(() -> eventConsumer.process(context));
}
return true;
}
public void setThreadPoolExecutor(ThreadPoolExecutor threadPoolExecutor) {
this.threadPoolExecutor = threadPoolExecutor;
}
}

View File

@@ -0,0 +1,78 @@
/*
* Copyright 1999-2019 Seata.io Group.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.seata.saga.proctrl.eventing.impl;
import java.util.List;
import java.util.Stack;
import io.seata.common.exception.FrameworkException;
import io.seata.common.util.CollectionUtils;
import io.seata.saga.proctrl.ProcessContext;
import io.seata.saga.proctrl.eventing.EventConsumer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Deliver event to event consumer directly
*
* @author lorne.cl
*/
public class DirectEventBus extends AbstractEventBus<ProcessContext> {
private static final Logger LOGGER = LoggerFactory.getLogger(DirectEventBus.class);
private static final String VAR_NAME_SYNC_EXE_STACK = "_sync_execution_stack_";
@Override
public boolean offer(ProcessContext context) throws FrameworkException {
List<EventConsumer> eventHandlers = getEventConsumers(context.getClass());
if (CollectionUtils.isEmpty(eventHandlers)) {
if (LOGGER.isWarnEnabled()) {
LOGGER.warn("cannot find event handler by class: " + context.getClass());
}
return false;
}
boolean isFirstEvent = false;
Stack<ProcessContext> currentStack = (Stack<ProcessContext>)context.getVariable(VAR_NAME_SYNC_EXE_STACK);
if (currentStack == null) {
synchronized (context) {
currentStack = (Stack<ProcessContext>)context.getVariable(VAR_NAME_SYNC_EXE_STACK);
if (currentStack == null) {
currentStack = new Stack<>();
context.setVariable(VAR_NAME_SYNC_EXE_STACK, currentStack);
isFirstEvent = true;
}
}
}
currentStack.push(context);
if (isFirstEvent) {
try {
while (currentStack.size() > 0) {
ProcessContext currentContext = currentStack.pop();
for (EventConsumer eventHandler : eventHandlers) {
eventHandler.process(currentContext);
}
}
} finally {
context.removeVariable(VAR_NAME_SYNC_EXE_STACK);
}
}
return true;
}
}

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.proctrl.eventing.impl;
import io.seata.common.exception.FrameworkException;
import io.seata.saga.proctrl.ProcessContext;
import io.seata.saga.proctrl.ProcessController;
import io.seata.saga.proctrl.eventing.EventConsumer;
/**
* ProcessCtrl Event Consumer
*
* @author lorne.cl
*/
public class ProcessCtrlEventConsumer implements EventConsumer<ProcessContext> {
private ProcessController processController;
@Override
public void process(ProcessContext event) throws FrameworkException {
processController.process(event);
}
@Override
public boolean accept(Class<ProcessContext> clazz) {
return ProcessContext.class.isAssignableFrom(clazz);
}
public void setProcessController(ProcessController processController) {
this.processController = processController;
}
}

View File

@@ -0,0 +1,40 @@
/*
* 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.proctrl.eventing.impl;
import io.seata.common.exception.FrameworkException;
import io.seata.saga.proctrl.ProcessContext;
import io.seata.saga.proctrl.eventing.EventBus;
import io.seata.saga.proctrl.eventing.EventPublisher;
/**
* ProcessCtrl Event Pulisher
*
* @author lorne.cl
*/
public class ProcessCtrlEventPublisher implements EventPublisher<ProcessContext> {
private EventBus<ProcessContext> eventBus;
@Override
public boolean publish(ProcessContext event) throws FrameworkException {
return eventBus.offer(event);
}
public void setEventBus(EventBus<ProcessContext> eventBus) {
this.eventBus = eventBus;
}
}

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.proctrl.handler;
import java.util.Map;
import io.seata.common.exception.FrameworkErrorCode;
import io.seata.common.exception.FrameworkException;
import io.seata.saga.proctrl.Instruction;
import io.seata.saga.proctrl.ProcessContext;
import io.seata.saga.proctrl.ProcessRouter;
import io.seata.saga.proctrl.ProcessType;
import io.seata.saga.proctrl.eventing.EventPublisher;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Default Router handler
*
* @author jin.xie
* @author lorne.cl
*/
public class DefaultRouterHandler implements RouterHandler {
private static final Logger LOGGER = LoggerFactory.getLogger(DefaultRouterHandler.class);
private EventPublisher<ProcessContext> eventPublisher;
private Map<String, ProcessRouter> processRouters;
public static ProcessType matchProcessType(ProcessContext context) {
ProcessType processType = (ProcessType)context.getVariable(ProcessContext.VAR_NAME_PROCESS_TYPE);
if (processType == null) {
processType = ProcessType.STATE_LANG;
}
return processType;
}
@Override
public void route(ProcessContext context) throws FrameworkException {
try {
ProcessType processType = matchProcessType(context);
if (processType == null) {
if (LOGGER.isWarnEnabled()) {
LOGGER.warn("Process type not found, context= {}", context);
}
throw new FrameworkException(FrameworkErrorCode.ProcessTypeNotFound);
}
ProcessRouter processRouter = processRouters.get(processType.getCode());
if (processRouter == null) {
LOGGER.error("Cannot find process router by type {}, context = {}", processType.getCode(), context);
throw new FrameworkException(FrameworkErrorCode.ProcessRouterNotFound);
}
Instruction instruction = processRouter.route(context);
if (instruction == null) {
LOGGER.info("route instruction is null, process end");
} else {
context.setInstruction(instruction);
eventPublisher.publish(context);
}
} catch (FrameworkException e) {
throw e;
} catch (Exception ex) {
throw new FrameworkException(ex, ex.getMessage(), FrameworkErrorCode.UnknownAppError);
}
}
public void setEventPublisher(EventPublisher<ProcessContext> eventPublisher) {
this.eventPublisher = eventPublisher;
}
public void setProcessRouters(Map<String, ProcessRouter> processRouters) {
this.processRouters = processRouters;
}
}

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.proctrl.handler;
import io.seata.common.exception.FrameworkException;
import io.seata.saga.proctrl.ProcessContext;
/**
* Process Handler
*
* @author jin.xie
* @author lorne.cl
*/
public interface ProcessHandler {
/**
* process
*
* @param context
* @throws FrameworkException
*/
void process(ProcessContext context) throws FrameworkException;
}

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.proctrl.handler;
import io.seata.common.exception.FrameworkException;
import io.seata.saga.proctrl.ProcessContext;
/**
* Router Handler
*
* @author jin.xie
* @author lorne.cl
*/
public interface RouterHandler {
/**
* route
*
* @param context
* @throws FrameworkException
*/
void route(ProcessContext context) throws FrameworkException;
}

View File

@@ -0,0 +1,168 @@
/*
* 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.proctrl.impl;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import io.seata.saga.proctrl.HierarchicalProcessContext;
import io.seata.saga.proctrl.Instruction;
import io.seata.saga.proctrl.ProcessContext;
/**
* The default process context implementation
*
* @author lorne.cl
*/
public class ProcessContextImpl implements HierarchicalProcessContext, ProcessContext {
private Map<String, Object> variables = new ConcurrentHashMap<>();
private Instruction instruction;
private ProcessContext parent;
@Override
public Object getVariable(String name) {
if (variables.containsKey(name)) {
return variables.get(name);
}
if (parent != null) {
return parent.getVariable(name);
}
return null;
}
@Override
public void setVariable(String name, Object value) {
if (variables.containsKey(name)) {
setVariableLocally(name, value);
} else {
if (parent != null) {
parent.setVariable(name, value);
} else {
setVariableLocally(name, value);
}
}
}
@Override
public Map<String, Object> getVariables() {
final Map<String, Object> collectedVariables = new HashMap<>();
if (parent != null) {
collectedVariables.putAll(parent.getVariables());
}
variables.forEach(collectedVariables::put);
return collectedVariables;
}
@Override
public void setVariables(final Map<String, Object> variables) {
if (variables != null) {
variables.forEach(this::setVariable);
}
}
@Override
public Object getVariableLocally(String name) {
return variables.get(name);
}
@Override
public void setVariableLocally(String name, Object value) {
variables.put(name, value);
}
@Override
public Map<String, Object> getVariablesLocally() {
return Collections.unmodifiableMap(variables);
}
@Override
public void setVariablesLocally(Map<String, Object> variables) {
this.variables.putAll(variables);
}
@Override
public boolean hasVariable(String name) {
if (variables.containsKey(name)) {
return true;
}
if (parent != null) {
return parent.hasVariable(name);
}
return false;
}
@Override
public Instruction getInstruction() {
return instruction;
}
@Override
public void setInstruction(Instruction instruction) {
this.instruction = instruction;
}
@Override
public <T extends Instruction> T getInstruction(Class<T> clazz) {
return (T)instruction;
}
@Override
public boolean hasVariableLocal(String name) {
return variables.containsKey(name);
}
@Override
public Object removeVariable(String name) {
if (variables.containsKey(name)) {
return variables.remove(name);
}
if (parent != null) {
return parent.removeVariable(name);
}
return null;
}
@Override
public Object removeVariableLocally(String name) {
return variables.remove(name);
}
@Override
public void clearLocally() {
variables.clear();
}
public ProcessContext getParent() {
return parent;
}
public void setParent(ProcessContext parent) {
this.parent = parent;
}
@Override
public String toString() {
return "{" + "variables=" + variables + ", instruction=" + instruction + ", parent=" + parent + '}';
}
}

View File

@@ -0,0 +1,58 @@
/*
* 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.proctrl.impl;
import io.seata.common.exception.FrameworkErrorCode;
import io.seata.common.exception.FrameworkException;
import io.seata.saga.proctrl.ProcessContext;
import io.seata.saga.proctrl.ProcessController;
import io.seata.saga.proctrl.process.BusinessProcessor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Default implementation of Process controller
*
* @author jin.xie
* @author lorne.cl
*/
public class ProcessControllerImpl implements ProcessController {
private static final Logger LOGGER = LoggerFactory.getLogger(ProcessControllerImpl.class);
private BusinessProcessor businessProcessor;
@Override
public void process(ProcessContext context) throws FrameworkException {
try {
businessProcessor.process(context);
businessProcessor.route(context);
} catch (FrameworkException fex) {
throw fex;
} catch (Exception ex) {
LOGGER.error("Unknown exception occurred, context = {}", context, ex);
throw new FrameworkException(ex, "Unknown exception occurred", FrameworkErrorCode.UnknownAppError);
}
}
public void setBusinessProcessor(BusinessProcessor businessProcessor) {
this.businessProcessor = businessProcessor;
}
}

View File

@@ -0,0 +1,44 @@
/*
* Copyright 1999-2019 Seata.io Group.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.seata.saga.proctrl.process;
import io.seata.common.exception.FrameworkException;
import io.seata.saga.proctrl.ProcessContext;
/**
* Business logic processor
*
* @author jin.xie
* @author lorne.cl
*/
public interface BusinessProcessor {
/**
* process business logic
*
* @param context
* @throws FrameworkException
*/
void process(ProcessContext context) throws FrameworkException;
/**
* route
*
* @param context
* @throws FrameworkException
*/
void route(ProcessContext context) throws FrameworkException;
}

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.proctrl.process.impl;
import java.util.Map;
import io.seata.common.exception.FrameworkErrorCode;
import io.seata.common.exception.FrameworkException;
import io.seata.saga.proctrl.ProcessContext;
import io.seata.saga.proctrl.ProcessType;
import io.seata.saga.proctrl.handler.ProcessHandler;
import io.seata.saga.proctrl.handler.RouterHandler;
import io.seata.saga.proctrl.process.BusinessProcessor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Customizable Business Processor
*
* @author jin.xie
* @author lorne.cl
*/
public class CustomizeBusinessProcessor implements BusinessProcessor {
private static final Logger LOGGER = LoggerFactory.getLogger(CustomizeBusinessProcessor.class);
private Map<String, ProcessHandler> processHandlers;
private Map<String, RouterHandler> routerHandlers;
public static ProcessType matchProcessType(ProcessContext context) {
ProcessType processType = (ProcessType)context.getVariable(ProcessContext.VAR_NAME_PROCESS_TYPE);
if (processType == null) {
processType = ProcessType.STATE_LANG;
}
return processType;
}
@Override
public void process(ProcessContext context) throws FrameworkException {
ProcessType processType = matchProcessType(context);
if (processType == null) {
if (LOGGER.isWarnEnabled()) {
LOGGER.warn("Process type not found, context= {}", context);
}
throw new FrameworkException(FrameworkErrorCode.ProcessTypeNotFound);
}
ProcessHandler processor = processHandlers.get(processType.getCode());
if (processor == null) {
LOGGER.error("Cannot find process handler by type {}, context= {}", processType.getCode(), context);
throw new FrameworkException(FrameworkErrorCode.ProcessHandlerNotFound);
}
processor.process(context);
}
@Override
public void route(ProcessContext context) throws FrameworkException {
ProcessType processType = matchProcessType(context);
if (processType == null) {
if (LOGGER.isWarnEnabled()) {
LOGGER.warn("Process type not found, the process is no longer advanced, context= {}", context);
}
return;
}
RouterHandler router = routerHandlers.get(processType.getCode());
if (router == null) {
LOGGER.error("Cannot find router handler by type {}, context= {}", processType.getCode(), context);
return;
}
router.route(context);
}
public void setProcessHandlers(Map<String, ProcessHandler> processHandlers) {
this.processHandlers = processHandlers;
}
public void setRouterHandlers(Map<String, RouterHandler> routerHandlers) {
this.routerHandlers = routerHandlers;
}
}

View File

@@ -0,0 +1,142 @@
/*
* 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.proctrl;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import io.seata.saga.proctrl.eventing.impl.AsyncEventBus;
import io.seata.saga.proctrl.eventing.impl.DirectEventBus;
import io.seata.saga.proctrl.eventing.impl.ProcessCtrlEventConsumer;
import io.seata.saga.proctrl.eventing.impl.ProcessCtrlEventPublisher;
import io.seata.saga.proctrl.handler.DefaultRouterHandler;
import io.seata.saga.proctrl.handler.ProcessHandler;
import io.seata.saga.proctrl.handler.RouterHandler;
import io.seata.saga.proctrl.impl.ProcessContextImpl;
import io.seata.saga.proctrl.impl.ProcessControllerImpl;
import io.seata.saga.proctrl.mock.MockInstruction;
import io.seata.saga.proctrl.mock.MockProcessHandler;
import io.seata.saga.proctrl.mock.MockProcessRouter;
import io.seata.saga.proctrl.process.impl.CustomizeBusinessProcessor;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
/**
* ProcessController Tests
*
* @author lorne.cl
*/
public class ProcessControllerTests {
@Test
public void testSimpleProcessCtrl() {
try {
ProcessCtrlEventPublisher processCtrlEventPublisher = buildEventPublisher();
ProcessContext context = new ProcessContextImpl();
MockInstruction instruction = new MockInstruction();
instruction.setTestString("one");
context.setInstruction(instruction);
context.setVariable("TEST", "test");
processCtrlEventPublisher.publish(context);
} catch (Exception e) {
Assertions.fail(e);
}
}
@Test
public void testSimpleProcessCtrlAsync() {
try {
ProcessCtrlEventPublisher processCtrlEventPublisher = buildAsyncEventPublisher();
ProcessContext context = new ProcessContextImpl();
MockInstruction instruction = new MockInstruction();
instruction.setTestString("one");
context.setInstruction(instruction);
context.setVariable("TEST", "test");
processCtrlEventPublisher.publish(context);
} catch (Exception e) {
Assertions.fail(e);
}
}
private ProcessCtrlEventPublisher buildEventPublisher() throws Exception {
ProcessCtrlEventPublisher syncEventPublisher = new ProcessCtrlEventPublisher();
ProcessControllerImpl processorController = createProcessorController(syncEventPublisher);
ProcessCtrlEventConsumer processCtrlEventConsumer = new ProcessCtrlEventConsumer();
processCtrlEventConsumer.setProcessController(processorController);
DirectEventBus directEventBus = new DirectEventBus();
syncEventPublisher.setEventBus(directEventBus);
directEventBus.registerEventConsumer(processCtrlEventConsumer);
return syncEventPublisher;
}
private ProcessCtrlEventPublisher buildAsyncEventPublisher() throws Exception {
ProcessCtrlEventPublisher asyncEventPublisher = new ProcessCtrlEventPublisher();
ProcessControllerImpl processorController = createProcessorController(asyncEventPublisher);
ProcessCtrlEventConsumer processCtrlEventConsumer = new ProcessCtrlEventConsumer();
processCtrlEventConsumer.setProcessController(processorController);
AsyncEventBus asyncEventBus = new AsyncEventBus();
asyncEventBus.setThreadPoolExecutor(
new ThreadPoolExecutor(1, 5, 5000, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<>()));
asyncEventPublisher.setEventBus(asyncEventBus);
asyncEventBus.registerEventConsumer(processCtrlEventConsumer);
return asyncEventPublisher;
}
private ProcessControllerImpl createProcessorController(ProcessCtrlEventPublisher eventPublisher) throws Exception {
DefaultRouterHandler defaultRouterHandler = new DefaultRouterHandler();
defaultRouterHandler.setEventPublisher(eventPublisher);
Map<String, ProcessRouter> processRouterMap = new HashMap<>(1);
processRouterMap.put(ProcessType.STATE_LANG.getCode(), new MockProcessRouter());
defaultRouterHandler.setProcessRouters(processRouterMap);
CustomizeBusinessProcessor customizeBusinessProcessor = new CustomizeBusinessProcessor();
Map<String, ProcessHandler> processHandlerMap = new HashMap<>(1);
processHandlerMap.put(ProcessType.STATE_LANG.getCode(), new MockProcessHandler());
customizeBusinessProcessor.setProcessHandlers(processHandlerMap);
Map<String, RouterHandler> routerHandlerMap = new HashMap<>(1);
routerHandlerMap.put(ProcessType.STATE_LANG.getCode(), defaultRouterHandler);
customizeBusinessProcessor.setRouterHandlers(routerHandlerMap);
ProcessControllerImpl processorController = new ProcessControllerImpl();
processorController.setBusinessProcessor(customizeBusinessProcessor);
return processorController;
}
}

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.proctrl.mock;
import io.seata.saga.proctrl.Instruction;
/**
* @author lorne.cl
*/
public class MockInstruction implements Instruction {
private String testString;
public String getTestString() {
return testString;
}
public void setTestString(String testString) {
this.testString = testString;
}
@Override
public String toString() {
return "MockInstruction{" + "testString='" + testString + '\'' + '}';
}
}

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.proctrl.mock;
import io.seata.common.exception.FrameworkException;
import io.seata.saga.proctrl.ProcessContext;
import io.seata.saga.proctrl.handler.ProcessHandler;
/**
* @author lorne.cl
*/
public class MockProcessHandler implements ProcessHandler {
@Override
public void process(ProcessContext context) throws FrameworkException {
System.out.println("MockProcessHandler.process executed. context: " + context);
}
}

View File

@@ -0,0 +1,44 @@
/*
* Copyright 1999-2019 Seata.io Group.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.seata.saga.proctrl.mock;
import io.seata.common.exception.FrameworkException;
import io.seata.saga.proctrl.Instruction;
import io.seata.saga.proctrl.ProcessContext;
import io.seata.saga.proctrl.ProcessRouter;
/**
* @author lorne.cl
*/
public class MockProcessRouter implements ProcessRouter {
@Override
public Instruction route(ProcessContext context) throws FrameworkException {
System.out.println("MockProcessRouter.route executed. context: " + context);
MockInstruction instruction = context.getInstruction(MockInstruction.class);
if (instruction != null) {
if ("one".equals(instruction.getTestString())) {
instruction.setTestString("two");
} else if ("two".equals(instruction.getTestString())) {
instruction.setTestString("three");
} else {
instruction.setTestString(null);
return null;//end process
}
}
return instruction;
}
}