I asked this question on stack overflow but didn't get a response so asking here https://stackoverflow.com/questions/78720143/camel-notifybuilder-does-not-match-when-exception-is-thrown
It seems that camel's NotifyBuilder <https://camel.apache.org/manual/notify-builder.html> works in normal message processing but does not work when exceptions are thrown. Please see the following test case where I create the following routes - direct:route-a adds "a" to a list and passes to direct:route-b - direct:route-b adds "b" to a list and throws exception - errorHandler routes to direct:dead-letter as a deadLetterChannel - direct:dead-letter adds "dead" to a list I can see that the list contains "a", "b", "dead" when I send a message to direct:route-a But the NotifyBuilders for direct:route-b and direct:dead-letter don't match for some reason. Is there a bug in NotifyBuilder where they don't match when exceptions are thrown? Am I using NotifyBuilder incorrectly? I've tried both NotifyBuilder.whenDone(1) and NotifyBuilder.whenFailed(1) and neither causes a match. Looking at the NotifyBuilder javadoc <https://www.javadoc.io/doc/org.apache.camel/camel-core/2.25.4/org/apache/camel/builder/NotifyBuilder.html#whenDone-int-> I see the following comment for whenDone(...) > The difference between done and completed is that done can also include failed messages, where as completed is only successful processed messages. import lombok.extern.slf4j.Slf4j;import org.apache.camel.CamelContext;import org.apache.camel.ProducerTemplate;import org.apache.camel.builder.NotifyBuilder;import org.apache.camel.builder.RouteBuilder;import org.apache.camel.impl.DefaultCamelContext;import org.apache.camel.impl.engine.DefaultProducerTemplate;import org.junit.jupiter.api.AfterEach;import org.junit.jupiter.api.BeforeEach;import org.junit.jupiter.api.Test; import java.util.LinkedList;import java.util.List;import java.util.concurrent.TimeUnit; import static org.assertj.core.api.Assertions.assertThat; @Slf4jpublic class NotifyBuilderTest { private CamelContext camelContext; private ProducerTemplate producerTemplate; private List<String> list; @BeforeEach public void beforeEach() throws Exception { list = new LinkedList<>(); camelContext = new DefaultCamelContext(); camelContext.addRoutes(new RouteBuilder() { @Override public void configure() { from("direct:route-a") .process(exchange -> list.add("a")) .to("direct:route-b"); from("direct:route-b") .process(exchange -> list.add("b")) .process(exchange -> { throw new Exception("foo"); }); errorHandler(deadLetterChannel("direct:dead-letter")); from("direct:dead-letter") .process(exchange -> list.add("dead")); } }); producerTemplate = new DefaultProducerTemplate(camelContext); camelContext.start(); producerTemplate.start(); } @AfterEach public void afterEach() { producerTemplate.stop(); camelContext.stop(); } @Test public void testNotify() { NotifyBuilder notifierA = new NotifyBuilder(camelContext) .from("direct:route-a") .whenDone(1) .create(); NotifyBuilder notifierB = new NotifyBuilder(camelContext) .from("direct:route-b") .whenDone(1) .create(); NotifyBuilder notifierDead = new NotifyBuilder(camelContext) .from("direct:dead-letter") .whenDone(1) .create(); producerTemplate.sendBody("direct:route-a", null); // this succeeds assertThat(list).containsExactly("a", "b", "dead"); // this succeeds assertThat(notifierA.matches(5, TimeUnit.SECONDS)).isTrue(); // this fails assertThat(notifierB.matches(5, TimeUnit.SECONDS)).isTrue(); // this fails assertThat(notifierDead.matches(5, TimeUnit.SECONDS)).isTrue(); } }