Looking for advice on error handling using spring IntegrationFlows. I have a set up with many flows with a mixture of routing, transformations and channels this is fully working.
Currently for error handling I catch all checked expectations that a given .handle(..) may throw and re-throw them as a Runtime exception. This causes the exception to hit my error channel, where I deal with the problem. I am looking to see if this is how the use of channels and Integrations flows was designed.
return IntegrationFlows.from("test")
.handle(X.class, (p, h) -> process(p))
.handle(More::EndPorcess)
.get();
Sorry, what is the question though?
It isn't regular Java, so there is something in between operators. It is message channel. And that is really designed to end up in the errorChannel if there is no ability to bubble exception as a StackTrace: http://docs.spring.io/spring-integration/docs/4.3.5.RELEASE/reference/html/configuration.html#namespace-errorhandler
Related
I want to write to a channel adapter only if the previous channel adapter write has been written successfully. I’m trying to do this by:
#Bean
public IntegrationFlow buildFlow() {
return IntegrationFlows.from(someChannelAdapter)
.handle(outboundChannelAdapter1)
.handle(outboundChannelAdapter2)
.get();
}
But I’m getting the following exception: The ‘currentComponent’ (…ReactiveMessageHandlerAdapter) is a one-way 'MessageHandler’ and it isn’t appropriate to configure ‘outputChannel’. This is the end of the integration flow.
How can I perform this?
If your handler implementation is one-way, fire-n-forget, then indeed there is no justification to continue the flow. It can go ahead with the configuration if the current handler is reply-producing and there will be something we can build a message to send to the next channel.
In your case .handle(outboundChannelAdapter1) is just void, so the next .handle(outboundChannelAdapter2) is not going to have anything to continue the flow. So, the framework gives you a hint that such a configuration is wrong. It is called flow for a reason: the result of the current endpoint is going to be an input for the next one. If no result, no continuation. How else it could work in your opinion?
The point is that there need to be something to write to your channel adapter. One of the solution is a PublishSubscribeChannel which distributes the same input message to all its subscribers. If that is what would fit to your expectations, then take a look into its support in Java DSL: https://docs.spring.io/spring-integration/docs/current/reference/html/dsl.html#java-dsl-subflows.
Another way is a RecipientListRouter pattern: https://docs.spring.io/spring-integration/docs/current/reference/html/message-routing.html#router-implementations-recipientlistrouter.
You may achieve the same with WireTap as well, but it depends on a business logic of your solution: https://docs.spring.io/spring-integration/docs/current/reference/html/core.html#channel-wiretap.
But anyway: you need to understand that the second handler can be called only if there is an input message for its channel. In all those cases I showed you it is exactly the same message you send to a first handler. If your expectations are different, please elaborate what kind of message you'd like to have for a second handler if the first does not return anything.
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.
I have successfully implemented some spring Integration Flow.
I am looking to have a circuit breaker either the same one for each endpoints or either at the flow level.
I have already read this documentation https://docs.spring.io/spring-integration/reference/html/handler-advice.html, but I havent find my answer.
Should I use some AOP ?
Thanks
G.
I'm not sure what you have missed in the mentioned docs, but RequestHandlerCircuitBreakerAdvice is indeed over there: https://docs.spring.io/spring-integration/reference/html/handler-advice.html#circuit-breaker-advice
The advises like this should be applied in the Java DSL with this configuration option:
.transform(..., c -> c.advice(expressionAdvice()))
Pay attention to that advice(expressionAdvice()) call. The expressionAdvice() is a bean method. So, you can do something similar for the RequestHandlerCircuitBreakerAdvice and any your endpoints in the flow which need to be guarded by the circuit.
And yes, you can use only a single bean for the RequestHandlerCircuitBreakerAdvice. It does keep a state for any endpoint it is called against:
protected Object doInvoke(ExecutionCallback callback, Object target, Message<?> message) {
AdvisedMetadata metadata = this.metadataMap.get(target);
if (metadata == null) {
this.metadataMap.putIfAbsent(target, new AdvisedMetadata());
metadata = this.metadataMap.get(target);
}
Thanks for your answer #artem-bilan.
I really appreciate that a spring integration team member anwsered to this.
After more thoughts, I have reformulated my problem.
Given an IntegrationFlow, with a specific error channel, if there are more than a given amount of errors in given span time (more than 10 errors in 10s), I want to stop polling the input channel.
So I redirect all the errors for this flow to the specific flow error channel.
An error counter is incremented, and then if the threshold is reached in the given span time, I stop the poller.
I have a second flow that monitor "stopped" pollers, and it restart them after some time.
[UPDATE]
I do have use your recommendations.
Mainly because I the framework dont solve your problem, your probably wrong.
And I was wrong.
Thanks !
In our application we have error handling mechanism, where we throw runtime exceptions on an error. I noticed a strange behavior and I want to understand the mechanism underlying this one
1) Situation 1: Exception thrown from ServiceActivator is converted to MessageHandlingException
When an error occurs in a ServiceActivator, we throw an exception. The message we get on ErrorChannel has PayLoad as org.springframework.integration.MessageHandlingException and actual exception thrown as cause
2) Situation 2: Exception thrown from Filter is not masked with MessageHandlingException
When an error occurs in Filter, and we throw exception, then PayLoad is actual exception, and is not masked with org.springframework.integration.MessageHandlingException
I have a few questions:
Why exception throwing from ServiceActivator behaves differently than in Filter
Are there some "best practices" around error handling in Spring-integration projects, while utilizing the errorChannel and related infrastructure
Update 1:
Filter extends AbstractFileListFilter which is part of a filter chain- a custom CompositeFileFilter which implements FileListFilter
CompositeFileFilter is being used by a file:inbound-channel-adapter and which passes the output to a Channel declared below:
<int:channel
id="channelForFilesComingIn"
datatype="java.io.File"
>
<int:dispatcher task-executor="dispatchExecutor" />
</int:channel>
Update 2:
Whet we are trying to do is read files from filesystem and process them. In file reading part, using file:inbound-channel-adapter with a CompositeFilter which filters files which are not completely uploaded or don't meet naming standards.
After all filters pass, file is handed over to a ServiceActivator for processing
In any of above (Filter chain or Service) , if there is an error condition, it has to be reported to DB and by email. For achieving this we are throwing ApplicationException which are caught by errorChannel, and passed to specialized channels.
Just to make it clear, a MessageHandlingException is thrown (wraps user exception) when a Message HANDLER fails - a message handler is something that HANDLES a message.
If an exception is thrown in the MessageSource, there is no message yet so a MessageHandlingException (or any MessagingException) does not apply.
Instead, the poll fails and the exception is thrown back to the poller.
If you want to handle exceptions in a polled endpoint (MessageSource), you need to give the poller an ErrorHandlingTaskExecutor, to which you can provide an ErrorHandler and do what you want with the exception, but since there is no message yet, it is the original exception thrown by the MessageSource.
If you want to send it to the error channel, you'll need to do that in your custom ErrorHandler.
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.