How do I overwrite Camel Body? - apache-camel

I have used other integration frameworks and libraries before, and in general the way they deal with the message body or payload is to drop everything except the most recent "update" so to speak. Here is an example of what I mean:
XML Message from Queue (payload is XML message) -> use XPath to get something out of XML (payload still XML message) -> Call to some REST API (payload still XML message) -> Return from REST API (payload is now whatever the REST API returned)
I am looking for this sort of behavior in Camel. Right now what Camel is doing is after the "Return from REST API", the payload is whatever the REST API returned appended with the original XML message.
What is the best way for me to only refer to the payload/body that the REST API returns?

Exchange body {exchange.getIn().getBody()} will only contain the response of REST API. Please check the API how it is forming response, it may be appending input xml to output.

Related

Data not transferring from one endpoint to another endpoint with camel

I'm requesting to https://xx.xx.x.xxx/consumers/ domain and I'm getting some response data as JSON format, and I'm passing it another endpoint direct:consumer, but in direct:consumer endpoint if print body I'm getting empty, could anyone help me how to transfer the data from one endpoint to another endpoint.
from("timer://runOnce?repeatCount=1")
.process(consumerCreate)
.to("https://xx.xx.x.xxx/consumers/").log("response data from create:: ${body}")
.to("direct:consumer");
In the below endpoint, if print the body getting an empty response, not getting JSON data
from("direct:consumer").log("the body is ${body} ");
Can anyone please help me is it expected behaviour or am I missing something?
Take a look at enabling steam caching:
https://camel.apache.org/manual/latest/stream-caching.html
Without it, the stream returned by http producer can only be read once, e.g in the first log message. Hence why nothing is printed in the second direct:consumer route when it comes to log the message body.

How to split a message do some extra processing on one of them and aggregate them back

I need to configure some camel routes based on some configuration files.
All configured routes will need to split a message into one or two sub messages then do some JMS integration work on the first one and then aggregate together the JMS reply with the optional second message. In a simplified picture it will look like below:
message -- > split --> message 1 --> JMS request/reply --> aggregate --> more processing
\--> message 2 /
The aggregation will be done on completion size which I am able to know upfront if it is going to be 1 or 2 depending of the route meta data. When the second message is present no other processing is needed before being merged back with the JMS reply.
Si in short I need a split followed by a routing followed by an aggregation which is quite a common pattern. The only particularity is is that in case the second split message is present I don't need to do anything on it before aggregating it back.
In java DSL it will looks something like this:
from("direct:abc")
// The splitter below will set the JmsIntegration flag
.split().method(MySplitter.class, "split")
.choice()
.when(header("JmsIntegration"))
.inOut("jms:someQueue"))
.otherwise()
// what should I have on here?
.to(???)
.end()
.aggregate(...)to(...);
So my questions would be:
What should I put on the otherwise branch?
What I need in fact is an if: if the split message needs JMS go to JMS and then move to aggregator if it is not just go straight to the aggregator. I am considering creating a dummy processor which will actually do nothing but this seems to me a naive approach.
Am I on a wrong path. If so what would be the alternative
Initially I was thinking about a message enricher but I would not like to sent the original message to the JMS
I also considered putting my aggregation strategy inside my splitter but again I could not put it all together.
Based off your post it looks like you are trying to have the return of your enrichment merge with the original message, but you want to send a custom message to the jms endpoint. I would recommend storing your original message in either a bean or a cache or something of the sort, leveraging all of your conversions with camel and then have your aggregation strategy leverage your storage to return your desired format.
from("direct:abc")
.split().method(MySplitter.class, "split")
.choice()
.when(header("JmsIntegration"))
.beanRef("MyStorageBean", "storeOriginal")
.convertBodyTo(MyJmsFormat.class)
//This aggregation strategy could have a reference
//to your storage bean and retrieve the instance
.enrich("jms:someQueue", myCustomAggreationStrategyInstance)
.otherwise()
.end()
.aggregate(...)
.to("direct:continueProcessing");
Option #2: Based off of your comment saying you needed the "original message that the direct:abc endpoint received this can be simplified a lot. In this example we can use camel's existing Original message store to retrieve the message that was passed into direct:abc. If Your message after the split has a JmsIntegration header we will convert the body to the desired format for the jms call, leverage the enrich statement to make the jms call and a custom aggregator that gives you access to the message used to call the jms endpoint, the message that came back, and the original message direct:abc has. If your flow does not have a JmsIntegration header the message will go to the Otherwise statement in your route which does no additional processing before ending the choice statement and then the spit messages are aggregated back together with whatever custom strategy you need.
from("direct:abc")
.split().method(MySplitter.class, "split")
.choice()
.when(header("JmsIntegration"))
.convertBodyTo(MyJmsFormat.class)
//See aggregationStrategy sample below
.enrich("jms:someQueue", myAggStrat)
.otherwise()
//Non JmsIntegration header messages come here,
//but receive no work and are passed on.
.end()
.aggregate(...)
.to("direct:continueProcessing");
//Your Custom Aggregator
public Exchange aggregate(Exchange oldExchange, Exchange newExchange) {
//This logic will retrieve the original message passed into direct:abc
Message originalMessage =(Message)exchange.getUnitOfWork().getOriginalInMessage();
//TODO logic for manipulating your exchanges and returning the desired result
}
You said you considered using Enricher, but you don't want to send raw message. You can resolve this neatly by using a pre-JMS route:
from("direct:abc")
.enrich("direct:sendToJms", new MyAggregation());
.to("direct:continue");
from("direct:sendToJms")
// do marshalling or conversion here as necessary
.convertBodyTo(MyJmsRequest.class)
.to("jms:someQueue");
public class MyAggregation implements AggregationStrategy {
public Exchange aggregate(Exchange original, Exchange resource) {
MyBody originalBody = original.getIn().getBody(MyBody.class);
MyJmsResponse resourceResponse = resource.getIn().getBody(MyJmsResponse.class);
Object mergeResult = ... // combine original body and resource response
original.getIn().setBody(mergeResult);
return original;
}
}
Splitter automatically aggregates split exchanges back together. However, default (since 2.3) aggregation strategy is to return the original exchange. You can easily override the default strategy with your own by specifying it directly on the Splitter. Furthermore, if you don't have an alternative flow for your Choice, then it's much easier to use Filter. Example:
from("direct:abc")
.split().method(MySplitter.class, "split").aggregationStrategy(new MyStrategy())
.filter(header("JmsIntegration"))
.inOut("jms:someQueue"))
.end()
.end()
.to(...);
You still need to implement MyStrategy to combine the two messages.

How can we replace exchange with exchange copy?

Is there a way to replace an exchange with a copy? I get a copy of an exchange in a process and I need replace/overwrite existing exchange with a copy. Scenario is that a SOAP request is getting stored in exchange body and body type is a custom object. Now when request goes to the server, response gets stored in exchange body and body type changes to input stream. If response is not what I need, then I need to replace exchange with the exchange copy and resend the request.
Excerpt from process(...)
Exchange copy = null;
copy = ExchangeHelper.createCopy(exchange, true);
There is setBody, but nothing like setExchange or replaceExchangeWithCopy.
To solve your case in Camel there are 2 type of body i.e.
exchange.getIn.setBody
exchange.getOut.setBody
That mean you can store your request in IN body and the response in OUT body, so after checking your response which is in OUT body is correct or not you can again use your IN body content.
Make sure your Exchange pattern is INOUT.

PUT/GET with Payload using Restangular

I am using Restangular in one of my works
The server guys have give me the following calls which i need to integrate on the AngularJS client
PUT api/partners/password – RequestPayload[{password,confirmpassword}]
partner id is being sent in the header
GET api/partners/password/forgot/ - Request Payload [{emailaddress}]
partner id is being sent in the header
The javascript code that I have written to call these services is as follow
Restangular.all('Partners').one('Password').put(params); - sends params as query string
Restangular.all('Partners').one('Password').one('Forgot').get(params); - sends object in the url
I have tried other ways but it simply doesn't make the correct call.
Help me out guys!
So, for point #1. it puts the object at hand, not another object. So you have 2 options:
Option 1
var passReq = Restangular.all('Partners').one('Password');
passReq.confirmPassword = ....
passReq.put(); // confirmPassword and the params of the object will be sent
Option 2 is
var passReq = Restangular.all('Partners').one('Password').customPUT(obj);
For Point #2, you cannot send a request body (payload) in the GET unfortunately.

Apache Camel: can I put multiple statements in the when part of the conditional choice statement?

I would like to obtain the following kind of routing:
HTTP POST message with XML body enters CAMEL
I store some of the parameters of the XML body
The message is routed to an external endpoint
The external endpoint (external server) replies
-> at this moment, I would like to check whether the reply from the external endpoint is a HTTP 200 OK containing a XML parameter equal to SUCCESS.
-> if so, then I would like to use some of the stored parameters to construct a new HTTP message (method = PUT this time) and send it out to an external endpoint
Problem that I am currently having, is the following:
.choice()
.when(simple("${in.headers.CamelHttpResponseCode} == 200"))
// now I want do a few things, eg: check also the XML body via xpath
// and change the message to be sent out (change Method to PUT, ...)
.to("http://myserver.com")
.otherwise()
// if no 200 OK, I want the route to be stopped ... not sure how ?
.end()
Question: any idea how to add those extra statements in case the HTTP response code was 200 OK ? It looks like the when does not allow me to add extra statements ...
(I got an error in my Eclipse IDE).
Thanks in advance.
Note: could it be that I have to route the message in case the 200 OK matches to a 'new endpoint' and then create a new from route with this new endpoint ?
Eg:
.choice()
.when(simple("${in.headers.CamelHttpResponseCode} == 200"))
.to("mynewendpoint")
.otherwise()
// if no 200 OK, I want the route to be stopped ... not sure how ?
.end();
from("mynewendpoint").
.setHeader(etc etc)
.to("http://myserver.com")
In this latter case, how exactly should I define this 'newendpoint' ?
In the programming language DSLs such as Java, you can build predicates together. I posted a blog entry some years ago about this at: http://davsclaus.blogspot.com/2009/02/apache-camel-and-using-compound.html
For example having two predicates
Predicate p1 = header("hl7.msh.messageType").isEqualTo("ORM"):
Predicate p2 = header("hl7.msh.triggerEvent").isEqualTo("001");
You can chain them together, using and or or.
Predicate isOrm = PredicateBuilder.and(p1, p2);
And then you can use isOrm in the route
from("hl7listener")
.unmarshal(hl7format)
.choice()
.when(isOrm).beanRef("hl7handler", "handleORM")
.otherwise().beanRef("hl7handler", "badMessage")
.end()
.marshal(hl7format);
yep, you can have multiple statements between the .when() and .otherwise() and you can always call .endChoice() to explicitly end each conditional block...
to your other question, you can use camel-direct to chain together multiple routes, etc...

Resources