Browse Source

Introduce base exception class for arg resolution

Issue: SPR-11584
pull/497/merge
Rossen Stoyanchev 12 years ago
parent
commit
2c1d5efbb0
  1. 68
      spring-messaging/src/main/java/org/springframework/messaging/handler/annotation/support/AbstractMethodArgumentResolutionException.java
  2. 36
      spring-messaging/src/main/java/org/springframework/messaging/handler/annotation/support/MessageMethodArgumentResolver.java
  3. 39
      spring-messaging/src/main/java/org/springframework/messaging/handler/annotation/support/MethodArgumentNotValidException.java
  4. 12
      spring-messaging/src/main/java/org/springframework/messaging/handler/annotation/support/MethodArgumentTypeMismatchException.java
  5. 63
      spring-messaging/src/test/java/org/springframework/messaging/handler/annotation/support/MessageMethodArgumentResolverTests.java

68
spring-messaging/src/main/java/org/springframework/messaging/handler/annotation/support/AbstractMethodArgumentResolutionException.java

@ -0,0 +1,68 @@ @@ -0,0 +1,68 @@
/*
* Copyright 2002-2014 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.annotation.support;
import org.springframework.core.MethodParameter;
import org.springframework.messaging.Message;
import org.springframework.messaging.MessagingException;
/**
* Base class for exceptions resulting from the invocation of
* {@link org.springframework.messaging.handler.invocation.HandlerMethodArgumentResolver}.
*
* @author Rossen Stoyanchev
* @since 4.0.3
*/
@SuppressWarnings("serial")
public abstract class AbstractMethodArgumentResolutionException extends MessagingException {
private final MethodParameter parameter;
/**
* Create a new instance providing the invalid {@code MethodParameter}.
*/
protected AbstractMethodArgumentResolutionException(Message<?> message, MethodParameter parameter) {
this(message, parameter, getMethodParamMessage(parameter));
}
/**
* Create a new instance providing the invalid {@code MethodParameter} and
* a prepared description. Sub-classes should prepend the description with
* the help of {@link #getMethodParamMessage(org.springframework.core.MethodParameter)}.
*/
protected AbstractMethodArgumentResolutionException(Message<?> message,
MethodParameter parameter, String description) {
super(message, description);
this.parameter = parameter;
}
/**
* Return the MethodParameter that was rejected.
*/
public MethodParameter getMethodParameter() {
return this.parameter;
}
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()).toString();
}
}

36
spring-messaging/src/main/java/org/springframework/messaging/handler/annotation/support/MessageMethodArgumentResolver.java

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2002-2013 the original author or authors.
* Copyright 2002-2014 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.
@ -21,6 +21,8 @@ import org.springframework.core.ResolvableType; @@ -21,6 +21,8 @@ import org.springframework.core.ResolvableType;
import org.springframework.messaging.Message;
import org.springframework.messaging.handler.invocation.HandlerMethodArgumentResolver;
import java.lang.reflect.Type;
/**
* A {@link HandlerMethodArgumentResolver} for {@link Message} parameters. Validates
* that the generic type of the payload matches with the message value.
@ -39,28 +41,30 @@ public class MessageMethodArgumentResolver implements HandlerMethodArgumentResol @@ -39,28 +41,30 @@ public class MessageMethodArgumentResolver implements HandlerMethodArgumentResol
@Override
public Object resolveArgument(MethodParameter parameter, Message<?> message) throws Exception {
// Validate the message type is assignable
if (!parameter.getParameterType().isAssignableFrom(message.getClass())) {
throw new MethodArgumentTypeMismatchException(message,
"Could not resolve Message parameter: invalid message type:"
+ "expected [" + message.getClass().getName() + "] but got ["
+ parameter.getParameterType().getName() + "]");
Class<?> paramType = parameter.getParameterType();
if (!paramType.isAssignableFrom(message.getClass())) {
throw new MethodArgumentTypeMismatchException(message, parameter,
"The actual message type [" + message.getClass().getName() + "] " +
"does not match the expected type [" + paramType.getName() + "]");
}
// validate that the payload type matches
Class<?> effectivePayloadType = getPayloadType(parameter);
if (effectivePayloadType != null && !effectivePayloadType.isInstance(message.getPayload())) {
throw new MethodArgumentTypeMismatchException(message,
"Could not resolve Message parameter: invalid payload type: "
+ "expected [" + effectivePayloadType.getName() + "] but got ["
+ message.getPayload().getClass().getName() + "]");
Class<?> expectedPayloadType = getPayloadType(parameter);
Object payload = message.getPayload();
if (expectedPayloadType != null && !expectedPayloadType.isInstance(payload)) {
throw new MethodArgumentTypeMismatchException(message, parameter,
"The expected Message<?> payload type [" + expectedPayloadType.getClass().getName() +
"] does not match the actual payload type [" + payload.getClass().getName() + "]");
}
return message;
}
private Class<?> getPayloadType(MethodParameter parameter) {
ResolvableType resolvableType = ResolvableType
.forType(parameter.getGenericParameterType()).as(Message.class);
Type genericParamType = parameter.getGenericParameterType();
ResolvableType resolvableType = ResolvableType.forType(genericParamType).as(Message.class);
return resolvableType.getGeneric(0).resolve(Object.class);
}

39
spring-messaging/src/main/java/org/springframework/messaging/handler/annotation/support/MethodArgumentNotValidException.java

@ -18,22 +18,19 @@ package org.springframework.messaging.handler.annotation.support; @@ -18,22 +18,19 @@ 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.validation.BindingResult;
import org.springframework.validation.ObjectError;
/**
* Exception to be thrown when a method argument is not valid. For instance, this
* can be issued if a validation on a method parameter annotated with
* {@code @Valid} fails.
* Exception to be thrown when a method argument fails validation perhaps as a
* result of {@code @Valid} style validation, or perhaps because it is required.
*
* @author Brian Clozel
* @author Rossen Stoyanchev
* @since 4.0.1
*/
@SuppressWarnings("serial")
public class MethodArgumentNotValidException extends MessagingException {
private final MethodParameter parameter;
public class MethodArgumentNotValidException extends AbstractMethodArgumentResolutionException {
private final BindingResult bindingResult;
@ -41,7 +38,6 @@ public class MethodArgumentNotValidException extends MessagingException { @@ -41,7 +38,6 @@ public class MethodArgumentNotValidException extends MessagingException {
/**
* Create a new instance with the invalid {@code MethodParameter}.
*/
public MethodArgumentNotValidException(Message<?> message, MethodParameter parameter) {
this(message, parameter, null);
}
@ -53,19 +49,13 @@ public class MethodArgumentNotValidException extends MessagingException { @@ -53,19 +49,13 @@ public class MethodArgumentNotValidException extends MessagingException {
public MethodArgumentNotValidException(Message<?> message, MethodParameter parameter,
BindingResult bindingResult) {
super(message, generateMessage(parameter, bindingResult));
this.parameter = parameter;
super(message, parameter, getMethodParamMessage(parameter) +
getValidationErrorMessage(parameter, bindingResult));
this.bindingResult = bindingResult;
}
/**
* Return the MethodParameter that was rejected.
*/
public MethodParameter getMethodParameter() {
return this.parameter;
}
/**
* Return the BindingResult if the failure is validation-related or {@code null}.
*/
@ -74,21 +64,18 @@ public class MethodArgumentNotValidException extends MessagingException { @@ -74,21 +64,18 @@ public class MethodArgumentNotValidException extends MessagingException {
}
private static String generateMessage(MethodParameter parameter, BindingResult bindingResult) {
StringBuilder sb = new StringBuilder("Invalid parameter at index ")
.append(parameter.getParameterIndex()).append(" in method: ")
.append(parameter.getMethod().toGenericString());
private static String getValidationErrorMessage(MethodParameter parameter, 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 "";
}
return sb.toString();
}
}

12
spring-messaging/src/main/java/org/springframework/messaging/handler/annotation/support/MethodArgumentTypeMismatchException.java

@ -16,6 +16,7 @@ @@ -16,6 +16,7 @@
package org.springframework.messaging.handler.annotation.support;
import org.springframework.core.MethodParameter;
import org.springframework.messaging.Message;
import org.springframework.messaging.MessagingException;
@ -26,9 +27,14 @@ import org.springframework.messaging.MessagingException; @@ -26,9 +27,14 @@ import org.springframework.messaging.MessagingException;
* @author Stephane Nicoll
* @since 4.0.3
*/
public class MethodArgumentTypeMismatchException extends MessagingException {
@SuppressWarnings("serial")
public class MethodArgumentTypeMismatchException extends AbstractMethodArgumentResolutionException {
public MethodArgumentTypeMismatchException(Message<?> message, String description) {
super(message, description);
/**
* Create a new instance with the invalid {@code MethodParameter}.
*/
public MethodArgumentTypeMismatchException(Message<?> message, MethodParameter parameter, String description) {
super(message, parameter, getMethodParamMessage(parameter) + description);
}
}

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

@ -33,6 +33,8 @@ import org.springframework.messaging.support.GenericMessage; @@ -33,6 +33,8 @@ import org.springframework.messaging.support.GenericMessage;
import org.springframework.messaging.support.MessageBuilder;
/**
* Unit tests for
* {@link org.springframework.messaging.handler.annotation.support.MessageMethodArgumentResolver}.
*
* @author Stephane Nicoll
*/
@ -45,101 +47,102 @@ public class MessageMethodArgumentResolverTests { @@ -45,101 +47,102 @@ public class MessageMethodArgumentResolverTests {
private Method method;
@Before
public void setup() throws Exception {
method = MessageMethodArgumentResolverTests.class.getDeclaredMethod("handleMessage",
this.method = MessageMethodArgumentResolverTests.class.getDeclaredMethod("handleMessage",
Message.class, Message.class, Message.class, Message.class, ErrorMessage.class);
}
@Test
public void resolveAnyPayloadType() throws Exception {
Message<String> message = MessageBuilder.withPayload("test").build();
MethodParameter parameter = new MethodParameter(method, 0);
MethodParameter parameter = new MethodParameter(this.method, 0);
assertTrue("Parameter '" + parameter + "' should be supported", resolver.supportsParameter(parameter));
assertSame(message, resolver.resolveArgument(parameter, message));
assertTrue("Parameter '" + parameter + "' should be supported", this.resolver.supportsParameter(parameter));
assertSame(message, this.resolver.resolveArgument(parameter, message));
}
@Test
public void resolvePayloadTypeExactType() throws Exception {
Message<Integer> message = MessageBuilder.withPayload(123).build();
MethodParameter parameter = new MethodParameter(method, 1);
MethodParameter parameter = new MethodParameter(this.method, 1);
assertTrue("Parameter '" + parameter + "' should be supported", resolver.supportsParameter(parameter));
assertSame(message, resolver.resolveArgument(parameter, message));
assertTrue("Parameter '" + parameter + "' should be supported", this.resolver.supportsParameter(parameter));
assertSame(message, this.resolver.resolveArgument(parameter, message));
}
@Test
public void resolvePayloadTypeSubClass() throws Exception {
Message<Integer> message = MessageBuilder.withPayload(123).build();
MethodParameter parameter = new MethodParameter(method, 2);
MethodParameter parameter = new MethodParameter(this.method, 2);
assertTrue("Parameter '" + parameter + "' should be supported", resolver.supportsParameter(parameter));
assertSame(message, resolver.resolveArgument(parameter, message));
assertTrue("Parameter '" + parameter + "' should be supported", this.resolver.supportsParameter(parameter));
assertSame(message, this.resolver.resolveArgument(parameter, message));
}
@Test
public void resolveInvalidPayloadType() throws Exception {
Message<String> message = MessageBuilder.withPayload("test").build();
MethodParameter parameter = new MethodParameter(method, 1);
MethodParameter parameter = new MethodParameter(this.method, 1);
assertTrue("Parameter '" + parameter + "' should be supported", resolver.supportsParameter(parameter));
assertTrue("Parameter '" + parameter + "' should be supported", this.resolver.supportsParameter(parameter));
thrown.expect(MethodArgumentTypeMismatchException.class);
thrown.expectMessage(Integer.class.getName());
thrown.expectMessage(String.class.getName());
resolver.resolveArgument(parameter, message);
this.resolver.resolveArgument(parameter, message);
}
@Test
public void resolveUpperBoundPayloadType() throws Exception {
Message<Integer> message = MessageBuilder.withPayload(123).build();
MethodParameter parameter = new MethodParameter(method, 3);
MethodParameter parameter = new MethodParameter(this.method, 3);
assertTrue("Parameter '" + parameter + "' should be supported", resolver.supportsParameter(parameter));
assertSame(message, resolver.resolveArgument(parameter, message));
assertTrue("Parameter '" + parameter + "' should be supported", this.resolver.supportsParameter(parameter));
assertSame(message, this.resolver.resolveArgument(parameter, message));
}
@Test
public void resolveOutOfBoundPayloadType() throws Exception {
Message<Locale> message = MessageBuilder.withPayload(Locale.getDefault()).build();
MethodParameter parameter = new MethodParameter(method, 3);
MethodParameter parameter = new MethodParameter(this.method, 3);
assertTrue("Parameter '" + parameter + "' should be supported", resolver.supportsParameter(parameter));
assertTrue("Parameter '" + parameter + "' should be supported", this.resolver.supportsParameter(parameter));
thrown.expect(MethodArgumentTypeMismatchException.class);
thrown.expectMessage(Number.class.getName());
thrown.expectMessage(Locale.class.getName());
resolver.resolveArgument(parameter, message);
this.resolver.resolveArgument(parameter, message);
}
@Test
public void resolveMessageSubTypeExactMatch() throws Exception {
ErrorMessage message = new ErrorMessage(new UnsupportedOperationException());
MethodParameter parameter = new MethodParameter(method, 4);
MethodParameter parameter = new MethodParameter(this.method, 4);
assertTrue("Parameter '" + parameter + "' should be supported", resolver.supportsParameter(parameter));
assertSame(message, resolver.resolveArgument(parameter, message));
assertTrue("Parameter '" + parameter + "' should be supported", this.resolver.supportsParameter(parameter));
assertSame(message, this.resolver.resolveArgument(parameter, message));
}
@Test
public void resolveMessageSubTypeSubClass() throws Exception {
ErrorMessage message = new ErrorMessage(new UnsupportedOperationException());
MethodParameter parameter = new MethodParameter(method, 0);
MethodParameter parameter = new MethodParameter(this.method, 0);
assertTrue("Parameter '" + parameter + "' should be supported", resolver.supportsParameter(parameter));
assertSame(message, resolver.resolveArgument(parameter, message));
assertTrue("Parameter '" + parameter + "' should be supported", this.resolver.supportsParameter(parameter));
assertSame(message, this.resolver.resolveArgument(parameter, message));
}
@Test
public void resolveWrongMessageType() throws Exception {
Message<? extends Throwable> message = new GenericMessage<Throwable>(
new UnsupportedOperationException());
MethodParameter parameter = new MethodParameter(method, 4);
Message<? extends Throwable> message = new GenericMessage<Throwable>(new UnsupportedOperationException());
MethodParameter parameter = new MethodParameter(this.method, 4);
assertTrue("Parameter '" + parameter + "' should be supported", resolver.supportsParameter(parameter));
assertTrue("Parameter '" + parameter + "' should be supported", this.resolver.supportsParameter(parameter));
thrown.expect(MethodArgumentTypeMismatchException.class);
thrown.expectMessage(ErrorMessage.class.getName());
thrown.expectMessage(GenericMessage.class.getName());
assertSame(message, resolver.resolveArgument(parameter, message));
assertSame(message, this.resolver.resolveArgument(parameter, message));
}
@SuppressWarnings("unused")

Loading…
Cancel
Save