Hello,

I just noticed an issue in our app, that http redelivery was sending
extra messages to the target url. I first thought it to be related to
jms transactions, but with a closer look this happens with just a
simple retry.

Here is a simple test case to view the result. The message should be
sent twice (original and retry), but it gets sent four times.

import org.apache.camel.Exchange;
import org.apache.camel.LoggingLevel;
import org.apache.camel.builder.RouteBuilder;
import org.springframework.stereotype.Component;

@Component
public class TestRoute extends RouteBuilder {
    private static final String DIRECT_EXHAUSTED = "direct:exhausted";
    private static final long DEFAULT_RESPONSE_CODE = 503;
    public static final String SINK = "http://localhost:29999/sink";;

    @Override
    public void configure() {

 
errorHandler(deadLetterChannel(DIRECT_EXHAUSTED).maximumRedeliveries(1).redeliveryDelay(10000).logExhausted(false));
        from("timer:camel-timer?repeatCount=1")
                .setBody(constant("Hello world!"))
                .log(LoggingLevel.INFO, "This is request ")
                .to(SINK);

        from(DIRECT_EXHAUSTED)
                .routeId("exhausted")
                .log(LoggingLevel.INFO, "Retries exhausted ${exception}");

        from("jetty:"+SINK)
                .log("Request headers: ${headers}")
                .setHeader(Exchange.HTTP_RESPONSE_CODE,
constant(DEFAULT_RESPONSE_CODE))
                .log("Finished");
    }
}


When I set http activity logging to be used, it sent the message only
twice and failed with a null pointer exception in the logger. I
created a simpler logger to just log the invocations without anything
that could fail when values are null

import org.apache.camel.Exchange;
import org.apache.camel.component.http.HttpActivityListener;
import org.apache.hc.core5.http.HttpEntity;
import org.apache.hc.core5.http.HttpHost;
import org.apache.hc.core5.http.HttpResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.apache.hc.core5.http.HttpRequest;

@Component
public class HttpActivityLogger implements HttpActivityListener {

    private static final Logger LOGGER =
LoggerFactory.getLogger(HttpActivityLogger.class);

    @Override
    public void onRequestSubmitted(Object source, Exchange exchange,
HttpHost host, HttpRequest request, HttpEntity entity) {
        LOGGER.info("{} onRequestSubmitted: {}", exchange, source);
        LOGGER.trace("{} Stack trace: {}", exchange,
Thread.currentThread().getStackTrace());
    }

    @Override
    public void onResponseReceived(Object source, Exchange exchange,
HttpHost host, HttpResponse response, HttpEntity entity, long elapsed)
{
        LOGGER.info("{} Spent {} milliseconds", exchange, elapsed);
    }
}


Here is the log from the execution where the extra call with null
exchange occurs a few milliseconds later that the original

2025-09-25T10:32:22.403+03:00 Apache Camel 4.10.6 (camel-1) started in
362ms (build:0ms init:0ms start:362ms boot:985ms)
2025-09-25T10:32:22.405+03:00 Started CamelTestApplication in 2.671
seconds (process running for 3.157)
2025-09-25T10:32:23.416+03:00 This is request
2025-09-25T10:32:23.455+03:00
Exchange[DDE7F0CB4616F92-0000000000000000] onRequestSubmitted:
org.apache.camel.component.http.HttpEndpoint$1@427b9a63
2025-09-25T10:32:23.548+03:00 Request headers: {Accept-Encoding=gzip,
x-gzip, deflate, CamelHttpMethod=POST, CamelHttpPath=,
CamelHttpQuery=null, CamelHttpUri=/sink,
CamelHttpUrl=http://localhost:29999/sink,
CamelServletContextPath=/sink, Connection=keep-alive,
Content-Length=12, Host=localhost:29999,
User-Agent=Apache-HttpClient/5.5 (Java/17.0.15)}
2025-09-25T10:32:23.548+03:00 Finished
2025-09-25T10:32:23.562+03:00
Exchange[DDE7F0CB4616F92-0000000000000000] Spent 106 milliseconds
2025-09-25T10:32:24.574+03:00 null onRequestSubmitted:
org.apache.camel.component.http.HttpEndpoint$1@427b9a63
2025-09-25T10:32:24.576+03:00 Request headers: {Accept-Encoding=gzip,
x-gzip, deflate, CamelHttpMethod=POST, CamelHttpPath=,
CamelHttpQuery=null, CamelHttpUri=/sink,
CamelHttpUrl=http://localhost:29999/sink,
CamelServletContextPath=/sink, Connection=keep-alive,
Content-Length=12, Host=localhost:29999,
User-Agent=Apache-HttpClient/5.5 (Java/17.0.15)}
2025-09-25T10:32:24.576+03:00 Finished
2025-09-25T10:32:24.577+03:00 null Spent 3 milliseconds
2025-09-25T10:32:34.591+03:00
Exchange[DDE7F0CB4616F92-0000000000000000] onRequestSubmitted:
org.apache.camel.component.http.HttpEndpoint$1@427b9a63
2025-09-25T10:32:34.592+03:00 Request headers: {Accept-Encoding=gzip,
x-gzip, deflate, CamelHttpMethod=POST, CamelHttpPath=,
CamelHttpQuery=null, CamelHttpUri=/sink,
CamelHttpUrl=http://localhost:29999/sink,
CamelServletContextPath=/sink, Connection=keep-alive,
Content-Length=12, Host=localhost:29999,
User-Agent=Apache-HttpClient/5.5 (Java/17.0.15)}
2025-09-25T10:32:34.593+03:00 Finished
2025-09-25T10:32:34.594+03:00
Exchange[DDE7F0CB4616F92-0000000000000000] Spent 2 milliseconds
2025-09-25T10:32:35.599+03:00 null onRequestSubmitted:
org.apache.camel.component.http.HttpEndpoint$1@427b9a63
2025-09-25T10:32:35.601+03:00 Request headers: {Accept-Encoding=gzip,
x-gzip, deflate, CamelHttpMethod=POST, CamelHttpPath=,
CamelHttpQuery=null, CamelHttpUri=/sink,
CamelHttpUrl=http://localhost:29999/sink,
CamelServletContextPath=/sink, Connection=keep-alive,
Content-Length=12, Host=localhost:29999,
User-Agent=Apache-HttpClient/5.5 (Java/17.0.15)}
2025-09-25T10:32:35.601+03:00 Finished
2025-09-25T10:32:35.602+03:00 null Spent 2 milliseconds
2025-09-25T10:32:35.606+03:00 Retries exhausted
org.apache.camel.http.base.HttpOperationFailedException: HTTP
operation failed invoking http://localhost:29999/sink with statusCode:
503


To me this looks like a bug in the retry logic, based on the exchange
being null in that unwanted call.

I tested this with camel 4.14.0 and 4.10.6 and both behave the same way.

-- 
Veijo Länsikunnas

Reply via email to