Spring-integration ExpressionEvaluatingRequestHandlerAdvice unable to evaluate the failureExpression - spring

Following a question I made yesterday, I found some issues with the ExpressionEvaluatingRequestHandlerAdvice because it isn't working as I expected.
I tried the doc example and my expectation were to get two AdviceMessage: one saying "good was successful" and another one saying "bad was bad, with reason..." but when I run the code I get an AdviceMessage and an ErrorMessage.
AdviceMessage [payload=good was successful, headers={id=2eb612ea-d3cd-f853-3295-236505a130f3, timestamp=1611307975199}, inputMessage=GenericMessage [payload=good, headers={id=725e89ea-be09-7534-1b29-5c7412a99a79, timestamp=1611307975196}]]
ErrorMessage [payload=org.springframework.integration.handler.advice.ExpressionEvaluatingRequestHandlerAdvice$MessageHandlingExpressionEvaluatingAdviceException: Handler Failed; nested exception is java.lang.IllegalStateException: Could not invoke the method 'public java.lang.Object com.example.demo.EerhaApplication$$Lambda$299/0x00000008401d7040.handle(java.lang.Object,org.springframework.messaging.MessageHeaders)', failedMessage=GenericMessage [payload=bad, headers={id=7639425f-7676-d521-5994-4c74e5b09f4a, timestamp=1611307975200}], headers={id=aaa07286-4bc3-71e5-09fb-6b55a46e0ecf, timestamp=1611307975201}]
Apparently an Exception is always thrown evaluating the failureExpression never getting the desired result.
I'm using Spring Boot 2.4.2 but I've also tried with older versions, having always the same behaviour.
What am I doing wrong?

We need to see your code for this ExpressionEvaluatingRequestHandlerAdvice, but any way, the AdviceMessage is sent only in case of successful evaluation of the onSuccessExpression:
if (evalResult != null && this.successChannel != null) {
AdviceMessage<?> resultMessage = new AdviceMessage<>(evalResult, message);
this.messagingTemplate.send(this.successChannel, resultMessage);
}
When you MessageHandler fails (even if onSuccessExpression fails), we go to this branch:
catch (RuntimeException e) {
Exception actualException = unwrapExceptionIfNecessary(e);
if (this.onFailureExpression != null) {
Object evalResult = evaluateFailureExpression(message, actualException);
if (this.returnFailureExpressionResult) {
return evalResult;
}
}
and there we send an ErrorMessage:
if (evalResult != null && this.failureChannel != null) {
MessagingException messagingException =
new MessageHandlingExpressionEvaluatingAdviceException(message, "Handler Failed",
unwrapThrowableIfNecessary(exception), evalResult);
ErrorMessage errorMessage = new ErrorMessage(messagingException);
this.messagingTemplate.send(this.failureChannel, errorMessage);
}
So, what you show is really an expected behavior.
Therefore need to see you config and understand what is your expectation in both cases.

in order to get the the String "bad was bad, with reason..." printed this was my solution:
#Bean
public IntegrationFlow failure() {
return f -> f.handle(m -> {
System.out.println(((ExpressionEvaluatingRequestHandlerAdvice.MessageHandlingExpressionEvaluatingAdviceException)m.getPayload()).getEvaluationResult());
});
}

Related

How to handle errors in Spring reactor Mono or Flux?

I have below code retuning Mono<Foo>:
try {
return userRepository.findById(id) // step 1
.flatMap(user -> barRepository.findByUserId( user.getId()) // step 2
.map(bar-> Foo.builder().msg("Already exists").build()) // step 3
.switchIfEmpty(barRepository.save(Bar.builder().userId(user.getId()).build()) // step 4
.map(bar-> Foo.builder().msg("Created").build()) // step 5
))
.doOnError(throwable -> Mono.just(handleError(throwable)));
} catch(Exception e) {
log.error("from catch block");
return Mono.just(handleError(e));
}
If error occurs in step 1 (e.g. user does not exist by the specified id), will it be caught by doOnError or by try catch block or none of these two?
Same question if error happens in step 2, step3, step 4.
What is the correct code so that error is always caught by doOnError and eliminate try catch?
I am using
public interface UserRepository extends ReactiveMongoRepository<User, String> same for barRepository.
handleError(throwable) simply does log.error(e.getMessage() and retuns Foo.
I think the first error is in the title: "Mono or Flux" is not related with the error handling.
Mono can only emit one item at the most (streams one element)
Flux can emit more complex stuff (i.e. List)
To handle errors you can follow this example:
return webClient.get()
.uri(url)
.retrieve()
.bodyToMono(ModelYouAreRetrieving.class)
.doOnError(throwable -> logger.error("Failed for some reason", throwable))
.onErrorReturn(new ModelYouAreRetrieving(...))
.block();
DoOnError will only perform side effects and assuming the findById are will return a Mono.Error() if it fails something like this should work.
return userRepository.findById(id)
.flatMap ( user ->
barRepository.findByUserId(user.getId())
.map((user,bar)-> Foo.builder().msg("Already exists").build())
.switchIfEmpty(barRepository.save(Bar.builder().userId(user.getId()).build())
.map(bar-> Foo.builder().msg("Created").build())
))
.onErrorReturn(throwable -> Mono.just(handleError(throwable)));
The try catch will only work if you either call a blocking operation of the chain, or a runtime error occurs before you enter the reactive chain. the doOn operations do not modify the chain, they are used for side effects only. Since flatMap expects a producer, you will need to return a Mono from the call, and in this case if an error occurs, then it will just propagate the error. In all reactive chains the error will propagate unless otherwise handled.
Use Exceptions.propagate(e) which wraps a checked exception into a special runtime exception that can be handled by onError
Below Code tries to covers User attributes in upper case. Now, when it encounters kyle the checked exception is throws and MIKE is returned from onErrorReturn
#Test
void Test19() {
Flux.fromIterable(Arrays.asList(new User("jhon", "10000"),
new User("kyle", "bot")))
.map(x -> {
try {
return toUpper(x);
} catch (TestException e) {
throw Exceptions.propagate(e);
}
})
.onErrorReturn(new User("MIKE", "BOT")).subscribe(x -> System.out.println(x));
}
protected final class TestException extends Exception {
private static final long serialVersionUID = -831485594512095557L;
}
private User toUpper(User user) throws TestException{
if (user.getName().equals("kyle")) {
throw new TestException();
}
return new User(user.getName().toUpperCase(), user.getProfession().toUpperCase());
}
Output
User [name=JHON, profession=10000]
User [name=MIKE, profession=BOT]
#Gianluca Pinto's last line of code is also incorrect. The code won't be compiled. onErrorReturn is not suitable for complicated error handling. What you should use is onErrorResume.
see: https://grokonez.com/reactive-programming/reactor/reactor-handle-error#21_By_falling_back_to_another_Flux
onErrorResume will fall back to another Flux and let you catch and manage the exception thrown by previous Flux. if look into the implementation of onErrorReturn, you will find onErrorReturn is actually using onErrorResume.
So here the code should be:
.onErrorResume(throwable -> Mono.just(handleError(throwable)));
The last line of the code of #James Ralston is wrong. The correct code should be:
return userRepository.findById(id)
.flatMap ( user ->
barRepository.findByUserId(user.getId())
.map((user,bar)-> Foo.builder().msg("Already exists").build())
.switchIfEmpty(barRepository.save(Bar.builder().userId(user.getId()).build())
.map(bar-> Foo.builder().msg("Created").build())
))
.onErrorReturn(Mono.just(handleError(throwable)));
While creating the reactive flow, we need to use onError* as it provides a fallback Mono/Flux while doOn* are side-effect operators.
NOTE: The examples are in Kotlin
Below is an example:
fun saveItems(item: Item) = testRepository.save(item)
.onErrorResume {
Mono.error(
onErrorResumeHandler(
it,
"APP-1002",
"Error occurred while saving the something :P, contact admin"
)
)
}
fun onErrorResumeHandler(exception: Throwable, errorCode: String, errorMessage: String) =
if (exception is TestRepositoryException) exception else
TestServiceException(errorCode, errorMessage)
There should be a central exception handler, we can create by extending AbstractErrorWebExceptionHandler. The order is -2 to supersede the default.
Below is an example:
#Component
#Order(-2)
class BaseControllerAdvice(
errorAttributes: ErrorAttributes,
resources: WebProperties.Resources,
applicationContext: ApplicationContext,
serverCodecConfigurer: ServerCodecConfigurer
) : AbstractErrorWebExceptionHandler(errorAttributes, resources, applicationContext) {
val log = logger()
init {
setMessageWriters(serverCodecConfigurer.writers)
}
override fun getRoutingFunction(errorAttributes: ErrorAttributes?) =
router {
RequestPredicates.all().invoke(this#BaseControllerAdvice::renderErrorResponse)
}
//RouterFunctions.route(RequestPredicates.all(),this::renderErrorResponse)
fun renderErrorResponse(
request: ServerRequest
): Mono<ServerResponse> {
val errorPropertiesMap = getErrorAttributes(
request,
ErrorAttributeOptions.defaults()
)
val ex: ApplicationException = getError(request) as ApplicationException
log.info("Error attributes:{}", request)
return ServerResponse.status(HttpStatus.BAD_REQUEST)
.contentType(MediaType.APPLICATION_JSON)
.body(BodyInserters.fromValue(ErrorResponseVO(ex.errorCode, ex.errorMessage)))
}
data class ErrorResponseVO(val errorMessage: String, val errorCode: String)
}

Spring Integration and returning schema validation errors

We are using Spring Integration to process a JSON payload passed into a RESTful endpoint. As part of this flow we are using a filter to validate the JSON:
.filter(schemaValidationFilter, s -> s
.discardFlow(f -> f
.handle(message -> {
throw new SchemaValidationException(message);
}))
)
This works great. However, if the validation fails we want to capture the parsing error and return that to the user so they can act on the error. Here is the overridden accept method in the SchemaValidationFilter class:
#Override
public boolean accept(Message<?> message) {
Assert.notNull(message);
Assert.isTrue(message.getHeaders().containsKey(TYPE_NAME));
String historyType = (String)message.getHeaders().get(TYPE_NAME);
JSONObject payload = (JSONObject) message.getPayload();
String jsonString = payload.toJSONString();
try {
ProcessingReport report = schemaValidator.validate(historyType, payload);
return report.isSuccess();
} catch (IOException | ProcessingException e) {
throw new MessagingException(message, e);
}
}
What we have done is in the catch block we throw a MessageException which seems to solve the problem. However this seems to break what a filter should do (simply return a true or false).
Is there a best practice for passing the error details from the filter to the client? Is the filter the right solution for this use case?
Thanks for your help!
John
I'd say you go correct way. Please, refer to the XmlValidatingMessageSelector, so your JsonValidatingMessageSelector should be similar and must follow the same design.
Since we have a throwExceptionOnRejection option we always can be sure that throwing Exception instead of just true/false is correct behavior.
What Gary says is good, too, but according to the existing logic in that MessageSelector impl we can go ahead with the same and continue to use .filter(), but, of course, already without .discardFlow(), because we won't send invalid message to the discardChannel.
When your JsonValidatingMessageSelector is ready, feel free to contribute it back to the Framework!
It's probably more correct to do the validation in a <service-activator/>...
public Message<?> validate(Message<?> message) {
...
try {
ProcessingReport report = schemaValidator.validate(historyType, payload);
return message;
}
catch (IOException | ProcessingException e) {
throw new MessagingException(message, e);
}
}
...since you're never really filtering.

MVC 5 Jquery Load - Catching Inner Exception

I have a partial view which loads a ObjectResult from a SQL procedure. This is using the built in EF method.
public virtual ObjectResult<Something_Result> getSomething()
{
return ((IObjectContextAdapter)this).ObjectContext.ExecuteFunction<Something_Result>("getSomething");
}
So a usual problem is timeouts from the database and I would like to show this. But I cant seem to find this in the exception object from the Ajax.Load() functon .
In the responseText I only get "{"message":"An error occurred while executing the command definition. See the inner exception for details."}" and with status: 500
So is it an easy way to get the inner exception aswell?
Here is the exception thrown by the Ajax.load() function http://i.imgur.com/ZIrTrMU.jpg
And here is the Inner Exception from EF: http://i.imgur.com/LgOhOlB.png
You can put a try catch block in your Data access Layer where you are calling the getSomething method and send appropriate response text up the stack. Depending on where you are putting the catch block, you can check if:
if(ex.Number == -2)
or
if(ex.InnerException != null && ex.InnerException.Number == -2)
return Json(new { successful = false, Message = "Timeout" });
and return appropriate response text as a Json response or if only want to handle TimeOutException you can put in the specific catch block for 'Timeout Exception'
catch(TimeoutException ex)
{ return Json(new { successful = false, Message = "Timeout" }); }

Should a method should be throwing an exception to the Unit Test?

I have a simple method for sending emails:
public void notifyEmail(string messageSubject, string messageBody)
{
MailMessage message = new MailMessage(from, to);
message.Subject = messageSubject;
message.Body = messageBody;
SmtpClient client = new SmtpClient(smtp_client);
client.Send(message);
message.Dispose();//release everything related
}
And a unit test (I'm learning):
[TestMethod()]
public void notifyEmailTest()
{
eMail target = new eMail("TEST Subject","TEST Body"); // TODO: Initialize to an appropriate value
bool testSent = true;
try
{
target.notifyEmail();
}
catch (Exception)
{
testSent = false;
}
Assert.IsTrue(testSent);
}
I deliberately set the smtp_client variable value to something invalid.
Running the code in my project results in an error.
Running the test method results in a Pass. Should my test or method be structured differently so that errors will fail the test?
I always do everything I can to avoid putting try-catch clauses on my unit tests. Instead try using the ExpectedException attribute (the attribute is the same for NUnit and MSTest) and set the type to the exception you are expecting i.e.
[TestMethod]
[ExpectedException(typeof(NetworkException))]
public void ShouldThrowNetworkExceptionIfSmtpServerIsInvalid)
{
//... test code here.
}
Another approach that I have used is to create a static class with an AssertExpectedException method since sometimes a method can throw the same type of exception for different reasons and the only way to know for sure if the accurate message is being returned is with custom code since the attribute does not assert the message the thrown exception is returning.
Hope this helps.
Regards.
If you expect that target.notifyEmail() should be throwing an exception, then that's what you should be testing for. If you were using NUnit you could use Assert.Throws<T>, e.g.
[Test]
public void notifyEmailTestFails()
{
// TODO: Initialize to an appropriate value
eMail target = new eMail("TEST Subject","TEST Body");
Assert.Throws<InvalidOperationException>(target.notifyEmail());
}
However, now I see you're using VSUnit you should be using [ExpectedException(typeof(...))]
as mentioned in other answers.
In general you should have separate tests for success, failure, and for exception conditions.
The way I normally do this is to decorate the test with ExpectedException (
http://msdn.microsoft.com/en-us/library/microsoft.visualstudio.testtools.unittesting.expectedexceptionattribute(v=vs.80).aspx)
. But you want to catch something MUCH less generic than "Exception."
If you don't want to use expected exception, then instead of:
bool testSent = true;
try
{
target.notifyEmail();
}
catch (Exception)
{
testSent = false;
}
Assert.IsTrue(testSent);
You can be a little less verbose:
try{
target.notifyEmail();
Assert.Fail("Expected an exception here");
}
catch (SmtpException){
}
I would highly recommend you to try the FluenAssertions:
http://fluentassertions.codeplex.com/
They are simple awesome and Elegant
And they let you check the exception message (You can not do that with the ExpectedException attribute)
Example:
using FluentAssertions;
[TestMethod]
public void notifyEmailTest()
{
eMail target = new eMail("TEST Subject","TEST Body"); // TODO: Initialize to an appropriate value
target.Invoking(x => x.notifyEmail())
.ShouldThrow<YourExcpectedException>()
.WithMessage("Your expected message", FluentAssertions.Assertions.ComparisonMode.Substring);
}

About Spring Transaction Manager

Currently i am using spring declarative transaction manager in my application. During DB operations if any constraint violated i want to check the error code against the database. i mean i want to run one select query after the exception happened. So i am catching the DataIntegrityViolationException inside my Catch block and then i am trying to execute one more error code query. But that query is not get executed . I am assuming since i am using the transaction manager if any exception happened the next query is not getting executed. Is that right?. i want to execute that error code query before i am returning the results to the client. Any way to do this?
#Override
#Transactional
public LineOfBusinessResponse create(
CreateLineOfBusiness createLineOfBusiness)
throws GenericUpcException {
logger.info("Start of createLineOfBusinessEntity()");
LineOfBusinessEntity lineOfBusinessEntity =
setLineOfBusinessEntityProperties(createLineOfBusiness);
try {
lineOfBusinessDao.create(lineOfBusinessEntity);
return setUpcLineOfBusinessResponseProperties(lineOfBusinessEntity);
}
// Some db constraints is failed
catch (DataIntegrityViolationException dav) {
String errorMessage =
errorCodesBd.findErrorCodeByErrorMessage(dav.getMessage());
throw new GenericUpcException(errorMessage);
}
// General Exceptions handling
catch (Exception exc) {
logger.debug("<<<<Coming inside General >>>>");
System.out.print("<<<<Coming inside General >>>>");
throw new GenericUpcException(exc.getMessage());
}
}
public String findErrorCodeByErrorMessage(String errorMessage)throws GenericUpcException {
try{
int first=errorMessage.indexOf("[",errorMessage.indexOf("constraint"));
int last=errorMessage.indexOf("]",first);
String errorCode=errorMessage.substring(first+1, last);
//return errorCodesDao.find(errorCode);
return errorCode;
}
catch(Exception e)
{
throw new GenericUpcException(e.getMessage());
}
}
Please help me.
I don't think problem you're describing has anything to do with Transaction management. If DataIntegrityViolationException happens within your try() block you code within catch() should execute. Perhaps exception different from DataIntegrityViolationException happens or your findErrorCodeByErrorMessage() throwing another exception. In general, Transaction logic would be applied only once you return from your method call, until then you could do whatever you like using normal Java language constructs. I suggest you put breakpoint in your error error handler or some debug statements to see what's actually happening.

Resources