shashank created CAMEL-26421:
--------------------------------
Summary: camel-core - a route called from a doTry does not use the
error handler of its routing slip, multicast, recipient list or dynamic router:
duplicate deliveries, no redelivery or a lost failure
Key: CAMEL-26421
URL: https://issues.apache.org/jira/browse/CAMEL-26421
Project: Camel
Issue Type: Bug
Components: camel-core
Reporter: shashank
Assignee: shashank
When a step of a {{doTry}} calls another route ({{direct}}, {{seda}} with
{{waitForTaskToComplete}}, {{enrich}}, ...), the EIPs of the called route that
wrap each output in the error handler (routing slip, dynamic router, multicast,
recipient list) do not do so (main 017f4fd11c4a). The called route has its own
error handler, and a plain {{to()}} in it is redelivered as usual, but these
EIPs behave as if they were in a doTry of their own:
* a routing slip redelivers the whole slip, so an endpoint that already
succeeded gets the message again;
* a multicast or recipient list does not redeliver the failed output (one
attempt instead of 1 + maximumRedeliveries);
* a dynamic router that counts its calls in an exchange property, as in the
dynamic router documentation, loses the failure: the redelivery of the whole
router "succeeds" because the bean returns null.
The same happens with a doTry that has only a doFinally, and with a route
called from the steps of a circuit breaker.
{code:java}
onException(IllegalStateException.class).maximumRedeliveries(2).redeliveryDelay(0);
from("direct:r0").doTry().to("direct:r1").doCatch(Exception.class).to("mock:caught").end();
from("direct:r1").routingSlip(constant("mock:m1,direct:fail"));
from("direct:fail").errorHandler(noErrorHandler()).process(e -> { throw new
IllegalStateException("boom"); });
// without the doTry: mock:m1 gets 1 message, direct:fail 3 attempts
// with it: mock:m1 gets 3 messages
{code}
*Cause*: {{TryProcessor}} sets the exchange property {{TryRouteBlock}} while
the steps of the doTry run. {{RoutingSlip.createErrorHandler}} and
{{MulticastProcessor.wrapInErrorHandler}} (multicast and recipient list) read
it and leave out the error handler per output, because the steps of a doTry
have no error handler (CAMEL-7275). The property stayed set while the called
route ran: {{direct}} hands over the same exchange, {{seda}} and {{enrich}}
copy it. A split is not affected (its steps are channels with the route's error
handler).
*Fix* (proposed): {{CamelInternalProcessor.RouteLifecycleAdvice}} removes
{{TryRouteBlock}} when an exchange enters a route and sets the caller's value
again when the route is done (as advice state, so recursion and asynchronous
completion are covered). A doTry inside the called route still turns off the
error handler per output of its own steps. Upgrade guide entry: the called
route can make more delivery attempts than before; to keep the old behaviour,
put the EIP in a doTry of the called route or use
{{errorHandler(noErrorHandler())}} on it.
*Tests*: {{DoTryCalledRouteErrorHandlerTest}} (camel-core, 18 tests): routing
slip (also asynchronous), multicast, recipient list, dynamic router, doCatch,
seda and enrich callers, recursion, a doTry inside the called route, the
caller's scope after a successful and a failed call, and controls. 9 fail on
main and pass with the fix. camel-core: no test class goes from passing to
failing.
*Related*: CAMEL-25114 restored the marker after a nested doTry; CAMEL-26332
and CAMEL-26333 fix other defects of a route called from a doTry (other
properties and classes); the fixes combine.
*How it was found*: a Rocq specification of Camel's routing semantics, written
from the documentation, compared with real Camel on generated routes, then
reduced to a plain Java DSL test.
*Affected*: main, 4.23.0 and 4.22.1 (same results).
_Filed with Claude Code on behalf of allthingssecurity._
--
This message was sent by Atlassian Jira
(v10.3.26#10030026)