diff --git a/spring-messaging/src/main/java/org/springframework/messaging/MessageHandler.java b/spring-messaging/src/main/java/org/springframework/messaging/MessageHandler.java index ac2a3ebbb4..7eee45192e 100644 --- a/spring-messaging/src/main/java/org/springframework/messaging/MessageHandler.java +++ b/spring-messaging/src/main/java/org/springframework/messaging/MessageHandler.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2016 the original author or authors. + * Copyright 2002-2018 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -29,6 +29,7 @@ public interface MessageHandler { /** * Handle the given message. * @param message the message to be handled + * @throws MessagingException if the handler failed to process the message */ void handleMessage(Message message) throws MessagingException; diff --git a/spring-messaging/src/main/java/org/springframework/messaging/handler/invocation/AbstractMethodMessageHandler.java b/spring-messaging/src/main/java/org/springframework/messaging/handler/invocation/AbstractMethodMessageHandler.java index 76923a748f..5dbb9372b1 100644 --- a/spring-messaging/src/main/java/org/springframework/messaging/handler/invocation/AbstractMethodMessageHandler.java +++ b/spring-messaging/src/main/java/org/springframework/messaging/handler/invocation/AbstractMethodMessageHandler.java @@ -38,6 +38,7 @@ import org.springframework.core.MethodParameter; import org.springframework.lang.Nullable; import org.springframework.messaging.Message; import org.springframework.messaging.MessageHandler; +import org.springframework.messaging.MessageHandlingException; import org.springframework.messaging.MessagingException; import org.springframework.messaging.handler.DestinationPatternsMessageCondition; import org.springframework.messaging.handler.HandlerMethod; @@ -84,7 +85,7 @@ public abstract class AbstractMethodMessageHandler protected final Log logger = LogFactory.getLog(getClass()); - private Collection destinationPrefixes = new ArrayList<>(); + private final List destinationPrefixes = new ArrayList<>(); private final List customArgumentResolvers = new ArrayList<>(4); @@ -395,6 +396,7 @@ public abstract class AbstractMethodMessageHandler if (lookupDestination == null) { return; } + MessageHeaderAccessor headerAccessor = MessageHeaderAccessor.getMutableAccessor(message); headerAccessor.setHeader(DestinationPatternsMessageCondition.LOOKUP_DESTINATION_HEADER, lookupDestination); headerAccessor.setLeaveMutable(true); @@ -452,9 +454,9 @@ public abstract class AbstractMethodMessageHandler handleNoMatch(this.handlerMethods.keySet(), lookupDestination, message); return; } + Comparator comparator = new MatchComparator(getMappingComparator(message)); matches.sort(comparator); - if (logger.isTraceEnabled()) { logger.trace("Found " + matches.size() + " handler methods: " + matches); } @@ -531,16 +533,16 @@ public abstract class AbstractMethodMessageHandler processHandlerMethodException(handlerMethod, ex, message); } catch (Throwable ex) { - if (logger.isErrorEnabled()) { - logger.error("Error while processing message " + message, ex); - } + Exception handlingException = + new MessageHandlingException(message, "Unexpected handler method invocation error", ex); + processHandlerMethodException(handlerMethod, handlingException, message); } } - protected void processHandlerMethodException(HandlerMethod handlerMethod, Exception ex, Message message) { - InvocableHandlerMethod invocable = getExceptionHandlerMethod(handlerMethod, ex); + protected void processHandlerMethodException(HandlerMethod handlerMethod, Exception exception, Message message) { + InvocableHandlerMethod invocable = getExceptionHandlerMethod(handlerMethod, exception); if (invocable == null) { - logger.error("Unhandled exception from message handler method", ex); + logger.error("Unhandled exception from message handler method", exception); return; } invocable.setMessageMethodArgumentResolvers(this.argumentResolvers); @@ -548,7 +550,10 @@ public abstract class AbstractMethodMessageHandler logger.debug("Invoking " + invocable.getShortLogMessage()); } try { - Object returnValue = invocable.invoke(message, ex, handlerMethod); + Throwable cause = exception.getCause(); + Object returnValue = (cause != null ? + invocable.invoke(message, exception, cause, handlerMethod) : + invocable.invoke(message, exception, handlerMethod)); MethodParameter returnType = invocable.getReturnType(); if (void.class == returnType.getParameterType()) { return; diff --git a/spring-messaging/src/test/java/org/springframework/messaging/handler/annotation/support/AnnotationExceptionHandlerMethodResolverTests.java b/spring-messaging/src/test/java/org/springframework/messaging/handler/annotation/support/AnnotationExceptionHandlerMethodResolverTests.java index 0835348893..c39280060c 100644 --- a/spring-messaging/src/test/java/org/springframework/messaging/handler/annotation/support/AnnotationExceptionHandlerMethodResolverTests.java +++ b/spring-messaging/src/test/java/org/springframework/messaging/handler/annotation/support/AnnotationExceptionHandlerMethodResolverTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2017 the original author or authors. + * Copyright 2002-2018 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -86,6 +86,12 @@ public class AnnotationExceptionHandlerMethodResolverTests { assertEquals("handleIOException", this.resolver.resolveMethod(exception).getName()); } + @Test + public void resolveMethodAgainstCause() { + IllegalStateException exception = new IllegalStateException(new IOException()); + assertEquals("handleIOException", this.resolver.resolveMethod(exception).getName()); + } + @Test(expected = IllegalStateException.class) public void ambiguousExceptionMapping() { new AnnotationExceptionHandlerMethodResolver(AmbiguousController.class); diff --git a/spring-messaging/src/test/java/org/springframework/messaging/simp/annotation/support/SimpAnnotationMethodMessageHandlerTests.java b/spring-messaging/src/test/java/org/springframework/messaging/simp/annotation/support/SimpAnnotationMethodMessageHandlerTests.java index 14875b99e2..702c6db6f8 100644 --- a/spring-messaging/src/test/java/org/springframework/messaging/simp/annotation/support/SimpAnnotationMethodMessageHandlerTests.java +++ b/spring-messaging/src/test/java/org/springframework/messaging/simp/annotation/support/SimpAnnotationMethodMessageHandlerTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2017 the original author or authors. + * Copyright 2002-2018 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -75,7 +75,6 @@ import static org.mockito.BDDMockito.*; * @author Brian Clozel * @author Sebastien Deleuze */ -@SuppressWarnings("unused") public class SimpAnnotationMethodMessageHandlerTests { private static final String TEST_INVALID_VALUE = "invalidValue"; @@ -201,6 +200,30 @@ public class SimpAnnotationMethodMessageHandlerTests { assertEquals("illegalState", handlerMethod.getMethod().getName()); } + @Test + public void exceptionAsCause() { + Message message = createMessage("/pre/illegalStateCause"); + this.messageHandler.registerHandler(this.testController); + this.messageHandler.handleMessage(message); + + assertEquals("handleExceptionWithHandlerMethodArg", this.testController.method); + HandlerMethod handlerMethod = (HandlerMethod) this.testController.arguments.get("handlerMethod"); + assertNotNull(handlerMethod); + assertEquals("illegalStateCause", handlerMethod.getMethod().getName()); + } + + @Test + public void errorAsMessageHandlingException() { + Message message = createMessage("/pre/error"); + this.messageHandler.registerHandler(this.testController); + this.messageHandler.handleMessage(message); + + assertEquals("handleErrorWithHandlerMethodArg", this.testController.method); + HandlerMethod handlerMethod = (HandlerMethod) this.testController.arguments.get("handlerMethod"); + assertNotNull(handlerMethod); + assertEquals("errorAsThrowable", handlerMethod.getMethod().getName()); + } + @Test public void simpScope() { Map sessionAttributes = new ConcurrentHashMap<>(); @@ -412,7 +435,17 @@ public class SimpAnnotationMethodMessageHandlerTests { @MessageMapping("/illegalState") public void illegalState() { - throw new IllegalStateException(); + throw new IllegalStateException("my cause"); + } + + @MessageMapping("/illegalStateCause") + public void illegalStateCause() { + throw new RuntimeException(new IllegalStateException("my cause")); + } + + @MessageMapping("/error") + public void errorAsThrowable() { + throw new Error("my cause"); } @MessageExceptionHandler(MethodArgumentNotValidException.class) @@ -420,10 +453,18 @@ public class SimpAnnotationMethodMessageHandlerTests { this.method = "handleValidationException"; } - @MessageExceptionHandler(IllegalStateException.class) - public void handleExceptionWithHandlerMethodArg(HandlerMethod handlerMethod) { + @MessageExceptionHandler + public void handleExceptionWithHandlerMethodArg(IllegalStateException ex, HandlerMethod handlerMethod) { this.method = "handleExceptionWithHandlerMethodArg"; this.arguments.put("handlerMethod", handlerMethod); + assertEquals("my cause", ex.getMessage()); + } + + @MessageExceptionHandler + public void handleErrorWithHandlerMethodArg(Error ex, HandlerMethod handlerMethod) { + this.method = "handleErrorWithHandlerMethodArg"; + this.arguments.put("handlerMethod", handlerMethod); + assertEquals("my cause", ex.getMessage()); } @MessageMapping("/scope") @@ -446,7 +487,6 @@ public class SimpAnnotationMethodMessageHandlerTests { private String method; - @MessageMapping("foo") public void handleFoo() { this.method = "handleFoo";