001package com.identityworksllc.iiq.common.task;
002
003import com.identityworksllc.iiq.common.Utilities;
004import com.identityworksllc.iiq.common.annotation.Experimental;
005import sailpoint.api.SailPointContext;
006import sailpoint.api.TaskManager;
007import sailpoint.object.*;
008import sailpoint.task.AbstractTaskExecutor;
009import sailpoint.task.TaskMonitor;
010import sailpoint.tools.GeneralException;
011import sailpoint.tools.Message;
012
013import java.util.HashMap;
014import java.util.Map;
015import java.util.concurrent.atomic.AtomicBoolean;
016
017/**
018 * A task executor that will invoke a Rule that returns a boolean indicating that
019 * another task should be run. This can be used to skip an expensive task, or a task
020 * likely to cause problems, such as during externally defined maintenance windows,
021 * final exam periods, or other critical times.
022 *
023 * BETA!
024 */
025@Experimental
026public class ConditionalTask extends AbstractTaskExecutor {
027    /**
028     * The task manager, used to synchronously run the other task
029     */
030    private TaskManager taskManager;
031
032    /**
033     * A flag indicating that the task has been terminated
034     */
035    private final AtomicBoolean terminated;
036
037    /**
038     * Constructs a new conditional task
039     */
040    public ConditionalTask() {
041        this.terminated = new AtomicBoolean();
042    }
043
044    @Override
045    public void execute(SailPointContext context, TaskSchedule taskSchedule, TaskResult taskResult, Attributes<String, Object> attributes) throws Exception {
046        TaskMonitor monitor = new TaskMonitor(context, taskResult);
047        super.setMonitor(monitor);
048
049        this.taskManager = new TaskManager(context);
050
051        boolean wait = attributes.getBoolean("awaitCompletion", false);
052
053        String ruleName = attributes.getString("conditionalRuleName");
054        Rule conditionalRule = context.getObject(Rule.class, ruleName);
055        if (conditionalRule == null) {
056            throw new GeneralException("Unable to find conditional rule: " + ruleName);
057        }
058
059        String taskName = attributes.getString("taskName");
060        TaskDefinition taskDef = context.getObject(TaskDefinition.class, taskName);
061        if (taskDef == null) {
062            throw new GeneralException("Unable to find child task: " + taskName);
063        }
064
065        Map<String, Object> inputs = new HashMap<>();
066
067        Object ruleOutput = context.runRule(conditionalRule, inputs);
068        if (ruleOutput instanceof Boolean || ruleOutput instanceof String) {
069            boolean shouldRun = Utilities.isFlagSet(ruleOutput);
070            if (shouldRun) {
071                if (wait) {
072                    TaskResult result = taskManager.runSync(taskDef, new HashMap<>());
073
074                    TaskResult parent = monitor.lockMasterResult();
075                    try {
076                        parent.assimilateResult(result);
077                    } finally {
078                        monitor.commitMasterResult();
079                    }
080                } else {
081                    TaskResult result = taskManager.runWithResult(taskDef, new HashMap<>());
082                    if (result != null) {
083                        TaskResult parent = monitor.lockMasterResult();
084                        try {
085                            parent.addMessage(Message.info("Launched child task " + result.getName()));
086                        } finally {
087                            monitor.commitMasterResult();
088                        }
089                    }
090                }
091            } else {
092                TaskResult parent = monitor.lockMasterResult();
093                try {
094                    parent.addMessage(Message.info("Conditional rule returned false, indicating that task " + taskDef.getName() + " should be skipped"));
095                } finally {
096                    monitor.commitMasterResult();
097                }
098            }
099        } else {
100            throw new GeneralException("Illegal output of conditional rule: " + ruleOutput);
101        }
102    }
103
104    /**
105     * Terminates the task
106     * @return True, indicating that we have reacted to the termination
107     */
108    @Override
109    public boolean terminate() {
110        this.terminated.set(true);
111        if (this.taskManager != null) {
112            this.taskManager.terminate();
113        }
114        return true;
115    }
116}