From 047786acef33021ef7eb5a4d063c4d588866e6bc Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Mon, 16 Jan 2017 21:14:46 +0100 Subject: [PATCH] Revised InvocableHandlerMethod exception messages (controller vs endpoint vs handler) Introduces dedicated MethodArgumentResolutionException for spring-messaging invocations. Issue: SPR-15139 --- ...ractMethodArgumentResolutionException.java | 39 +++------- .../MethodArgumentNotValidException.java | 29 +++---- .../MethodArgumentTypeMismatchException.java | 14 ++-- .../invocation/InvocableHandlerMethod.java | 67 ++++++++-------- .../MethodArgumentResolutionException.java | 68 +++++++++++++++++ ...faultMessageHandlerMethodFactoryTests.java | 9 ++- .../result/method/InvocableHandlerMethod.java | 46 ++++++----- .../method/InvocableHandlerMethodTests.java | 76 +++++++++---------- .../support/InvocableHandlerMethod.java | 57 +++++++------- .../support/InvocableHandlerMethodTests.java | 6 +- 10 files changed, 223 insertions(+), 188 deletions(-) create mode 100644 spring-messaging/src/main/java/org/springframework/messaging/handler/invocation/MethodArgumentResolutionException.java diff --git a/spring-messaging/src/main/java/org/springframework/messaging/handler/annotation/support/AbstractMethodArgumentResolutionException.java b/spring-messaging/src/main/java/org/springframework/messaging/handler/annotation/support/AbstractMethodArgumentResolutionException.java index df4bce8bfa0..d6153dc61df 100644 --- a/spring-messaging/src/main/java/org/springframework/messaging/handler/annotation/support/AbstractMethodArgumentResolutionException.java +++ b/spring-messaging/src/main/java/org/springframework/messaging/handler/annotation/support/AbstractMethodArgumentResolutionException.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2017 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. @@ -18,7 +18,7 @@ package org.springframework.messaging.handler.annotation.support; import org.springframework.core.MethodParameter; import org.springframework.messaging.Message; -import org.springframework.messaging.MessagingException; +import org.springframework.messaging.handler.invocation.MethodArgumentResolutionException; /** * Base class for exceptions resulting from the invocation of @@ -26,43 +26,24 @@ import org.springframework.messaging.MessagingException; * * @author Rossen Stoyanchev * @since 4.0.3 + * @deprecated as of 4.3.6, in favor of the invocation-associated + * {@link MethodArgumentResolutionException} */ +@Deprecated @SuppressWarnings("serial") -public abstract class AbstractMethodArgumentResolutionException extends MessagingException { +public abstract class AbstractMethodArgumentResolutionException extends MethodArgumentResolutionException { - private final MethodParameter parameter; - - - /** - * Create a new instance providing the invalid {@code MethodParameter}. - */ protected AbstractMethodArgumentResolutionException(Message message, MethodParameter parameter) { - this(message, parameter, getMethodParamMessage(parameter)); + super(message, parameter); } - /** - * Create a new instance providing the invalid {@code MethodParameter} and - * a prepared description. Subclasses should prepend the description with - * the help of {@link #getMethodParamMessage(org.springframework.core.MethodParameter)}. - */ - protected AbstractMethodArgumentResolutionException(Message message, MethodParameter param, String description) { - super(message, description); - this.parameter = param; - } - - - /** - * Return the MethodParameter that was rejected. - */ - public final MethodParameter getMethodParameter() { - return this.parameter; + protected AbstractMethodArgumentResolutionException(Message message, MethodParameter parameter, String description) { + super(message, parameter, description); } protected static String getMethodParamMessage(MethodParameter param) { - return new StringBuilder("Could not resolve method parameter at index ") - .append(param.getParameterIndex()).append(" in method: ") - .append(param.getMethod().toGenericString()).append(" ").toString(); + return ""; } } diff --git a/spring-messaging/src/main/java/org/springframework/messaging/handler/annotation/support/MethodArgumentNotValidException.java b/spring-messaging/src/main/java/org/springframework/messaging/handler/annotation/support/MethodArgumentNotValidException.java index 81bd4a7e276..62d9f55fa55 100644 --- a/spring-messaging/src/main/java/org/springframework/messaging/handler/annotation/support/MethodArgumentNotValidException.java +++ b/spring-messaging/src/main/java/org/springframework/messaging/handler/annotation/support/MethodArgumentNotValidException.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2017 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,27 +29,25 @@ import org.springframework.validation.ObjectError; * @author Rossen Stoyanchev * @since 4.0.1 */ -@SuppressWarnings("serial") +@SuppressWarnings({"serial", "deprecation"}) public class MethodArgumentNotValidException extends AbstractMethodArgumentResolutionException { - private final BindingResult bindingResult; + private BindingResult bindingResult; /** * Create a new instance with the invalid {@code MethodParameter}. */ public MethodArgumentNotValidException(Message message, MethodParameter parameter) { - this(message, parameter, null); + super(message, parameter); } /** * Create a new instance with the invalid {@code MethodParameter} and a * {@link org.springframework.validation.BindingResult}. */ - public MethodArgumentNotValidException(Message message, MethodParameter parameter, - BindingResult bindingResult) { - - super(message, parameter, getMethodParamMessage(parameter) + getValidationErrorMessage(bindingResult)); + public MethodArgumentNotValidException(Message message, MethodParameter parameter, BindingResult bindingResult) { + super(message, parameter, getValidationErrorMessage(bindingResult)); this.bindingResult = bindingResult; } @@ -64,17 +62,12 @@ public class MethodArgumentNotValidException extends AbstractMethodArgumentResol private static String getValidationErrorMessage(BindingResult bindingResult) { - if (bindingResult != null) { - StringBuilder sb = new StringBuilder(); - sb.append(", with ").append(bindingResult.getErrorCount()).append(" error(s): "); - for (ObjectError error : bindingResult.getAllErrors()) { - sb.append("[").append(error).append("] "); - } - return sb.toString(); - } - else { - return ""; + StringBuilder sb = new StringBuilder(); + sb.append(bindingResult.getErrorCount()).append(" error(s): "); + for (ObjectError error : bindingResult.getAllErrors()) { + sb.append("[").append(error).append("] "); } + return sb.toString(); } } diff --git a/spring-messaging/src/main/java/org/springframework/messaging/handler/annotation/support/MethodArgumentTypeMismatchException.java b/spring-messaging/src/main/java/org/springframework/messaging/handler/annotation/support/MethodArgumentTypeMismatchException.java index 36bcd10a2ff..a8d10377c28 100644 --- a/spring-messaging/src/main/java/org/springframework/messaging/handler/annotation/support/MethodArgumentTypeMismatchException.java +++ b/spring-messaging/src/main/java/org/springframework/messaging/handler/annotation/support/MethodArgumentTypeMismatchException.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2017 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. @@ -20,20 +20,16 @@ import org.springframework.core.MethodParameter; import org.springframework.messaging.Message; /** - * Exception that indicates that a method argument has not the - * expected type. + * Exception that indicates that a method argument has not the expected type. * * @author Stephane Nicoll * @since 4.0.3 */ -@SuppressWarnings("serial") +@SuppressWarnings({"serial", "deprecation"}) public class MethodArgumentTypeMismatchException extends AbstractMethodArgumentResolutionException { - /** - * Create a new instance with the invalid {@code MethodParameter}. - */ - public MethodArgumentTypeMismatchException(Message message, MethodParameter param, String description) { - super(message, param, getMethodParamMessage(param) + description); + public MethodArgumentTypeMismatchException(Message message, MethodParameter parameter, String description) { + super(message, parameter, description); } } diff --git a/spring-messaging/src/main/java/org/springframework/messaging/handler/invocation/InvocableHandlerMethod.java b/spring-messaging/src/main/java/org/springframework/messaging/handler/invocation/InvocableHandlerMethod.java index 779a36c3a3c..848a65c144c 100644 --- a/spring-messaging/src/main/java/org/springframework/messaging/handler/invocation/InvocableHandlerMethod.java +++ b/spring-messaging/src/main/java/org/springframework/messaging/handler/invocation/InvocableHandlerMethod.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2016 the original author or authors. + * Copyright 2002-2017 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. @@ -139,36 +139,22 @@ public class InvocableHandlerMethod extends HandlerMethod { } catch (Exception ex) { if (logger.isDebugEnabled()) { - logger.debug(getArgumentResolutionErrorMessage("Error resolving argument", i), ex); + logger.debug(getArgumentResolutionErrorMessage("Failed to resolve", i), ex); } throw ex; } } if (args[i] == null) { - String msg = getArgumentResolutionErrorMessage("No suitable resolver for argument", i); - throw new IllegalStateException(msg); + throw new MethodArgumentResolutionException(message, parameter, + getArgumentResolutionErrorMessage("No suitable resolver for", i)); } } return args; } - private String getArgumentResolutionErrorMessage(String message, int index) { - MethodParameter param = getMethodParameters()[index]; - message += " [" + index + "] [type=" + param.getParameterType().getName() + "]"; - return getDetailedErrorMessage(message); - } - - /** - * Adds HandlerMethod details such as the controller type and method - * signature to the given error message. - * @param message error message to append the HandlerMethod details to - */ - protected String getDetailedErrorMessage(String message) { - StringBuilder sb = new StringBuilder(message).append("\n"); - sb.append("HandlerMethod details: \n"); - sb.append("Controller [").append(getBeanType().getName()).append("]\n"); - sb.append("Method [").append(getBridgedMethod().toGenericString()).append("]\n"); - return sb.toString(); + private String getArgumentResolutionErrorMessage(String text, int index) { + Class paramType = getMethodParameters()[index].getParameterType(); + return text + " argument " + index + " of type '" + paramType.getName() + "'"; } /** @@ -197,8 +183,8 @@ public class InvocableHandlerMethod extends HandlerMethod { } catch (IllegalArgumentException ex) { assertTargetBean(getBridgedMethod(), getBean(), args); - String message = (ex.getMessage() != null ? ex.getMessage() : "Illegal argument"); - throw new IllegalStateException(getInvocationErrorMessage(message, args), ex); + String text = (ex.getMessage() != null ? ex.getMessage() : "Illegal argument"); + throw new IllegalStateException(getInvocationErrorMessage(text, args), ex); } catch (InvocationTargetException ex) { // Unwrap for HandlerExceptionResolvers ... @@ -213,33 +199,33 @@ public class InvocableHandlerMethod extends HandlerMethod { throw (Exception) targetException; } else { - String msg = getInvocationErrorMessage("Failed to invoke controller method", args); - throw new IllegalStateException(msg, targetException); + String text = getInvocationErrorMessage("Failed to invoke handler method", args); + throw new IllegalStateException(text, targetException); } } } /** * Assert that the target bean class is an instance of the class where the given - * method is declared. In some cases the actual controller instance at request- + * method is declared. In some cases the actual endpoint instance at request- * processing time may be a JDK dynamic proxy (lazy initialization, prototype - * beans, and others). {@code @Controller}'s that require proxying should prefer + * beans, and others). Endpoint classes that require proxying should prefer * class-based proxy mechanisms. */ private void assertTargetBean(Method method, Object targetBean, Object[] args) { Class methodDeclaringClass = method.getDeclaringClass(); Class targetBeanClass = targetBean.getClass(); if (!methodDeclaringClass.isAssignableFrom(targetBeanClass)) { - String msg = "The mapped controller method class '" + methodDeclaringClass.getName() + - "' is not an instance of the actual controller bean class '" + - targetBeanClass.getName() + "'. If the controller requires proxying " + + String text = "The mapped handler method class '" + methodDeclaringClass.getName() + + "' is not an instance of the actual endpoint bean class '" + + targetBeanClass.getName() + "'. If the endpoint requires proxying " + "(e.g. due to @Transactional), please use class-based proxying."; - throw new IllegalStateException(getInvocationErrorMessage(msg, args)); + throw new IllegalStateException(getInvocationErrorMessage(text, args)); } } - private String getInvocationErrorMessage(String message, Object[] resolvedArgs) { - StringBuilder sb = new StringBuilder(getDetailedErrorMessage(message)); + private String getInvocationErrorMessage(String text, Object[] resolvedArgs) { + StringBuilder sb = new StringBuilder(getDetailedErrorMessage(text)); sb.append("Resolved arguments: \n"); for (int i = 0; i < resolvedArgs.length; i++) { sb.append("[").append(i).append("] "); @@ -254,6 +240,19 @@ public class InvocableHandlerMethod extends HandlerMethod { return sb.toString(); } + /** + * Adds HandlerMethod details such as the bean type and method signature to the message. + * @param text error message to append the HandlerMethod details to + */ + protected String getDetailedErrorMessage(String text) { + StringBuilder sb = new StringBuilder(text).append("\n"); + sb.append("HandlerMethod details: \n"); + sb.append("Endpoint [").append(getBeanType().getName()).append("]\n"); + sb.append("Method [").append(getBridgedMethod().toGenericString()).append("]\n"); + return sb.toString(); + } + + MethodParameter getAsyncReturnValueType(Object returnValue) { return new AsyncResultMethodParameter(returnValue); } @@ -268,7 +267,7 @@ public class InvocableHandlerMethod extends HandlerMethod { public AsyncResultMethodParameter(Object returnValue) { super(-1); this.returnValue = returnValue; - this.returnType = ResolvableType.forType(super.getGenericParameterType()).getGeneric(0); + this.returnType = ResolvableType.forType(super.getGenericParameterType()).getGeneric(); } protected AsyncResultMethodParameter(AsyncResultMethodParameter original) { diff --git a/spring-messaging/src/main/java/org/springframework/messaging/handler/invocation/MethodArgumentResolutionException.java b/spring-messaging/src/main/java/org/springframework/messaging/handler/invocation/MethodArgumentResolutionException.java new file mode 100644 index 00000000000..88a1df63191 --- /dev/null +++ b/spring-messaging/src/main/java/org/springframework/messaging/handler/invocation/MethodArgumentResolutionException.java @@ -0,0 +1,68 @@ +/* + * Copyright 2002-2017 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. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.messaging.handler.invocation; + +import org.springframework.core.MethodParameter; +import org.springframework.messaging.Message; +import org.springframework.messaging.MessagingException; + +/** + * Common exception resulting from the invocation of + * {@link org.springframework.messaging.handler.invocation.HandlerMethodArgumentResolver}. + * + * @author Juergen Hoeller + * @since 4.3.6 + * @see HandlerMethodArgumentResolver + */ +@SuppressWarnings("serial") +public class MethodArgumentResolutionException extends MessagingException { + + private final MethodParameter parameter; + + + /** + * Create a new instance providing the invalid {@code MethodParameter}. + */ + public MethodArgumentResolutionException(Message message, MethodParameter parameter) { + super(message, getMethodParameterMessage(parameter)); + this.parameter = parameter; + } + + /** + * Create a new instance providing the invalid {@code MethodParameter} and + * a prepared description. + */ + public MethodArgumentResolutionException(Message message, MethodParameter parameter, String description) { + super(message, getMethodParameterMessage(parameter) + ": " + description); + this.parameter = parameter; + } + + + /** + * Return the MethodParameter that was rejected. + */ + public final MethodParameter getMethodParameter() { + return this.parameter; + } + + + private static String getMethodParameterMessage(MethodParameter parameter) { + return "Could not resolve method parameter at index " + parameter.getParameterIndex() + + " in " + parameter.getMethod().toGenericString(); + } + +} diff --git a/spring-messaging/src/test/java/org/springframework/messaging/handler/annotation/support/DefaultMessageHandlerMethodFactoryTests.java b/spring-messaging/src/test/java/org/springframework/messaging/handler/annotation/support/DefaultMessageHandlerMethodFactoryTests.java index 18dd1859861..6cf0148d09b 100644 --- a/spring-messaging/src/test/java/org/springframework/messaging/handler/annotation/support/DefaultMessageHandlerMethodFactoryTests.java +++ b/spring-messaging/src/test/java/org/springframework/messaging/handler/annotation/support/DefaultMessageHandlerMethodFactoryTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2016 the original author or authors. + * Copyright 2002-2017 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. @@ -41,6 +41,7 @@ import org.springframework.messaging.converter.MessageConverter; import org.springframework.messaging.handler.annotation.Payload; import org.springframework.messaging.handler.invocation.HandlerMethodArgumentResolver; import org.springframework.messaging.handler.invocation.InvocableHandlerMethod; +import org.springframework.messaging.handler.invocation.MethodArgumentResolutionException; import org.springframework.messaging.support.MessageBuilder; import org.springframework.util.ReflectionUtils; import org.springframework.validation.Errors; @@ -53,13 +54,14 @@ import static org.junit.Assert.*; */ public class DefaultMessageHandlerMethodFactoryTests { + private final SampleBean sample = new SampleBean(); + @Rule public final TestName name = new TestName(); @Rule public final ExpectedException thrown = ExpectedException.none(); - private final SampleBean sample = new SampleBean(); @Test public void customConversion() throws Exception { @@ -146,7 +148,7 @@ public class DefaultMessageHandlerMethodFactoryTests { InvocableHandlerMethod invocableHandlerMethod2 = createInvocableHandlerMethod(instance, "simpleString", String.class); - thrown.expect(IllegalStateException.class); + thrown.expect(MethodArgumentResolutionException.class); thrown.expectMessage("No suitable resolver for"); invocableHandlerMethod2.invoke(message); } @@ -240,4 +242,5 @@ public class DefaultMessageHandlerMethodFactoryTests { return Locale.getDefault(); } } + } diff --git a/spring-web-reactive/src/main/java/org/springframework/web/reactive/result/method/InvocableHandlerMethod.java b/spring-web-reactive/src/main/java/org/springframework/web/reactive/result/method/InvocableHandlerMethod.java index 03879044dac..50414f86a1f 100644 --- a/spring-web-reactive/src/main/java/org/springframework/web/reactive/result/method/InvocableHandlerMethod.java +++ b/spring-web-reactive/src/main/java/org/springframework/web/reactive/result/method/InvocableHandlerMethod.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2016 the original author or authors. + * Copyright 2002-2017 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. @@ -45,6 +45,7 @@ import org.springframework.web.server.ServerWebExchange; * a {@link ServerWebExchange} and use that to invoke the underlying method. * * @author Rossen Stoyanchev + * @author Juergen Hoeller * @since 5.0 */ public class InvocableHandlerMethod extends HandlerMethod { @@ -89,14 +90,13 @@ public class InvocableHandlerMethod extends HandlerMethod { /** * Invoke the method for the given exchange. - * * @param exchange the current exchange * @param bindingContext the binding context to use * @param providedArgs optional list of argument values to match by type * @return Mono with a {@link HandlerResult}. */ - public Mono invoke(ServerWebExchange exchange, - BindingContext bindingContext, Object... providedArgs) { + public Mono invoke(ServerWebExchange exchange, BindingContext bindingContext, + Object... providedArgs) { return resolveArguments(exchange, bindingContext, providedArgs).then(args -> { try { @@ -108,14 +108,13 @@ public class InvocableHandlerMethod extends HandlerMethod { return Mono.error(ex.getTargetException()); } catch (Throwable ex) { - String msg = getInvocationErrorMessage(args); - return Mono.error(new IllegalStateException(msg)); + return Mono.error(new IllegalStateException(getInvocationErrorMessage(args))); } }); } - private Mono resolveArguments(ServerWebExchange exchange, - BindingContext bindingContext, Object... providedArgs) { + private Mono resolveArguments(ServerWebExchange exchange, BindingContext bindingContext, + Object... providedArgs) { if (ObjectUtils.isEmpty(getMethodParameters())) { return EMPTY_ARGS; @@ -125,7 +124,7 @@ public class InvocableHandlerMethod extends HandlerMethod { .map(param -> { param.initParameterNameDiscovery(this.parameterNameDiscoverer); GenericTypeResolver.resolveParameterType(param, getBean().getClass()); - return findProvidedArg(param, providedArgs) + return findProvidedArgument(param, providedArgs) .map(Mono::just) .orElseGet(() -> { HandlerMethodArgumentResolver resolver = findResolver(param); @@ -144,12 +143,12 @@ public class InvocableHandlerMethod extends HandlerMethod { } } - private Optional findProvidedArg(MethodParameter param, Object... providedArgs) { + private Optional findProvidedArgument(MethodParameter parameter, Object... providedArgs) { if (ObjectUtils.isEmpty(providedArgs)) { return Optional.empty(); } return Arrays.stream(providedArgs) - .filter(arg -> param.getParameterType().isInstance(arg)) + .filter(arg -> parameter.getParameterType().isInstance(arg)) .findFirst(); } @@ -157,34 +156,33 @@ public class InvocableHandlerMethod extends HandlerMethod { return this.resolvers.stream() .filter(r -> r.supportsParameter(param)) .findFirst() - .orElseThrow(() -> getArgumentError("No resolver for ", param, null)); + .orElseThrow(() -> getArgumentError("No suitable resolver for", param, null)); } - private Mono resolveArg(HandlerMethodArgumentResolver resolver, MethodParameter param, + private Mono resolveArg(HandlerMethodArgumentResolver resolver, MethodParameter parameter, BindingContext bindingContext, ServerWebExchange exchange) { try { - return resolver.resolveArgument(param, bindingContext, exchange) + return resolver.resolveArgument(parameter, bindingContext, exchange) .defaultIfEmpty(NO_ARG_VALUE) .doOnError(cause -> { if(logger.isDebugEnabled()) { - logger.debug(getDetailedErrorMessage("Error resolving ", param), cause); + logger.debug(getDetailedErrorMessage("Failed to resolve", parameter), cause); } }); } catch (Exception ex) { - throw getArgumentError("Error resolving ", param, ex); + throw getArgumentError("Failed to resolve", parameter, ex); } } - private IllegalStateException getArgumentError(String message, MethodParameter param, Throwable ex) { - return new IllegalStateException(getDetailedErrorMessage(message, param), ex); + private IllegalStateException getArgumentError(String text, MethodParameter parameter, Throwable ex) { + return new IllegalStateException(getDetailedErrorMessage(text, parameter), ex); } - private String getDetailedErrorMessage(String message, MethodParameter param) { - return message + "argument [" + param.getParameterIndex() + "] " + - "of type [" + param.getParameterType().getName() + "] " + - "on method [" + getBridgedMethod().toGenericString() + "]"; + private String getDetailedErrorMessage(String text, MethodParameter param) { + return text + " argument " + param.getParameterIndex() + " of type '" + + param.getParameterType().getName() + "' on " + getBridgedMethod().toGenericString(); } private Object doInvoke(Object[] args) throws Exception { @@ -207,8 +205,8 @@ public class InvocableHandlerMethod extends HandlerMethod { "[" + i + "][type=" + args[i].getClass().getName() + "][value=" + args[i] + "]" : "[" + i + "][null]")) .collect(Collectors.joining(",", " ", " ")); - return "Failed to invoke controller with resolved arguments:" + argumentDetails + - "on method [" + getBridgedMethod().toGenericString() + "]"; + return "Failed to invoke handler method with resolved arguments:" + argumentDetails + + "on " + getBridgedMethod().toGenericString(); } } diff --git a/spring-web-reactive/src/test/java/org/springframework/web/reactive/result/method/InvocableHandlerMethodTests.java b/spring-web-reactive/src/test/java/org/springframework/web/reactive/result/method/InvocableHandlerMethodTests.java index ec4c297415e..a8a3c0c729b 100644 --- a/spring-web-reactive/src/test/java/org/springframework/web/reactive/result/method/InvocableHandlerMethodTests.java +++ b/spring-web-reactive/src/test/java/org/springframework/web/reactive/result/method/InvocableHandlerMethodTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2016 the original author or authors. + * Copyright 2002-2017 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. @@ -34,18 +34,16 @@ import org.springframework.web.server.UnsupportedMediaTypeStatusException; import org.springframework.web.server.adapter.DefaultServerWebExchange; import org.springframework.web.server.session.MockWebSessionManager; -import static org.hamcrest.Matchers.instanceOf; -import static org.hamcrest.Matchers.is; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertThat; -import static org.junit.Assert.assertTrue; +import static org.hamcrest.Matchers.*; +import static org.junit.Assert.*; import static org.mockito.Mockito.any; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; +import static org.mockito.Mockito.*; /** * Unit tests for {@link InvocableHandlerMethod}. + * * @author Rossen Stoyanchev + * @author Juergen Hoeller */ @SuppressWarnings("ThrowableResultOfMethodCallIgnored") public class InvocableHandlerMethodTests { @@ -93,14 +91,14 @@ public class InvocableHandlerMethodTests { InvocableHandlerMethod hm = handlerMethod("singleArg"); Mono mono = hm.invoke(this.exchange, new BindingContext()); - StepVerifier.create(mono) - .expectNextCount(0) - .consumeErrorWith(error -> { - assertThat(error, instanceOf(IllegalStateException.class)); - assertThat(error.getMessage(), is("No resolver for argument [0] of type [java.lang.String] " + - "on method [" + hm.getMethod().toGenericString() + "]")); - }) - .verify(); + try { + mono.block(); + fail("Expected IllegalStateException"); + } + catch (IllegalStateException ex) { + assertThat(ex.getMessage(), is("No suitable resolver for argument 0 of type 'java.lang.String' " + + "on " + hm.getMethod().toGenericString())); + } } @Test @@ -109,13 +107,13 @@ public class InvocableHandlerMethodTests { addResolver(hm, Mono.error(new UnsupportedMediaTypeStatusException("boo"))); Mono mono = hm.invoke(this.exchange, new BindingContext()); - StepVerifier.create(mono) - .expectNextCount(0) - .consumeErrorWith(error -> { - assertThat(error, instanceOf(UnsupportedMediaTypeStatusException.class)); - assertThat(error.getMessage(), is("Request failure [status: 415, reason: \"boo\"]")); - }) - .verify(); + try { + mono.block(); + fail("Expected UnsupportedMediaTypeStatusException"); + } + catch (UnsupportedMediaTypeStatusException ex) { + assertThat(ex.getMessage(), is("Request failure [status: 415, reason: \"boo\"]")); + } } @Test @@ -124,15 +122,15 @@ public class InvocableHandlerMethodTests { addResolver(hm, Mono.just(1)); Mono mono = hm.invoke(this.exchange, new BindingContext()); - StepVerifier.create(mono) - .expectNextCount(0) - .consumeErrorWith(error -> { - assertThat(error, instanceOf(IllegalStateException.class)); - assertThat(error.getMessage(), is("Failed to invoke controller with resolved arguments: " + - "[0][type=java.lang.Integer][value=1] " + - "on method [" + hm.getMethod().toGenericString() + "]")); - }) - .verify(); + try { + mono.block(); + fail("Expected IllegalStateException"); + } + catch (IllegalStateException ex) { + assertThat(ex.getMessage(), is("Failed to invoke handler method with resolved arguments: " + + "[0][type=java.lang.Integer][value=1] " + + "on " + hm.getMethod().toGenericString())); + } } @Test @@ -140,13 +138,13 @@ public class InvocableHandlerMethodTests { InvocableHandlerMethod hm = handlerMethod("exceptionMethod"); Mono mono = hm.invoke(this.exchange, new BindingContext()); - StepVerifier.create(mono) - .expectNextCount(0) - .consumeErrorWith(error -> { - assertThat(error, instanceOf(IllegalStateException.class)); - assertThat(error.getMessage(), is("boo")); - }) - .verify(); + try { + mono.block(); + fail("Expected IllegalStateException"); + } + catch (IllegalStateException ex) { + assertThat(ex.getMessage(), is("boo")); + } } diff --git a/spring-web/src/main/java/org/springframework/web/method/support/InvocableHandlerMethod.java b/spring-web/src/main/java/org/springframework/web/method/support/InvocableHandlerMethod.java index 949dcdbb0dc..4803b2dc46c 100644 --- a/spring-web/src/main/java/org/springframework/web/method/support/InvocableHandlerMethod.java +++ b/spring-web/src/main/java/org/springframework/web/method/support/InvocableHandlerMethod.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2016 the original author or authors. + * Copyright 2002-2017 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. @@ -163,36 +163,23 @@ public class InvocableHandlerMethod extends HandlerMethod { } catch (Exception ex) { if (logger.isDebugEnabled()) { - logger.debug(getArgumentResolutionErrorMessage("Error resolving argument", i), ex); + logger.debug(getArgumentResolutionErrorMessage("Failed to resolve", i), ex); } throw ex; } } if (args[i] == null) { - String msg = getArgumentResolutionErrorMessage("No suitable resolver for argument", i); - throw new IllegalStateException(msg); + throw new IllegalStateException("Could not resolve method parameter at index " + + parameter.getParameterIndex() + " in " + parameter.getMethod().toGenericString() + + ": " + getArgumentResolutionErrorMessage("No suitable resolver for", i)); } } return args; } - private String getArgumentResolutionErrorMessage(String message, int index) { - MethodParameter param = getMethodParameters()[index]; - message += " [" + index + "] [type=" + param.getParameterType().getName() + "]"; - return getDetailedErrorMessage(message); - } - - /** - * Adds HandlerMethod details such as the controller type and method - * signature to the given error message. - * @param message error message to append the HandlerMethod details to - */ - protected String getDetailedErrorMessage(String message) { - StringBuilder sb = new StringBuilder(message).append("\n"); - sb.append("HandlerMethod details: \n"); - sb.append("Controller [").append(getBeanType().getName()).append("]\n"); - sb.append("Method [").append(getBridgedMethod().toGenericString()).append("]\n"); - return sb.toString(); + private String getArgumentResolutionErrorMessage(String text, int index) { + Class paramType = getMethodParameters()[index].getParameterType(); + return text + " argument " + index + " of type '" + paramType.getName() + "'"; } /** @@ -221,8 +208,8 @@ public class InvocableHandlerMethod extends HandlerMethod { } catch (IllegalArgumentException ex) { assertTargetBean(getBridgedMethod(), getBean(), args); - String message = (ex.getMessage() != null ? ex.getMessage() : "Illegal argument"); - throw new IllegalStateException(getInvocationErrorMessage(message, args), ex); + String text = (ex.getMessage() != null ? ex.getMessage() : "Illegal argument"); + throw new IllegalStateException(getInvocationErrorMessage(text, args), ex); } catch (InvocationTargetException ex) { // Unwrap for HandlerExceptionResolvers ... @@ -237,8 +224,8 @@ public class InvocableHandlerMethod extends HandlerMethod { throw (Exception) targetException; } else { - String msg = getInvocationErrorMessage("Failed to invoke controller method", args); - throw new IllegalStateException(msg, targetException); + String text = getInvocationErrorMessage("Failed to invoke handler method", args); + throw new IllegalStateException(text, targetException); } } } @@ -254,16 +241,16 @@ public class InvocableHandlerMethod extends HandlerMethod { Class methodDeclaringClass = method.getDeclaringClass(); Class targetBeanClass = targetBean.getClass(); if (!methodDeclaringClass.isAssignableFrom(targetBeanClass)) { - String msg = "The mapped controller method class '" + methodDeclaringClass.getName() + + String text = "The mapped handler method class '" + methodDeclaringClass.getName() + "' is not an instance of the actual controller bean class '" + targetBeanClass.getName() + "'. If the controller requires proxying " + "(e.g. due to @Transactional), please use class-based proxying."; - throw new IllegalStateException(getInvocationErrorMessage(msg, args)); + throw new IllegalStateException(getInvocationErrorMessage(text, args)); } } - private String getInvocationErrorMessage(String message, Object[] resolvedArgs) { - StringBuilder sb = new StringBuilder(getDetailedErrorMessage(message)); + private String getInvocationErrorMessage(String text, Object[] resolvedArgs) { + StringBuilder sb = new StringBuilder(getDetailedErrorMessage(text)); sb.append("Resolved arguments: \n"); for (int i = 0; i < resolvedArgs.length; i++) { sb.append("[").append(i).append("] "); @@ -278,4 +265,16 @@ public class InvocableHandlerMethod extends HandlerMethod { return sb.toString(); } + /** + * Adds HandlerMethod details such as the bean type and method signature to the message. + * @param text error message to append the HandlerMethod details to + */ + protected String getDetailedErrorMessage(String text) { + StringBuilder sb = new StringBuilder(text).append("\n"); + sb.append("HandlerMethod details: \n"); + sb.append("Controller [").append(getBeanType().getName()).append("]\n"); + sb.append("Method [").append(getBridgedMethod().toGenericString()).append("]\n"); + return sb.toString(); + } + } diff --git a/spring-web/src/test/java/org/springframework/web/method/support/InvocableHandlerMethodTests.java b/spring-web/src/test/java/org/springframework/web/method/support/InvocableHandlerMethodTests.java index b044b257b16..f64fdde4632 100644 --- a/spring-web/src/test/java/org/springframework/web/method/support/InvocableHandlerMethodTests.java +++ b/spring-web/src/test/java/org/springframework/web/method/support/InvocableHandlerMethodTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2016 the original author or authors. + * Copyright 2002-2017 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. @@ -93,7 +93,7 @@ public class InvocableHandlerMethodTests { fail("Expected exception"); } catch (IllegalStateException ex) { - assertTrue(ex.getMessage().contains("No suitable resolver for argument [0] [type=java.lang.Integer]")); + assertTrue(ex.getMessage().contains("No suitable resolver for argument 0 of type 'java.lang.Integer'")); } } @@ -192,7 +192,7 @@ public class InvocableHandlerMethodTests { catch (IllegalStateException actual) { assertNotNull(actual.getCause()); assertSame(expected, actual.getCause()); - assertTrue(actual.getMessage().contains("Failed to invoke controller method")); + assertTrue(actual.getMessage().contains("Failed to invoke handler method")); } }