Allow use of @JsonView on @MessageMapping methods

Issue: SPR-12741
This commit is contained in:
Sebastien Deleuze
2015-05-19 12:40:02 +02:00
parent c36435c042
commit 876c9694c4
7 changed files with 310 additions and 18 deletions

View File

@@ -24,6 +24,7 @@ import java.util.List;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.core.MethodParameter;
import org.springframework.messaging.Message;
import org.springframework.messaging.MessageHeaders;
import org.springframework.messaging.support.MessageBuilder;
@@ -38,13 +39,24 @@ import org.springframework.util.MimeType;
* and MIME type.
*
* @author Rossen Stoyanchev
* @author Sebastien Deleuze
* @since 4.0
*/
public abstract class AbstractMessageConverter implements MessageConverter {
protected final Log logger = LogFactory.getLog(getClass());
/**
* Name of the header that can be set to provide further information
* ({@link MethodParameter} instance) about the origin of the payload (for
* {@link #toMessage(Object, MessageHeaders)}) or about the target of the payload
* ({@link #fromMessage(Message, Class)}).
*
* @since 4.2
*/
public static final String METHOD_PARAMETER_HINT_HEADER = "methodParameterHint";
protected final Log logger = LogFactory.getLog(getClass());
private final List<MimeType> supportedMimeTypes;
private ContentTypeResolver contentTypeResolver = new DefaultContentTypeResolver();

View File

@@ -24,6 +24,7 @@ import java.nio.charset.Charset;
import java.util.Arrays;
import java.util.concurrent.atomic.AtomicReference;
import com.fasterxml.jackson.annotation.JsonView;
import com.fasterxml.jackson.core.JsonEncoding;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.util.DefaultPrettyPrinter;
@@ -33,6 +34,7 @@ import com.fasterxml.jackson.databind.MapperFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import org.springframework.core.MethodParameter;
import org.springframework.messaging.Message;
import org.springframework.messaging.MessageHeaders;
import org.springframework.util.Assert;
@@ -212,16 +214,27 @@ public class MappingJackson2MessageConverter extends AbstractMessageConverter {
@Override
public Object convertToInternal(Object payload, MessageHeaders headers) {
try {
Class<?> serializationView = getSerializationView(headers);
if (byte[].class.equals(getSerializedPayloadClass())) {
ByteArrayOutputStream out = new ByteArrayOutputStream(1024);
JsonEncoding encoding = getJsonEncoding(getMimeType(headers));
JsonGenerator generator = this.objectMapper.getFactory().createGenerator(out, encoding);
this.objectMapper.writeValue(generator, payload);
if (serializationView != null) {
this.objectMapper.writerWithView(serializationView).writeValue(generator, payload);
}
else {
this.objectMapper.writeValue(generator, payload);
}
payload = out.toByteArray();
}
else {
Writer writer = new StringWriter();
this.objectMapper.writeValue(writer, payload);
if (serializationView != null) {
this.objectMapper.writerWithView(serializationView).writeValue(writer, payload);
}
else {
this.objectMapper.writeValue(writer, payload);
}
payload = writer.toString();
}
}
@@ -231,6 +244,24 @@ public class MappingJackson2MessageConverter extends AbstractMessageConverter {
return payload;
}
private Class<?> getSerializationView(MessageHeaders headers) {
MethodParameter returnType = (headers == null ? null :
(MethodParameter)headers.get(METHOD_PARAMETER_HINT_HEADER));
if (returnType == null) {
return null;
}
JsonView annotation = returnType.getMethodAnnotation(JsonView.class);
if (annotation == null) {
return null;
}
Class<?>[] classes = annotation.value();
if (classes.length != 1) {
throw new IllegalArgumentException(
"@JsonView only supported for handler methods with exactly 1 class argument: " + returnType);
}
return classes[0];
}
/**
* Determine the JSON encoding to use for the given content type.
* @param contentType the MIME type from the MessageHeaders, if any

View File

@@ -25,6 +25,7 @@ import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.messaging.Message;
import org.springframework.messaging.MessageChannel;
import org.springframework.messaging.MessageHeaders;
import org.springframework.messaging.converter.AbstractMessageConverter;
import org.springframework.messaging.handler.DestinationPatternsMessageCondition;
import org.springframework.messaging.handler.annotation.SendTo;
import org.springframework.messaging.handler.annotation.support.DestinationVariableMethodArgumentResolver;
@@ -51,6 +52,7 @@ import org.springframework.util.PropertyPlaceholderHelper.PlaceholderResolver;
* the message is sent to each destination.
*
* @author Rossen Stoyanchev
* @author Sebastien Deleuze
* @since 4.0
*/
public class SendToMethodReturnValueHandler implements HandlerMethodReturnValueHandler {
@@ -162,10 +164,10 @@ public class SendToMethodReturnValueHandler implements HandlerMethodReturnValueH
for (String destination : destinations) {
destination = this.placeholderHelper.replacePlaceholders(destination, varResolver);
if (broadcast) {
this.messagingTemplate.convertAndSendToUser(user, destination, returnValue);
this.messagingTemplate.convertAndSendToUser(user, destination, returnValue, createHeaders(null, returnType));
}
else {
this.messagingTemplate.convertAndSendToUser(user, destination, returnValue, createHeaders(sessionId));
this.messagingTemplate.convertAndSendToUser(user, destination, returnValue, createHeaders(sessionId, returnType));
}
}
}
@@ -174,7 +176,7 @@ public class SendToMethodReturnValueHandler implements HandlerMethodReturnValueH
String[] destinations = getTargetDestinations(sendTo, message, this.defaultDestinationPrefix);
for (String destination : destinations) {
destination = this.placeholderHelper.replacePlaceholders(destination, varResolver);
this.messagingTemplate.convertAndSend(destination, returnValue, createHeaders(sessionId));
this.messagingTemplate.convertAndSend(destination, returnValue, createHeaders(sessionId, returnType));
}
}
}
@@ -210,12 +212,15 @@ public class SendToMethodReturnValueHandler implements HandlerMethodReturnValueH
new String[] {defaultPrefix + destination} : new String[] {defaultPrefix + "/" + destination});
}
private MessageHeaders createHeaders(String sessionId) {
private MessageHeaders createHeaders(String sessionId, MethodParameter returnType) {
SimpMessageHeaderAccessor headerAccessor = SimpMessageHeaderAccessor.create(SimpMessageType.MESSAGE);
if (getHeaderInitializer() != null) {
getHeaderInitializer().initHeaders(headerAccessor);
}
headerAccessor.setSessionId(sessionId);
if (sessionId != null) {
headerAccessor.setSessionId(sessionId);
}
headerAccessor.setHeader(AbstractMessageConverter.METHOD_PARAMETER_HINT_HEADER, returnType);
headerAccessor.setLeaveMutable(true);
return headerAccessor.getMessageHeaders();
}

View File

@@ -22,6 +22,7 @@ import org.apache.commons.logging.LogFactory;
import org.springframework.core.MethodParameter;
import org.springframework.messaging.Message;
import org.springframework.messaging.MessageHeaders;
import org.springframework.messaging.converter.AbstractMessageConverter;
import org.springframework.messaging.core.MessageSendingOperations;
import org.springframework.messaging.handler.annotation.SendTo;
import org.springframework.messaging.handler.invocation.HandlerMethodReturnValueHandler;
@@ -44,6 +45,7 @@ import org.springframework.util.Assert;
* input message. The message is then sent directly back to the connected client.
*
* @author Rossen Stoyanchev
* @author Sebastien Deleuze
* @since 4.0
*/
public class SubscriptionMethodReturnValueHandler implements HandlerMethodReturnValueHandler {
@@ -109,16 +111,17 @@ public class SubscriptionMethodReturnValueHandler implements HandlerMethodReturn
logger.debug("Reply to @SubscribeMapping: " + returnValue);
}
this.messagingTemplate.convertAndSend(destination, returnValue, createHeaders(sessionId, subscriptionId));
this.messagingTemplate.convertAndSend(destination, returnValue, createHeaders(sessionId, subscriptionId, returnType));
}
private MessageHeaders createHeaders(String sessionId, String subscriptionId) {
private MessageHeaders createHeaders(String sessionId, String subscriptionId, MethodParameter returnType) {
SimpMessageHeaderAccessor headerAccessor = SimpMessageHeaderAccessor.create(SimpMessageType.MESSAGE);
if (getHeaderInitializer() != null) {
getHeaderInitializer().initHeaders(headerAccessor);
}
headerAccessor.setSessionId(sessionId);
headerAccessor.setSubscriptionId(subscriptionId);
headerAccessor.setHeader(AbstractMessageConverter.METHOD_PARAMETER_HINT_HEADER, returnType);
headerAccessor.setLeaveMutable(true);
return headerAccessor.getMessageHeaders();
}