How to catch IOException's while using Spring WebClient? - spring

I'm working on a project in which we are migrating from RestTemplate to WebClient.
The WebClient is implemented like this:
try {
...
return webClient
.get()
.uri(someUri)
.retrieve()
.toEntity(SomeBusinessClass.class).block()
} catch(WebClientException e) {
// do some stuff
// want to catch IOExceptions here as well
}
While refactoring the code I had to refactor the tests as well and I've come across a test in which we basically throw an ConnectException to see if our internal code catch them according to our needs. With RestTemplate's exception classes we was able to define the exception like this:
ResourceAccessException exc = new ResourceAccessException("I/O error on GET request", new ConnectException("Connection refused: connect"))
I tried to do the same with WebClient's provided exception class WebClientException but that's an abstract class and the only class inheriting from it is WebClientResponseException and that don't provide a constructor which would allow to do the same. So my only option was to do it with RuntimeException:
RuntimeException exc = new RuntimeException("I/O error on GET request", new ConnectException("Connection refused: connect"))
But since I don't want to rewrite our internal code to catch exceptions on RuntimeException level but on WebClientException level, is that not an option and I'm wondering how to do that?
I tried to find out in the Spring docs how to handle IOException's while using WebClient but couldn't find anything.
What would be the approach here?

The nicest way would almost certainly be to handle all errors in the reactive stream itself. Server response errors are usually best handled by using exchange() rather than retrieve() and then dealing with the response manually, and an underlying IOException by using the onErrorResume(), onErrorReturn() etc. reactive operators available for this purpose.
However, you mention you're migrating from blocking code, so I understand that practically that may not (yet) be on the cards. If you want to stick to catching exceptions:
But since I don't want to rewrite our internal code to catch exceptions on RuntimeException level but on WebClientException level, is that not an option and I'm wondering how to do that?
Wanting to catch all transport errors under the umbrella of WebClientException is not a sensible option. As you say, neither is just catching RuntimeException for obvious reasons.
Simplifying it, WebClientException means "I connected to the URL and sent stuff to it without an issue, but it told me to sod off" (ie. it generated an error code rather than a 200 response.)
That might be because of a 404 (resource not found), 500 (server error), 418 (you're trying to connect to a teapot, not a server), etc.
IOException on the other hand means "Couldn't even establish a connection to this URL." That could be because the connection was actively refused, the domain name couldn't be resolved, the SSL cert expired, etc.
The two are not analogous, and it would be rather odd and confusing to treat them that way.
If you want to handle them in the same block, then that's fine - naively you might just do:
catch(WebClientException|IOException e) {
// do some stuff
}
...but you can't of course, because IOException is checked. (Reactive streams in Java don't throw checked exceptions, each checked exception is mapped to a RuntimeException instead.)
However, you can map all IOException to an UncheckedIOException:
return webClient
.get()
.uri(someUri)
.retrieve()
.toEntity(SomeBusinessClass.class)
.onErrorMap(IOException.class, UncheckedIOException::new)
.block()
...and then either do catch(WebClientException|UncheckedIOException ex), or deal with them in separate catch blocks.
This certainly isn't the "nice" way to handle exceptions from a reactive mindset, but if you're aiming to migrate with the fewest possible changes, this is likely what you're after.

Related

Spring Integragion + Message Gateway error handling

I have a message gateway which is called by a REST controller and I wonder what is the correct approach to handle any errors that might occur on the downstream flow.
What I mean by "handle any errors" is to log some information about them, take some other action whatever that might be, and finally be able to return a 400 to the controller.
For me, the approach that makes more sense is to have an errorChannel on the gateway, BUT I think that a replyChannel might make sense as well.
I've been able to handle the errors with the "errorChannel" approach, don't know if this is the way to go:
#MessagingGateway(errorChannel = "integrationFlowErrorChannel")
public interface OrderGateway {
#Gateway(requestChannel = "orders.input")
void processOrderRequest(Order order);
}
Any error sent to the errorChannel are handle by the following service activator:
#ServiceActivator(inputChannel="integrationFlowErrorChannel")
public void handleExceptions(Message<MessageHandlingException> message) throws RuntimeException {
log.error(String.format("error: %s", message));
log.error(String.format("error: %s", message.getPayload()));
throw new RuntimeException("something bad happened");
}
Thanks
What you have is correct.
BUT I think that a replyChannel might make sense as well.
It's not clear what you mean by that; a reply channel is generally not needed unless you want to do something like logging the reply via a wiretap or sending the reply to multiple places.

MassTransit fault consumer not invoked for request/response

What is the best practice for handling exceptions in MassTransit 3+ with regard to Request/Response pattern? The docs here mention that if a ResponseAddress exists on a message, the Fault message will be sent to that address, but how does one consumer/receive the messages at that address? The ResponseAddress for Bus.Request seems to be an auto-generated MassTransit address that I don't have control over, so I don't know how to access the exception thrown in the main consumer. What am I missing? Here's my code to register the consumer and its fault consumer using Unity container:
cfg.ReceiveEndpoint(host, "request_response_queue", e =>
{
e.Consumer<IConsumer<IRequestResponse>>(container);
e.Consumer(() => container.Resolve<IMessageFaultConsumer<IRequestResponse>>() as IConsumer<Fault<IRequestResponse>>);
});
And here's my attempt at a global message fault consumer:
public interface IMessageFaultConsumer<TMessage>
{
}
public class MessageFaultConsumer<TMessage> : IConsumer<Fault<TMessage>>, IMessageFaultConsumer<TMessage>
{
public Task Consume(ConsumeContext<Fault<TMessage>> context)
{
Console.WriteLine("MessageFaultConsumer");
return Task.FromResult(0);
}
}
This approach DOES work when I use Bus.Publish as opposed to Bus.Request. I also looked into creating an IConsumeObserver and putting my global exception logging code into the ConsumeFault method, but that has the downside of being invoked every exception prior to the re-tries giving up. What is the proper way to handle exceptions for request/response?
First of all, the request/response support in MassTransit is meant to be used with the .Request() method, or the request client (MessageRequestClient or PublishRequestClient). With these methods, if the consumer of the request message throws an exception, that exception is packaged into the Fault<T>, which is sent to the ResponseAddress. Since the .Request() method, and the request client are both asynchronous, using await will throw an exception with the exception data from the fault included. That's how it is designed, await the request and it will either complete, timeout, or fault (throw an exception upon await).
If you are trying to put in some global "exception handler" code for logging purposes, you really should log those at the service boundary, and an observer is the best way to handle it. This way, you can just implement the ConsumeFault method, and log to your event sink. However, this is synchronous within the consumer pipeline, so recognize the delay that could be introduced.
The other option is to of course just consume Fault<T>, but as you mentioned, it does not get published when the request client is used with the response address in the header. In this case, perhaps your requester should publish an event indicating that operation X faulted, and you can log that -- at the business context level versus the service level.
There are many options here, it's just choosing the one that fits your use case best.

Is it possible to add additional information for crashes handled by Xamarin.Insights analytics framework

I have an xamarin.android with xamarin.insights intergrated.
Right now every time I handle error manually (try/catch) I'm adding information about environment (staging/production):
try
{
ExceptionThrowingFunction();
}
catch (Exception exception)
{
exception.Data["Environment"] = "staging";
throw;
}
But this information is missing in case if error handled by xamarin.insights itself (in case of crash).
It is possible to add additional exception data in case of crash?
docs reference I used
From reading the docs page reference that you mentioned, I still get the impression that you have to call the .Report method as well as in:-
Insights.Report(exception, new Dictionary <string, string> {
{"Some additional info", "foobar"}
});
What I believe they are saying in this example:-
try {
ExceptionThrowingFunction();
}
catch (Exception exception) {
exception.Data["AccountType"] = "standard";
throw;
}
Is that you have the ability when any Exception is encountered, to package additional information that you can later send to the Insights server, as the Data property of the Exception is just a Key/Value Dictionary.
So if you had an Exception several layers deep, you can choose to re-throw the Exception with additional information contained within it that you will later send to the Insights server.
At a higher level, you can then take the Exception that was thrown deeper down the call-hierarchy and then call the Insights.Report, with:-
Insights.Report(
{the rethrown exception in your higher up try..catch block},
{rethrown exception}.Data
);
that will then send all the additional Key/Value information previously captured.
From seeing your last part of your question though it looks like you are interested in Insights handling and sending this additional .Data automatically should there be an unhandled exception.
If it is not currently being sent, then perhaps suggest to them that this can be sent also? As it sounds a feasible request for this to automatically be sent as well incase of an unhandled exception.
Update 1:-
Yes - I understand about the unhandled exception scenario now that you are referring to.
I have not dealt with this component directly, so there may be hooks / event handlers or something already defined where you can tap into this, and execute some custom code just prior to this being sent.
If this is not available, then perhaps suggest this to them to include as its a Beta product?
Alternatively, you could still achieve this yourself by capturing the unhandled exceptions just prior to them falling. You'd have to code this however on each platform.
For instance on Windows Phone in the App class there is Application_UnhandledException(object sender, ApplicationUnhandledExceptionEventArgs e) to which you could then supplement the Exception thrown with this extra .Data.
For Android you could take a look at this post that describes how to catch uncaughtException that will help you in capturing the unhandled exceptions.
Whether just supplementing the Exception in these handlers above is enough all depends on how they've written their hook into this, as to how well it behaves and whether it is executed first, prior to their implementation.
You will have to try and see if it does. If it doesn't behave well, allowing you to supplement extra data prior to the automatic call to Insights, you have another fallback solution, to just do the .Report call manually within these unhandled exception handlers yourself to make this work and supplement the extra .Data to achieve your aim.

Spring Hibernate eats exceptions

With #Transaction and with trace level logging of Spring I see that Hibernate has an exception on a db constraint but it just rolls back the transaction. I tried using #Exception without success to try to catch it.
I see a suggestion online to use Spring AOP #AfterThrowing to catch such events.
This seems like a rather complex way to handle a commonplace event. It seems so much easier with try/catch and old fashioned commits/rollbacks. Is there no better way
in Spring Hibernate?
I'm processing XML messages from a queue. Depending on the type of the exception I get I might want to just catch it and put the bad incoming queue message into an db error table and continue the transaction. (If I just blindly rollback the message will get put back on the queue which might be good in some cases but not in others).
From what I've read there are ways of being made aware of some errors (for some reason not the constraint error) and logging them. Are there ways of interrupting the transaction after an exception and deciding if one wants to commit, continue or rollback?
If not, can one use old style commits and rollback with spring hibernate?
Configure SimpleMappingException resolver and log the exception there:
public class MyExceptionResolver extends SimpleMappingExceptionResolver {
private Logger logger = LoggerFactory.getLogger(MyExceptionResolver .class);
#Override
protected void logException(Exception ex, HttpServletRequest request) {
this.logger.warn(buildLogMessage(ex, request), ex);
}
<bean class="com.mypackage.MyExceptionResolver"/>
EDIT:
You can choose to do anything. The above class has nothing to do with rolling back. It's useful to intercept exceptions and do whatever you want with them.
By default, Spring rolls back for any RuntimeException.
If you want to configure rollback policy:
#Transactional(rollbackFor = { CustomException.class})
or
#Transactional(noRollBackFor= { CustomException.class})

Exception handling strategy

Imagine you have some code that could potentially throw an exception. For example, you try
to send an e-mail message to a mail server, or write a file to disk while you’re not sure if you have the right permissions to do so. What kind of exception handling strategy would you use to avoid the exception from being displayed in the browser? What code would you need?
All languages that can throw exceptions have some manner by which to catch them.
They often look something like this:
try
{
some_risky_thing();
}
catch(Exception e)
{
handle_the_exception();
}
By catching the exception you stop it's propagation up the call stack (where it will eventually find the user).
In order to stop all exceptions getting to the user put one of these at the very top level you have available. Then you can catch any stray exceptions you've missed and do something more appropriate than throw them at the user (like log them somewhere discretely).
It depends.
For those cases, I would probably wrap the code that can throw the exception in a try/catch block. Different languages call this construct something different - sometimes it's try/catch/finally, others it's try/except.
However, it's easy to abuse exceptions and exception handling. A few things that you need to avoid are using exception handling for flow control, handling exceptions too soon (keep passing them up the call stack until they can be appropriately handled), and treating non-exceptional conditions as exceptional.

Resources