Skip to content

Commit f39adcf

Browse files
committed
AbstractMethodMessageHandler processes Error as MessageHandlingException
Issue: SPR-16912
1 parent da049f4 commit f39adcf

File tree

4 files changed

+69
-17
lines changed

4 files changed

+69
-17
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-2016 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.
@@ -29,6 +29,7 @@ public interface MessageHandler {
2929
/**
3030
* Handle the given message.
3131
* @param message the message to be handled
32+
* @throws MessagingException if the handler failed to process the message
3233
*/
3334
void handleMessage(Message<?> message) throws MessagingException;
3435

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

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
import org.springframework.lang.Nullable;
3939
import org.springframework.messaging.Message;
4040
import org.springframework.messaging.MessageHandler;
41+
import org.springframework.messaging.MessageHandlingException;
4142
import org.springframework.messaging.MessagingException;
4243
import org.springframework.messaging.handler.DestinationPatternsMessageCondition;
4344
import org.springframework.messaging.handler.HandlerMethod;
@@ -84,7 +85,7 @@ public abstract class AbstractMethodMessageHandler<T>
8485

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

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

8990
private final List<HandlerMethodArgumentResolver> customArgumentResolvers = new ArrayList<>(4);
9091

@@ -395,6 +396,7 @@ public void handleMessage(Message<?> message) throws MessagingException {
395396
if (lookupDestination == null) {
396397
return;
397398
}
399+
398400
MessageHeaderAccessor headerAccessor = MessageHeaderAccessor.getMutableAccessor(message);
399401
headerAccessor.setHeader(DestinationPatternsMessageCondition.LOOKUP_DESTINATION_HEADER, lookupDestination);
400402
headerAccessor.setLeaveMutable(true);
@@ -452,9 +454,9 @@ protected void handleMessageInternal(Message<?> message, String lookupDestinatio
452454
handleNoMatch(this.handlerMethods.keySet(), lookupDestination, message);
453455
return;
454456
}
457+
455458
Comparator<Match> comparator = new MatchComparator(getMappingComparator(message));
456459
matches.sort(comparator);
457-
458460
if (logger.isTraceEnabled()) {
459461
logger.trace("Found " + matches.size() + " handler methods: " + matches);
460462
}
@@ -531,24 +533,27 @@ protected void handleMatch(T mapping, HandlerMethod handlerMethod, String lookup
531533
processHandlerMethodException(handlerMethod, ex, message);
532534
}
533535
catch (Throwable ex) {
534-
if (logger.isErrorEnabled()) {
535-
logger.error("Error while processing message " + message, ex);
536-
}
536+
Exception handlingException =
537+
new MessageHandlingException(message, "Unexpected handler method invocation error", ex);
538+
processHandlerMethodException(handlerMethod, handlingException, message);
537539
}
538540
}
539541

540-
protected void processHandlerMethodException(HandlerMethod handlerMethod, Exception ex, Message<?> message) {
541-
InvocableHandlerMethod invocable = getExceptionHandlerMethod(handlerMethod, ex);
542+
protected void processHandlerMethodException(HandlerMethod handlerMethod, Exception exception, Message<?> message) {
543+
InvocableHandlerMethod invocable = getExceptionHandlerMethod(handlerMethod, exception);
542544
if (invocable == null) {
543-
logger.error("Unhandled exception from message handler method", ex);
545+
logger.error("Unhandled exception from message handler method", exception);
544546
return;
545547
}
546548
invocable.setMessageMethodArgumentResolvers(this.argumentResolvers);
547549
if (logger.isDebugEnabled()) {
548550
logger.debug("Invoking " + invocable.getShortLogMessage());
549551
}
550552
try {
551-
Object returnValue = invocable.invoke(message, ex, handlerMethod);
553+
Throwable cause = exception.getCause();
554+
Object returnValue = (cause != null ?
555+
invocable.invoke(message, exception, cause, handlerMethod) :
556+
invocable.invoke(message, exception, handlerMethod));
552557
MethodParameter returnType = invocable.getReturnType();
553558
if (void.class == returnType.getParameterType()) {
554559
return;

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

Lines changed: 7 additions & 1 deletion
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.
@@ -86,6 +86,12 @@ public void resolveMethodInherited() {
8686
assertEquals("handleIOException", this.resolver.resolveMethod(exception).getName());
8787
}
8888

89+
@Test
90+
public void resolveMethodAgainstCause() {
91+
IllegalStateException exception = new IllegalStateException(new IOException());
92+
assertEquals("handleIOException", this.resolver.resolveMethod(exception).getName());
93+
}
94+
8995
@Test(expected = IllegalStateException.class)
9096
public void ambiguousExceptionMapping() {
9197
new AnnotationExceptionHandlerMethodResolver(AmbiguousController.class);

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

Lines changed: 46 additions & 6 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.
@@ -75,7 +75,6 @@
7575
* @author Brian Clozel
7676
* @author Sebastien Deleuze
7777
*/
78-
@SuppressWarnings("unused")
7978
public class SimpAnnotationMethodMessageHandlerTests {
8079

8180
private static final String TEST_INVALID_VALUE = "invalidValue";
@@ -201,6 +200,30 @@ public void exceptionWithHandlerMethodArg() {
201200
assertEquals("illegalState", handlerMethod.getMethod().getName());
202201
}
203202

203+
@Test
204+
public void exceptionAsCause() {
205+
Message<?> message = createMessage("/pre/illegalStateCause");
206+
this.messageHandler.registerHandler(this.testController);
207+
this.messageHandler.handleMessage(message);
208+
209+
assertEquals("handleExceptionWithHandlerMethodArg", this.testController.method);
210+
HandlerMethod handlerMethod = (HandlerMethod) this.testController.arguments.get("handlerMethod");
211+
assertNotNull(handlerMethod);
212+
assertEquals("illegalStateCause", handlerMethod.getMethod().getName());
213+
}
214+
215+
@Test
216+
public void errorAsMessageHandlingException() {
217+
Message<?> message = createMessage("/pre/error");
218+
this.messageHandler.registerHandler(this.testController);
219+
this.messageHandler.handleMessage(message);
220+
221+
assertEquals("handleErrorWithHandlerMethodArg", this.testController.method);
222+
HandlerMethod handlerMethod = (HandlerMethod) this.testController.arguments.get("handlerMethod");
223+
assertNotNull(handlerMethod);
224+
assertEquals("errorAsThrowable", handlerMethod.getMethod().getName());
225+
}
226+
204227
@Test
205228
public void simpScope() {
206229
Map<String, Object> sessionAttributes = new ConcurrentHashMap<>();
@@ -412,18 +435,36 @@ public void payloadValidation(@Validated @Payload String payload) {
412435

413436
@MessageMapping("/illegalState")
414437
public void illegalState() {
415-
throw new IllegalStateException();
438+
throw new IllegalStateException("my cause");
439+
}
440+
441+
@MessageMapping("/illegalStateCause")
442+
public void illegalStateCause() {
443+
throw new RuntimeException(new IllegalStateException("my cause"));
444+
}
445+
446+
@MessageMapping("/error")
447+
public void errorAsThrowable() {
448+
throw new Error("my cause");
416449
}
417450

418451
@MessageExceptionHandler(MethodArgumentNotValidException.class)
419452
public void handleValidationException() {
420453
this.method = "handleValidationException";
421454
}
422455

423-
@MessageExceptionHandler(IllegalStateException.class)
424-
public void handleExceptionWithHandlerMethodArg(HandlerMethod handlerMethod) {
456+
@MessageExceptionHandler
457+
public void handleExceptionWithHandlerMethodArg(IllegalStateException ex, HandlerMethod handlerMethod) {
425458
this.method = "handleExceptionWithHandlerMethodArg";
426459
this.arguments.put("handlerMethod", handlerMethod);
460+
assertEquals("my cause", ex.getMessage());
461+
}
462+
463+
@MessageExceptionHandler
464+
public void handleErrorWithHandlerMethodArg(Error ex, HandlerMethod handlerMethod) {
465+
this.method = "handleErrorWithHandlerMethodArg";
466+
this.arguments.put("handlerMethod", handlerMethod);
467+
assertEquals("my cause", ex.getMessage());
427468
}
428469

429470
@MessageMapping("/scope")
@@ -446,7 +487,6 @@ private static class DotPathSeparatorController {
446487

447488
private String method;
448489

449-
450490
@MessageMapping("foo")
451491
public void handleFoo() {
452492
this.method = "handleFoo";

0 commit comments

Comments
 (0)