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