Skip to content

Commit 1d6f717

Browse files
committed
AbstractMethodMessageHandler processes Error as MessageHandlingException
Issue: SPR-16912
1 parent 4a6e9a5 commit 1d6f717

File tree

4 files changed

+89
-35
lines changed

4 files changed

+89
-35
lines changed

spring-messaging/src/main/java/org/springframework/messaging/MessageHandler.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2013 the original author or authors.
2+
* Copyright 2002-2018 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -28,6 +28,7 @@ public interface MessageHandler {
2828
/**
2929
* Handle the given message.
3030
* @param message the message to be handled
31+
* @throws MessagingException if the handler failed to process the message
3132
*/
3233
void handleMessage(Message<?> message) throws MessagingException;
3334

spring-messaging/src/main/java/org/springframework/messaging/handler/invocation/AbstractMethodMessageHandler.java

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
import org.springframework.core.MethodParameter;
3838
import org.springframework.messaging.Message;
3939
import org.springframework.messaging.MessageHandler;
40+
import org.springframework.messaging.MessageHandlingException;
4041
import org.springframework.messaging.MessagingException;
4142
import org.springframework.messaging.handler.DestinationPatternsMessageCondition;
4243
import org.springframework.messaging.handler.HandlerMethod;
@@ -83,7 +84,7 @@ public abstract class AbstractMethodMessageHandler<T>
8384

8485
protected final Log logger = LogFactory.getLog(getClass());
8586

86-
private Collection<String> destinationPrefixes = new ArrayList<String>();
87+
private final List<String> destinationPrefixes = new ArrayList<String>();
8788

8889
private final List<HandlerMethodArgumentResolver> customArgumentResolvers =
8990
new ArrayList<HandlerMethodArgumentResolver>(4);
@@ -442,9 +443,9 @@ protected void handleMessageInternal(Message<?> message, String lookupDestinatio
442443
handleNoMatch(this.handlerMethods.keySet(), lookupDestination, message);
443444
return;
444445
}
446+
445447
Comparator<Match> comparator = new MatchComparator(getMappingComparator(message));
446448
Collections.sort(matches, comparator);
447-
448449
if (logger.isTraceEnabled()) {
449450
logger.trace("Found " + matches.size() + " handler methods: " + matches);
450451
}
@@ -520,24 +521,27 @@ protected void handleMatch(T mapping, HandlerMethod handlerMethod, String lookup
520521
processHandlerMethodException(handlerMethod, ex, message);
521522
}
522523
catch (Throwable ex) {
523-
if (logger.isErrorEnabled()) {
524-
logger.error("Error while processing message " + message, ex);
525-
}
524+
Exception handlingException =
525+
new MessageHandlingException(message, "Unexpected handler method invocation error", ex);
526+
processHandlerMethodException(handlerMethod, handlingException, message);
526527
}
527528
}
528529

529-
protected void processHandlerMethodException(HandlerMethod handlerMethod, Exception ex, Message<?> message) {
530-
InvocableHandlerMethod invocable = getExceptionHandlerMethod(handlerMethod, ex);
530+
protected void processHandlerMethodException(HandlerMethod handlerMethod, Exception exception, Message<?> message) {
531+
InvocableHandlerMethod invocable = getExceptionHandlerMethod(handlerMethod, exception);
531532
if (invocable == null) {
532-
logger.error("Unhandled exception from message handler method", ex);
533+
logger.error("Unhandled exception from message handler method", exception);
533534
return;
534535
}
535536
invocable.setMessageMethodArgumentResolvers(this.argumentResolvers);
536537
if (logger.isDebugEnabled()) {
537538
logger.debug("Invoking " + invocable.getShortLogMessage());
538539
}
539540
try {
540-
Object returnValue = invocable.invoke(message, ex, handlerMethod);
541+
Throwable cause = exception.getCause();
542+
Object returnValue = (cause != null ?
543+
invocable.invoke(message, exception, cause, handlerMethod) :
544+
invocable.invoke(message, exception, handlerMethod));
541545
MethodParameter returnType = invocable.getReturnType();
542546
if (void.class == returnType.getParameterType()) {
543547
return;

spring-messaging/src/test/java/org/springframework/messaging/handler/annotation/support/AnnotationExceptionHandlerMethodResolverTests.java

Lines changed: 21 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2017 the original author or authors.
2+
* Copyright 2002-2018 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -37,56 +37,59 @@
3737
*/
3838
public class AnnotationExceptionHandlerMethodResolverTests {
3939

40+
private final AnnotationExceptionHandlerMethodResolver resolver =
41+
new AnnotationExceptionHandlerMethodResolver(ExceptionController.class);
42+
43+
4044
@Test
4145
public void resolveMethodFromAnnotation() {
42-
AnnotationExceptionHandlerMethodResolver resolver = new AnnotationExceptionHandlerMethodResolver(ExceptionController.class);
4346
IOException exception = new IOException();
44-
assertEquals("handleIOException", resolver.resolveMethod(exception).getName());
47+
assertEquals("handleIOException", this.resolver.resolveMethod(exception).getName());
4548
}
4649

4750
@Test
4851
public void resolveMethodFromArgument() {
49-
AnnotationExceptionHandlerMethodResolver resolver = new AnnotationExceptionHandlerMethodResolver(ExceptionController.class);
5052
IllegalArgumentException exception = new IllegalArgumentException();
51-
assertEquals("handleIllegalArgumentException", resolver.resolveMethod(exception).getName());
53+
assertEquals("handleIllegalArgumentException", this.resolver.resolveMethod(exception).getName());
5254
}
5355

5456
@Test
5557
public void resolveMethodFromArgumentWithErrorType() {
56-
AnnotationExceptionHandlerMethodResolver resolver = new AnnotationExceptionHandlerMethodResolver(ExceptionController.class);
5758
AssertionError exception = new AssertionError();
58-
assertEquals("handleAssertionError", resolver.resolveMethod(new IllegalStateException(exception)).getName());
59+
assertEquals("handleAssertionError", this.resolver.resolveMethod(new IllegalStateException(exception)).getName());
5960
}
6061

6162
@Test
6263
public void resolveMethodExceptionSubType() {
63-
AnnotationExceptionHandlerMethodResolver resolver = new AnnotationExceptionHandlerMethodResolver(ExceptionController.class);
6464
IOException ioException = new FileNotFoundException();
65-
assertEquals("handleIOException", resolver.resolveMethod(ioException).getName());
65+
assertEquals("handleIOException", this.resolver.resolveMethod(ioException).getName());
6666
SocketException bindException = new BindException();
67-
assertEquals("handleSocketException", resolver.resolveMethod(bindException).getName());
67+
assertEquals("handleSocketException", this.resolver.resolveMethod(bindException).getName());
6868
}
6969

7070
@Test
7171
public void resolveMethodBestMatch() {
72-
AnnotationExceptionHandlerMethodResolver resolver = new AnnotationExceptionHandlerMethodResolver(ExceptionController.class);
7372
SocketException exception = new SocketException();
74-
assertEquals("handleSocketException", resolver.resolveMethod(exception).getName());
73+
assertEquals("handleSocketException", this.resolver.resolveMethod(exception).getName());
7574
}
7675

7776
@Test
7877
public void resolveMethodNoMatch() {
79-
AnnotationExceptionHandlerMethodResolver resolver = new AnnotationExceptionHandlerMethodResolver(ExceptionController.class);
8078
Exception exception = new Exception();
81-
assertNull("1st lookup", resolver.resolveMethod(exception));
82-
assertNull("2nd lookup from cache", resolver.resolveMethod(exception));
79+
assertNull("1st lookup", this.resolver.resolveMethod(exception));
80+
assertNull("2nd lookup from cache", this.resolver.resolveMethod(exception));
8381
}
8482

8583
@Test
8684
public void resolveMethodInherited() {
87-
AnnotationExceptionHandlerMethodResolver resolver = new AnnotationExceptionHandlerMethodResolver(InheritedController.class);
8885
IOException exception = new IOException();
89-
assertEquals("handleIOException", resolver.resolveMethod(exception).getName());
86+
assertEquals("handleIOException", this.resolver.resolveMethod(exception).getName());
87+
}
88+
89+
@Test
90+
public void resolveMethodAgainstCause() {
91+
IllegalStateException exception = new IllegalStateException(new IOException());
92+
assertEquals("handleIOException", this.resolver.resolveMethod(exception).getName());
9093
}
9194

9295
@Test(expected = IllegalStateException.class)
@@ -101,6 +104,7 @@ public void noExceptionMapping() {
101104

102105

103106
@Controller
107+
@SuppressWarnings("unused")
104108
static class ExceptionController {
105109

106110
public void handle() {}

spring-messaging/src/test/java/org/springframework/messaging/simp/annotation/support/SimpAnnotationMethodMessageHandlerTests.java

Lines changed: 53 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2017 the original author or authors.
2+
* Copyright 2002-2018 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -50,6 +50,7 @@
5050
import org.springframework.messaging.simp.SimpAttributesContextHolder;
5151
import org.springframework.messaging.simp.SimpMessageHeaderAccessor;
5252
import org.springframework.messaging.simp.SimpMessageSendingOperations;
53+
import org.springframework.messaging.simp.SimpMessageType;
5354
import org.springframework.messaging.simp.SimpMessagingTemplate;
5455
import org.springframework.messaging.simp.annotation.SubscribeMapping;
5556
import org.springframework.messaging.support.MessageBuilder;
@@ -73,7 +74,6 @@
7374
* @author Brian Clozel
7475
* @author Sebastien Deleuze
7576
*/
76-
@SuppressWarnings("unused")
7777
public class SimpAnnotationMethodMessageHandlerTests {
7878

7979
private static final String TEST_INVALID_VALUE = "invalidValue";
@@ -158,7 +158,7 @@ public void messageMappingDestinationVariableResolution() {
158158

159159
@Test
160160
public void subscribeEventDestinationVariableResolution() {
161-
Message<?> message = createMessage("/pre/sub/bar/value");
161+
Message<?> message = createMessage(SimpMessageType.SUBSCRIBE, "/pre/sub/bar/value", null);
162162
this.messageHandler.registerHandler(this.testController);
163163
this.messageHandler.handleMessage(message);
164164

@@ -199,6 +199,30 @@ public void exceptionWithHandlerMethodArg() {
199199
assertEquals("illegalState", handlerMethod.getMethod().getName());
200200
}
201201

202+
@Test
203+
public void exceptionAsCause() {
204+
Message<?> message = createMessage("/pre/illegalStateCause");
205+
this.messageHandler.registerHandler(this.testController);
206+
this.messageHandler.handleMessage(message);
207+
208+
assertEquals("handleExceptionWithHandlerMethodArg", this.testController.method);
209+
HandlerMethod handlerMethod = (HandlerMethod) this.testController.arguments.get("handlerMethod");
210+
assertNotNull(handlerMethod);
211+
assertEquals("illegalStateCause", handlerMethod.getMethod().getName());
212+
}
213+
214+
@Test
215+
public void errorAsMessageHandlingException() {
216+
Message<?> message = createMessage("/pre/error");
217+
this.messageHandler.registerHandler(this.testController);
218+
this.messageHandler.handleMessage(message);
219+
220+
assertEquals("handleErrorWithHandlerMethodArg", this.testController.method);
221+
HandlerMethod handlerMethod = (HandlerMethod) this.testController.arguments.get("handlerMethod");
222+
assertNotNull(handlerMethod);
223+
assertEquals("errorAsThrowable", handlerMethod.getMethod().getName());
224+
}
225+
202226
@Test
203227
public void simpScope() {
204228
Map<String, Object> sessionAttributes = new ConcurrentHashMap<>();
@@ -327,7 +351,11 @@ private Message<?> createMessage(String destination) {
327351
}
328352

329353
private Message<?> createMessage(String destination, Map<String, Object> headers) {
330-
SimpMessageHeaderAccessor accessor = SimpMessageHeaderAccessor.create();
354+
return createMessage(SimpMessageType.MESSAGE, destination, headers);
355+
}
356+
357+
private Message<?> createMessage(SimpMessageType messageType, String destination, Map<String, Object> headers) {
358+
SimpMessageHeaderAccessor accessor = SimpMessageHeaderAccessor.create(messageType);
331359
accessor.setSessionId("session1");
332360
accessor.setSessionAttributes(new HashMap<>());
333361
accessor.setDestination(destination);
@@ -406,18 +434,36 @@ public void payloadValidation(@Validated @Payload String payload) {
406434

407435
@MessageMapping("/illegalState")
408436
public void illegalState() {
409-
throw new IllegalStateException();
437+
throw new IllegalStateException("my cause");
438+
}
439+
440+
@MessageMapping("/illegalStateCause")
441+
public void illegalStateCause() {
442+
throw new RuntimeException(new IllegalStateException("my cause"));
443+
}
444+
445+
@MessageMapping("/error")
446+
public void errorAsThrowable() {
447+
throw new Error("my cause");
410448
}
411449

412450
@MessageExceptionHandler(MethodArgumentNotValidException.class)
413451
public void handleValidationException() {
414452
this.method = "handleValidationException";
415453
}
416454

417-
@MessageExceptionHandler(IllegalStateException.class)
418-
public void handleExceptionWithHandlerMethodArg(HandlerMethod handlerMethod) {
455+
@MessageExceptionHandler
456+
public void handleExceptionWithHandlerMethodArg(IllegalStateException ex, HandlerMethod handlerMethod) {
419457
this.method = "handleExceptionWithHandlerMethodArg";
420458
this.arguments.put("handlerMethod", handlerMethod);
459+
assertEquals("my cause", ex.getMessage());
460+
}
461+
462+
@MessageExceptionHandler
463+
public void handleErrorWithHandlerMethodArg(Error ex, HandlerMethod handlerMethod) {
464+
this.method = "handleErrorWithHandlerMethodArg";
465+
this.arguments.put("handlerMethod", handlerMethod);
466+
assertEquals("my cause", ex.getMessage());
421467
}
422468

423469
@MessageMapping("/scope")
@@ -440,7 +486,6 @@ private static class DotPathSeparatorController {
440486

441487
private String method;
442488

443-
444489
@MessageMapping("foo")
445490
public void handleFoo() {
446491
this.method = "handleFoo";

0 commit comments

Comments
 (0)