I want to do retrying on a service task, but use a fallback path whenever the retries do not lead to a successful result. A naive modeling would be e.g. this:
I could write an incident handler to be called at the right moment, when all retries are done. But I cannot send a message like the modeles one - as the engine has not yet entered the node (because auf asyncBefore=true).
Any spontaneous ideas how to implement such a behavior?
Another route I could envision is to code an own behavior and then check for the retry count of the job. If 0 a BPMN-error is raised instead of just throwing an exception from the attached delegate. What do you think? Then it would look like this:
I like the idea with the BPMN error, as it uses the error event for handling an error condition in the process. You could transparently hook in your custom behavior by implementing a parse listener that wraps and replaces the actual activity behavior and catches any exception, deciding if it should be propagated or become a BPMN error (under the assumption that the engine has no dirty instanceof tricks somewhere ). Then it works with all types of service task implementations.
Yeah - cool idea -that fits what I had in mind. Do you know spontaneously if and how I can easily retrieve the currently executed Job from within a behavior?
You can access it from internal API via org.camunda.bpm.engine.impl.context.Context.getJobExecutorContext().getCurrentJob(). Note that #getJobExecutorContext returns null when the code is not executed by the job executor.
public class GuardedServiceA implements JavaDelegate {
private static final String NO_RETRIES_ERROR = "NO_RETRIES";
public static boolean fail = false;
public static int countFailed = 0;
public static int countSuccess = 0;
@Override
public void execute(DelegateExecution ctx) throws Exception {
JobExecutorContext jobExecutorContext = Context.getJobExecutorContext();
if (jobExecutorContext!=null && jobExecutorContext.getCurrentJob()!=null) {
// this is called from a Job
if (jobExecutorContext.getCurrentJob().getRetries()<=1) {
// and the job will run out of retries when it fails again
try {
doExecute(ctx);
} catch (Exception ex) {
// Probably save the exception somewhere
throw new BpmnError(NO_RETRIES_ERROR);
}
return;
}
}
// otherwise normal behavior (including retries possibly)
doExecute(ctx);
}
private void doExecute(DelegateExecution ctx) {
if (fail) {
countFailed++;
throw new RuntimeException("ServiceA fails as expected");
}
countSuccess++;
}
}
It has to be on the delegate yes. Depending on the environment you might use some tricks to make it more generic though, see e.g. this example where I used an annotation:
and weaved in the logic using aspects:
Not that I think that’s the best approach - but there are ways to make this more generic if you fancy it