2121import io .serverlessworkflow .impl .WorkflowContext ;
2222import io .serverlessworkflow .impl .WorkflowModel ;
2323import io .serverlessworkflow .impl .executors .TaskExecutor ;
24- import java .util .LinkedList ;
2524import java .util .Map ;
2625import java .util .Optional ;
27- import java .util .Queue ;
26+ import java .util .concurrent . CompletableFuture ;
2827import java .util .stream .Collectors ;
2928
3029public class FlexibleProcessManager {
@@ -38,48 +37,104 @@ public FlexibleProcessManager(
3837 this .executors = executors ;
3938 }
4039
41- public void run (
40+ public CompletableFuture < WorkflowModel > run (
4241 WorkflowContext workflowContext , Optional <TaskContext > parentContext , WorkflowModel input ) {
43- boolean exit = flexibleProcess .getExitCondition ().test (input );
44- int counter = flexibleProcess .getMaxAttempts ();
45- while (!exit ) {
46- Map <Activity , TaskExecutor <?>> availableExecutors = getExecutors (input );
47- if (availableExecutors .isEmpty ()) {
48- return ;
49- }
50- Queue <Map .Entry <Activity , TaskExecutor <?>>> executorQueue =
51- new LinkedList <>(availableExecutors .entrySet ());
52- while (!executorQueue .isEmpty ()) {
53- Map .Entry <Activity , TaskExecutor <?>> entry = executorQueue .poll ();
54- Activity activity = entry .getKey ();
55- TaskExecutor <?> executor = entry .getValue ();
56- try {
57- executor
58- .apply (workflowContext , parentContext , input )
59- .join (); // blocking, because we run flexible process one by one
60- if (activity .getPostAction () != null ) {
61- activity .getPostAction ().accept (input );
42+
43+ int maxAttempts = flexibleProcess .getMaxAttempts ();
44+ CompletableFuture <WorkflowModel > promise = new CompletableFuture <>();
45+ runAttempt (workflowContext , parentContext , input , maxAttempts , promise );
46+ return promise ;
47+ }
48+
49+ private void runAttempt (
50+ WorkflowContext workflowContext ,
51+ Optional <TaskContext > parentContext ,
52+ WorkflowModel input ,
53+ int remainingAttempts ,
54+ CompletableFuture <WorkflowModel > promise ) {
55+
56+ if (promise .isDone ()) {
57+ return ;
58+ }
59+
60+ if (flexibleProcess .getExitCondition ().test (input )) {
61+ promise .complete (input );
62+ return ;
63+ }
64+
65+ if (remainingAttempts <= 0 ) {
66+ promise .complete (input );
67+ return ;
68+ }
69+
70+ Map <Activity , TaskExecutor <?>> availableExecutors = getExecutors (input );
71+ if (availableExecutors .isEmpty ()) {
72+ promise .complete (input );
73+ return ;
74+ }
75+
76+ CompletableFuture <WorkflowModel > passFuture =
77+ runOnePassSequentially (workflowContext , parentContext , input , availableExecutors );
78+
79+ passFuture .whenComplete (
80+ (updatedInput , ex ) -> {
81+ if (ex != null ) {
82+ promise .completeExceptionally (ex );
83+ return ;
84+ }
85+
86+ if (flexibleProcess .getExitCondition ().test (updatedInput )) {
87+ promise .complete (updatedInput );
88+ return ;
6289 }
63- activity .setExecuted ();
64- } catch (Exception e ) {
65- throw new RuntimeException ("Error executing activity: " + activity .getName (), e );
66- }
67- exit = flexibleProcess .getExitCondition ().test (input );
68- if (exit ) {
69- break ;
70- }
71- }
72- counter --;
73- if (counter <= 0 ) {
74- break ;
75- }
90+ runAttempt (workflowContext , parentContext , updatedInput , remainingAttempts - 1 , promise );
91+ });
92+ }
93+
94+ private CompletableFuture <WorkflowModel > runOnePassSequentially (
95+ WorkflowContext workflowContext ,
96+ Optional <TaskContext > parentContext ,
97+ WorkflowModel input ,
98+ Map <Activity , TaskExecutor <?>> availableExecutors ) {
99+
100+ return runNextExecutor (
101+ workflowContext , parentContext , input , availableExecutors .entrySet ().iterator ());
102+ }
103+
104+ private CompletableFuture <WorkflowModel > runNextExecutor (
105+ WorkflowContext workflowContext ,
106+ Optional <TaskContext > parentContext ,
107+ WorkflowModel input ,
108+ java .util .Iterator <Map .Entry <Activity , TaskExecutor <?>>> it ) {
109+
110+ if (flexibleProcess .getExitCondition ().test (input ) || !it .hasNext ()) {
111+ return CompletableFuture .completedFuture (input );
76112 }
113+
114+ Map .Entry <Activity , TaskExecutor <?>> entry = it .next ();
115+ Activity activity = entry .getKey ();
116+ TaskExecutor <?> executor = entry .getValue ();
117+
118+ return executor
119+ .apply (workflowContext , parentContext , input )
120+ .thenApply (
121+ taskContext -> {
122+ if (activity .getPostAction () != null ) {
123+ activity .getPostAction ().accept (input );
124+ }
125+ activity .setExecuted ();
126+ return input ;
127+ })
128+ .thenCompose (
129+ updatedInput -> runNextExecutor (workflowContext , parentContext , updatedInput , it ));
77130 }
78131
79132 private Map <Activity , TaskExecutor <?>> getExecutors (WorkflowModel input ) {
80133 return executors .entrySet ().stream ()
81- .filter (activity -> activity .getKey ().isRepeatable () || !activity .getKey ().isExecuted ())
82- .filter (e -> e .getKey ().getEntryCondition ().test (input ))
134+ .filter (
135+ activity ->
136+ (activity .getKey ().isRepeatable () || !activity .getKey ().isExecuted ())
137+ && activity .getKey ().getEntryCondition ().test (input ))
83138 .collect (Collectors .toMap (Map .Entry ::getKey , Map .Entry ::getValue ));
84139 }
85140}
0 commit comments