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

@@ -17,14 +17,17 @@
package org.springframework.messaging.converter;
import java.io.IOException;
import java.lang.reflect.Method;
import java.nio.charset.Charset;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import com.fasterxml.jackson.annotation.JsonView;
import com.fasterxml.jackson.databind.DeserializationFeature;
import org.junit.Test;
import org.springframework.core.MethodParameter;
import org.springframework.messaging.Message;
import org.springframework.messaging.MessageHeaders;
import org.springframework.messaging.support.MessageBuilder;
@@ -174,6 +177,23 @@ public class MappingJackson2MessageConverterTests {
assertEquals(contentType, message.getHeaders().get(MessageHeaders.CONTENT_TYPE));
}
@Test
public void jsonView() throws Exception {
MappingJackson2MessageConverter converter = new MappingJackson2MessageConverter();
Map<String, Object> map = new HashMap<>();
Method method = this.getClass().getDeclaredMethod("handle");
MethodParameter returnType = new MethodParameter(method, -1);
map.put(AbstractMessageConverter.METHOD_PARAMETER_HINT_HEADER, returnType);
MessageHeaders headers = new MessageHeaders(map);
Message<?> message = converter.toMessage(handle(), headers);
String actual = new String((byte[]) message.getPayload(), UTF_8);
assertThat(actual, containsString("\"withView1\":\"with\""));
assertThat(actual, not(containsString("\"withView2\":\"with\"")));
assertThat(actual, not(containsString("\"withoutView\":\"without\"")));
}
public static class MyBean {
@@ -238,4 +258,51 @@ public class MappingJackson2MessageConverterTests {
}
}
public interface MyJacksonView1 {};
public interface MyJacksonView2 {};
public static class JacksonViewBean {
@JsonView(MyJacksonView1.class)
private String withView1;
@JsonView(MyJacksonView2.class)
private String withView2;
private String withoutView;
public String getWithView1() {
return withView1;
}
public void setWithView1(String withView1) {
this.withView1 = withView1;
}
public String getWithView2() {
return withView2;
}
public void setWithView2(String withView2) {
this.withView2 = withView2;
}
public String getWithoutView() {
return withoutView;
}
public void setWithoutView(String withoutView) {
this.withoutView = withoutView;
}
}
@JsonView(MyJacksonView1.class)
public JacksonViewBean handle() {
JacksonViewBean bean = new JacksonViewBean();
bean.setWithView1("with");
bean.setWithView2("with");
bean.setWithoutView("without");
return bean;
}
}

View File

@@ -16,6 +16,7 @@
package org.springframework.messaging.simp.annotation.support;
import com.fasterxml.jackson.annotation.JsonView;
import static org.junit.Assert.*;
import static org.mockito.BDDMockito.*;
import static org.springframework.messaging.handler.annotation.support.DestinationVariableMethodArgumentResolver.DESTINATION_TEMPLATE_VARIABLES_HEADER;
@@ -23,6 +24,7 @@ import static org.springframework.messaging.support.MessageHeaderAccessor.*;
import java.lang.reflect.Method;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.security.Principal;
import java.util.LinkedHashMap;
import java.util.Map;
@@ -41,6 +43,8 @@ import org.springframework.core.MethodParameter;
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.converter.MappingJackson2MessageConverter;
import org.springframework.messaging.converter.StringMessageConverter;
import org.springframework.messaging.handler.DestinationPatternsMessageCondition;
import org.springframework.messaging.handler.annotation.SendTo;
@@ -57,6 +61,7 @@ import org.springframework.util.MimeType;
* Test fixture for {@link SendToMethodReturnValueHandlerTests}.
*
* @author Rossen Stoyanchev
* @author Sebastien Deleuze
*/
public class SendToMethodReturnValueHandlerTests {
@@ -69,18 +74,21 @@ public class SendToMethodReturnValueHandlerTests {
private SendToMethodReturnValueHandler handlerAnnotationNotRequired;
private SendToMethodReturnValueHandler jsonHandler;
@Mock private MessageChannel messageChannel;
@Captor ArgumentCaptor<Message<?>> messageCaptor;
@Captor private ArgumentCaptor<Message<?>> messageCaptor;
private MethodParameter noAnnotationsReturnType;
private MethodParameter sendToReturnType;
private MethodParameter sendToDefaultDestReturnType;
private MethodParameter sendToWithPlaceholdersType;
private MethodParameter sendToWithPlaceholdersReturnType;
private MethodParameter sendToUserReturnType;
private MethodParameter sendToUserSingleSessionReturnType;
private MethodParameter sendToUserDefaultDestReturnType;
private MethodParameter sendToUserSingleSessionDefaultDestReturnType;
private MethodParameter jsonViewReturnType;
@Before
@@ -90,10 +98,13 @@ public class SendToMethodReturnValueHandlerTests {
SimpMessagingTemplate messagingTemplate = new SimpMessagingTemplate(this.messageChannel);
messagingTemplate.setMessageConverter(new StringMessageConverter());
this.handler = new SendToMethodReturnValueHandler(messagingTemplate, true);
this.handlerAnnotationNotRequired = new SendToMethodReturnValueHandler(messagingTemplate, false);
SimpMessagingTemplate jsonMessagingTemplate = new SimpMessagingTemplate(this.messageChannel);
jsonMessagingTemplate.setMessageConverter(new MappingJackson2MessageConverter());
this.jsonHandler = new SendToMethodReturnValueHandler(jsonMessagingTemplate, true);
Method method = this.getClass().getDeclaredMethod("handleNoAnnotations");
this.noAnnotationsReturnType = new MethodParameter(method, -1);
@@ -104,7 +115,7 @@ public class SendToMethodReturnValueHandlerTests {
this.sendToReturnType = new MethodParameter(method, -1);
method = this.getClass().getDeclaredMethod("handleAndSendToWithPlaceholders");
this.sendToWithPlaceholdersType = new MethodParameter(method, -1);
this.sendToWithPlaceholdersReturnType = new MethodParameter(method, -1);
method = this.getClass().getDeclaredMethod("handleAndSendToUser");
this.sendToUserReturnType = new MethodParameter(method, -1);
@@ -117,6 +128,9 @@ public class SendToMethodReturnValueHandlerTests {
method = this.getClass().getDeclaredMethod("handleAndSendToUserDefaultDestinationSingleSession");
this.sendToUserSingleSessionDefaultDestReturnType = new MethodParameter(method, -1);
method = this.getClass().getDeclaredMethod("handleAndSendToJsonView");
this.jsonViewReturnType = new MethodParameter(method, -1);
}
@@ -143,6 +157,7 @@ public class SendToMethodReturnValueHandlerTests {
assertEquals("/topic/dest", accessor.getDestination());
assertEquals(MIME_TYPE, accessor.getContentType());
assertNull("Subscription id should not be copied", accessor.getSubscriptionId());
assertEquals(this.noAnnotationsReturnType, accessor.getHeader(AbstractMessageConverter.METHOD_PARAMETER_HINT_HEADER));
}
@Test
@@ -161,12 +176,14 @@ public class SendToMethodReturnValueHandlerTests {
assertEquals("/dest1", accessor.getDestination());
assertEquals(MIME_TYPE, accessor.getContentType());
assertNull("Subscription id should not be copied", accessor.getSubscriptionId());
assertEquals(this.sendToReturnType, accessor.getHeader(AbstractMessageConverter.METHOD_PARAMETER_HINT_HEADER));
accessor = getCapturedAccessor(1);
assertEquals(sessionId, accessor.getSessionId());
assertEquals("/dest2", accessor.getDestination());
assertEquals(MIME_TYPE, accessor.getContentType());
assertNull("Subscription id should not be copied", accessor.getSubscriptionId());
assertEquals(this.sendToReturnType, accessor.getHeader(AbstractMessageConverter.METHOD_PARAMETER_HINT_HEADER));
}
@Test
@@ -185,6 +202,7 @@ public class SendToMethodReturnValueHandlerTests {
assertEquals("/topic/dest", accessor.getDestination());
assertEquals(MIME_TYPE, accessor.getContentType());
assertNull("Subscription id should not be copied", accessor.getSubscriptionId());
assertEquals(this.sendToDefaultDestReturnType, accessor.getHeader(AbstractMessageConverter.METHOD_PARAMETER_HINT_HEADER));
}
@Test
@@ -220,6 +238,7 @@ public class SendToMethodReturnValueHandlerTests {
assertTrue(accessor.isMutable());
assertEquals("sess1", accessor.getSessionId());
assertNull("Subscription id should not be copied", accessor.getSubscriptionId());
assertEquals(this.noAnnotationsReturnType, accessor.getHeader(AbstractMessageConverter.METHOD_PARAMETER_HINT_HEADER));
}
@Test
@@ -261,7 +280,7 @@ public class SendToMethodReturnValueHandlerTests {
accessor.setSubscriptionId("sub1");
accessor.setHeader(DESTINATION_TEMPLATE_VARIABLES_HEADER, vars);
Message<?> message = MessageBuilder.createMessage(PAYLOAD, accessor.getMessageHeaders());
this.handler.handleReturnValue(PAYLOAD, this.sendToWithPlaceholdersType, message);
this.handler.handleReturnValue(PAYLOAD, this.sendToWithPlaceholdersReturnType, message);
verify(this.messageChannel, times(1)).send(this.messageCaptor.capture());
@@ -287,12 +306,14 @@ public class SendToMethodReturnValueHandlerTests {
assertEquals(MIME_TYPE, accessor.getContentType());
assertEquals("/user/" + user.getName() + "/dest1", accessor.getDestination());
assertNull("Subscription id should not be copied", accessor.getSubscriptionId());
assertEquals(this.sendToUserSingleSessionReturnType, accessor.getHeader(AbstractMessageConverter.METHOD_PARAMETER_HINT_HEADER));
accessor = getCapturedAccessor(1);
assertEquals(sessionId, accessor.getSessionId());
assertEquals("/user/" + user.getName() + "/dest2", accessor.getDestination());
assertEquals(MIME_TYPE, accessor.getContentType());
assertNull("Subscription id should not be copied", accessor.getSubscriptionId());
assertEquals(this.sendToUserSingleSessionReturnType, accessor.getHeader(AbstractMessageConverter.METHOD_PARAMETER_HINT_HEADER));
}
@Test
@@ -364,6 +385,7 @@ public class SendToMethodReturnValueHandlerTests {
assertEquals("/user/" + user.getName() + "/queue/dest", accessor.getDestination());
assertEquals(MIME_TYPE, accessor.getContentType());
assertNull("Subscription id should not be copied", accessor.getSubscriptionId());
assertEquals(this.sendToUserSingleSessionDefaultDestReturnType, accessor.getHeader(AbstractMessageConverter.METHOD_PARAMETER_HINT_HEADER));
}
@Test
@@ -386,6 +408,22 @@ public class SendToMethodReturnValueHandlerTests {
assertEquals("sess1", accessor.getSessionId());
}
@Test
public void jsonView() throws Exception {
given(this.messageChannel.send(any(Message.class))).willReturn(true);
String sessionId = "sess1";
Message<?> inputMessage = createInputMessage(sessionId, "sub1", "/app", "/dest", null);
this.jsonHandler.handleReturnValue(handleAndSendToJsonView(), this.jsonViewReturnType, inputMessage);
verify(this.messageChannel).send(this.messageCaptor.capture());
Message<?> message = this.messageCaptor.getValue();
assertNotNull(message);
assertEquals("{\"withView1\":\"with\"}", new String((byte[])message.getPayload(), StandardCharsets.UTF_8));
}
private Message<?> createInputMessage(String sessId, String subsId, String destinationPrefix,
String destination, Principal principal) {
@@ -475,4 +513,54 @@ public class SendToMethodReturnValueHandlerTests {
return PAYLOAD;
}
@SuppressWarnings("unused")
@SendTo({"/dest"})
@JsonView(MyJacksonView1.class)
public JacksonViewBean handleAndSendToJsonView() {
JacksonViewBean payload = new JacksonViewBean();
payload.setWithView1("with");
payload.setWithView2("with");
payload.setWithoutView("without");
return payload;
}
private interface MyJacksonView1 {};
private interface MyJacksonView2 {};
private static class JacksonViewBean {
@JsonView(MyJacksonView1.class)
private String withView1;
@JsonView(MyJacksonView2.class)
private String withView2;
private String withoutView;
public String getWithView1() {
return withView1;
}
public void setWithView1(String withView1) {
this.withView1 = withView1;
}
public String getWithView2() {
return withView2;
}
public void setWithView2(String withView2) {
this.withView2 = withView2;
}
public String getWithoutView() {
return withoutView;
}
public void setWithoutView(String withoutView) {
this.withoutView = withoutView;
}
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2014 the original author or authors.
* Copyright 2002-2015 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,8 +18,10 @@ package org.springframework.messaging.simp.annotation.support;
import java.lang.reflect.Method;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.security.Principal;
import com.fasterxml.jackson.annotation.JsonView;
import org.junit.Before;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
@@ -32,6 +34,8 @@ import org.springframework.core.MethodParameter;
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.converter.MappingJackson2MessageConverter;
import org.springframework.messaging.converter.StringMessageConverter;
import org.springframework.messaging.core.MessageSendingOperations;
import org.springframework.messaging.handler.annotation.MessageMapping;
@@ -50,6 +54,7 @@ import static org.mockito.BDDMockito.*;
* Test fixture for {@link SubscriptionMethodReturnValueHandler}.
*
* @author Rossen Stoyanchev
* @author Sebastien Deleuze
*/
public class SubscriptionMethodReturnValueHandlerTests {
@@ -60,9 +65,11 @@ public class SubscriptionMethodReturnValueHandlerTests {
private SubscriptionMethodReturnValueHandler handler;
private SubscriptionMethodReturnValueHandler jsonHandler;
@Mock private MessageChannel messageChannel;
@Captor ArgumentCaptor<Message<?>> messageCaptor;
@Captor private ArgumentCaptor<Message<?>> messageCaptor;
private MethodParameter subscribeEventReturnType;
@@ -70,6 +77,8 @@ public class SubscriptionMethodReturnValueHandlerTests {
private MethodParameter messageMappingReturnType;
private MethodParameter subscribeEventJsonViewReturnType;
@Before
public void setup() throws Exception {
@@ -78,9 +87,12 @@ public class SubscriptionMethodReturnValueHandlerTests {
SimpMessagingTemplate messagingTemplate = new SimpMessagingTemplate(this.messageChannel);
messagingTemplate.setMessageConverter(new StringMessageConverter());
this.handler = new SubscriptionMethodReturnValueHandler(messagingTemplate);
SimpMessagingTemplate jsonMessagingTemplate = new SimpMessagingTemplate(this.messageChannel);
jsonMessagingTemplate.setMessageConverter(new MappingJackson2MessageConverter());
this.jsonHandler = new SubscriptionMethodReturnValueHandler(jsonMessagingTemplate);
Method method = this.getClass().getDeclaredMethod("getData");
this.subscribeEventReturnType = new MethodParameter(method, -1);
@@ -89,6 +101,9 @@ public class SubscriptionMethodReturnValueHandlerTests {
method = this.getClass().getDeclaredMethod("handle");
this.messageMappingReturnType = new MethodParameter(method, -1);
method = this.getClass().getDeclaredMethod("getJsonView");
this.subscribeEventJsonViewReturnType = new MethodParameter(method, -1);
}
@@ -123,6 +138,7 @@ public class SubscriptionMethodReturnValueHandlerTests {
assertEquals(subscriptionId, headerAccessor.getSubscriptionId());
assertEquals(destination, headerAccessor.getDestination());
assertEquals(MIME_TYPE, headerAccessor.getContentType());
assertEquals(this.subscribeEventReturnType, headerAccessor.getHeader(AbstractMessageConverter.METHOD_PARAMETER_HINT_HEADER));
}
@Test
@@ -149,6 +165,26 @@ public class SubscriptionMethodReturnValueHandlerTests {
assertTrue(headerAccessor.isMutable());
assertEquals(sessionId, headerAccessor.getSessionId());
assertEquals(subscriptionId, headerAccessor.getSubscriptionId());
assertEquals(this.subscribeEventReturnType, headerAccessor.getHeader(AbstractMessageConverter.METHOD_PARAMETER_HINT_HEADER));
}
@Test
public void testJsonView() throws Exception {
given(this.messageChannel.send(any(Message.class))).willReturn(true);
String sessionId = "sess1";
String subscriptionId = "subs1";
String destination = "/dest";
Message<?> inputMessage = createInputMessage(sessionId, subscriptionId, destination, null);
this.jsonHandler.handleReturnValue(getJsonView(), this.subscribeEventJsonViewReturnType, inputMessage);
verify(this.messageChannel).send(this.messageCaptor.capture());
Message<?> message = this.messageCaptor.getValue();
assertNotNull(message);
assertEquals("{\"withView1\":\"with\"}", new String((byte[])message.getPayload(), StandardCharsets.UTF_8));
}
@@ -177,4 +213,54 @@ public class SubscriptionMethodReturnValueHandlerTests {
public String handle() {
return PAYLOAD;
}
@SubscribeMapping("/jsonview") // not needed for the tests but here for completeness
@JsonView(MyJacksonView1.class)
public JacksonViewBean getJsonView() {
JacksonViewBean payload = new JacksonViewBean();
payload.setWithView1("with");
payload.setWithView2("with");
payload.setWithoutView("without");
return payload;
}
private interface MyJacksonView1 {};
private interface MyJacksonView2 {};
private static class JacksonViewBean {
@JsonView(MyJacksonView1.class)
private String withView1;
@JsonView(MyJacksonView2.class)
private String withView2;
private String withoutView;
public String getWithView1() {
return withView1;
}
public void setWithView1(String withView1) {
this.withView1 = withView1;
}
public String getWithView2() {
return withView2;
}
public void setWithView2(String withView2) {
this.withView2 = withView2;
}
public String getWithoutView() {
return withoutView;
}
public void setWithoutView(String withoutView) {
this.withoutView = withoutView;
}
}
}