Add @ReplyTo/@ReplyToUser, remove deps on spring-web

This commit is contained in:
Rossen Stoyanchev
2013-07-16 22:07:46 -04:00
parent 55dae74f15
commit 078cfb3e78
30 changed files with 1385 additions and 307 deletions

View File

@@ -0,0 +1,144 @@
/*
* Copyright 2002-2013 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 java.io.FileNotFoundException;
import java.io.IOException;
import java.net.BindException;
import java.net.SocketException;
import org.junit.Test;
import org.springframework.messaging.handler.annotation.MessageExceptionHandler;
import org.springframework.stereotype.Controller;
import org.springframework.util.ClassUtils;
import static org.junit.Assert.*;
/**
* Test fixture for {@link ExceptionHandlerMethodResolver} tests.
*
* @author Rossen Stoyanchev
*/
public class ExceptionHandlerMethodResolverTests {
@Test
public void resolveMethodFromAnnotation() {
ExceptionHandlerMethodResolver resolver = new ExceptionHandlerMethodResolver(ExceptionController.class);
IOException exception = new IOException();
assertEquals("handleIOException", resolver.resolveMethod(exception).getName());
}
@Test
public void resolveMethodFromArgument() {
ExceptionHandlerMethodResolver resolver = new ExceptionHandlerMethodResolver(ExceptionController.class);
IllegalArgumentException exception = new IllegalArgumentException();
assertEquals("handleIllegalArgumentException", resolver.resolveMethod(exception).getName());
}
@Test
public void resolveMethodExceptionSubType() {
ExceptionHandlerMethodResolver resolver = new ExceptionHandlerMethodResolver(ExceptionController.class);
IOException ioException = new FileNotFoundException();
assertEquals("handleIOException", resolver.resolveMethod(ioException).getName());
SocketException bindException = new BindException();
assertEquals("handleSocketException", resolver.resolveMethod(bindException).getName());
}
@Test
public void resolveMethodBestMatch() {
ExceptionHandlerMethodResolver resolver = new ExceptionHandlerMethodResolver(ExceptionController.class);
SocketException exception = new SocketException();
assertEquals("handleSocketException", resolver.resolveMethod(exception).getName());
}
@Test
public void resolveMethodNoMatch() {
ExceptionHandlerMethodResolver resolver = new ExceptionHandlerMethodResolver(ExceptionController.class);
Exception exception = new Exception();
assertNull("1st lookup", resolver.resolveMethod(exception));
assertNull("2nd lookup from cache", resolver.resolveMethod(exception));
}
@Test
public void resolveMethodInherited() {
ExceptionHandlerMethodResolver resolver = new ExceptionHandlerMethodResolver(InheritedController.class);
IOException exception = new IOException();
assertEquals("handleIOException", resolver.resolveMethod(exception).getName());
}
@Test(expected = IllegalStateException.class)
public void ambiguousExceptionMapping() {
new ExceptionHandlerMethodResolver(AmbiguousController.class);
}
@Test(expected = IllegalArgumentException.class)
public void noExceptionMapping() {
new ExceptionHandlerMethodResolver(NoExceptionController.class);
}
@Controller
static class ExceptionController {
public void handle() {}
@MessageExceptionHandler(IOException.class)
public void handleIOException() {
}
@MessageExceptionHandler(SocketException.class)
public void handleSocketException() {
}
@MessageExceptionHandler
public void handleIllegalArgumentException(IllegalArgumentException exception) {
}
}
@Controller
static class InheritedController extends ExceptionController {
@Override
public void handleIOException() {
}
}
@Controller
static class AmbiguousController {
public void handle() {}
@MessageExceptionHandler({BindException.class, IllegalArgumentException.class})
public String handle1(Exception ex) throws IOException {
return ClassUtils.getShortName(ex.getClass());
}
@MessageExceptionHandler
public String handle2(IllegalArgumentException ex) {
return ClassUtils.getShortName(ex.getClass());
}
}
@Controller
static class NoExceptionController {
@MessageExceptionHandler
public void handle() {
}
}
}

View File

@@ -0,0 +1,196 @@
/*
* Copyright 2002-2013 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.simp.annotation.support;
import java.lang.reflect.Method;
import java.security.Principal;
import javax.security.auth.Subject;
import org.junit.Before;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.springframework.core.MethodParameter;
import org.springframework.messaging.Message;
import org.springframework.messaging.MessageChannel;
import org.springframework.messaging.handler.annotation.MessageMapping;
import org.springframework.messaging.handler.annotation.ReplyTo;
import org.springframework.messaging.simp.SimpMessageHeaderAccessor;
import org.springframework.messaging.simp.SimpMessagingTemplate;
import org.springframework.messaging.simp.annotation.ReplyToUser;
import org.springframework.messaging.support.MessageBuilder;
import org.springframework.messaging.support.converter.MessageConverter;
import static org.junit.Assert.*;
import static org.mockito.Matchers.*;
import static org.mockito.Mockito.*;
/**
* Test fixture for {@link ReplyToMethodReturnValueHandlerTests}.
*
* @author Rossen Stoyanchev
*/
public class ReplyToMethodReturnValueHandlerTests {
private static final String payloadContent = "payload";
private ReplyToMethodReturnValueHandler handler;
@Mock private MessageChannel messageChannel;
@Captor ArgumentCaptor<Message<?>> messageCaptor;
@Mock private MessageConverter messageConverter;
private MethodParameter replyToReturnType;
private MethodParameter replyToUserReturnType;
private MethodParameter missingReplyToReturnType;
@SuppressWarnings("unchecked")
@Before
public void setup() throws Exception {
MockitoAnnotations.initMocks(this);
Message<String> message = MessageBuilder.withPayload(payloadContent).build();
when(this.messageConverter.toMessage(payloadContent)).thenReturn(message);
SimpMessagingTemplate messagingTemplate = new SimpMessagingTemplate(this.messageChannel);
messagingTemplate.setConverter(this.messageConverter);
this.handler = new ReplyToMethodReturnValueHandler(messagingTemplate);
Method method = this.getClass().getDeclaredMethod("handleAndReplyTo");
this.replyToReturnType = new MethodParameter(method, -1);
method = this.getClass().getDeclaredMethod("handleAndReplyToUser");
this.replyToUserReturnType = new MethodParameter(method, -1);
method = this.getClass().getDeclaredMethod("handleWithMissingReplyTo");
this.missingReplyToReturnType = new MethodParameter(method, -1);
}
@Test
public void supportsReturnType() throws Exception {
assertTrue(this.handler.supportsReturnType(this.replyToReturnType));
assertTrue(this.handler.supportsReturnType(this.replyToUserReturnType));
assertFalse(this.handler.supportsReturnType(this.missingReplyToReturnType));
}
@Test
public void replyToMethod() throws Exception {
when(this.messageChannel.send(any(Message.class))).thenReturn(true);
String sessionId = "sess1";
Message<?> inputMessage = createInputMessage(sessionId, "sub1", "/dest", null);
this.handler.handleReturnValue(payloadContent, this.replyToReturnType, inputMessage);
verify(this.messageChannel, times(2)).send(this.messageCaptor.capture());
Message<?> message = this.messageCaptor.getAllValues().get(0);
SimpMessageHeaderAccessor headers = SimpMessageHeaderAccessor.wrap(message);
assertEquals(sessionId, headers.getSessionId());
assertNull(headers.getSubscriptionId());
assertEquals("/dest1", headers.getDestination());
message = this.messageCaptor.getAllValues().get(1);
headers = SimpMessageHeaderAccessor.wrap(message);
assertEquals(sessionId, headers.getSessionId());
assertNull(headers.getSubscriptionId());
assertEquals("/dest2", headers.getDestination());
}
@Test
public void replyToUserMethod() throws Exception {
when(this.messageChannel.send(any(Message.class))).thenReturn(true);
String sessionId = "sess1";
TestUser user = new TestUser();
Message<?> inputMessage = createInputMessage(sessionId, "sub1", "/dest", user);
this.handler.handleReturnValue(payloadContent, this.replyToUserReturnType, inputMessage);
verify(this.messageChannel, times(2)).send(this.messageCaptor.capture());
Message<?> message = this.messageCaptor.getAllValues().get(0);
SimpMessageHeaderAccessor headers = SimpMessageHeaderAccessor.wrap(message);
assertEquals(sessionId, headers.getSessionId());
assertNull(headers.getSubscriptionId());
assertEquals("/user/" + user.getName() + "/dest1", headers.getDestination());
message = this.messageCaptor.getAllValues().get(1);
headers = SimpMessageHeaderAccessor.wrap(message);
assertEquals(sessionId, headers.getSessionId());
assertNull(headers.getSubscriptionId());
assertEquals("/user/" + user.getName() + "/dest2", headers.getDestination());
}
private Message<?> createInputMessage(String sessId, String subsId, String dest, Principal principal) {
SimpMessageHeaderAccessor headers = SimpMessageHeaderAccessor.create();
headers.setSessionId(sessId);
headers.setSubscriptionId(subsId);
headers.setDestination(dest);
headers.setUser(principal);
return MessageBuilder.withPayload(new byte[0]).copyHeaders(headers.toMap()).build();
}
private static class TestUser implements Principal {
public String getName() {
return "joe";
}
public boolean implies(Subject subject) {
return false;
}
}
@MessageMapping("/handle") // not needed for the tests but here for completeness
public String handleWithMissingReplyTo() {
return payloadContent;
}
@MessageMapping("/handle") // not needed for the tests but here for completeness
@ReplyTo({"/dest1", "/dest2"})
public String handleAndReplyTo() {
return payloadContent;
}
@MessageMapping("/handle") // not needed for the tests but here for completeness
@ReplyToUser({"/dest1", "/dest2"})
public String handleAndReplyToUser() {
return payloadContent;
}
}

View File

@@ -0,0 +1,150 @@
/*
* Copyright 2002-2013 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.simp.annotation.support;
import java.lang.reflect.Method;
import java.security.Principal;
import org.junit.Before;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.springframework.core.MethodParameter;
import org.springframework.messaging.Message;
import org.springframework.messaging.MessageChannel;
import org.springframework.messaging.handler.annotation.MessageMapping;
import org.springframework.messaging.handler.annotation.ReplyTo;
import org.springframework.messaging.simp.SimpMessageHeaderAccessor;
import org.springframework.messaging.simp.SimpMessagingTemplate;
import org.springframework.messaging.simp.annotation.SubscribeEvent;
import org.springframework.messaging.support.MessageBuilder;
import org.springframework.messaging.support.converter.MessageConverter;
import static org.junit.Assert.*;
import static org.mockito.Matchers.*;
import static org.mockito.Mockito.*;
/**
* Test fixture for {@link SubscriptionMethodReturnValueHandler}.
*
* @author Rossen Stoyanchev
*/
public class SubscriptionMethodReturnValueHandlerTests {
private static final String payloadContent = "payload";
private SubscriptionMethodReturnValueHandler handler;
@Mock private MessageChannel messageChannel;
@Captor ArgumentCaptor<Message<?>> messageCaptor;
@Mock private MessageConverter messageConverter;
private MethodParameter subscribeEventReturnType;
private MethodParameter subscribeEventReplyToReturnType;
private MethodParameter messageMappingReturnType;
@SuppressWarnings("unchecked")
@Before
public void setup() throws Exception {
MockitoAnnotations.initMocks(this);
Message<String> message = MessageBuilder.withPayload(payloadContent).build();
when(this.messageConverter.toMessage(payloadContent)).thenReturn(message);
SimpMessagingTemplate messagingTemplate = new SimpMessagingTemplate(this.messageChannel);
messagingTemplate.setConverter(this.messageConverter);
this.handler = new SubscriptionMethodReturnValueHandler(messagingTemplate);
Method method = this.getClass().getDeclaredMethod("getData");
this.subscribeEventReturnType = new MethodParameter(method, -1);
method = this.getClass().getDeclaredMethod("getDataAndReplyTo");
this.subscribeEventReplyToReturnType = new MethodParameter(method, -1);
method = this.getClass().getDeclaredMethod("handle");
this.messageMappingReturnType = new MethodParameter(method, -1);
}
@Test
public void supportsReturnType() throws Exception {
assertTrue(this.handler.supportsReturnType(this.subscribeEventReturnType));
assertFalse(this.handler.supportsReturnType(this.subscribeEventReplyToReturnType));
assertFalse(this.handler.supportsReturnType(this.messageMappingReturnType));
}
@Test
public void subscribeEventMethod() throws Exception {
when(this.messageChannel.send(any(Message.class))).thenReturn(true);
String sessionId = "sess1";
String subscriptionId = "subs1";
String destination = "/dest";
Message<?> inputMessage = createInputMessage(sessionId, subscriptionId, destination, null);
this.handler.handleReturnValue(payloadContent, this.subscribeEventReturnType, inputMessage);
verify(this.messageChannel).send(this.messageCaptor.capture());
assertNotNull(this.messageCaptor.getValue());
Message<?> message = this.messageCaptor.getValue();
SimpMessageHeaderAccessor headers = SimpMessageHeaderAccessor.wrap(message);
assertEquals("sessionId should always be copied", sessionId, headers.getSessionId());
assertEquals(subscriptionId, headers.getSubscriptionId());
assertEquals(destination, headers.getDestination());
}
private Message<?> createInputMessage(String sessId, String subsId, String dest, Principal principal) {
SimpMessageHeaderAccessor headers = SimpMessageHeaderAccessor.create();
headers.setSessionId(sessId);
headers.setSubscriptionId(subsId);
headers.setDestination(dest);
headers.setUser(principal);
return MessageBuilder.withPayload(new byte[0]).copyHeaders(headers.toMap()).build();
}
@SubscribeEvent("/data") // not needed for the tests but here for completeness
private String getData() {
return payloadContent;
}
@SubscribeEvent("/data") // not needed for the tests but here for completeness
@ReplyTo("/replyToDest")
private String getDataAndReplyTo() {
return payloadContent;
}
@MessageMapping("/handle") // not needed for the tests but here for completeness
public String handle() {
return payloadContent;
}
}

View File

@@ -36,7 +36,7 @@ import static org.mockito.BDDMockito.*;
import static org.mockito.Mockito.*;
/**
* Tests for {@link TaskExecutorSubscribableChannel}.
* Tests for {@link ExecutorSubscribableChannel}.
*
* @author Phillip Webb
*/
@@ -46,7 +46,7 @@ public class PublishSubscibeChannelTests {
public ExpectedException thrown = ExpectedException.none();
private TaskExecutorSubscribableChannel channel = new TaskExecutorSubscribableChannel();
private ExecutorSubscribableChannel channel = new ExecutorSubscribableChannel();
@Mock
private MessageHandler handler;
@@ -71,14 +71,6 @@ public class PublishSubscibeChannelTests {
this.channel.send(null);
}
@Test
public void payloadMustNotBeNull() throws Exception {
Message<?> message = mock(Message.class);
thrown.expect(IllegalArgumentException.class);
thrown.expectMessage("Message payload must not be null");
this.channel.send(message);
}
@Test
public void sendWithoutExecutor() {
this.channel.subscribe(this.handler);
@@ -89,7 +81,7 @@ public class PublishSubscibeChannelTests {
@Test
public void sendWithExecutor() throws Exception {
TaskExecutor executor = mock(TaskExecutor.class);
this.channel = new TaskExecutorSubscribableChannel(executor);
this.channel = new ExecutorSubscribableChannel(executor);
this.channel.subscribe(this.handler);
this.channel.send(this.message);
verify(executor).execute(this.runnableCaptor.capture());