001/* 002 * Copyright 2002-2016 the original author or authors. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * https://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017package org.springframework.messaging.handler.annotation.support; 018 019import java.lang.reflect.Type; 020 021import org.springframework.core.MethodParameter; 022import org.springframework.core.ResolvableType; 023import org.springframework.messaging.Message; 024import org.springframework.messaging.converter.MessageConversionException; 025import org.springframework.messaging.converter.MessageConverter; 026import org.springframework.messaging.converter.SmartMessageConverter; 027import org.springframework.messaging.handler.invocation.HandlerMethodArgumentResolver; 028import org.springframework.messaging.support.MessageBuilder; 029import org.springframework.util.ClassUtils; 030import org.springframework.util.StringUtils; 031 032/** 033 * {@code HandlerMethodArgumentResolver} for {@link Message} method arguments. 034 * Validates that the generic type of the payload matches to the message value 035 * or otherwise applies {@link MessageConverter} to convert to the expected 036 * payload type. 037 * 038 * @author Rossen Stoyanchev 039 * @author Stephane Nicoll 040 * @author Juergen Hoeller 041 * @since 4.0 042 */ 043public class MessageMethodArgumentResolver implements HandlerMethodArgumentResolver { 044 045 private final MessageConverter converter; 046 047 048 /** 049 * Create a default resolver instance without message conversion. 050 */ 051 public MessageMethodArgumentResolver() { 052 this(null); 053 } 054 055 /** 056 * Create a resolver instance with the given {@link MessageConverter}. 057 * @param converter the MessageConverter to use (may be {@code null}) 058 * @since 4.3 059 */ 060 public MessageMethodArgumentResolver(MessageConverter converter) { 061 this.converter = converter; 062 } 063 064 065 @Override 066 public boolean supportsParameter(MethodParameter parameter) { 067 return Message.class.isAssignableFrom(parameter.getParameterType()); 068 } 069 070 @Override 071 public Object resolveArgument(MethodParameter parameter, Message<?> message) throws Exception { 072 Class<?> targetMessageType = parameter.getParameterType(); 073 Class<?> targetPayloadType = getPayloadType(parameter); 074 075 if (!targetMessageType.isAssignableFrom(message.getClass())) { 076 throw new MethodArgumentTypeMismatchException(message, parameter, "Actual message type '" + 077 ClassUtils.getDescriptiveType(message) + "' does not match expected type '" + 078 ClassUtils.getQualifiedName(targetMessageType) + "'"); 079 } 080 081 Object payload = message.getPayload(); 082 if (payload == null || targetPayloadType.isInstance(payload)) { 083 return message; 084 } 085 086 if (isEmptyPayload(payload)) { 087 throw new MessageConversionException(message, "Cannot convert from actual payload type '" + 088 ClassUtils.getDescriptiveType(payload) + "' to expected payload type '" + 089 ClassUtils.getQualifiedName(targetPayloadType) + "' when payload is empty"); 090 } 091 092 payload = convertPayload(message, parameter, targetPayloadType); 093 return MessageBuilder.createMessage(payload, message.getHeaders()); 094 } 095 096 private Class<?> getPayloadType(MethodParameter parameter) { 097 Type genericParamType = parameter.getGenericParameterType(); 098 ResolvableType resolvableType = ResolvableType.forType(genericParamType).as(Message.class); 099 return resolvableType.getGeneric(0).resolve(Object.class); 100 } 101 102 /** 103 * Check if the given {@code payload} is empty. 104 * @param payload the payload to check (can be {@code null}) 105 */ 106 protected boolean isEmptyPayload(Object payload) { 107 if (payload == null) { 108 return true; 109 } 110 else if (payload instanceof byte[]) { 111 return ((byte[]) payload).length == 0; 112 } 113 else if (payload instanceof String) { 114 return !StringUtils.hasText((String) payload); 115 } 116 else { 117 return false; 118 } 119 } 120 121 private Object convertPayload(Message<?> message, MethodParameter parameter, Class<?> targetPayloadType) { 122 Object result = null; 123 if (this.converter instanceof SmartMessageConverter) { 124 SmartMessageConverter smartConverter = (SmartMessageConverter) this.converter; 125 result = smartConverter.fromMessage(message, targetPayloadType, parameter); 126 } 127 else if (this.converter != null) { 128 result = this.converter.fromMessage(message, targetPayloadType); 129 } 130 131 if (result == null) { 132 throw new MessageConversionException(message, "No converter found from actual payload type '" + 133 ClassUtils.getDescriptiveType(message.getPayload()) + "' to expected payload type '" + 134 ClassUtils.getQualifiedName(targetPayloadType) + "'"); 135 } 136 return result; 137 } 138 139}