extends Describable {
+
+
+ /**
+ * 适配前处理
+ *
+ * @param context 流程上下文
+ * @return 返回需要适配的组件的入参和出参
+ */
+ Pair before(EngineContext context);
+
+ /**
+ * 适配后置处理
+ *
+ * @param origin 原流程上下文
+ * @param newContext 适配的组件返回的上下文
+ */
+ void after(EngineContext origin, EngineContext newContext);
+
+}
diff --git a/smart-flow-core/src/main/java/org/smartboot/flow/core/AsyncCallResult.java b/smart-flow-core/src/main/java/org/smartboot/flow/core/AsyncCallResult.java
index 06a273f30070452f371e45668690882a0f4aea0f..7d3ea68f354bf20dc6a9d91818cc56d73df88a5b 100644
--- a/smart-flow-core/src/main/java/org/smartboot/flow/core/AsyncCallResult.java
+++ b/smart-flow-core/src/main/java/org/smartboot/flow/core/AsyncCallResult.java
@@ -1,9 +1,8 @@
package org.smartboot.flow.core;
import org.smartboot.flow.core.component.Component;
+import org.smartboot.flow.core.invoker.InvokeListener;
-import java.util.ArrayList;
-import java.util.List;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
@@ -18,7 +17,12 @@ public class AsyncCallResult {
private long timeout;
private Future future;
private Component source;
- private final List> belongs = new ArrayList<>(0);
+ private InvokeListener listeners;
+ private volatile boolean called;
+
+ public void setListeners(InvokeListener listeners) {
+ this.listeners = listeners;
+ }
public String getName() {
return name;
@@ -52,26 +56,26 @@ public class AsyncCallResult {
this.source = source;
}
- public void setBelongs(List> belongs) {
- this.belongs.addAll(belongs);
- }
+ public synchronized void checkAndWait(EngineContext context) {
+ if (called) {
+ return;
+ }
+
+ called = true;
- public void checkAndWait(EngineContext context) {
try {
this.future.get(this.timeout, TimeUnit.MILLISECONDS);
} catch (Throwable e) {
if (source.isDegradable()) {
- context.broken(false);
+ EngineContext.LOGGER.warn("degrade component {}", source.getName(), e);
} else {
- EngineContext.LOGGER.error("wait component async-execute failed {}", source.describe(), e);
+ EngineContext.LOGGER.error("wait component async-execute timeout {}ms {}", timeout, source.describe(), e);
context.setFatal(e);
context.setRollback(true);
context.broken(true);
}
} finally {
- if (source.isRollbackable(context) && !belongs.contains(source)) {
- belongs.add(source);
- }
+ listeners.onCompleted(source, context);
}
}
diff --git a/smart-flow-core/src/main/java/org/smartboot/flow/core/Condition.java b/smart-flow-core/src/main/java/org/smartboot/flow/core/Condition.java
index 83555a1d9f15d8224d0f0f74f2bb8bd92b59b570..24c956b803f00f4406ac3a8905638efc61037b32 100644
--- a/smart-flow-core/src/main/java/org/smartboot/flow/core/Condition.java
+++ b/smart-flow-core/src/main/java/org/smartboot/flow/core/Condition.java
@@ -11,7 +11,9 @@ public abstract class Condition implements Describable {
return this.test(context.getReq(), context.getResult());
}
- public abstract Object test(T t, S s);
+ public Object test(T t, S s) {
+ return null;
+ }
@Override
public String describe() {
diff --git a/smart-flow-core/src/main/java/org/smartboot/flow/core/DegradeCallback.java b/smart-flow-core/src/main/java/org/smartboot/flow/core/DegradeCallback.java
new file mode 100644
index 0000000000000000000000000000000000000000..cbc0aacb9d3bdf3ec771bdc262f8ca24c7378c17
--- /dev/null
+++ b/smart-flow-core/src/main/java/org/smartboot/flow/core/DegradeCallback.java
@@ -0,0 +1,18 @@
+package org.smartboot.flow.core;
+
+/**
+ * @author qinluo
+ * @date 2022-11-28 20:10:46
+ * @since 1.0.0
+ */
+public interface DegradeCallback extends Describable {
+
+ default void doWithDegrade(EngineContext context, Throwable e) {
+
+ }
+
+ @Override
+ default String describe() {
+ return this.getClass().getName();
+ }
+}
diff --git a/smart-flow-core/src/main/java/org/smartboot/flow/core/EngineContext.java b/smart-flow-core/src/main/java/org/smartboot/flow/core/EngineContext.java
index 6aa5b360b5652d55c2244faed828acc09b68df85..33ee074b3f56bb1368082b397250697dff16810d 100644
--- a/smart-flow-core/src/main/java/org/smartboot/flow/core/EngineContext.java
+++ b/smart-flow-core/src/main/java/org/smartboot/flow/core/EngineContext.java
@@ -2,45 +2,81 @@ package org.smartboot.flow.core;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import org.smartboot.flow.core.component.Component;
import org.smartboot.flow.core.exception.ExceptionHandler;
+import org.smartboot.flow.core.invoker.Invoker;
import org.smartboot.flow.core.trace.Tracer;
import java.io.Serializable;
-import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Future;
/**
* @author qinluo
* @date 2022-11-12 21:00:58
* @since 1.0.0
*/
-public class EngineContext {
+public class EngineContext{
public static final int DISABLED = -1;
public static final int EXECUTING = 1;
public static final int ROLLBACK = 2;
public static final Logger LOGGER = LoggerFactory.getLogger(EngineContext.class);
- private T req;
- private S result;
- ExecutorService executor;
- private final Map, Value> extensions = new ConcurrentHashMap<>();
- private final Tracer tracer = new Tracer();
- private boolean broken;
- private Throwable fatal;
- private ExceptionHandler handler;
- private boolean rollback;
- private final Map> asyncInvokes = new ConcurrentHashMap<>();
- private String engineName;
- private ExecutionListener listener;
+ protected T req;
+ protected S result;
+ protected ExecutorService executor;
+ protected Map, Value> extensions = new ConcurrentHashMap<>();
+ protected Tracer tracer = new Tracer();
+ protected boolean broken;
+ protected Throwable fatal;
+ protected ExceptionHandler handler;
+ protected boolean rollback;
+ protected Map asyncInvokes = new ConcurrentHashMap<>();
+ protected String engineName;
+ protected ExecutionListener listener;
+ protected Invoker invoker;
/**
* 执行状态
*/
- private int executing;
+ protected int executing;
+ protected EngineContext parent;
+
+ public void copy(EngineContext
dest) {
+ // Reuse extensions.
+ dest.extensions = this.extensions;
+ dest.invoker = this.invoker;
+ dest.tracer = this.tracer;
+ dest.engineName = this.engineName;
+ dest.executing = this.executing;
+ dest.listener = this.listener;
+ dest.handler = this.handler;
+ dest.executor = this.executor;
+ dest.fatal = this.fatal;
+ dest.broken = this.broken;
+ dest.asyncInvokes = this.asyncInvokes;
+ }
+
+ /**
+ * New context for subprocess.
+ *
+ */
+ public EngineContext newContext() {
+ EngineContext newContext = new EngineContext<>();
+ newContext.setReq(req);
+ newContext.setResult(result);
+ // Reuse extensions.
+ newContext.extensions = this.extensions;
+ newContext.invoker = this.invoker;
+ newContext.tracer = this.tracer;
+ newContext.engineName = this.engineName;
+ newContext.executing = this.executing;
+ newContext.listener = this.listener;
+ newContext.handler = this.handler;
+ newContext.parent = this;
+ newContext.executor = this.executor;
+ return newContext;
+ }
/**
* Returns current invoked trace.
@@ -51,6 +87,14 @@ public class EngineContext {
return tracer.getTrace();
}
+ public Invoker getInvoker() {
+ return invoker;
+ }
+
+ public void setInvoker(Invoker invoker) {
+ this.invoker = invoker;
+ }
+
public boolean getRollback() {
return rollback;
}
@@ -71,10 +115,23 @@ public class EngineContext {
return broken;
}
+ /**
+ * Break current pipeline.
+ */
public void broken(boolean broken) {
this.broken = broken;
}
+ /**
+ * Broken full pipeline
+ */
+ public void brokenAll(boolean broken) {
+ this.broken = broken;
+ if (this.parent != null) {
+ this.parent.brokenAll(broken);
+ }
+ }
+
public T getReq() {
return req;
}
@@ -123,14 +180,16 @@ public class EngineContext {
this.engineName = engineName;
}
- public void addAsyncInvoke(Component component, Future future, List> belongs) {
- AsyncCallResult result = new AsyncCallResult<>();
- result.setFuture(future);
- result.setName(component.getName());
- result.setTimeout(component.getTimeout());
- result.setSource(component);
- result.setBelongs(belongs);
- this.asyncInvokes.put(component.getName(), result);
+ public ExecutorService getExecutor() {
+ return executor;
+ }
+
+ public void setExecutor(ExecutorService executor) {
+ this.executor = executor;
+ }
+
+ public void addAsyncInvoke(AsyncCallResult result) {
+ this.asyncInvokes.put(result.getName(), result);
}
public AsyncCallResult getAsyncCall(String name) {
@@ -144,9 +203,9 @@ public class EngineContext {
public void enter(Object obj) {
String message = this.executing == ROLLBACK ? "rollback " : "";
if (obj instanceof Describable) {
- message += ("rollback " + ((Describable) obj).describe());
+ message += (((Describable) obj).describe());
} else if (obj instanceof String) {
- message += ("rollback " + obj);
+ message += (obj);
}
this.tracer.enter(message);
@@ -188,6 +247,16 @@ public class EngineContext {
extensions.put(key, value);
}
+ @SuppressWarnings("unchecked")
+ public P remove(Key
key) {
+ Value removed = extensions.remove(key);
+ if (removed == null || removed == Value.NULL) {
+ return null;
+ }
+
+ return (P) removed.get();
+ }
+
public void clear() {
this.tracer.reset();
this.asyncInvokes.clear();
@@ -198,6 +267,15 @@ public class EngineContext {
this.listener = null;
this.engineName = null;
this.executing = DISABLED;
+ this.executor = null;
+ this.parent = null;
+ }
+
+ /**
+ * Apply subprocess fields to parent ctx.
+ */
+ public void apply() {
+
}
private static class Value implements Serializable {
diff --git a/smart-flow-core/src/main/java/org/smartboot/flow/core/ExecutionListener.java b/smart-flow-core/src/main/java/org/smartboot/flow/core/ExecutionListener.java
index 88328754706b07fedb262a843fadfb6034e47c1e..d9b624fe919316479b17f76f5422347b58d78201 100644
--- a/smart-flow-core/src/main/java/org/smartboot/flow/core/ExecutionListener.java
+++ b/smart-flow-core/src/main/java/org/smartboot/flow/core/ExecutionListener.java
@@ -7,6 +7,9 @@ package org.smartboot.flow.core;
*/
public interface ExecutionListener {
+ void start(EngineContext context);
+ void completed(EngineContext context);
+
void beforeExecute(EngineContext context, Object object);
void afterExecute(EngineContext context, Object object);
void beforeRollback(EngineContext context, Object object);
diff --git a/smart-flow-core/src/main/java/org/smartboot/flow/core/ExecutionListenerSupport.java b/smart-flow-core/src/main/java/org/smartboot/flow/core/ExecutionListenerSupport.java
index f8062d7cfdc5031e65b3faa1e0108d0c24867f0e..c502bc07e33c560f8bd7d27057f5a4ce88d9bf0d 100644
--- a/smart-flow-core/src/main/java/org/smartboot/flow/core/ExecutionListenerSupport.java
+++ b/smart-flow-core/src/main/java/org/smartboot/flow/core/ExecutionListenerSupport.java
@@ -7,6 +7,16 @@ package org.smartboot.flow.core;
*/
public class ExecutionListenerSupport implements ExecutionListener {
+ @Override
+ public void start(EngineContext context) {
+
+ }
+
+ @Override
+ public void completed(EngineContext context) {
+
+ }
+
@Override
public void beforeExecute(EngineContext context, Object object) {
diff --git a/smart-flow-core/src/main/java/org/smartboot/flow/core/ExecutionListeners.java b/smart-flow-core/src/main/java/org/smartboot/flow/core/ExecutionListeners.java
index 5e62709104ec5b9e6f13070b312dacceec22bd74..e4eb24997c2c40baaaec6f7a538601f72f16d0d5 100644
--- a/smart-flow-core/src/main/java/org/smartboot/flow/core/ExecutionListeners.java
+++ b/smart-flow-core/src/main/java/org/smartboot/flow/core/ExecutionListeners.java
@@ -20,6 +20,28 @@ public class ExecutionListeners implements ExecutionListener {
this.listeners = listeners;
}
+ @Override
+ public void start(EngineContext context) {
+ for (ExecutionListener listener : listeners) {
+ try {
+ listener.start(context);
+ } catch (Throwable e) {
+ LOGGER.warn("execute listener {} failed", listener.getClass().getName(), e);
+ }
+ }
+ }
+
+ @Override
+ public void completed(EngineContext context) {
+ for (ExecutionListener listener : listeners) {
+ try {
+ listener.completed(context);
+ } catch (Throwable e) {
+ LOGGER.warn("execute listener {} failed", listener.getClass().getName(), e);
+ }
+ }
+ }
+
@Override
public void beforeExecute(EngineContext context, Object object) {
for (ExecutionListener listener : listeners) {
diff --git a/smart-flow-core/src/main/java/org/smartboot/flow/core/FlowEngine.java b/smart-flow-core/src/main/java/org/smartboot/flow/core/FlowEngine.java
index 3389146a518fe809f5add7e3e5cf023510a9ab4e..6b2e71bf517be5df738ea630718da99f75927503 100644
--- a/smart-flow-core/src/main/java/org/smartboot/flow/core/FlowEngine.java
+++ b/smart-flow-core/src/main/java/org/smartboot/flow/core/FlowEngine.java
@@ -1,7 +1,9 @@
package org.smartboot.flow.core;
+import org.smartboot.flow.core.exception.DefaultExceptionHandler;
import org.smartboot.flow.core.exception.ExceptionHandler;
+import org.smartboot.flow.core.invoker.Invoker;
import org.smartboot.flow.core.manager.DefaultEngineManager;
import org.smartboot.flow.core.metrics.MetricExecutionListener;
import org.smartboot.flow.core.util.AssertUtil;
@@ -19,7 +21,7 @@ import java.util.concurrent.ExecutorService;
public class FlowEngine implements Describable, Validator, Measurable {
private Pipeline pipeline;
- private ExceptionHandler exceptionHandler;
+ private ExceptionHandler exceptionHandler = new DefaultExceptionHandler();
private String name;
private volatile boolean validateCalled = false;
@@ -44,6 +46,9 @@ public class FlowEngine implements Describable, Validator, Measurable {
initContext(context);
+ // fire start
+ start(context);
+
context.enter(this);
boolean rollback = false;
@@ -54,8 +59,6 @@ public class FlowEngine implements Describable, Validator, Measurable {
rollback = true;
}
- context.ensureFinished();
-
if (rollback || context.getRollback()) {
context.setExecuting(EngineContext.ROLLBACK);
pipeline.rollback(context);
@@ -65,6 +68,9 @@ public class FlowEngine implements Describable, Validator, Measurable {
context.setExecuting(EngineContext.EXECUTING);
context.exit(this);
+ // complete execute.
+ complete(context);
+
if (context.getFatal() != null && exceptionHandler != null) {
context.getHandler().handle(context, context.getFatal());
}
@@ -72,12 +78,23 @@ public class FlowEngine implements Describable, Validator, Measurable {
return context;
}
+ private void start(EngineContext context) {
+ context.listener.start(context);
+ }
+
+ private void complete(EngineContext context) {
+ context.listener.completed(context);
+ }
+
+
+
protected void initContext(EngineContext context) {
context.clear();
context.setHandler(exceptionHandler);
context.executor = executor;
context.setEngineName(this.name);
context.setExecuting(EngineContext.EXECUTING);
+ context.setInvoker(new Invoker());
// Execution Listener.
ExecutionListeners listeners = new ExecutionListeners(ExecutionListenerRegistry.getRegistered());
@@ -120,8 +137,10 @@ public class FlowEngine implements Describable, Validator, Measurable {
public void visit(EngineVisitor engineVisitor) {
AssertUtil.notNull(engineVisitor, "visitor must not be null!");
- engineVisitor.visit(this.name);
- PipelineVisitor pipelineVisitor = engineVisitor.visitPipeline(pipeline);
+ engineVisitor.visit(this.name, this.executor);
+ engineVisitor.visitSource(this);
+
+ PipelineVisitor pipelineVisitor = engineVisitor.visitPipeline(pipeline.describe());
pipeline.visit(pipelineVisitor);
// Visit completed.
diff --git a/smart-flow-core/src/main/java/org/smartboot/flow/core/Pipeline.java b/smart-flow-core/src/main/java/org/smartboot/flow/core/Pipeline.java
index b9d845758b0b2539464067363fb5f49b987c4eec..9ca9da774d5c9b690db468e51946ed48da152d6b 100644
--- a/smart-flow-core/src/main/java/org/smartboot/flow/core/Pipeline.java
+++ b/smart-flow-core/src/main/java/org/smartboot/flow/core/Pipeline.java
@@ -1,12 +1,16 @@
package org.smartboot.flow.core;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
import org.smartboot.flow.core.component.Component;
+import org.smartboot.flow.core.invoker.InvokeListener;
import org.smartboot.flow.core.util.AssertUtil;
import org.smartboot.flow.core.visitor.ComponentVisitor;
import org.smartboot.flow.core.visitor.PipelineVisitor;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.List;
import java.util.concurrent.Future;
@@ -17,8 +21,11 @@ import java.util.concurrent.Future;
*/
public class Pipeline implements Rollback, Describable, Validator, Measurable {
+ private static final Logger LOGGER = LoggerFactory.getLogger(Pipeline.class);
+
private final List> components = new ArrayList<>();
private String name;
+ private volatile boolean validateCalled = false;
public void setName(String name) {
this.name = name;
@@ -29,9 +36,9 @@ public class Pipeline implements Rollback, Describable, Validator, M
return name;
}
- public void execute(EngineContext context) throws Exception {
+ public void execute(EngineContext context) throws Throwable {
// Executed sequences.
- List> executed = new ArrayList<>();
+ List executed = new ArrayList<>();
context.putExt(Key.of(this), executed);
// Enter record track
@@ -42,78 +49,26 @@ public class Pipeline implements Rollback, Describable, Validator, M
break;
}
- // component is disabled.
- if (!component.isEnabled()) {
- continue;
- }
-
- // Ensure async dependencies are called and finished.
- ensureAllDependsExecuted(component, context);
-
- // Compatible-check after ensureAllDependsExecuted called.
- if (context.isBroken()) {
- break;
- }
-
- if (component.isAsync()) {
- AssertUtil.notNull(context.executor, "executor must not be null");
-
- Future submitted = context.executor.submit(() -> executeComponent(component, context, executed));
- context.addAsyncInvoke(component, submitted, executed);
- continue;
- }
-
- executeComponent(component, context, executed);
+ context.getInvoker().invoke(context, component, new InvokeListener() {
+ @Override
+ public void onCompleted(Component component, EngineContext context) {
+ // rollback
+ if (component.isRollbackable(context) && !executed.contains(component)) {
+ executed.add(component);
+ }
+ }
+ });
}
// Exit record track
context.exit(this);
- }
-
- private void ensureAllDependsExecuted(Component component, EngineContext context) {
- for (String depends : component.getDependsOn()) {
- AsyncCallResult asyncCall = context.getAsyncCall(depends);
- if (asyncCall == null) {
- EngineContext.LOGGER.warn("could not find dependsOn call on component {} with dependency {}", component.getName(), depends);
- continue;
- }
-
- // Await current future finished.
- asyncCall.checkAndWait(context);
- if (context.isBroken()) {
- break;
- }
- }
- }
-
- private int executeComponent(Component component,
- EngineContext context,
- List> executed) {
- try {
- component.invoke(context);
- } catch (Throwable e) {
- if (component.isDegradable()) {
- context.broken(false);
- } else {
- EngineContext.LOGGER.error("execute component failed {}", component.describe(), e);
- context.setFatal(e);
- context.setRollback(true);
- context.broken(true);
- }
- } finally {
- // rollback
- if (component.isRollbackable(context)) {
- executed.add(component);
- }
- }
-
- return 1;
+ context.ensureFinished();
}
@Override
- public void rollback(EngineContext context) {
- List> executed = context.getExt(Key.of(this));
+ public synchronized void rollback(EngineContext context) {
+ List> executed = context.remove(Key.of(this));
if (executed == null || executed.size() == 0) {
return;
}
@@ -126,7 +81,7 @@ public class Pipeline implements Rollback, Describable, Validator, M
try {
component.rollback(context);
} catch (Exception e) {
- EngineContext.LOGGER.error("rollback failed {}", component.describe());
+ LOGGER.error("{} rollback failed {}", this.name, component.describe());
}
}
@@ -142,21 +97,27 @@ public class Pipeline implements Rollback, Describable, Validator, M
this.components.addAll(components);
}
- public void addComponent(Component component) {
- this.components.add(component);
- }
-
public void visit(PipelineVisitor pipelineVisitor) {
+ pipelineVisitor.visitSource(this);
+
for (Component component : components) {
- ComponentVisitor visitor = pipelineVisitor.visitComponent(component);
- component.visit(visitor);
+ ComponentVisitor visitor = pipelineVisitor.visitComponent(component.getType(), component.getName(), component.describe());
+ if (visitor != null) {
+ component.visit(visitor);
+ }
}
}
@Override
public void validate() {
+ if (validateCalled) {
+ return;
+ }
+
AssertUtil.notNull(components, "pipeline [" + describe() + "] components must not be null");
AssertUtil.isTrue(components.size() != 0, "pipeline [" + describe() + "] components must not be null");
components.forEach(Validator::validate);
+
+ validateCalled = true;
}
}
diff --git a/smart-flow-core/src/main/java/org/smartboot/flow/core/component/AttributeHolder.java b/smart-flow-core/src/main/java/org/smartboot/flow/core/attribute/AttributeHolder.java
similarity index 92%
rename from smart-flow-core/src/main/java/org/smartboot/flow/core/component/AttributeHolder.java
rename to smart-flow-core/src/main/java/org/smartboot/flow/core/attribute/AttributeHolder.java
index f40eada7ed52d9bc79d2b573f6c931f375676d43..1cac30fc4b325e85e237e19082c4d2568064f1a6 100644
--- a/smart-flow-core/src/main/java/org/smartboot/flow/core/component/AttributeHolder.java
+++ b/smart-flow-core/src/main/java/org/smartboot/flow/core/attribute/AttributeHolder.java
@@ -1,4 +1,4 @@
-package org.smartboot.flow.core.component;
+package org.smartboot.flow.core.attribute;
/**
* @author qinluo
diff --git a/smart-flow-core/src/main/java/org/smartboot/flow/core/attribute/AttributeValueResolver.java b/smart-flow-core/src/main/java/org/smartboot/flow/core/attribute/AttributeValueResolver.java
new file mode 100644
index 0000000000000000000000000000000000000000..86e7e28ebe1503dbbd68423d0023c7a81e3fedad
--- /dev/null
+++ b/smart-flow-core/src/main/java/org/smartboot/flow/core/attribute/AttributeValueResolver.java
@@ -0,0 +1,101 @@
+package org.smartboot.flow.core.attribute;
+
+import org.smartboot.flow.core.exception.FlowException;
+import org.smartboot.flow.core.parser.DefaultObjectCreator;
+import org.smartboot.flow.core.parser.ObjectCreator;
+import org.smartboot.flow.core.util.AssertUtil;
+
+import java.math.BigDecimal;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * @author qinluo
+ * @date 2022-11-28 20:41:17
+ * @since 1.0.0
+ */
+public class AttributeValueResolver {
+
+ private ObjectCreator objectCreator = DefaultObjectCreator.getInstance();
+
+ public void setObjectCreator(ObjectCreator objectCreator) {
+ this.objectCreator = objectCreator;
+ }
+
+ public Object resolve(Attributes attribute, Object value) {
+ if (value == null) {
+ return null;
+ }
+
+ if (attribute.accept(value)) {
+ return value;
+ }
+
+ Class> accepted = attribute.accept;
+ // Must as string
+ String strValue = String.valueOf(value);
+
+ // For string
+ if (attribute.accept(strValue)) {
+ return strValue;
+ }
+
+ // For boolean
+ if (accepted == Boolean.class || accepted == boolean.class) {
+ return Boolean.parseBoolean(strValue);
+ }
+
+ // For numbers
+ if (Number.class.isAssignableFrom(accepted) || accepted.isPrimitive()) {
+ Double numeric = Double.valueOf(strValue);
+
+ if (accepted == Long.class || accepted == long.class) {
+ return numeric.longValue();
+ }
+
+ if (accepted == Double.class || accepted == double.class) {
+ return numeric;
+ }
+
+ if (accepted == Integer.class || accepted == int.class) {
+ return numeric.intValue();
+ }
+
+ if (accepted == Float.class || accepted == float.class) {
+ return numeric.floatValue();
+ }
+
+ if (accepted == Short.class || accepted == short.class) {
+ return numeric.shortValue();
+ }
+
+ if (accepted == Byte.class || accepted == byte.class) {
+ return numeric.byteValue();
+ }
+
+ if (accepted == Character.class || accepted == char.class) {
+ return (char)numeric.intValue();
+ }
+
+ if (accepted == BigDecimal.class) {
+ return new BigDecimal(strValue);
+ }
+
+ AssertUtil.shouldNotReachHere();
+ }
+
+ // For string list.
+ if (accepted == List.class) {
+ return Arrays.asList(strValue.split(","));
+ }
+
+ // for classname.
+ try {
+ return objectCreator.create(strValue, true);
+ } catch (Exception ignored) {
+ // Maybe not a class.
+ }
+
+ throw new FlowException("Can't not resolve attribute [" + attribute.getName() + "] value with " + value);
+ }
+}
diff --git a/smart-flow-core/src/main/java/org/smartboot/flow/core/component/Attributes.java b/smart-flow-core/src/main/java/org/smartboot/flow/core/attribute/Attributes.java
similarity index 54%
rename from smart-flow-core/src/main/java/org/smartboot/flow/core/component/Attributes.java
rename to smart-flow-core/src/main/java/org/smartboot/flow/core/attribute/Attributes.java
index 566344084e1d4a831791cde5b1f7a009a638670b..bc9eafee613b97af8e96846dd667f05721d2285b 100644
--- a/smart-flow-core/src/main/java/org/smartboot/flow/core/component/Attributes.java
+++ b/smart-flow-core/src/main/java/org/smartboot/flow/core/attribute/Attributes.java
@@ -1,9 +1,9 @@
-package org.smartboot.flow.core.component;
+package org.smartboot.flow.core.attribute;
-import org.smartboot.flow.core.util.AssertUtil;
+import org.smartboot.flow.core.DegradeCallback;
+import org.smartboot.flow.core.component.Component;
-import java.util.Arrays;
import java.util.List;
@@ -12,6 +12,7 @@ import java.util.List;
* @date 2022-11-12 18:46:30
* @since 1.0.0
*/
+@SuppressWarnings("unchecked")
public enum Attributes {
NAME("name", String.class) {
@@ -26,9 +27,6 @@ public enum Attributes {
@Override
public void apply(Component component, Object value) {
super.apply(component, value);
- if (value instanceof String) {
- value = Boolean.parseBoolean((String) value);
- }
component.setRollback((Boolean) value);
}
},
@@ -37,9 +35,6 @@ public enum Attributes {
@Override
public void apply(Component component, Object value) {
super.apply(component, value);
- if (value instanceof String) {
- value = Boolean.parseBoolean((String) value);
- }
component.setDegradable((Boolean) value);
}
},
@@ -48,11 +43,6 @@ public enum Attributes {
@Override
public void apply(Component component, Object value) {
super.apply(component, value);
-
- if (value instanceof String) {
- value = Boolean.parseBoolean((String) value);
- }
-
component.setAsync((Boolean) value);
}
},
@@ -61,52 +51,16 @@ public enum Attributes {
@Override
public void apply(Component component, Object value) {
super.apply(component, value);
-
- if (value instanceof String) {
- value = Long.parseLong((String) value);
- }
-
component.setTimeout(((Number) value).longValue());
}
-
- @Override
- public boolean accept(Object value) {
- return value instanceof Long || value instanceof Integer || value instanceof String;
- }
},
- DEPENDS_ON("dependsOn", Void.class) {
+ DEPENDS_ON("dependsOn", List.class) {
@Override
public void apply(Component component, Object value) {
super.apply(component, value);
- if (value instanceof String) {
- String values = (String) value;
- component.setDependsOn(Arrays.asList(values.split(",+")));
- } else if (value instanceof List) {
- //noinspection unchecked
- component.setDependsOn((List)value);
- } else {
- AssertUtil.shouldNotReachHere();
- }
- }
-
- @Override
- public boolean accept(Object value) {
- if (value instanceof String) {
- return true;
- } else if (value instanceof List) {
- List> values = (List>) value;
- for (Object item : values) {
- if (!(item instanceof String)) {
- return false;
- }
- }
-
- return true;
- }
-
- AssertUtil.shouldNotReachHere();
- return true;
+ //noinspection unchecked
+ component.setDependsOn((List)value);
}
},
@@ -119,18 +73,27 @@ public enum Attributes {
@Override
public void apply(Component component, Object value) {
super.apply(component, value);
-
- if (value instanceof String) {
- value = Boolean.parseBoolean((String) value);
- }
-
component.setEnabled((Boolean) value);
}
},
+ /**
+ * 降级回调
+ *
+ * @since 1.0.2
+ */
+ DEGRADE_CALLBACK("degrade-callback", DegradeCallback.class) {
+ @Override
+ public void apply(Component component, Object value) {
+ super.apply(component, value);
+ component.setDegradeCallback((DegradeCallback) value);
+ }
+ },
+
;
private final String name;
+
/**
* Attribute accepted type.
*/
@@ -146,20 +109,14 @@ public enum Attributes {
}
public boolean accept(Object value) {
- boolean directInstance = accept.isInstance(value);
- if (directInstance) {
- return true;
- }
-
- // String类型暂时返回true
- return value instanceof String;
+ return accept.isInstance(value);
}
public void apply(Component component, Object value) {
component.addAttribute(new AttributeHolder(this, value));
}
- public static Attributes with(String name) {
+ public static Attributes byName(String name) {
for (Attributes v : values()) {
if (v.name.equals(name)) {
return v;
diff --git a/smart-flow-core/src/main/java/org/smartboot/flow/core/builder/AbstractComponentBuilder.java b/smart-flow-core/src/main/java/org/smartboot/flow/core/builder/AbstractComponentBuilder.java
index adb528b75d1fc8d5a292ed67e834115150fe3a5a..6ccc39da16a3467fa92f7016710e45712513f1cb 100644
--- a/smart-flow-core/src/main/java/org/smartboot/flow/core/builder/AbstractComponentBuilder.java
+++ b/smart-flow-core/src/main/java/org/smartboot/flow/core/builder/AbstractComponentBuilder.java
@@ -1,7 +1,8 @@
package org.smartboot.flow.core.builder;
-import org.smartboot.flow.core.component.Attributes;
+import org.smartboot.flow.core.attribute.AttributeValueResolver;
+import org.smartboot.flow.core.attribute.Attributes;
import org.smartboot.flow.core.component.Component;
import org.smartboot.flow.core.util.AssertUtil;
@@ -15,6 +16,8 @@ import java.util.Map;
*/
public abstract class AbstractComponentBuilder {
+ private final AttributeValueResolver valueResolver = new AttributeValueResolver();
+
/**
* Component attribute attributes.
*/
@@ -24,8 +27,11 @@ public abstract class AbstractComponentBuilder {
public AbstractComponentBuilder apply(Attributes attributes, Object value) {
AssertUtil.notNull(attributes, "Unknown attribute");
AssertUtil.notNull(value, "null");
- AssertUtil.isTrue(attributes.accept(value), "Un-matched type");
- this.settings.put(attributes, value);
+
+ Object resolved = valueResolver.resolve(attributes, value);
+
+ AssertUtil.isTrue(attributes.accept(resolved), "Un-matched type");
+ this.settings.put(attributes, resolved);
return this;
}
diff --git a/smart-flow-core/src/main/java/org/smartboot/flow/core/builder/AdapterBuilder.java b/smart-flow-core/src/main/java/org/smartboot/flow/core/builder/AdapterBuilder.java
new file mode 100644
index 0000000000000000000000000000000000000000..af380278e0c59f1de933d1f77e3f1f483646afc9
--- /dev/null
+++ b/smart-flow-core/src/main/java/org/smartboot/flow/core/builder/AdapterBuilder.java
@@ -0,0 +1,39 @@
+package org.smartboot.flow.core.builder;
+
+import org.smartboot.flow.core.Adapter;
+import org.smartboot.flow.core.attribute.Attributes;
+import org.smartboot.flow.core.component.AdapterComponent;
+import org.smartboot.flow.core.component.Component;
+
+/**
+ * @author huqiang
+ * @since 2022/12/7 19:36
+ */
+public class AdapterBuilder extends ExecutableBuilder {
+
+ private final Adapter adapter;
+
+ private final Component component;
+
+ public AdapterBuilder(Adapter adapter, Component component) {
+ this.adapter = adapter;
+ this.component = component;
+ }
+
+ public AdapterComponent build() {
+ AdapterComponent adapterComponent = new AdapterComponent<>();
+ adapterComponent.setAdapter(adapter);
+ adapterComponent.setComponent(component);
+ applyValues(adapterComponent);
+ return adapterComponent;
+ }
+
+ /**
+ * Resolve return type.
+ */
+ public AdapterBuilder apply(Attributes attributes, Object value) {
+ super.apply(attributes, value);
+ return this;
+ }
+
+}
diff --git a/smart-flow-core/src/main/java/org/smartboot/flow/core/builder/ChooseBuilder.java b/smart-flow-core/src/main/java/org/smartboot/flow/core/builder/ChooseBuilder.java
index 7844778fc49ce66eb2ed560642af0012de942630..fab6a2111e3deb8ccda834a6830b510f0e327976 100644
--- a/smart-flow-core/src/main/java/org/smartboot/flow/core/builder/ChooseBuilder.java
+++ b/smart-flow-core/src/main/java/org/smartboot/flow/core/builder/ChooseBuilder.java
@@ -2,7 +2,7 @@ package org.smartboot.flow.core.builder;
import org.smartboot.flow.core.Condition;
-import org.smartboot.flow.core.component.Attributes;
+import org.smartboot.flow.core.attribute.Attributes;
import org.smartboot.flow.core.component.ChooseComponent;
import org.smartboot.flow.core.component.Component;
import org.smartboot.flow.core.executable.Executable;
@@ -44,7 +44,7 @@ public class ChooseBuilder extends ExecutableBuilder {
public ChooseBuilder newBranch(Object branch, Executable executable) {
AssertUtil.notNull(branch, "branch must not be null");
- branches.put(branch, newAdapter(executable));
+ branches.put(branch, asComponent(executable));
return this;
}
@@ -53,10 +53,10 @@ public class ChooseBuilder extends ExecutableBuilder {
}
public PipelineBuilder end(Executable defaultBranch) {
- return end(newAdapter(defaultBranch));
+ return end(asComponent(defaultBranch));
}
- public Component build(Component defaultBranch) {
+ public ChooseComponent build(Component defaultBranch) {
ChooseComponent chooseComponent = new ChooseComponent<>(condition, branches, defaultBranch);
applyValues(chooseComponent);
return chooseComponent;
@@ -67,7 +67,7 @@ public class ChooseBuilder extends ExecutableBuilder {
}
public Component build(Executable defaultBranch) {
- return build(newAdapter(defaultBranch));
+ return build(asComponent(defaultBranch));
}
public PipelineBuilder end(Component defaultBranch) {
diff --git a/smart-flow-core/src/main/java/org/smartboot/flow/core/builder/ExecutableBuilder.java b/smart-flow-core/src/main/java/org/smartboot/flow/core/builder/ExecutableBuilder.java
index 506f8fe53659b5630025fbd0aa7d95a90d1d4eb2..db2e27e0c86338a8d3eac00bce7771595b96ac8b 100644
--- a/smart-flow-core/src/main/java/org/smartboot/flow/core/builder/ExecutableBuilder.java
+++ b/smart-flow-core/src/main/java/org/smartboot/flow/core/builder/ExecutableBuilder.java
@@ -1,7 +1,7 @@
package org.smartboot.flow.core.builder;
-import org.smartboot.flow.core.component.Attributes;
+import org.smartboot.flow.core.attribute.Attributes;
import org.smartboot.flow.core.component.Component;
import org.smartboot.flow.core.executable.Executable;
import org.smartboot.flow.core.executable.ExecutableAdapter;
@@ -21,6 +21,14 @@ public class ExecutableBuilder extends AbstractComponentBuilder {
return adapter;
}
+ /**
+ * Created as pure component.
+ */
+ public Component asComponent(Executable executable) {
+ AssertUtil.notNull(executable, "must not be null");
+ return new ExecutableAdapter<>(executable);
+ }
+
public ExecutableBuilder apply(Attributes attributes, Object value) {
super.apply(attributes, value);
return this;
diff --git a/smart-flow-core/src/main/java/org/smartboot/flow/core/builder/IfComponentBuilder.java b/smart-flow-core/src/main/java/org/smartboot/flow/core/builder/IfComponentBuilder.java
index 8a2fad8a39602058c27e39b03721ae1d5d80f681..f38b26697977a0829646a559bfa3fc01234b4486 100644
--- a/smart-flow-core/src/main/java/org/smartboot/flow/core/builder/IfComponentBuilder.java
+++ b/smart-flow-core/src/main/java/org/smartboot/flow/core/builder/IfComponentBuilder.java
@@ -2,7 +2,7 @@ package org.smartboot.flow.core.builder;
import org.smartboot.flow.core.Condition;
-import org.smartboot.flow.core.component.Attributes;
+import org.smartboot.flow.core.attribute.Attributes;
import org.smartboot.flow.core.component.Component;
import org.smartboot.flow.core.component.IfComponent;
import org.smartboot.flow.core.executable.Executable;
@@ -51,13 +51,13 @@ public class IfComponentBuilder extends ExecutableBuilder{
}
public PipelineBuilder then(Executable executable) {
- Component execute = super.newAdapter(executable);
+ Component execute = super.asComponent(executable);
return this.then(execute, null);
}
public PipelineBuilder then(Executable executable, Executable otherwise) {
- Component execute = super.newAdapter(executable);
- Component component = otherwise != null ? super.newAdapter(otherwise) : null;
+ Component execute = super.asComponent(executable);
+ Component component = otherwise != null ? super.asComponent(otherwise) : null;
return then(execute, component);
}
@@ -74,8 +74,8 @@ public class IfComponentBuilder extends ExecutableBuilder{
}
public Component build(Executable executable, Executable otherwise) {
- Component execute = super.newAdapter(executable);
- Component component = otherwise != null ? super.newAdapter(otherwise) : null;
+ Component execute = super.asComponent(executable);
+ Component component = otherwise != null ? super.asComponent(otherwise) : null;
return this.build(execute, component);
}
diff --git a/smart-flow-core/src/main/java/org/smartboot/flow/core/builder/PipelineBuilder.java b/smart-flow-core/src/main/java/org/smartboot/flow/core/builder/PipelineBuilder.java
index 4b5adff317125ba071336f4b8c55ee80fd9389ac..a7df4ccce59afcff19032e570469b5e2bfcef9a8 100644
--- a/smart-flow-core/src/main/java/org/smartboot/flow/core/builder/PipelineBuilder.java
+++ b/smart-flow-core/src/main/java/org/smartboot/flow/core/builder/PipelineBuilder.java
@@ -1,6 +1,7 @@
package org.smartboot.flow.core.builder;
+import org.smartboot.flow.core.Adapter;
import org.smartboot.flow.core.Condition;
import org.smartboot.flow.core.Pipeline;
import org.smartboot.flow.core.component.Component;
@@ -59,6 +60,10 @@ public class PipelineBuilder {
return new IfComponentBuilder<>(this, condition);
}
+ public PipelineBuilder adapter(Adapter adapter, Component component) {
+ return this.next(new AdapterBuilder<>(adapter, component).build());
+ }
+
public ChooseBuilder choose(Condition condition) {
AssertUtil.notNull(condition, "must not be null");
return new ChooseBuilder<>(this, condition);
diff --git a/smart-flow-core/src/main/java/org/smartboot/flow/core/common/ComponentType.java b/smart-flow-core/src/main/java/org/smartboot/flow/core/common/ComponentType.java
index 288a406e8b68c858d5b4be87366201e28bc2b4f8..942b9f279e443bb48a7840e37b162dbc20610ff8 100644
--- a/smart-flow-core/src/main/java/org/smartboot/flow/core/common/ComponentType.java
+++ b/smart-flow-core/src/main/java/org/smartboot/flow/core/common/ComponentType.java
@@ -10,6 +10,6 @@ public enum ComponentType {
IF,
CHOOSE,
PIPELINE,
+ ADAPTER,
- ;
}
diff --git a/smart-flow-core/src/main/java/org/smartboot/flow/core/common/Pair.java b/smart-flow-core/src/main/java/org/smartboot/flow/core/common/Pair.java
new file mode 100644
index 0000000000000000000000000000000000000000..4ef734dafc955ea0a08f6885c226e1efc53d004c
--- /dev/null
+++ b/smart-flow-core/src/main/java/org/smartboot/flow/core/common/Pair.java
@@ -0,0 +1,39 @@
+package org.smartboot.flow.core.common;
+
+import java.io.Serializable;
+
+/**
+ * @author qinluo
+ * @date 2022/12/7 21:14
+ * @since 1.0.0
+ */
+public class Pair implements Serializable {
+
+ private static final long serialVersionUID = -6226657512890578902L;
+
+ private T left;
+ private S right;
+
+ public static Pair of(T left, S right) {
+ Pair pair = new Pair<>();
+ pair.left = left;
+ pair.right = right;
+ return pair;
+ }
+
+ public T getLeft() {
+ return left;
+ }
+
+ public void setLeft(T left) {
+ this.left = left;
+ }
+
+ public S getRight() {
+ return right;
+ }
+
+ public void setRight(S right) {
+ this.right = right;
+ }
+}
diff --git a/smart-flow-core/src/main/java/org/smartboot/flow/core/component/AdapterComponent.java b/smart-flow-core/src/main/java/org/smartboot/flow/core/component/AdapterComponent.java
new file mode 100644
index 0000000000000000000000000000000000000000..c2258a1c1d8adb60a8d6e568be9e53e464de4ee2
--- /dev/null
+++ b/smart-flow-core/src/main/java/org/smartboot/flow/core/component/AdapterComponent.java
@@ -0,0 +1,106 @@
+package org.smartboot.flow.core.component;
+
+import org.smartboot.flow.core.Adapter;
+import org.smartboot.flow.core.EngineContext;
+import org.smartboot.flow.core.Key;
+import org.smartboot.flow.core.common.ComponentType;
+import org.smartboot.flow.core.common.Pair;
+import org.smartboot.flow.core.util.AssertUtil;
+import org.smartboot.flow.core.visitor.ComponentVisitor;
+
+/**
+ * 适配器组件
+ *
+ * @author huqiang
+ * @since 2022/12/7 19:01
+ */
+public class AdapterComponent extends Component {
+
+ /**
+ * 业务适配器接口定义
+ */
+ private Adapter adapter;
+
+ /**
+ * 待执行的适配流程组件
+ */
+ private Component component;
+
+ public void setAdapter(Adapter adapter) {
+ this.adapter = adapter;
+ }
+
+ public void setComponent(Component component) {
+ this.component = component;
+ }
+
+ @Override
+ public String describe() {
+ return "adapter@" + adapter.describe();
+ }
+
+ @Override
+ public void rollback(EngineContext context) {
+ AdapterContext newContext = context.remove(Key.of(this));
+ if (newContext == null) {
+ return;
+ }
+ component.rollback(newContext);
+ }
+
+ @Override
+ public boolean isRollbackable(EngineContext context) {
+ AdapterContext newContext = context.getExt(Key.of(this));
+ return newContext != null;
+ }
+
+ @Override
+ public void doValidate() {
+ AssertUtil.notNull(adapter, "adapter[" + getName() + "] adapter must not be null");
+ AssertUtil.notNull(component, "component[" + getName() + "] component must not be null");
+ }
+
+ @Override
+ public int invoke(EngineContext context) throws Throwable {
+ context.enter(this);
+
+ // Convert.
+ Pair pair = adapter.before(context);
+ AssertUtil.notNull(pair, "adapter[" + getName() + "] result must not be null");
+
+ // Adapter ctx 本质只是进行参数的转换,并不是像子流程那样新起一个ctx,所以对一些操作类的方法,相关属性需要回设到父流程去
+ AdapterContext
newContext = new AdapterContext<>();
+ newContext.setReq(pair.getLeft());
+ newContext.setResult(pair.getRight());
+ // copy parent
+ context.copy(newContext);
+ newContext.setParent(context);
+
+ // Store converted objects.
+ context.putExt(Key.of(this), newContext);
+
+ int invoke;
+ try {
+ invoke = component.invoke(newContext);
+ } finally {
+ context.exit(this);
+ }
+
+ // Apply result to parent context.
+ adapter.after(context, newContext);
+ return invoke;
+ }
+
+ @Override
+ public void visit(ComponentVisitor visitor) {
+ ComponentVisitor componentVisitor = visitor.visitComponent(component.getType(), component.getName(), component.describe());
+ if (componentVisitor != null) {
+ component.visit(componentVisitor);
+ }
+ }
+
+ @Override
+ public ComponentType getType() {
+ return ComponentType.ADAPTER;
+ }
+}
diff --git a/smart-flow-core/src/main/java/org/smartboot/flow/core/component/AdapterContext.java b/smart-flow-core/src/main/java/org/smartboot/flow/core/component/AdapterContext.java
new file mode 100644
index 0000000000000000000000000000000000000000..caf23c55249eb5b5c708fc1d9cf7c8d8830250c1
--- /dev/null
+++ b/smart-flow-core/src/main/java/org/smartboot/flow/core/component/AdapterContext.java
@@ -0,0 +1,47 @@
+package org.smartboot.flow.core.component;
+
+import org.smartboot.flow.core.EngineContext;
+
+/**
+ * @author qinluo
+ * @date 2022/12/11 20:09
+ * @since 1.0.0
+ */
+public class AdapterContext extends EngineContext {
+
+ @Override
+ public String getTrace() {
+ return this.parent.getTrace();
+ }
+
+ @Override
+ public boolean getRollback() {
+ return super.getRollback();
+ }
+
+ @Override
+ public void setRollback(boolean rollback) {
+ this.parent.setRollback(rollback);
+ super.setRollback(rollback);
+ }
+
+ @Override
+ public Throwable getFatal() {
+ return super.getFatal();
+ }
+
+ @Override
+ public void setFatal(Throwable fatal) {
+ this.parent.setFatal(fatal);
+ super.setFatal(fatal);
+ }
+
+ @Override
+ public void broken(boolean broken) {
+ super.brokenAll(broken);
+ }
+
+ public void setParent(EngineContext ctx) {
+ this.parent = ctx;
+ }
+}
diff --git a/smart-flow-core/src/main/java/org/smartboot/flow/core/component/ChooseComponent.java b/smart-flow-core/src/main/java/org/smartboot/flow/core/component/ChooseComponent.java
index 4e4563e5c1a378d9b019b109a931fe287e7a045e..f11520b4fc4a473a62c83583c6bc102939c7a30a 100644
--- a/smart-flow-core/src/main/java/org/smartboot/flow/core/component/ChooseComponent.java
+++ b/smart-flow-core/src/main/java/org/smartboot/flow/core/component/ChooseComponent.java
@@ -4,6 +4,7 @@ package org.smartboot.flow.core.component;
import org.smartboot.flow.core.Condition;
import org.smartboot.flow.core.EngineContext;
import org.smartboot.flow.core.Key;
+import org.smartboot.flow.core.common.ComponentType;
import org.smartboot.flow.core.util.AssertUtil;
import org.smartboot.flow.core.visitor.ComponentVisitor;
@@ -28,8 +29,6 @@ public class ChooseComponent extends Component {
this.branches = branches;
this.condition = condition;
this.defaultBranch = defaultBranch;
- // Compatible parse from xml.
- this.allBranchWasString = branches.keySet().stream().allMatch(p -> p instanceof String);
}
public void setBranches(Map