001/*
002 * (C) Copyright 2012-2018 Nuxeo (http://nuxeo.com/) and others.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *     http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 *
016 * Contributors:
017 *     Florent Guillaume
018 */
019package org.nuxeo.ecm.platform.routing.core.impl;
020
021import java.io.Serializable;
022import java.util.ArrayList;
023import java.util.Calendar;
024import java.util.Date;
025import java.util.List;
026import java.util.Map;
027
028import org.apache.commons.lang3.BooleanUtils;
029import org.apache.commons.lang3.builder.ToStringBuilder;
030import org.nuxeo.ecm.core.api.CoreSession;
031import org.nuxeo.ecm.core.api.DocumentModel;
032import org.nuxeo.ecm.core.api.model.Property;
033import org.nuxeo.ecm.core.api.model.impl.ListProperty;
034import org.nuxeo.ecm.core.api.model.impl.MapProperty;
035import org.nuxeo.ecm.platform.routing.api.DocumentRoute;
036import org.nuxeo.ecm.platform.routing.api.exception.DocumentRouteException;
037
038/**
039 * A node for a route graph. Represents operation chains, associated task and form, output transitions and their
040 * conditions, etc.
041 *
042 * @since 5.6
043 */
044public interface GraphNode {
045
046    String MERGE_ONE = "one";
047
048    String MERGE_ALL = "all";
049
050    String PROP_NODE_ID = "rnode:nodeId";
051
052    String PROP_TITLE = "dc:title";
053
054    String PROP_START = "rnode:start";
055
056    String PROP_STOP = "rnode:stop";
057
058    String PROP_MERGE = "rnode:merge";
059
060    String PROP_COUNT = "rnode:count";
061
062    String PROP_CANCELED = "rnode:canceled";
063
064    String PROP_INPUT_CHAIN = "rnode:inputChain";
065
066    String PROP_OUTPUT_CHAIN = "rnode:outputChain";
067
068    String PROP_HAS_TASK = "rnode:hasTask";
069
070    String PROP_VARIABLES_FACET = "rnode:variablesFacet";
071
072    String PROP_TRANSITIONS = "rnode:transitions";
073
074    String PROP_TRANS_NAME = "name";
075
076    String PROP_TRANS_TARGET = "targetId";
077
078    String PROP_TRANS_CONDITION = "condition";
079
080    String PROP_TRANS_RESULT = "result";
081
082    String PROP_TRANS_CHAIN = "chain";
083
084    String PROP_TRANS_LABEL = "label";
085
086    /**
087     * @since 7.1 a transition can hold a custom path
088     */
089    String PROP_TRANS_PATH = "path";
090
091    String PROP_TASK_ASSIGNEES = "rnode:taskAssignees";
092
093    String PROP_TASK_ASSIGNEES_VAR = "rnode:taskAssigneesExpr";
094
095    String PROP_TASK_ASSIGNEES_PERMISSION = "rnode:taskAssigneesPermission";
096
097    String PROP_TASK_DUE_DATE = "rnode:taskDueDate";
098
099    String PROP_TASK_DIRECTIVE = "rnode:taskDirective";
100
101    String PROP_TASK_LAYOUT = "rnode:taskLayout";
102
103    String PROP_TASK_BUTTONS = "rnode:taskButtons";
104
105    String PROP_BTN_NAME = "name";
106
107    String PROP_BTN_LABEL = "label";
108
109    String PROP_BTN_FILTER = "filter";
110
111    String PROP_NODE_X_COORDINATE = "rnode:taskX";
112
113    String PROP_NODE_Y_COORDINATE = "rnode:taskY";
114
115    /**
116     * @since 5.7.3 a node can create multiple tasks, in this case, this stores the status of the last task ended
117     */
118    String PROP_NODE_BUTTON = "rnode:button";
119
120    String PROP_NODE_START_DATE = "rnode:startDate";
121
122    String PROP_NODE_END_DATE = "rnode:endDate";
123
124    String PROP_NODE_LAST_ACTOR = "rnode:lastActor";
125
126    String PROP_TASK_DOC_TYPE = "rnode:taskDocType";
127
128    String PROP_TASK_NOTIFICATION_TEMPLATE = "rnode:taskNotificationTemplate";
129
130    String PROP_TASK_DUE_DATE_EXPR = "rnode:taskDueDateExpr";
131
132    /** @since 5.7.2 */
133    String PROP_EXECUTE_ONLY_FIRST_TRANSITION = "rnode:executeOnlyFirstTransition";
134
135    /**
136     * The sub-route model id (expression) to run, if present.
137     *
138     * @since 5.7.2
139     */
140    String PROP_SUB_ROUTE_MODEL_EXPR = "rnode:subRouteModelExpr";
141
142    /**
143     * The sub-route instance id being run while this node is suspended.
144     *
145     * @since 5.7.2
146     */
147    String PROP_SUB_ROUTE_INSTANCE_ID = "rnode:subRouteInstanceId";
148
149    /**
150     * The sub-route variables to set (key/value list).
151     *
152     * @since 5.7.2
153     */
154    String PROP_SUB_ROUTE_VARS = "rnode:subRouteVariables";
155
156    /** @since 5.7.2 */
157    String PROP_KEYVALUE_KEY = "key";
158
159    /** @since 5.7.2 */
160    String PROP_KEYVALUE_VALUE = "value";
161
162    // @since 5.7.2
163    String PROP_ESCALATION_RULES = "rnode:escalationRules";
164
165    // @since 5.7.2
166    String PROP_ESCALATION_RULE_ID = "name";
167
168    // @since 5.7.2
169    String PROP_ESCALATION_RULE_LABEL = "label";
170
171    // @since 5.7.2
172    String PROP_ESCALATION_RULE_MULTIPLE_EXECUTION = "multipleExecution";
173
174    // @since 5.7.2
175    String PROP_ESCALATION_RULE_CONDITION = "condition";
176
177    // @since 5.7.2
178    String PROP_ESCALATION_RULE_CHAIN = "chain";
179
180    // @since 5.7.2
181    String PROP_ESCALATION_RULE_EXECUTED = "executed";
182
183    // @since 5.9.3
184    String PROP_LAST_EXECUTION_TIME = "executionDate";
185
186    // @since 5.7.3
187    String PROP_HAS_MULTIPLE_TASKS = "rnode:hasMultipleTasks";
188
189    // @since 5.7.3
190    String PROP_TASKS_INFO = "rnode:tasksInfo";
191
192    // @since 5.7.3
193    String PROP_TASK_INFO_ACTOR = "actor";
194
195    // @since 5.7.3
196    String PROP_TASK_INFO_COMMENT = "comment";
197
198    // @since 5.7.3
199    String PROP_TASK_INFO_STATUS = "status";
200
201    // @since 5.7.3
202    String PROP_TASK_INFO_ENDED = "ended";
203
204    // @since 5.7.3
205    String PROP_TASK_INFO_TASK_DOC_ID = "taskDocId";
206
207    // @since 5.7.3
208    String PROP_ALLOW_TASK_REASSIGNMENT = "rnode:allowTaskReassignment";
209
210    // @since 5.6
211    // if present as the node variable, acts as a built-in variable:
212    // is passed to the task comment and logged in by audit;
213    // internally rested when set on node having multiple tasks.
214    String NODE_VARIABLE_COMMENT = "comment";
215
216    /**
217     * The internal state of a node.
218     */
219    enum State {
220        /** Node is ready. */
221        READY("ready", "toReady"),
222        /** Merge node is waiting for more incoming transitions. */
223        WAITING("waiting", "toWaiting"),
224        /** While executing input phase. Not persisted. */
225        RUNNING_INPUT,
226        /** Task node is waiting for task to be done. */
227        SUSPENDED("suspended", "toSuspended"),
228        /** While executing output phase. Not persisted. */
229        RUNNING_OUTPUT;
230
231        private final String lifeCycleState;
232
233        private final String transition;
234
235        private State() {
236            lifeCycleState = null;
237            transition = null;
238        }
239
240        private State(String lifeCycleState, String transition) {
241            this.lifeCycleState = lifeCycleState;
242            this.transition = transition;
243        }
244
245        /**
246         * Corresponding lifecycle state.
247         */
248        public String getLifeCycleState() {
249            return lifeCycleState;
250        }
251
252        /**
253         * Transition leading to this state.
254         */
255        public String getTransition() {
256            return transition;
257        }
258
259        public static State fromString(String s) {
260            try {
261                return State.valueOf(s.toUpperCase());
262            } catch (IllegalArgumentException e) {
263                throw new IllegalArgumentException(s);
264            }
265        }
266    }
267
268    /**
269     * @since 7.1
270     */
271    class Point {
272
273        public double x;
274
275        public double y;
276
277        public Point(double x, double y) {
278            this.x = x;
279            this.y = y;
280        }
281    }
282
283    class Transition implements Comparable<Transition>, Serializable {
284
285        public GraphNode source;
286
287        public MapProperty prop;
288
289        public String id;
290
291        public String condition;
292
293        public String chain;
294
295        public String target;
296
297        public String label;
298
299        public boolean result;
300
301        /**
302         * @since 7.1
303         */
304        public List<Point> path;
305
306        /** Computed by graph. */
307        public boolean loop;
308
309        protected Transition(GraphNode source, Property p) {
310            this.source = source;
311            prop = (MapProperty) p;
312            id = (String) prop.get(PROP_TRANS_NAME).getValue();
313            condition = (String) prop.get(PROP_TRANS_CONDITION).getValue();
314            chain = (String) prop.get(PROP_TRANS_CHAIN).getValue();
315            target = (String) prop.get(PROP_TRANS_TARGET).getValue();
316            label = (String) prop.get(PROP_TRANS_LABEL).getValue();
317            Property resultProp = prop.get(PROP_TRANS_RESULT);
318            if (resultProp != null) {
319                result = BooleanUtils.isTrue(resultProp.getValue(Boolean.class));
320            }
321        }
322
323        protected void setResult(boolean bool) {
324            result = bool;
325            prop.get(PROP_TRANS_RESULT).setValue(Boolean.valueOf(bool));
326        }
327
328        @Override
329        public int compareTo(Transition other) {
330            return id.compareTo(other.id);
331        }
332
333        @Override
334        public String toString() {
335            return new ToStringBuilder(this).append("id", id).append("condition", condition).append("result", result).toString();
336        }
337
338        public String getTarget() {
339            return target;
340        }
341
342        public String getId() {
343            return id;
344        }
345
346        public String getLabel() {
347            return label;
348        }
349
350        /**
351         * @since 7.1
352         */
353        public List<Point> getPath() {
354            if (path == null) {
355                path = computePath();
356            }
357            return path;
358        }
359
360        protected List<Point> computePath() {
361            ListProperty props = (ListProperty) prop.get(PROP_TRANS_PATH);
362            List<Point> points = new ArrayList<>(props.size());
363            for (Property p : props) {
364                points.add(new Point(
365                    (Double) p.get("x").getValue(),
366                    (Double) p.get("y").getValue()));
367            }
368            return points;
369        }
370    }
371
372    public class Button implements Comparable<Button> {
373
374        public GraphNode source;
375
376        public String name;
377
378        public String label;
379
380        public String filter;
381
382        public MapProperty prop;
383
384        public Button(GraphNode source, Property p) {
385            this.source = source;
386            this.prop = (MapProperty) p;
387            name = (String) prop.get(PROP_BTN_NAME).getValue();
388            label = (String) prop.get(PROP_BTN_LABEL).getValue();
389            filter = (String) prop.get(PROP_BTN_FILTER).getValue();
390        }
391
392        @Override
393        public int compareTo(Button other) {
394            return name.compareTo(other.name);
395        }
396
397        public String getLabel() {
398            return label;
399        }
400
401        public String getName() {
402            return name;
403        }
404
405        public String getFilter() {
406            return filter;
407        }
408    }
409
410    /**
411     * @since 5.7.2
412     */
413    class EscalationRule implements Comparable<EscalationRule> {
414
415        protected String id;
416
417        protected String label;
418
419        protected boolean multipleExecution;
420
421        protected String condition;
422
423        protected boolean executed;
424
425        protected String chain;
426
427        protected MapProperty prop;
428
429        protected GraphNode node;
430
431        /**
432         * @since 5.9.3
433         */
434        protected Calendar lastExcutionTime;
435
436        public EscalationRule(GraphNode node, Property p) {
437            this.prop = (MapProperty) p;
438            this.node = node;
439            this.id = (String) p.get(PROP_ESCALATION_RULE_ID).getValue();
440            this.label = (String) p.get(PROP_ESCALATION_RULE_LABEL).getValue();
441            Property multipleEvaluationProp = prop.get(PROP_ESCALATION_RULE_MULTIPLE_EXECUTION);
442            if (multipleEvaluationProp != null) {
443                multipleExecution = BooleanUtils.isTrue(multipleEvaluationProp.getValue(Boolean.class));
444            }
445            this.condition = (String) p.get(PROP_ESCALATION_RULE_CONDITION).getValue();
446            Property evaluatedProp = prop.get(PROP_ESCALATION_RULE_EXECUTED);
447            if (evaluatedProp != null) {
448                executed = BooleanUtils.isTrue(evaluatedProp.getValue(Boolean.class));
449            }
450            this.chain = (String) p.get(PROP_ESCALATION_RULE_CHAIN).getValue();
451            this.lastExcutionTime = (Calendar) p.get(PROP_LAST_EXECUTION_TIME).getValue();
452        }
453
454        @Override
455        public int compareTo(EscalationRule o) {
456            return id.compareTo(o.id);
457        }
458
459        public String getLabel() {
460            return label;
461        }
462
463        public String getChain() {
464            return chain;
465        }
466
467        public GraphNode getNode() {
468            return node;
469        }
470
471        public void setExecuted(boolean executed) {
472            this.executed = executed;
473            prop.get(PROP_ESCALATION_RULE_EXECUTED).setValue(Boolean.valueOf(executed));
474            if (executed) {
475                setExecutionTime(Calendar.getInstance());
476            }
477        }
478
479        protected void setExecutionTime(Calendar time) {
480            prop.get(PROP_LAST_EXECUTION_TIME).setValue(time);
481            this.lastExcutionTime = time;
482        }
483
484        public boolean isExecuted() {
485            return executed;
486        }
487
488        public String getId() {
489            return id;
490        }
491
492        public boolean isMultipleExecution() {
493            return multipleExecution;
494        }
495
496        /**
497         * @since 5.9.3 Returns 'null' if the node was not executed, or the executed date was not computed ( for rules
498         *        created before 5.9.3)
499         */
500        public Calendar getLastExecutionTime() {
501            if (executed && lastExcutionTime != null) {
502                return lastExcutionTime;
503            }
504            return null;
505        }
506    }
507
508    /**
509     * @since 5.7.3
510     */
511    class TaskInfo implements Comparable<TaskInfo>, Serializable {
512
513        protected String taskDocId;
514
515        protected String actor;
516
517        protected String comment;
518
519        protected String status;
520
521        protected boolean ended;
522
523        protected MapProperty prop;
524
525        protected GraphNode node;
526
527        public TaskInfo(GraphNode node, Property p) {
528            this.prop = (MapProperty) p;
529            this.node = node;
530            this.taskDocId = (String) p.get(PROP_TASK_INFO_TASK_DOC_ID).getValue();
531            this.status = (String) p.get(PROP_TASK_INFO_STATUS).getValue();
532            this.actor = (String) p.get(PROP_TASK_INFO_ACTOR).getValue();
533            this.comment = (String) p.get(PROP_TASK_INFO_COMMENT).getValue();
534            Property ended = prop.get(PROP_TASK_INFO_ENDED);
535            if (ended != null) {
536                this.ended = BooleanUtils.isTrue(ended.getValue(Boolean.class));
537            }
538        }
539
540        public TaskInfo(GraphNode node, String taskDocId) {
541            this.node = node;
542            this.prop = (MapProperty) ((ListProperty) node.getDocument().getProperty(PROP_TASKS_INFO)).addEmpty();
543            this.prop.get(PROP_TASK_INFO_TASK_DOC_ID).setValue(taskDocId);
544            this.taskDocId = taskDocId;
545        }
546
547        @Override
548        public int compareTo(TaskInfo o) {
549            return taskDocId.compareTo(o.taskDocId);
550        }
551
552        public String getTaskDocId() {
553            return taskDocId;
554        }
555
556        public String getActor() {
557            return actor;
558        }
559
560        public String getComment() {
561            return comment;
562        }
563
564        public String getStatus() {
565            return status;
566        }
567
568        public GraphNode getNode() {
569            return node;
570        }
571
572        public boolean isEnded() {
573            return ended;
574        }
575
576        public void setComment(String comment) {
577            this.comment = comment;
578            prop.get(PROP_TASK_INFO_COMMENT).setValue(comment);
579        }
580
581        public void setStatus(String status) {
582            this.status = status;
583            prop.get(PROP_TASK_INFO_STATUS).setValue(status);
584
585        }
586
587        public void setActor(String actor) {
588            this.actor = actor;
589            prop.get(PROP_TASK_INFO_ACTOR).setValue(actor);
590        }
591
592        public void setEnded(boolean ended) {
593            this.ended = ended;
594            prop.get(PROP_TASK_INFO_ENDED).setValue(Boolean.valueOf(ended));
595        }
596    }
597
598    /**
599     * Get the node id.
600     *
601     * @return the node id
602     */
603    String getId();
604
605    /**
606     * Get the node state.
607     *
608     * @return the node state
609     */
610    State getState();
611
612    /**
613     * Set the node state.
614     *
615     * @param state the node state
616     */
617    void setState(State state);
618
619    /**
620     * Checks if this is the start node.
621     */
622    boolean isStart();
623
624    /**
625     * Checks if this is a stop node.
626     */
627    boolean isStop();
628
629    /**
630     * Checks if this is a merge node.
631     */
632    boolean isMerge();
633
634    /**
635     * Checks if the merge is ready to execute (enough input transitions are present).
636     */
637    boolean canMerge();
638
639    /**
640     * Notes that this node was canceled (increments canceled counter).
641     */
642    void setCanceled();
643
644    /**
645     * Gets the canceled count for this node.
646     */
647    long getCanceledCount();
648
649    /**
650     * Cancels the tasks not ended on this node.
651     */
652    void cancelTasks();
653
654    /**
655     * Get input chain.
656     *
657     * @return the input chain
658     */
659    String getInputChain();
660
661    /**
662     * Get output chain.
663     *
664     * @return the output chain
665     */
666    String getOutputChain();
667
668    /**
669     * Checks it this node has an associated user task.
670     */
671    boolean hasTask();
672
673    /**
674     * Gets the task assignees
675     *
676     * @return the task assignees
677     */
678    List<String> getTaskAssignees();
679
680    /**
681     * Gets the due date
682     */
683    Date getTaskDueDate();
684
685    /**
686     * Gets the task directive
687     */
688    String getTaskDirective();
689
690    /**
691     * Gets the permission to the granted to the actors on this task on the document following the workflow
692     */
693    String getTaskAssigneesPermission();
694
695    /**
696     * Gets the task layout
697     */
698    String getTaskLayout();
699
700    /**
701     * @return the taskDocType. If none is specified, the default task type is returned.
702     */
703    String getTaskDocType();
704
705    String getTaskNotificationTemplate();
706
707    /**
708     * Does bookkeeping at node start.
709     */
710    void starting();
711
712    /**
713     * Does bookkeeping at node end.
714     */
715    void ending();
716
717    /**
718     * Executes an Automation chain in the context of this node.
719     *
720     * @param chainId the chain
721     */
722    void executeChain(String chainId) throws DocumentRouteException;
723
724    /** Internal during graph init. */
725    void initAddInputTransition(Transition transition);
726
727    /**
728     * Gets the input transitions.
729     */
730    List<Transition> getInputTransitions();
731
732    /**
733     * Gets the output transitions.
734     */
735    List<Transition> getOutputTransitions();
736
737    String getTaskDueDateExpr();
738
739    /**
740     * Executes an Automation chain in the context of this node for a given transition
741     *
742     * @param transition the transition
743     */
744    void executeTransitionChain(Transition transition) throws DocumentRouteException;
745
746    /**
747     * Evaluates transition conditions and returns the transitions that were true.
748     * <p>
749     * Transitions are evaluated in the order set on the node when the workflow was designed. Since @5.7.2 if the node
750     * has the property "executeOnlyFirstTransition" set to true, only the first transition evaluated to true is
751     * returned
752     *
753     * @return the true transitions
754     */
755    List<Transition> evaluateTransitions() throws DocumentRouteException;
756
757    /**
758     * Sets the graph and node variables.
759     *
760     * @param map the map of variables
761     */
762    void setAllVariables(Map<String, Object> map);
763
764    /**
765     * Sets the graph and node variables.
766     *
767     * @param map the map of variables
768     * @param allowGlobalVariablesAssignement if set to false, throw a DocumentRouteException when trying to set global
769     *            variables when not supposed to
770     * @since 7.2
771     */
772    void setAllVariables(Map<String, Object> map, final boolean allowGlobalVariablesAssignement);
773
774    /**
775     * Gets the task buttons
776     */
777    List<Button> getTaskButtons();
778
779    /**
780     * Has the node the given action.
781     *
782     * @since 7.2
783     */
784    boolean hasTaskButton(final String name);
785
786    /**
787     * Gets the document representing this node
788     */
789    DocumentModel getDocument();
790
791    /**
792     * Gets a map containing the variables currently defined on this node
793     */
794    Map<String, Serializable> getVariables();
795
796    /**
797     * Gets a map containing the Json formatted variables currently defined on this node
798     * @since 7.2
799     */
800    Map<String, Serializable> getJsonVariables();
801
802    /**
803     * Sets the property button on the node, keeping the id of the last action executed by the user on the associated
804     * task if any
805     */
806    void setButton(String status);
807
808    /**
809     * Sets the last actor on a node (user who completed the task).
810     *
811     * @param actor the user id
812     */
813    void setLastActor(String actor);
814
815    /**
816     * Evaluates the task assignees from the taskAssigneesVar
817     */
818    List<String> evaluateTaskAssignees() throws DocumentRouteException;
819
820    /**
821     * Evaluates the task due date from the taskDueDateExpr and sets it as the dueDate
822     */
823    Date computeTaskDueDate() throws DocumentRouteException;
824
825    /**
826     * Gets a map containing the workflow and node variables and workflow documents.
827     *
828     * @param detached The documents added into this map can be detached or not
829     */
830    Map<String, Serializable> getWorkflowContextualInfo(CoreSession session, boolean detached);
831
832    /**
833     * When workflow engine runs an exclusive node, it evaluates the transition one by one and stops a soon as one of
834     * the transition is evaluated to true
835     *
836     * @since 5.7.2
837     */
838    boolean executeOnlyFirstTransition();
839
840    /**
841     * Checks if this node has a sub-route model defined.
842     *
843     * @return {@code true} if there is a sub-route
844     * @since 5.7.2
845     */
846    boolean hasSubRoute() throws DocumentRouteException;
847
848    /**
849     * Gets the sub-route model id.
850     * <p>
851     * If this is present, then this node will be suspended while the sub-route is run. When the sub-route ends, this
852     * node will resume.
853     *
854     * @return the sub-route id, or {@code null} if none is defined
855     * @since 5.7.2
856     */
857    String getSubRouteModelId() throws DocumentRouteException;
858
859    /**
860     * Starts the sub-route on this node.
861     *
862     * @return the sub-route
863     * @since 5.7.2
864     */
865    DocumentRoute startSubRoute() throws DocumentRouteException;
866
867    /**
868     * Cancels the sub-route if there is one.
869     *
870     * @since 5.7.2
871     */
872    void cancelSubRoute() throws DocumentRouteException;
873
874    /**
875     * Evaluates the rules for the escalation rules and returns the ones to be executed. The rules already executed and
876     * not having the property multipleExecution = true are also ignored
877     *
878     * @since 5.7.2
879     */
880    List<EscalationRule> evaluateEscalationRules();
881
882    /**
883     * Gets the list of all escalation rules for the node
884     *
885     * @since 5.7.2
886     */
887    List<EscalationRule> getEscalationRules();
888
889    /**
890     * Checks if this node has created multiple tasks, one for each assignees.
891     *
892     * @since 5.7.3
893     */
894    boolean hasMultipleTasks();
895
896    /**
897     * Gets all the tasks info for the tasks created from this node
898     *
899     * @since 5.7.3
900     */
901    List<TaskInfo> getTasksInfo();
902
903    /**
904     * Persist the info when a new task is created from this node
905     *
906     * @since 5.7.3
907     */
908    void addTaskInfo(String taskId);
909
910    /**
911     * Persist these info from the task on the node. Status is the id of the button clicked to end the task by the
912     * actor.
913     *
914     * @since 5.7.3
915     */
916    void updateTaskInfo(String taskId, boolean ended, String status, String actor, String comment);
917
918    /**
919     * Gets all the ended tasks originating from this node. This also counts the canceled tasks.
920     *
921     * @since 5.7.3
922     */
923    List<TaskInfo> getEndedTasksInfo();
924
925    /**
926     * Gets all the ended tasks originating from this node that were processed with a status. Doesn't count the canceled
927     * tasks.
928     *
929     * @since 5.7.3
930     */
931    List<TaskInfo> getProcessedTasksInfo();
932
933    /**
934     * Returns false if all tasks created from this node were ended.
935     *
936     * @since 5.7.3
937     */
938    boolean hasOpenTasks();
939
940    /**
941     * Returns true if tasks created from this node can be reassigned.
942     *
943     * @since 5.7.3
944     */
945    boolean allowTaskReassignment();
946
947    /**
948     * Sets the variable on this node if it exists as a Node Variable.
949     *
950     * @since 5.8
951     */
952    void setVariable(String name, String value);
953
954    /**
955     * Sets the node variables.
956     *
957     * @since 5.9.3, 5.8.0-HF11
958     * @param map the map of variables
959     */
960    void setVariables(Map<String, Serializable> map);
961
962    /**
963     * Sets the variables of the workflow based on their JSON representation (especially for scalar lists). Eg.
964     * Map<String, String> map = new HashMap<String, String>();
965     * map.put("contributors","[\"John Doe\", \"John Smith\"]"); map.put("title","Test Title");
966     *
967     * @param map the map of variables
968     * @since 5.9.3, 5.8.0-HF11
969     */
970    void setJSONVariables(Map<String, String> map);
971
972    /**
973     * @since 7.4
974     */
975    void removeTaskInfo(String taskId);
976
977}