Wait for condition in bot config before executing further
Imagine you are trying to delay further processing until some event happens or want to wait for an external system to produce a result.
There are quite a few possible approaches to achieving this, each having it's own pros and cons. Here, we cover a few examples.
While loop with Thread.sleep
This approach is considered to be a bad practice. You are strongly encouraged to use other, more effective approaches.
Example of ineffective approach:
<?xml version="1.0" encoding="UTF-8"?>
<config xmlns="http://web-harvest.sourceforge.net/schema/1.0/config"
scriptlang="groovy">
<required name="service_url"/>
<required name="task_id"/>
<function name="getStatus">
<return>
<http url="${serviceUrl}/getTaskStatus?taskId=${taskId}" />
</return>
</function>
<var-def name="status">
<call name="getStatus">
<call-param name="taskId">
<template>${task_id}</template>
</call-param>
<call-param name="serviceUrl">
<template>${service_url}</template>
</call-param>
</call>
</var-def>
<while condition='${!status.toString().equals("Completed")}'>
<log>Waiting for the task to be finished by the xxx service</log>
<script></script>
<var-def name="status">
<call name="getStatus">
<call-param name="taskId">
<template>${task_id}</template>
</call-param>
<call-param name="serviceUrl">
<template>${service_url}</template>
</call-param>
</call>
</var-def>
</while>
<log>Task finished by the xxx service</log>
<export include-original-data="true" />
</config>
Highlight of the issues in the code above:
- Memory leak
- Missing empty="true". So a
<while>plugin accumulates output of every iteration. - The logic inside of while is as a rule more complex and may cause heavy object creation, and accumulating those objects in memory
- Missing empty="true". So a
- Execution of other tasks prevented
- Such "eager" wait holds a thread from bot source.
- No error handling
- In case the service returns error response - the code above will keep waiting
- Code duplication
- Invocation of getStatus
Many of the issues can be mitigated by code refactoring or by utilizing alternative approaches.
Release plugin
See detailed description in the release plugin article.
<?xml version="1.0" encoding="UTF-8"?>
<config xmlns="http://web-harvest.sourceforge.net/schema/1.0/config"
scriptlang="groovy">
<required name="service_url"/>
<required name="task_id"/>
<function name="getStatus">
<return>
<http url="${serviceUrl}/getTaskStatus?taskId=${taskId}" />
</return>
</function>
<var-def name="status">
<call name="getStatus">
<call-param name="taskId">
<template>${task_id}</template>
</call-param>
<call-param name="serviceUrl">
<template>${service_url}</template>
</call-param>
</call>
</var-def>
<case>
<if condition='${status.toString().equals("Completed")}'>
<log>Task finished by the xxx service</log>
<export include-original-data="true" />
</if>
<if condition='${status.toString().equals("In Progress")}'>
<log>Waiting for the task to be finished by the xxx service</log>
<release time-in-seconds="180"/>
</if>
<else>
<script><![CDATA[
throw new RuntimeException("Exception status response returned by the service. Response returned: " + status);
]]></script>
</else>
</case>
</config>
The code demonstrated addresses the points described in the while loop with Thread.sleep section.
There are still a few considerations to keep in mind:
- Wait timeout. If the remote service returns "In Progress" status for unexpectedly long time, the code above will still be patiently waiting and pinging the service every 3 minutes.
- Launching of the service. The code expects that the task execution has been already started, and will not try to send a re-launch command. If this is required - it should be a part of the business process logic.
Trigger inversion
Sometimes, it is possible to control the system or events at the other end (where such events occur).
For example, once the task is finished,launch the post processing business process, or embed the required steps right there.
tip
See also WorkFusion REST API and the task-start plugin.
Schedule
Simple schedule can be configured to start a checker process to validate a condition on defined dates/times. Refer to Schedule Tasks and BPs for more details.