Reputation: 959
Am I approaching this correctly? I'm using onException to catch the HttpOperationFailedException when I receive an http 401 error (although I haven't figured out how to catch a 401 specifically). When I see one of these, I want to make sure the token is fresh, so I regenerate one and set the header.
Here is a simplified example, cutting out what I know is working and using some hard coding.
onException(HttpOperationFailedException.class).maximumRedeliveries(1)
.setHeader("Authorization", constant("abcde"))
.log("Newly Set Header: ${headers.Authorization} ${body}");
from("jms:queue:Queue")
.unmarshal(receiptFormat)
.bean(ModelTranslator.class, "reqest")
.marshal(requestFormat)
.setHeader(Exchange.CONTENT_TYPE, constant("application/json"))
.setHeader("Accept", constant("application/json"))
.setHeader(Exchange.HTTP_METHOD, constant(org.apache.camel.component.http.HttpMethods.POST))
.setHeader("Authorization", constant("12345"))
.to("log:DEBUG?showBody=true&showHeaders=true")
.to("https://test.net/rest/api/email")
.to("log:DEBUG?showBody=true&showHeaders=true");
I intentionally set a bad token before the request to https://test.net/rest/api/email, which causes the error. I successfully catch it and set the good token. I can see that the good token is set with the .log(). And if I understand correctly, onException will re-try starting where it failed, which is the .to("https://test.net/rest/api/email"), correct?
But the call to https://test.net/rest/api/email fails with a 401 still... So I'm doing something wrong.
Upvotes: 2
Views: 5100
Reputation: 2197
The logic after .maximumRedeliveries(1)
in the onException
will only be called if the redeliveries fail as well. This means that the .setHeader("Authorization", constant("abcde"))
gets executed after the redeliveries have already been made and since .handled(true)
isn't called the execution of the route will stop to the exception.
If you want to change the token and call the http producer-endpoint again with the new token you can do that with try-catch instead.
from("jms:queue:Queue")
//...
.setHeader("Authorization", constant("12345"))
.to("log:DEBUG?showBody=true&showHeaders=true")
.doTry()
.to("https://test.net/rest/api/email")
.doCatch(HttpOperationFailedException.class)
.setHeader("Authorization", constant("abcde"))
.log("Newly Set Header: ${headers.Authorization} ${body}")
.to("https://test.net/rest/api/email")
.end()
.to("log:DEBUG?showBody=true&showHeaders=true");
Alternatively you could also do the same with onException but I would rather avoid defining global exception handler and use route specific one instead.
from("jms:queue:Queue")
.onException(HttpOperationFailedException.class)
.setHeader("Authorization", constant("abcde"))
.log("Newly Set Header: ${headers.Authorization} ${body}")
.to("https://test.net/rest/api/email")
// Use handled(true) if you want to stop the route instead.
.continued(true)
.end()
// ...
.setHeader("Authorization", constant("12345"))
.to("log:DEBUG?showBody=true&showHeaders=true")
.to("https://test.net/rest/api/email")
.to("log:DEBUG?showBody=true&showHeaders=true");
Upvotes: 4
Reputation: 12859
An alternative to what Pasi posted is to specify throwExceptionOnFailure
set to false on the endpoint you invoke to prevent Camel from raising an exception as you handle that event yourself.
In the next step you simply define a choice
that queries the HTTP response code present in the headers of the currently processed exchange and trigger the business logic you want in cases you need to handle:
route("direct:perform_request")
.routeId("performRequest")
.to("http4:....?throwExceptionOnFailure=false")
.choice()
.when(header(Exchange.HTTP_RESPONSE_CODE).isEqualTo(401))
// divert to a different route responsible for creating the token which
// is stored in the exchange headers
.to("direct:generate_auth_token");
// ... use the generated token of the previous route which should be available
// in the headers to update the authorization header
.setHeader("Authorization", header(HeaderConstants.AUTH_TOKEN)
// invoke itself with the updated credentials
.to("direct:perform_request")
...
.endChoice()
.when(header(Exchange.HTTP_RESPONSE_CODE).isEqualTo(400))
// handle bad request responses here
...
.endChoice()
.otherwise()
// regular stuff, if needed
...
.endChoicee()
.end()
...
This approach, however, might be less than optimal in your current situation as you'd need to more or less refactor that logic to your own route which you pass the exchange to as long as the response doesn't meet your expectations. You might further need to preserve the original message as the route generating your token as well as the initial response of the request might update the current message body of the processed exchange.
The beauty of an exception handler here is that Camel will continue from the point on the exception initially occurred once the exception got resolved, if resolvable at all. On top of that, .useOriginalBody()
guarantees that the initially used message payload is used.
The sample outlined here just illustrates that there are multiple ways in Camel to achieve things. While this approach may be more flexible, it also puts a bit more burden onto you as you need to design the flow of the exchange through the routes a bit more carefully and evaluate when certain headers or the message body itself is modified.
Upvotes: 1