Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Apache Camel: access both request and reply message at end of route

Tags:

apache-camel

I would like to process both request and response messages at the end of my route. However, I do not see a way how to access the original request message.

I have the terrible feeling I am struggling with some basic concept.

Here is a simple example route in DSL to outline my problem (streamCaching is enabled for the whole context):

from("activemq:queue:myQueue")
.to("log:" + getClass().getName() + "?showOut=true")
.to("http://localhost:8080/someBackend")
.log("Now in.Body returns this: ${in.body} and out.Body this: ${out.body}")
.to("log:" + getClass().getName() + "?showOut=true");

Here is an according excerpt from my logs (line-breaks edited for better reading). As one can see, the original SOAP message is lost once the http server replied, and the SOAP response object is stored in the inBody of the message.

2012-09-25 17:28:08,312 local.bar.foo.MyRouteBuilder INFO - 
    Exchange[ExchangePattern:InOut, BodyType:byte[],
    Body:<?xml version="1.0" encoding="UTF-8"?><env:Envelope xmlns:env="http://schemas.xmlsoap.org/soap/envelope/"><env:Header /><env:Body><urn:someRequest  xmlns:urn="http://foo.bar.local/ns"></urn:someRequest></env:Body></env:Envelope>, 
    Out: null]
2012-09-25 17:28:08,398 org.apache.camel.component.http.HttpProducer DEBUG - 
    Executing http POST method: http://localhost:8080/someBackend
2012-09-25 17:28:09,389 org.apache.camel.component.http.HttpProducer DEBUG - 
    Http responseCode: 200
2012-09-25 17:28:09,392 route2 INFO - 
    Now in.Body returns this: <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"><soap:Body><ns2:someResponse xmlns:ns2="http://foo.bar.local/ns"</ns2:someResponse></soap:Body></soap:Envelope>
    and out.Body this: 
2012-09-25 17:28:09,392 local.bar.foo.MyRouteBuilder INFO - 
    Exchange[ExchangePattern:InOut,  
    BodyType:org.apache.camel.converter.stream.InputStreamCache,
    Body:[Body is instance of org.apache.camel.StreamCache],
    Out: null]

I would have expected to have in.body and out.body be preserved across the whole route?

Alternative solutions I am considering:

  • Make use of the Correlation Identifier pattern to correlate both request and reply. But would this preserve the message bodies as well? Also, my request/reply messages do not have unique identifiers for correlation.
  • Write a custom bean, which performs the call to the http backend, processing both request and reply objects (but this is basically a no-Camel solution, reinventing the wheel and hence not preferred)

Already failed approaches:

I tried to access the original request message using a Processor like this at the end of my route, with no success:

 process(new Processor() {
   @Override
   public void process(Exchange exchange) throws Exception {
      Message originalInMessage = exchange.getUnitOfWork().getOriginalInMessage();
         logger.debug(originalInMessage.getBody(String.class));
         logger.debug(exchange.getIn().getBody(String.class));
   }
 });

Thanks for any help

like image 318
user1627943 Avatar asked Sep 25 '12 16:09

user1627943


People also ask

What is direct endpoint in Camel?

The direct: component provides direct, synchronous invocation of any consumers when a producer sends a message exchange. This endpoint can be used to connect existing routes in the same camel context.

Is Apache Camel outdated?

Many open source projects and closed source technologies did not withstand the tests of time and have disappeared from the middleware stacks for good. After a decade, however, Apache Camel is still here and becoming even stronger for the next decade of integration.

What is the difference between to and ToD in Camel?

Different between To and ToDThe to is used for sending messages to a static endpoint. In other words to sends message only to the same endpoint. The toD is used for sending message to a dynamic endpoint. The dynamic endpoint is evaluated on-demand by an Expression.

Is Apache Camel asynchronous?

Background. The new Async API in Camel 2.0 leverages in much greater detail the Java Concurrency API and its support for executing tasks asynchronous. Therefore the Camel Async API should be familiar for users with knowledge of the Java Concurrency API.


3 Answers

Simply store the original body of the in message in a header or a property and retrieve it at the end:

from("activemq:queue:myQueue")
.setProperty("origInBody", body())
.to("http://localhost:8080/someBackend")

After the http call you can then access the property origInBody.

like image 198
Christian Schneider Avatar answered Sep 28 '22 22:09

Christian Schneider


First, this article shows very well how in and out works in camel: http://camel.apache.org/using-getin-or-getout-methods-on-exchange.html

Typically, the out message is not always used, but rather copied from the in-message in each step.

In your case, where you want the original message to stay around til the end of the route, you could go ahead with the Enrichment EIP. http://camel.apache.org/content-enricher.html

Your route would be something like this:

public class MyAggregationStrategy implements AggregationStrategy {
  public Exchange aggregate(Exchange orig, Exchange httpExchange){
    // if you want to check something with the Http request, you better do that here 
    if( httpExchange is not correct in some way ) 
       throw new RuntimeException("Something went wrong");

    return orig;
  }
}

AggregationStrategy aggStrategy = new MyAggregationStrategy();

from("activemq:queue:myQueue")
  .enrich("http://localhost:8080/someBackend",aggStrategy)
  .//keep processing the original request here if you like, in the "in" message
like image 24
Petter Nordlander Avatar answered Sep 28 '22 21:09

Petter Nordlander


One of the biggest problem of camel, is the ease to misuse it. The best way to use it correctly is to think in terms of EIP : one of the main goals of camel, is to implement EIP in its DSL.

Here is a list of EIP

Now think about it. You want the request and the response at the end, for what use ? Logging, Aggregation, ... ? For logging, a correlationId should suffice, so I presume you need it to create a response, based on both request and the proxied-response. If that's what you want, you could do something like

from("direct:receiveRequest")
   .enrich("direct:proxyResponse", new RequestAndResponseAggregationStrategy())

You will have the opportunity to merge your Request (in oldExchange) and your Response (in newExchange).

With all the due respect I have for Christian Schneider, I do think the idea of putting the request in a property that could be reused later is a bad design. By doing it, you create side-effect between your routes. If your route is a subroute for another, you'll maybe erase their property. If you store it to put it back later, maybe you should do something like

from("direct:receiveRequest")
    .enrich("direct:subRouteToIgnoreResponse", AggregationStrategies.useOriginal())

A really really bad design that I have done too many time myself is to do :

from("direct:receiveRequest")
    .to("direct:subroute")

from("direct:subroute")
    .setProperty("originalBody", body())
    .to("direct:handling")
    .transform(property("originalBody")

This will lead to "properties/headers hell", and to routes that are just a successive call of processors.

And if you can't think of a solution of your problem with EIP, you should maybe use camel only to access their components. For example, something like :

from("http://api.com/services")
   .to(new SomeNotTranslatableToEIPProcessor())
   .to("ftp://destination")

But don't forget that those components has their own goals : creating a common abstraction between similar behaviour (e.g, time based polling consumer). If you have a very specific need, trying to bend a camel component to this specific need can lead to huge chunk of code not easily maintainable.

Don't let Camel become your Golden Hammer anti-pattern

like image 24
Jonathan Schoreels Avatar answered Sep 28 '22 22:09

Jonathan Schoreels