|
| 1 | +/* |
| 2 | + * Copyright 2020-Present The Serverless Workflow Specification Authors |
| 3 | + * |
| 4 | + * Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | + * you may not use this file except in compliance with the License. |
| 6 | + * You may obtain a copy of the License at |
| 7 | + * |
| 8 | + * http://www.apache.org/licenses/LICENSE-2.0 |
| 9 | + * |
| 10 | + * Unless required by applicable law or agreed to in writing, software |
| 11 | + * distributed under the License is distributed on an "AS IS" BASIS, |
| 12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 13 | + * See the License for the specific language governing permissions and |
| 14 | + * limitations under the License. |
| 15 | + */ |
1 | 16 | package io.serverlessworkflow.impl.executors; |
2 | 17 |
|
3 | 18 | import io.serverlessworkflow.api.types.RunShell; |
4 | 19 | import io.serverlessworkflow.api.types.RunTask; |
5 | | -import io.serverlessworkflow.api.types.RunTaskConfigurationUnion; |
| 20 | +import io.serverlessworkflow.api.types.TaskBase; |
6 | 21 | import io.serverlessworkflow.impl.TaskContext; |
| 22 | +import io.serverlessworkflow.impl.WorkflowApplication; |
7 | 23 | import io.serverlessworkflow.impl.WorkflowContext; |
8 | 24 | import io.serverlessworkflow.impl.WorkflowDefinition; |
9 | 25 | import io.serverlessworkflow.impl.WorkflowModel; |
10 | 26 | import io.serverlessworkflow.impl.WorkflowMutablePosition; |
11 | 27 | import io.serverlessworkflow.impl.WorkflowUtils; |
12 | 28 | import io.serverlessworkflow.impl.expressions.ExpressionUtils; |
13 | | - |
14 | 29 | import java.io.IOException; |
| 30 | +import java.io.OutputStream; |
15 | 31 | import java.util.Objects; |
16 | | -import java.util.Optional; |
17 | 32 | import java.util.concurrent.CompletableFuture; |
18 | 33 |
|
19 | | -public class RunExecutor extends RegularTaskExecutor<RunTask> { |
| 34 | +public abstract class RunExecutor<T extends TaskBase> extends RegularTaskExecutor<T> { |
| 35 | + |
| 36 | + protected RunExecutor(RegularTaskExecutorBuilder<T> builder) { |
| 37 | + super(builder); |
| 38 | + } |
20 | 39 |
|
21 | | - private final TaskExecutor<?> taskExecutor; |
| 40 | + public static class RunTaskExecutorBuilder |
| 41 | + extends RegularTaskExecutor.RegularTaskExecutorBuilder<RunTask> { |
22 | 42 |
|
23 | | - private RunExecutor(RunExecutorBuilder builder) { |
24 | | - super(builder); |
25 | | - this.taskExecutor = builder.taskExecutor; |
| 43 | + protected RunTaskExecutorBuilder( |
| 44 | + WorkflowMutablePosition position, RunTask task, WorkflowDefinition definition) { |
| 45 | + super(position, task, definition); |
26 | 46 | } |
27 | 47 |
|
28 | 48 | @Override |
29 | | - protected CompletableFuture<WorkflowModel> internalExecute(WorkflowContext workflow, TaskContext taskContext) { |
30 | | - return TaskExecutorHelper.processTaskList( |
31 | | - taskExecutor, workflow, Optional.of(taskContext), taskContext.input()); |
| 49 | + protected RegularTaskExecutor<RunTask> buildInstance() { |
| 50 | + if (task.getRun().getRunShell() != null) { |
| 51 | + return new RunShellExecutor(this); |
| 52 | + } else { |
| 53 | + throw new RuntimeException("Unsupported run task type"); |
| 54 | + } |
32 | 55 | } |
| 56 | + } |
33 | 57 |
|
34 | | - public static class RunExecutorBuilder extends RegularTaskExecutorBuilder<RunTask> { |
| 58 | + public static class RunShellExecutor extends RunExecutor<RunTask> { |
35 | 59 |
|
36 | | - private TaskExecutor<?> taskExecutor; |
| 60 | + private final WorkflowApplication application; |
37 | 61 |
|
38 | | - protected RunExecutorBuilder(WorkflowMutablePosition position, RunTask runTask, WorkflowDefinition definition) { |
39 | | - super(position, runTask, definition); |
40 | | - |
41 | | - RunTaskConfigurationUnion run = runTask.getRun(); |
| 62 | + protected RunShellExecutor(RegularTaskExecutorBuilder<RunTask> builder) { |
| 63 | + super(builder); |
| 64 | + this.application = builder.application; |
| 65 | + } |
42 | 66 |
|
43 | | - if (run.get() instanceof RunShell runShell) { |
44 | | - // For RunShell, no internal task executor is created yet. |
45 | | - taskExecutor = (workflowContext, parentContext, input) -> { |
| 67 | + @Override |
| 68 | + protected CompletableFuture<WorkflowModel> internalExecute( |
| 69 | + WorkflowContext workflow, TaskContext taskContext) { |
46 | 70 |
|
47 | | - String command = ExpressionUtils.isExpr(runShell.getShell().getCommand()) ? |
48 | | - WorkflowUtils.buildStringResolver(definition.application(), runShell.getShell().getCommand(), input.asJavaObject()) |
49 | | - .apply(workflowContext, parentContext.get(), input) |
50 | | - : runShell.getShell().getCommand(); |
| 71 | + if (taskContext.task() != null && taskContext.task() instanceof RunTask runTask) { |
| 72 | + RunShell runShell = runTask.getRun().getRunShell(); |
51 | 73 |
|
52 | | - Objects.requireNonNull(command, "Shell command must be provided in RunShell task"); |
| 74 | + try { |
| 75 | + String command = |
| 76 | + ExpressionUtils.isExpr(runShell.getShell().getCommand()) |
| 77 | + ? WorkflowUtils.buildStringResolver( |
| 78 | + application, |
| 79 | + runShell.getShell().getCommand(), |
| 80 | + taskContext.input().asJavaObject()) |
| 81 | + .apply(workflow, taskContext, taskContext.input()) |
| 82 | + : runShell.getShell().getCommand(); |
53 | 83 |
|
54 | | - ProcessBuilder builder = new ProcessBuilder(command.split(" ")); |
| 84 | + Objects.requireNonNull(command, "Shell command must be provided in RunShell taskContext"); |
55 | 85 |
|
56 | | - try { |
57 | | - Process process = builder.start(); |
| 86 | + ProcessBuilder builder = new ProcessBuilder(command.split(" ")); |
58 | 87 |
|
59 | | - // get output of process |
60 | | - StringBuilder output = new StringBuilder(); |
61 | | - StringBuilder error = new StringBuilder(); |
62 | | - process.getInputStream().transferTo(new java.io.OutputStream() { |
63 | | - @Override |
64 | | - public void write(int b) { |
65 | | - output.append((char) b); |
66 | | - } |
67 | | - }); |
| 88 | + Process process = builder.start(); |
68 | 89 |
|
69 | | - process.getErrorStream().transferTo(new java.io.OutputStream() { |
70 | | - @Override |
71 | | - public void write(int b) { |
72 | | - error.append((char) b); |
73 | | - } |
74 | | - }); |
| 90 | + // get output of process |
| 91 | + StringBuilder output = new StringBuilder(); |
| 92 | + StringBuilder error = new StringBuilder(); |
| 93 | + process.getInputStream().transferTo(getOutputStream(output)); |
| 94 | + process.getErrorStream().transferTo(getOutputStream(error)); |
75 | 95 |
|
76 | | - boolean isAwait = runTask.getRun().get().isAwait(); |
77 | | - if (!isAwait) { |
78 | | - throw new UnsupportedOperationException("Non-await RunShell is not supported yet"); |
79 | | - } |
| 96 | + boolean isAwait = runTask.getRun().get().isAwait(); |
| 97 | + if (!isAwait) { |
| 98 | + throw new UnsupportedOperationException("Non-await RunShell is not supported yet"); |
| 99 | + } |
80 | 100 |
|
81 | | - int exitCode = process.waitFor(); |
| 101 | + int exitCode = process.waitFor(); |
82 | 102 |
|
83 | | - // Return a completed future with the input as output. |
84 | | - // get stdout and stderr in ProcessResult |
| 103 | + ProcessResult result = |
| 104 | + new ProcessResult(exitCode, output.toString().trim(), error.toString().trim()); |
85 | 105 |
|
86 | | - return CompletableFuture.completedFuture(new TaskContext( |
87 | | - definition.application().modelFactory().fromAny(new ProcessResult( |
88 | | - exitCode, output.toString().trim(), error.toString() |
89 | | - )) |
90 | | - , position, parentContext, "", runTask |
91 | | - )); |
| 106 | + return CompletableFuture.completedFuture(application.modelFactory().fromAny(result)); |
92 | 107 |
|
93 | | - } catch (IOException | InterruptedException e) { |
94 | | - throw new RuntimeException(e); |
95 | | - } |
96 | | - }; |
97 | | - } else { |
98 | | - throw new UnsupportedOperationException(run.getClass() + " is not supported yet in RunTask"); |
99 | | - } |
| 108 | + } catch (IOException | InterruptedException e) { |
| 109 | + throw new RuntimeException(e); |
100 | 110 | } |
| 111 | + } |
| 112 | + |
| 113 | + throw new RuntimeException("Task must be of type RunTask"); |
| 114 | + } |
101 | 115 |
|
| 116 | + private static OutputStream getOutputStream(StringBuilder output) { |
| 117 | + return new OutputStream() { |
102 | 118 | @Override |
103 | | - protected RegularTaskExecutor<RunTask> buildInstance() { |
104 | | - return new RunExecutor(this); |
| 119 | + public void write(int b) { |
| 120 | + output.append((char) b); |
105 | 121 | } |
| 122 | + }; |
106 | 123 | } |
107 | | - |
| 124 | + } |
108 | 125 | } |
0 commit comments