http4 not setting HTTP_RESPONSE properties - apache-camel

I have a really simple route that GETs an URL and prints the content using Camel HTTP4 component:
from("timer://foo?fixedRate=true&delay=0&period=10000")
.to("http4://www.google.com")
.process(e -> System.out.println("Out body: " + e.getOut().getBody()));
Note that I'm using out.body because, as stated in Camel documentation:
Camel will store the HTTP response from the external server on the OUT
body. All headers from the IN message will be copied to the OUT
message, so headers are preserved during routing.
But I get null values from OUT (both body and headers). Everything is being filled only in the IN message.
Am I missing anything or is it a bug?

In Camel a route consists of nodes. Each node takes the Exchange. Exchange has an IN and OUT message. So in your case, node with http4 component took the Exchange, called google.com and wrote body and headers to OUT message. Next, node with your proccesor took the Exchange. Now IN message has the response from the previous node(http4), but you are printing OUT which is empty! So IN and OUT message are per node not per route!

You are getting the Out body from the processor without setting it up first. That’s why you get null. To make this work you first need explicitly copy the incoming message, headers and attachments to Out Body and then print it. Or more easily take the In message as you mentioned.
Next part is from “Camel in Action” book which is a great book and I think it is very helpful.
in practice there’s a common pitfall when using getOut: the incoming
message headers and attachments will be lost. This is often not what
you want, so you must copy the headers and attachments from the
incoming message to the outgoing message, which can be tedious.

Related

in-Message copied in out-Message

I have this simple route in my RouteBuilder.
from("amq:MyQueue").routeId(routeId).log(LoggingLevel.DEBUG, "Log: ${in.headers} - ${in.body}")
As stated in the doc for HTTP-component:
Camel will store the HTTP response from the external server on the OUT body. All headers from the IN message will be copied to the OUT message, ...
I would like to know if this concept also applies to amq-component, routeId, and log? Is it the default behaviour, that IN always gets copied to OUT?
Thank you,
Hadi
First of all: The concept of IN and OUT messages is deprecated in Camel 3.x.
This is mentioned in the Camel 3 migration guide and also annotated on the getOut method of the Camel Exchange.
However, it is not (yet) removed, but what you can take from it: don't care about the OUT message. Use the getMessage method and don't use getIn and getOut anymore.
To answer your question:
Yes, most components behave like this
Every step in the route takes the (IN) message and processes it
The body is typically overwritten with the new processing result
The headers typically stay, new headers can be added
So while the Camel Exchange traverses the route, typically the body is continuously updated and the header list grows.
However, some components like aggregator create new messages based on an AggregationStrategy. In such cases nothing is copied automatically and you have to implement the strategy to your needs.

Camel recipient List doesnt resolve special characters

I have a camel route which processes xml data and send to http endpoint. below is my http endpoint.
.recipientList(simple("http://${header.httpUri}?xmlData=${body}"))
where the httpUri ill get it from db hence set it header and used. Body contains xml data with special characters of different language. Whenever it tries to resolve endpoint, since the body contains special characters, ResolveEndpointException occurs. The tracer log shows the body with special characters resolved like %20B etc.,
I feel the problem is with simple() SimpleBuilder.
Is there any other way to resolve endpoint dynamically???
Tried POST, HTTP_URI and HTTP_QUERY. It didnt work. Then found it was related to character encoding. My request contained some ISO-8859-9 encoding data. I send request with character encoding ISO-8859-9, and it worked.

How to consume a Chunked HTTP Stream with Apache Camel?

I have an issue consuming a Chunked HTTP RSVP Stream using Camel.
The stream is here and you can find more information about it at the end of this page
I have created a simple route such:
from("http4://stream.meetup.com/2/rsvps").log(org.apache.camel.LoggingLevel.INFO, "MeetupElasticSearch.Log", "JSON RSVP ${body}")
But nothing happens no message are consumed. I tried by adding a camel timer before because I am not sure you can use http4 component directly in the from but result is the same.
Can you help me please?
You cannot use the http4 component in a from() directive.
However, there is several ways to call an URL and to retrieve the result:
One is to create a timer and call the http4 component from a to() like this:
from("quartz2://HttpPoll/myTimer?trigger.repeatCount=0")
.to("http4://stream.meetup.com/2/rsvps")
.log("Body is: ${body}")
.to(mockDestination);
Another way is to use the content enricher pattern if you want to for example aggegates results into one another stucture.
Note as well, if your URL is dynamic you must use recipientList() instead to().
UPDATE:
Another way to consume a stream from the Internet is to use another component than http4: the camel-stream.
This one you can declare it in a from()directive:
// Read stream from the given URL... an publish in a queue
from("stream:url?url=http://stream.meetup.com/2/rsvps&scanStream=true&retry=true").
to("seda:processingQueue");
// Read records from the processing queue
// and do something with them.
from("seda:processingQueue").to("log:out")
You can see more sample in this site.

Request Reply and Scatter Gather Using Apache Camel

I am attempting to construct a route which will do the following:
Consume a message from jms:sender-in. I am using a INOUTrequest reply pattern. The JMSReplyTo = sender-out
The above message will be routed to multiple recipients like jms:consumer1-in, jms:consumer2-in and jms:consumer3-in. All are using a request reply pattern. The JMSReplyTo is specified per consumer ( in this case, the JMSReplyTo are in this order jms:consumer1-out, jms:consumer2-out, jms:consumer3-out
I need to aggregate all the replies together and send the result back to jms:sender-out.
I constructed a route which will resemble this:
from("jms:sender-in")
.to("jms:consumer1-in?exchangePattern=InOut&replyTo=queue:consumer1-out&preserveMessageQos=true")
.to("jms:consumer2-in?exchangePattern=InOut&replyTo=queue:consumer2-out&preserveMessageQos=true")
.to("jms:consumer3-in?exchangePattern=InOut&replyTo=queue:consumer3-out&preserveMessageQos=true");
I then send the replies back to some queue to gather and aggreagte:
from("jms:consumer1-out?preserveMessageQos=true").to("jms:gather");
from("jms:consumer1-out?preserveMessageQos=true").to("jms:gather");
from("jms:consumer1-out?preserveMessageQos=true").to("jms:gather");
from("jms:gather").aggregate(header("TransactionID"), new GatherResponses()).completionSize(3).to("jms:sender-out");
To emulate the behavior of my consumers, I added the following route:
from("jms:consumer1-in").setBody(body());
from("jms:consumer2-in").setBody(body());
from("jms:consumer3-in").setBody(body());
I am getting a couple off issues:
I am getting a timeout error on the replies. If I comment out the gather part, then no issues. Why is there a timeout even though the replies are coming back to the queue and then forwarded to another queue.
How can I store the original JMSReplyTo value so Camel is able to send the aggregated result back to the sender's reply queue.
I have a feeling that I am struggling with some basic concepts. Any help is appreciated.
Thanks.
A good question!
There are two things you need to consider
Don't mix the exchange patterns, Request Reply (InOut) vs Event
message (InOnly). (Unless you have a good reason).
If you do a scatter-gather, you need to make the requests
multicast, otherwise they will be pipelined which is not
really scatter-gather.
I've made two examples which are similar to your case - one with Request Reply and one with (one way) Event messages.
Feel free to replace the activemq component with jms - it's the same thing in these examples.
Example one, using event messages - InOnly:
from("activemq:amq.in")
.multicast()
.to("activemq:amq.q1")
.to("activemq:amq.q2")
.to("activemq:amq.q3");
from("activemq:amq.q1").setBody(constant("q1")).to("activemq:amq.gather");
from("activemq:amq.q2").setBody(constant("q2")).to("activemq:amq.gather");
from("activemq:amq.q3").setBody(constant("q3")).to("activemq:amq.gather");
from("activemq:amq.gather")
.aggregate(new ConcatAggregationStrategy())
.header("breadcrumbId")
.completionSize(3)
.to("activemq:amq.out");
from("activemq:amq.out")
.log("${body}"); // logs "q1q2q3"
Example two, using Request reply - note that the scattering route has to gather the responses as they come in. The result is the same as the first example, but with less routes and less configuration.
from("activemq:amq.in2")
.multicast(new ConcatAggregationStrategy())
.inOut("activemq:amq.q4")
.inOut("activemq:amq.q5")
.inOut("activemq:amq.q6")
.end()
.log("Received replies: ${body}"); // logs "q4q5q6"
from("activemq:amq.q4").setBody(constant("q4"));
from("activemq:amq.q5").setBody(constant("q5"));
from("activemq:amq.q6").setBody(constant("q6"));
As for your question two - of course, it's possible to pass around JMSReplyTo headers and force exchange patterns along the road - but you will create hard to debug code. Keep your exchange patterns simple and clean - it keep bugs away.

processing messages received via tcp (right datatype, approach etc explanation inside)

Currently I'm receiving messages over tcp in a self written component that allows me to use Netty as a TCP server producer.
the messages i receive are formatted in XML style, for example:
<customheader>
<someattribute></someattribute>
</customheader>
<custombody>
</custombody>
The messages I receive are stored in a byte[] and to send it to another endpoint I create a new exchange via:
Exchange exchange = new DefaultExchange(endpoint);
exchange.getContext().createProducerTemplate().sendBody("someendpointuri", receivedbytes);
now my questions:
Is my approach with the new exchange correct?
If want to for example get rid of the header or use other camel components, do I need to convert the receivedbytes from byte[] to a different datatype or is byte[] okay?
If i want to remove the custom header can i use the remove header component from camel?
Thanks for your help
You're creating an new Exchange, but you're not actually using it. Instead, you only use it to access CamelContext. The method sendBody is creating a new Exchange for you and it is this Exchange that is actually being sent to endpoint specified by someendpointuri. Note that you should not create a new producer template every time you want to send a message.
When you say that you store messages as byte[], I assume you're storing it inside Message body. In this case you store both customheaders and custombody as byte[] and Camel treats them both as Message body, not headers.
If you want to use Camel header-related components or language constructs, you need to parse your customheaders and then set them on the Message as headers by using Exchange.getIn().setHeaders() (see API with a note about this). If you do that, you would probably only want to set content of custombody in the Exchange.getIn().setBody().
If you do these changes in your custom component, your component will now be handling this specific XML format only. If you want to keep your component generic, you can instead implement a custom DataFormat and call marshal() and unmarshal() in your routes. I think SOAP DataFormat does something very similar.

Resources