Sam Brightman
Sam Brightman

Reputation: 2950

Apache Camel runs part of onCompletion and doesn't show stack trace

I'm trying to use a Camel poll-once route that will use a file if it's present and log an error if not.

By default the route does nothing if the file does not exist so I've started by adding consumer.sendEmptyMessageWhenIdle=true to the URI. I then check for null body to decide whether to log an exception or continue:

    from(theFileUri)
        .onCompletion()
            .onCompleteOnly()
            .log("SUCCESS")
            .bean(theOtherAction, "start")
        .end()
        .onException(Exception.class)
            .logStackTrace(true)
            .log(ERROR, "Failed to load file")
            .handled(true)
        .end()
        .choice()
            .when(body().isNotNull())
                .to(NEXT_ROUTE_URI)
            .endChoice()
            .otherwise()
                .throwException(new FileNotFoundException(theFileUri))
            .endChoice();

There are two problems with this:

If there is a better way to do this then I'd welcome suggestions but I'd also like to know what I'm doing wrong in this method.

Upvotes: 1

Views: 1407

Answers (1)

Sam Brightman
Sam Brightman

Reputation: 2950

It's still not completely clear to me what is going on. However, I believe that the onException call needs to be separated from the chain. It looks to me that logStackTrace applies only to redelivery attempts. The number of attempts defaults to 0 and this is what I want. The only way to access the exception from the Java DSL appears to be a custom Processor. The getException() method will return null if you are using handled(true) so you must use Exchange.getProperty(Exchange.EXCEPTION_CAUGHT, Exception.class).

I also suspect that the log message from the onCompletion is due to it running in parallel before the exception aborts:

Camel 2.13 or older - On completion runs in separate thread Icon The onCompletion runs in a separate thread in parallel with the original route. It is therefore not intended to influence the outcome of the original route. The idea for on completion is to spin off a new thread to eg send logs to a central log database, send an email, send alterts to a monitoring system, store a copy of the result message etc. Therefore if you want to do some work that influence the original route, then do not use onCompletion for that. Notice: if you use the UnitOfWork API as mentioned in the top of this page, then you can register a Synchronization callback on the Exchange which is executed in the original route. That way allows you to do some custom code when the route is completed; this is how custom components can enlist on completion services which they need, eg the File component does that for work that moves/deletes the original file etc.

Since I want to not run this code on exception, I think I can just abort the route with the exception.

I currently have this:

    onException(Exception.class)
        .handled(true)
        .process(new Processor()
        {
            @Override
            public void process(Exchange anExchange) throws Exception
            {
                Exception myException = anExchange.getProperty(Exchange.EXCEPTION_CAUGHT, Exception.class);
                LOGGER.error("Failed to load", myException);
            }
        });
    from(theFileUri)
        .choice()
            .when(body().isNotNull())
                .to(NEXT_ROUTE_URI)
                .log("SUCCESS")
                .bean(theOtherAction, "start")
            .endChoice()
            .otherwise()
                .throwException(new FileNotFoundException(theFileUri));

Upvotes: 1

Related Questions