Loosing request body on the doCatch after exception Apache Camel - java

I'm having an issue on route configuration with Apache Camel.
For some reason, after falling into an exception, I'm loosing the ${body}, it's just simply wiping out. Is there any way to keep what I have on ${body} if my request fail?
This is the chunk of code:
.to(pricingDomainUrl + "?bridgeEndpoint=true").process(PRICING_DOMAIN_RESPONSE_PROCESSOR)
.doCatch(Exception.class)
.doTry()
.setProperty("exception", simple("true"))
.log(LoggingLevel.INFO, logger, "BODY ON TRY: ${body}")<- prints an empty body
.log(LoggingLevel.WARN, "First call failed, trying cleaning headers")
.process(PRICING_DOMAIN_REQUEST_PROCESSOR)
.doCatch(Exception.class)
.process(EXCEPTION_PROCESSOR);

Related

How to handle 4xx(without retry) and 5xx (with retry) exceptions in camel

I've a camel route which makes API request, the external service may though 4xx or 5xx. I have written HttpOperationFailedException handler to handle all HTTP related exception and I'm retrying all the Http exceptions irrespective of whether its client side or server side exceptions. I would like to handle them in a way, I need to avoid the reties for client side exceptions.
Here is my route and exception code, looks like. Can anyone suggest best way to handle these scenarios ?
onException(HttpOperationFailedException.class)
.handled(true)
.redeliveryDelay(100)
.maximumRedeliveries(2)
.log("${exception} Http Communication Exception while making API request")
.end();
from("direct:start")
.routeId("restApi")
.process(exchange -> exchange.getIn().setBody(
new RequestBody(
"${headers.camelFileName}")))
.marshal()
.json(JsonLibrary.Gson)
.setHeader(Exchange.HTTP_METHOD, constant("POST"))
.setHeader("Content-Type",constant("application/json"))
.to(url)
.end();
You could try something along the lines of:
onException(HttpOperationFailedException.class)
.choice()
.when(simple("${exception.getStatusCode()} == '400'"))
//doSomething
.endChoice()
.when(simple("${exception.getStatusCode()} == '500'"))
//doSomething
.otherwise()
//retries
.endChoice()
.end()
;

Generate multiple from() dynamically Apache Camel RouteBuilder

I was using camel-core 2.24.1 and was able to do the following:
from( sources.toArray(new String[0]) )
where sources is a list of URIs that I get from a configuration settings. I am trying to update the code to use Camel 3 (camel-core 3.0.0-RC2) but the method mentioned above was removed and I can't find another way to accomplish the same.
Basically I need something like:
from( String uri : sources )
{
// add the uri as from(uri) before continuing with the route
}
In case this would help understand better, the final route should look like:
from( sources.toArray(new String[0]) )
.routeId(Constants.ROUTE_ID)
.split().method(WorkRequestSplitter.class, "splitMessage")
.id(Constants.WORK_REQUEST_SPLITTER_ID)
.split().method(RequestSplitter.class, "splitMessage")
.id(Constants.REQUEST_SPLITTER_ID)
.choice()
.when(useReqProc)
.log(LoggingLevel.INFO, "Found the request processor using it")
.to("bean:" + reqName)
.endChoice()
.otherwise()
.log(LoggingLevel.ERROR, "requestProcessor not found, stopping route")
.stop()
.endChoice()
.end()
.log("Sending the request the URI")
.recipientList(header(Constants.HDR_ARES_URI))
.choice()
.when(useResProc)
.log(LoggingLevel.INFO, "Found the results processor using it")
.to("bean:" + resName)
.endChoice()
.otherwise()
.log(LoggingLevel.INFO, "resultProcessor not found, sending 'as is'")
.endChoice()
.end()
.log("Sending the request to all listeners")
.to( this.destinations.toArray( new String[0] ) );
Any help will be greatly appreciated.
This feature was removed with no direct replacement in CAMEL-6589.
See Migration guide:
In Camel 2.x you could have 2 or more inputs to Camel routes, however this was not supported in all use-cases in Camel, and this functionality is seldom in use. This has also been deprecated in Camel 2.x. In Camel 3 we have removed the remaining code for specifying multiple inputs to routes, and its now only possible to specify exactly only 1 input to a route.
You can always split your route definition to logical blocks with Direct endpoint. This can be also generated dynamically with for-each.
for(String uri : sources){
from(uri).to("direct:commonProcess");
}
from("direct:commonProcess")
.routeId(Constants.ROUTE_ID)
//...
.log("Sending the request to all listeners")
.to(this.destinations.toArray(new String[0]));

Camel Rest. Can receive body on DELETE, but can't send it

I have two apps. First of them executes some business logics and than calls other app with POST or DELETE
from("direct:firstapp").routeId("rst_firstapp").streamCaching()
//data preparation was here
.setHeader(Exchange.CONTENT_TYPE, simple(MediaType.APPLICATION_JSON_VALUE))
.convertBodyTo(MyRequest.class)
.choice().id("rst_req_lockCardChoice")
.when().simple("${mycondition} == '1'")
.setHeader(Exchange.HTTP_METHOD, simple("POST"))
.log(LoggingLevel.INFO, "Call post")
.when().simple("${mycondition} == '0'")
.setHeader(Exchange.HTTP_METHOD, simple("DELETE"))
.log(LoggingLevel.INFO, "Call delete")
.end()
.marshal(new JsonDataFormat(JsonLibrary.Jackson))
.log(LoggingLevel.INFO, " Call to service ${body}")
.to('myadress/mypath').id("rt_call_service")
.log(LoggingLevel.INFO, " Response from service ${body}");
Logs for line: .log(LoggingLevel.INFO, " Call to service ${body}")
print that body exists in both cases.
The second app receives this requests and does some other business logics.
rest().post("/mypath")
.consumes(MediaType.APPLICATION_JSON_VALUE)
.produces(MediaType.APPLICATION_JSON_VALUE)
.type(MyRequest.class)
.responseMessage().code("200").message("Success").endResponseMessage()
.route().routeId("rst_postrecieve")
.log(LoggingLevel.INFO, "Recieved request ${body}")
.id("rst_rst_post_recieved")
.to("direct:drt_rst_postbranch")
.endRest();
rest().delete("/mypath")
.consumes(MediaType.APPLICATION_JSON_VALUE)
.produces(MediaType.APPLICATION_JSON_VALUE)
.type(MyRequest.class)
.responseMessage().code("200").message("Success").endResponseMessage()
.route().routeId("rst_deleterecive")
.log(LoggingLevel.INFO, "Received request ${body}")
.id("rst_rst_delete_recieved")
.to("direct:drt_rst_deletebrach")
.endRest();
POST request works fine.
First app sends body. Second recieves it.
DELETE doesn't.
First app cuts the body out. Line .log(LoggingLevel.INFO, " Call to service ${body}") prints the body. But the reciever gets a DELETE request with empty body. I have sniffed the Http-request with Wireshark: the body is empty.
However, I'm perfectly able to send a DELETE request with body using any other tool like Swagger-ui, Postman, Soap-UI etc. The second app will recieve body and proccess it correctly.
Why does Camel forbid to send a DELETE body, while it allows to recieve it? I expected the behaviour to be the same.
What is the workaround? I can't change request type, it's a customer requirement.
Camel version: 2.21.0.000033-fuse-000001-redhat-1
P.S. I know that Http standards state that no body is expected for DELETE, however all the tools & frameworks I faced allowed to send and receive it. Including Spring, for example.
Most probably Delete method will ignore body while processing. This is no problem with your code, it was because of HTTP implementation.
If a DELETE request includes an entity body, the body is ignored [...]
If you still need to send some data from one app to another to Delete method. I suggest you can try with path param. So than you can get as parameter in second app and do you process.

Log JSON message in Camel Route

I tried googling this and found nothing.
I know that you can use expression language like ${file:name} to log the file name, but how do you log the contents. How do you log the contents of the json message being passed forward?
You can log the body and headers of the exchange by writing
.log(LoggingLevel.INFO, "test", "${body} and ${headers}")
Depending on how you log, but you can use a TraceFormatter with showBody set to true, read http://camel.apache.org/tracer.html

Apache-camel: Enabling bridgeEndpoint on the http endpoint

I created a simple route to get contact from a remote host. But, there seems to be a lot of confusion regarding the bridgeEndpoint option.
Initially, I added the route using the Java DSL as follows:
from("direct:getContact")
.marshal().json(JsonLibrary.Jackson)
.setHeader("Content-Type", constant("application/json"))
.setHeader("Accept", constant("application/json"))
.setHeader(Exchange.HTTP_METHOD, constant("GET"))
.recipientList(simple("http://<remoteHost>:8080/api/contact" +
"/${header.contactId}"))
.unmarshal().json(JsonLibrary.Jackson);
This route is just a proxy for the get contact API of the remote host.
I got the following error:
Invalid uri: /ib/contact/51702/contact/51702. If you are forwarding/bridging http endpoints, then enable the bridgeEndpoint option on the endpoint: Endpoint[http://<remoteHost>:8080/api/contact/51702]
/ib/* you see is the base url for the tomcat servlet. As suggested in the error, I added the bridgeEndpoint=true to the endpoint as shown below:
from("direct:getContact")
.marshal().json(JsonLibrary.Jackson)
.setHeader("Content-Type", constant("application/json"))
.setHeader("Accept", constant("application/json"))
.setHeader(Exchange.HTTP_METHOD, constant("GET"))
.recipientList(simple("http://<remoteHost>:8080/api/contact" +
"/${header.contactId}?bridgeEndpoint=true"))
.unmarshal().json(JsonLibrary.Jackson);
Then, I get a different error:
org.apache.camel.component.http.HttpOperationFailedException:
HTTP operation failed invoking
http://<remoteHost>:8080/api/contact/51702/contact/51702 with statusCode: 404
at org.apache.camel.component.http.HttpProducer.populateHttpOperationFailedException(HttpProducer.java:233)
at org.apache.camel.component.http.HttpProducer.process(HttpProducer.java:158)
at org.apache.camel.util.AsyncProcessorConverterHelper$ProcessorToAsyncProcessorBridge.process(AsyncProcessorConverterHelper.java:61)
at org.apache.camel.processor.RedeliveryErrorHandler.process(RedeliveryErrorHandler.java:448)
at org.apache.camel.processor.CamelInternalProcessor.process(CamelInternalProcessor.java:190)
at org.apache.camel.processor.MulticastProcessor.doProcessSequential(MulticastProcessor.java:652)
at org.apache.camel.processor.MulticastProcessor.doProcessSequential(MulticastProcessor.java:580)
at org.apache.camel.processor.MulticastProcessor.process(MulticastProcessor.java:227)
at org.apache.camel.processor.RecipientList.sendToRecipientList(RecipientList.java:167)
at org.apache.camel.processor.RecipientList.process(RecipientList.java:120)
at org.apache.camel.processor.Pipeline.process(Pipeline.java:118)
at org.apache.camel.processor.Pipeline.process(Pipeline.java:80)
It is still appending "contact/51702" to the url of the remote host, which is giving 404.
What am I missing here?
From the FAQ
In camel there are a number of components that use the http protocol headers to do their business.
I believe your producer does it as well. So the following could solve your problem.
from("direct:getContact")
.marshal().json(JsonLibrary.Jackson)
.setHeader("Content-Type", constant("application/json"))
.setHeader("Accept", constant("application/json"))
.setHeader(Exchange.HTTP_METHOD, constant("GET"))
.removeHeader(Exchange.HTTP_PATH)
.recipientList(simple("http://<remoteHost>:8080/api/contact" +
"/${header.contactId}?bridgeEndpoint=true"))
.unmarshal().json(JsonLibrary.Jackson);
You could also remove contact/${header.contactId} from the endpoint. As it looks redundant. But this depends on what you want to achieve.
Answer by #SubOptimal is almost correct (we can say his answer is suboptimal, lol), except it should be HTTP_URI header. From the doc:
If the bridgeEndpoint option is true, HttpProducer will ignore the Exchange.HTTP_URI header, and use the endpoint’s URI for request.
Therefore there are 2 solutions:
add .removeHeader(Exchange.HTTP_URI) to the route definition
add ?bridgeEndpoint=true query parameter
However this might not solve the problem if you have other headers that get in the way. Probably that was your case, that's why removing all Camel http headers helped.
Please be aware though that removing all headers might break your logic: for example HTTP_METHOD header is used to define the http method of the outgoing request. And it's up to you if you want to proxy the method too or not. You can find more in the same doc via the link above.

Categories