I have a custom workflow that launches a custom XHTML page for uploading files to the server. However, each time a user starts the workflow, it creates an active task that never completes. I would like to kindly ask if there is a recommended way to avoid this issue.
The reason the task is staying open is because the workflow doesn’t know when to finish. You’re using a custom XHTML form (fileUpload.xhtml), but there’s no return value or action telling the workflow to move forward.
To fix this:
In the workflow step, set the Return value to something like submitted.
In your XHTML form, make sure the submit button includes the same outcome:
<sp:button value="Submit" outcome="submitted"/>
In the workflow, add a transition that says:
5.If outcome is submitted, go to End.
This way, when the user submits the form, the workflow knows it’s done and will close the task.
Since this thread was auto-closed, I can’t add a comment, so I’m putting it here. The accepted solution is not a good approach. Deleting your workflow case just to “end” it is like using a sledgehammer to swat a fly. You’re hiding the problem with your form and workflow’s design instead of solving it.
/Edit
I know basically nothing about writing custom forms using XHTML, but I can tell those aren’t the same thing. I suspect the sp:button is a SailPoint-provided tag while the p:commandButton looks like it might not be. If I’m right, the outcome attribute would only be relevant to an sp:button and at best would have no effect. The sp:button presumably supports this attribute and SailPoint’s code automatically sets some property named outcome somewhere when that button is used to submit the form.
In your transition, your BeanShell code is setting a variable named outcome to the value “submitted” instead of checking for equality. Assuming that the outcome is accessible through a variable named outcome (please note I have no idea if it would be), then you would write "submitted".equals("outcome") to check for equality.
Instead of returning a value from the form and transitioning the workflow to the end, I developed a rule that deletes the task based on its name pattern. This approach has resolved the issue.
Here is the rule:
<?xml version='1.0' encoding='UTF-8'?>
<!DOCTYPE Rule PUBLIC "sailpoint.dtd" "sailpoint.dtd">
<Rule created="1750905277922" id="0ac928ce979f17e08197aa164de112ef" language="beanshell" modified="1750907840910" name="Delete Task by Name Rule 2" significantModified="1750907840910" type="Workflow">
<Description>
Rule to delete tasks by name. This rule can be used to clean up completed or failed tasks.
</Description>
<Signature returnType="void">
<Inputs>
<Argument name="context" type="sailpoint.api.SailPointContext"/>
</Inputs>
</Signature>
<Source>
import sailpoint.object.TaskResult;
import sailpoint.object.Filter;
import sailpoint.object.QueryOptions;
import java.util.List;
import java.util.Iterator;
try {
// Hard-coded task name pattern
String taskNamePattern = "input name pattern here";
System.out.println("Starting Delete Task Rule for tasks containing: " + taskNamePattern);
if (taskNamePattern == null || taskNamePattern.trim().isEmpty()) {
System.out.println("Task name pattern is null or empty");
return;
}
// Create filter to find tasks containing the pattern
Filter filter = Filter.like("name", taskNamePattern);
QueryOptions qo = new QueryOptions();
qo.addFilter(filter);
// Get all TaskResult objects with names containing the pattern
List taskResults = context.getObjects(TaskResult.class, qo);
if (taskResults == null || taskResults.isEmpty()) {
System.out.println("No tasks found containing: " + taskNamePattern);
return;
}
System.out.println("Found " + taskResults.size() + " task(s) containing: " + taskNamePattern);
int deletedCount = 0;
// Iterate through and delete each task
for (TaskResult taskResult : taskResults) {
try {
System.out.println("Deleting task: " + taskResult.getName() + " (ID: " + taskResult.getId() + ")");
// Delete the task
context.removeObject(taskResult);
deletedCount++;
System.out.println("Successfully deleted task: " + taskResult.getName());
} catch (Exception e) {
System.out.println("Error deleting task: " + taskResult.getName() + " - " + e.getMessage());
}
}
// Commit the changes
context.commitTransaction();
System.out.println("Delete Task Rule completed. Deleted " + deletedCount + " task(s) containing: " + taskNamePattern);
} catch (Exception e) {
System.out.println("Error in Delete Task by Name Rule: " + e.getMessage());
context.rollbackTransaction();
throw e;
}
</Source>
</Rule>