Gerold Broser
Gerold Broser

Reputation: 14782

Apache Camel trims off apostrophes from String message body

import static java.lang.System.out;

import org.apache.camel.ExchangePattern;
import org.apache.camel.Message;
import org.apache.camel.builder.RouteBuilder;
import org.apache.camel.impl.DefaultCamelContext;

class RequestReplyMessageSample {

    class Replier {

        String reply( final String /*Message*/ /*Exchange*/ request ) {
            out.println( "\n[ERROR] Expected: 'Re'quest', Actual: " + request
                    + "  <-- Am I doing somethig wrong, is this a bug or a feature?\n" );
            return String.format( "**** Replying to %s ****%n", request/*.getMessage()*/ /*.getBody( String.class )*/ );
        }
    } // Replier

    private static final DefaultCamelContext cc = new DefaultCamelContext();

    public static void main( final String... args ) throws Exception {

        final Replier replier = new RequestReplyMessageSample().new Replier();

        cc.setName( "Request Reply Sample" );
        cc.addRoutes( new RouteBuilder() {

            @Override
            public void configure() {

                from( "timer:start?repeatCount=1" )
                        .setExchangePattern( ExchangePattern.InOut )
                        .process().message( m -> m.setBody( "'Re'quest'" ) ) // EIP Request
                        .process().message( m -> print( "Request", m ) )
                        .log( "Requesting..." )
                        .log( "Replying..." )
                        // How to get Message (or Exchange) as argument in here?
                        .bean( replier, "reply(${body})" ) // EIP Requestor/Replier
                        .process().message( m -> print( "Reply", m ) ) // EIP Reply (in the body of the Message)
                        .setId( "Request/Reply" );
            }
        } );
        cc.start();
        Thread.sleep( 2000 );
        cc.stop();
        cc.close();
    } // main()

    static void print( final String endpoint, final Message m ) {

        out.printf( "%s %s: %s%n", endpoint, m, m.getBody() );
    } // print()

} // RequestReplyMessageSample

Output

...
Request Message: 'Re'quest'
[ple) thread #1 - timer://start] Request/Reply                  INFO  Requesting...
[ple) thread #1 - timer://start] Request/Reply                  INFO  Replying...

[ERROR] Expected: 'Re'quest', Actual: Re'quest  <-- Am I doing somethig wrong, is this a bug or a feature?

Reply Message: **** Replying to Re'quest ****
...

Upvotes: 0

Views: 677

Answers (2)

burki
burki

Reputation: 7035

This is because the body is passed as explicit method parameter to the bean.

The single quotes are preserved in the following scenarios:

  • pass the string as message body in the request
  • set (or overwrite) the body in the route (.setBody) as constant
  • set (or overwrite) the body in the route (.setBody) as expression
  • passing the body implicit to a bean
  • set (or overwrite) the body through a bean method return value
  • set (or overwrite) the body through a processor bean

Only when the body is passed explicitly to a bean method, the quotes are interpreted as string literal boundaries.

.bean(new EchoBean(), "echoBody(${body})")

becomes in your case

.bean(new EchoBean(), "echoBody('Re'quest')")

You can try this test class (Camel 3, JUnit 5). It fails because the quotes are lost in the last bean call with explicit parameters.

package test.camel;

import org.apache.camel.builder.RouteBuilder;
import org.apache.camel.test.junit5.CamelTestSupport;
import org.junit.jupiter.api.Test;

class QuoteTest extends CamelTestSupport {
    public static final String MOCK_OUTPUT = "mock:output";

    protected RouteBuilder createRouteBuilder() throws Exception {
        return new RouteBuilder() {
            @Override
            public void configure() throws Exception {
                from( "direct:start" )
                        .log("Reveived body: ${body}")
                        .setBody(constant("'Re'quest'"))
                        .log("Body overwritten in route: ${body}")
                        .bean(new EchoBean())
                        .log("Body overwritten through bean: ${body}")
                        .process().message( m -> m.setBody( "'Re'quest'" ) )
                        .log("Body overwritten through processor: ${body}")
                        .bean(new EchoBean(), "echoBody(${body})")
                        .log("Body overwritten through bean with methodparameter: ${body}")
                        .to(MOCK_OUTPUT);
            }
        };
    }

    @Test
    void routeTest_quoteHandling_processed() throws Exception {
        getMockEndpoint(MOCK_OUTPUT).expectedBodiesReceived("'Re'quest'");
        template.requestBody("direct:start", "'Re'quest'");
        assertMockEndpointsSatisfied();
    }

    static class EchoBean {
        public String echoBody(final String messageBody) {
            System.out.println("Expected: 'Re'quest', Actual: " + messageBody);
            return messageBody;
        }
    }
}

It produces the following output. As you can see, the quotes are preserved through all steps except the last bean call with explicit parameter passing

09:00:53.521 [main] INFO  route1 - Reveived body: 'Re'quest'
09:00:53.521 [main] INFO  route1 - Body overwritten in route: 'Re'quest'
Expected: 'Re'quest', Actual: 'Re'quest'
09:00:53.527 [main] INFO  route1 - Body overwritten through bean: 'Re'quest'
09:00:53.527 [main] INFO  route1 - Body overwritten through processor: 'Re'quest'
Expected: 'Re'quest', Actual: Re'quest  <<-- only when the body is passed as method parameter, the quotes are lost
09:00:53.529 [main] INFO  route1 - Body overwritten through bean with methodparameter: Re'quest

Upvotes: 1

fvaleri
fvaleri

Reputation: 777

It's a feature. In this case, the single quotes at the start/end of the expression are interpreted as a string literal start/end. You need to add another single quote to let the parser see it as character (i.e. m.setBody("''Re'quest''")).

Upvotes: 2

Related Questions