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