diff --git a/spring-messaging/src/main/java/org/springframework/messaging/simp/annotation/support/SimpAnnotationMethodMessageHandler.java b/spring-messaging/src/main/java/org/springframework/messaging/simp/annotation/support/SimpAnnotationMethodMessageHandler.java index 58f9c9a9a2c..75d0dc0ec78 100644 --- a/spring-messaging/src/main/java/org/springframework/messaging/simp/annotation/support/SimpAnnotationMethodMessageHandler.java +++ b/spring-messaging/src/main/java/org/springframework/messaging/simp/annotation/support/SimpAnnotationMethodMessageHandler.java @@ -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. @@ -27,6 +27,7 @@ import java.util.Set; import org.springframework.beans.factory.config.ConfigurableBeanFactory; import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.context.EmbeddedValueResolverAware; import org.springframework.context.SmartLifecycle; import org.springframework.core.annotation.AnnotationUtils; import org.springframework.core.convert.ConversionService; @@ -69,6 +70,7 @@ import org.springframework.util.Assert; import org.springframework.util.ClassUtils; import org.springframework.util.CollectionUtils; import org.springframework.util.PathMatcher; +import org.springframework.util.StringValueResolver; import org.springframework.validation.Validator; /** @@ -82,7 +84,7 @@ import org.springframework.validation.Validator; * @since 4.0 */ public class SimpAnnotationMethodMessageHandler extends AbstractMethodMessageHandler - implements SmartLifecycle { + implements EmbeddedValueResolverAware, SmartLifecycle { private static final boolean completableFuturePresent = ClassUtils.isPresent("java.util.concurrent.CompletableFuture", SimpAnnotationMethodMessageHandler.class.getClassLoader()); @@ -104,6 +106,8 @@ public class SimpAnnotationMethodMessageHandler extends AbstractMethodMessageHan private Validator validator; + private StringValueResolver valueResolver; + private MessageHeaderInitializer headerInitializer; private final Object lifecycleMonitor = new Object(); @@ -234,6 +238,11 @@ public class SimpAnnotationMethodMessageHandler extends AbstractMethodMessageHan this.validator = validator; } + @Override + public void setEmbeddedValueResolver(StringValueResolver resolver) { + this.valueResolver = resolver; + } + /** * Configure a {@link MessageHeaderInitializer} to pass on to * {@link org.springframework.messaging.handler.invocation.HandlerMethodReturnValueHandler}s @@ -376,13 +385,30 @@ public class SimpAnnotationMethodMessageHandler extends AbstractMethodMessageHan } private SimpMessageMappingInfo createMessageMappingCondition(MessageMapping annotation) { + String[] destinations = resolveEmbeddedValuesInDestinations(annotation.value()); return new SimpMessageMappingInfo(SimpMessageTypeMessageCondition.MESSAGE, - new DestinationPatternsMessageCondition(annotation.value(), this.pathMatcher)); + new DestinationPatternsMessageCondition(destinations, this.pathMatcher)); } private SimpMessageMappingInfo createSubscribeCondition(SubscribeMapping annotation) { + String[] destinations = resolveEmbeddedValuesInDestinations(annotation.value()); return new SimpMessageMappingInfo(SimpMessageTypeMessageCondition.SUBSCRIBE, - new DestinationPatternsMessageCondition(annotation.value(), this.pathMatcher)); + new DestinationPatternsMessageCondition(destinations, this.pathMatcher)); + } + + /** + * Resolve placeholder values in the given array of destinations. + * @return a new array with updated destinations + */ + protected String[] resolveEmbeddedValuesInDestinations(String[] destinations) { + if (this.valueResolver == null) { + return destinations; + } + String[] result = new String[destinations.length]; + for (int i = 0; i < destinations.length; i++) { + result[i] = this.valueResolver.resolveStringValue(destinations[i]); + } + return result; } @Override diff --git a/spring-messaging/src/test/java/org/springframework/messaging/simp/annotation/support/SimpAnnotationMethodMessageHandlerTests.java b/spring-messaging/src/test/java/org/springframework/messaging/simp/annotation/support/SimpAnnotationMethodMessageHandlerTests.java index c2f54494877..ac5bca021ae 100644 --- a/spring-messaging/src/test/java/org/springframework/messaging/simp/annotation/support/SimpAnnotationMethodMessageHandlerTests.java +++ b/spring-messaging/src/test/java/org/springframework/messaging/simp/annotation/support/SimpAnnotationMethodMessageHandlerTests.java @@ -28,11 +28,9 @@ import java.util.concurrent.ConcurrentHashMap; import org.junit.Before; import org.junit.Test; import org.mockito.ArgumentCaptor; -import static org.mockito.BDDMockito.given; import org.mockito.Captor; -import static org.mockito.Matchers.any; -import static org.mockito.Matchers.anyObject; import org.mockito.Mock; +import org.mockito.MockitoAnnotations; import org.springframework.context.support.StaticApplicationContext; import org.springframework.messaging.Message; @@ -62,10 +60,16 @@ import org.springframework.validation.Errors; import org.springframework.validation.Validator; import org.springframework.validation.annotation.Validated; -import static org.hamcrest.Matchers.*; -import static org.junit.Assert.*; +import static org.hamcrest.Matchers.is; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertTrue; +import static org.mockito.BDDMockito.given; +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.anyObject; import static org.mockito.Mockito.verify; -import org.mockito.MockitoAnnotations; /** * Test fixture for @@ -107,8 +111,7 @@ public class SimpAnnotationMethodMessageHandlerTests { this.messageHandler.setValidator(new StringTestValidator(TEST_INVALID_VALUE)); this.messageHandler.afterPropertiesSet(); - testController = new TestController(); - this.messageHandler.registerHandler(this.testController); + this.testController = new TestController(); } @@ -117,6 +120,7 @@ public class SimpAnnotationMethodMessageHandlerTests { public void headerArgumentResolution() { Map headers = Collections.singletonMap("foo", "bar"); Message message = createMessage("/pre/headers", headers); + this.messageHandler.registerHandler(this.testController); this.messageHandler.handleMessage(message); assertEquals("headers", this.testController.method); @@ -128,6 +132,7 @@ public class SimpAnnotationMethodMessageHandlerTests { public void optionalHeaderArgumentResolutionWhenPresent() { Map headers = Collections.singletonMap("foo", "bar"); Message message = createMessage("/pre/optionalHeaders", headers); + this.messageHandler.registerHandler(this.testController); this.messageHandler.handleMessage(message); assertEquals("optionalHeaders", this.testController.method); @@ -138,6 +143,7 @@ public class SimpAnnotationMethodMessageHandlerTests { @Test public void optionalHeaderArgumentResolutionWhenNotPresent() { Message message = createMessage("/pre/optionalHeaders"); + this.messageHandler.registerHandler(this.testController); this.messageHandler.handleMessage(message); assertEquals("optionalHeaders", this.testController.method); @@ -148,6 +154,7 @@ public class SimpAnnotationMethodMessageHandlerTests { @Test public void messageMappingDestinationVariableResolution() { Message message = createMessage("/pre/message/bar/value"); + this.messageHandler.registerHandler(this.testController); this.messageHandler.handleMessage(message); assertEquals("messageMappingDestinationVariable", this.testController.method); @@ -158,6 +165,7 @@ public class SimpAnnotationMethodMessageHandlerTests { @Test public void subscribeEventDestinationVariableResolution() { Message message = createMessage("/pre/sub/bar/value"); + this.messageHandler.registerHandler(this.testController); this.messageHandler.handleMessage(message); assertEquals("subscribeEventDestinationVariable", this.testController.method); @@ -168,6 +176,7 @@ public class SimpAnnotationMethodMessageHandlerTests { @Test public void simpleBinding() { Message message = createMessage("/pre/binding/id/12"); + this.messageHandler.registerHandler(this.testController); this.messageHandler.handleMessage(message); assertEquals("simpleBinding", this.testController.method); @@ -178,13 +187,16 @@ public class SimpAnnotationMethodMessageHandlerTests { @Test public void validationError() { Message message = createMessage("/pre/validation/payload"); + this.messageHandler.registerHandler(this.testController); this.messageHandler.handleMessage(message); + assertEquals("handleValidationException", this.testController.method); } @Test public void exceptionWithHandlerMethodArg() { Message message = createMessage("/pre/illegalState"); + this.messageHandler.registerHandler(this.testController); this.messageHandler.handleMessage(message); assertEquals("handleExceptionWithHandlerMethodArg", this.testController.method); @@ -203,6 +215,7 @@ public class SimpAnnotationMethodMessageHandlerTests { headers.setSessionAttributes(sessionAttributes); headers.setDestination("/pre/scope"); Message message = MessageBuilder.withPayload(new byte[0]).setHeaders(headers).build(); + this.messageHandler.registerHandler(this.testController); this.messageHandler.handleMessage(message); assertEquals("scope", this.testController.method); @@ -217,6 +230,7 @@ public class SimpAnnotationMethodMessageHandlerTests { this.messageHandler.setDestinationPrefixes(Arrays.asList("/app1", "/app2/")); Message message = createMessage("/app1/pre.foo"); + this.messageHandler.registerHandler(this.testController); this.messageHandler.handleMessage(message); assertEquals("handleFoo", controller.method); @@ -234,7 +248,6 @@ public class SimpAnnotationMethodMessageHandlerTests { given(this.channel.send(any(Message.class))).willReturn(true); given(this.converter.toMessage(anyObject(), any(MessageHeaders.class))).willReturn(emptyMessage); - ListenableFutureController controller = new ListenableFutureController(); this.messageHandler.registerHandler(controller); this.messageHandler.setDestinationPrefixes(Arrays.asList("/app1", "/app2/")); @@ -304,6 +317,17 @@ public class SimpAnnotationMethodMessageHandlerTests { assertTrue(controller.exceptionCaught); } + @Test + public void placeholder() throws Exception { + Message message = createMessage("/pre/myValue"); + this.messageHandler.setEmbeddedValueResolver(value -> ("/${myProperty}".equals(value) ? "/myValue" : value)); + this.messageHandler.registerHandler(this.testController); + this.messageHandler.handleMessage(message); + + assertEquals("placeholder", this.testController.method); + } + + private Message createMessage(String destination) { return createMessage(destination, null); } @@ -409,6 +433,11 @@ public class SimpAnnotationMethodMessageHandlerTests { assertThat(simpAttributes.getAttribute("name"), is("value")); this.method = "scope"; } + + @MessageMapping("/${myProperty}") + public void placeholder() { + this.method = "placeholder"; + } } @Controller