From 0e5e3112de9604b874b1832111e5a453b797eaeb Mon Sep 17 00:00:00 2001 From: Justin Rovang Date: Fri, 18 Jun 2021 00:48:45 -0500 Subject: [PATCH] Added SPEL evaluation against spring boot @Topic attributes (#556) * [WIP] Added SPEL evaluation against spring boot @Topic name and pubsubname attributes * [WIP] Updated SubscriberController to use SPEL in @Topic * 554 - SPEL for @Topic attributes: Updated documentation and examples * Updated documentation as discussed * Update README.md * Update SubscriberController.java Co-authored-by: Artur Souza --- .../io/dapr/examples/pubsub/http/README.md | 9 +++++++-- .../pubsub/http/SubscriberController.java | 2 +- .../dapr/springboot/DaprBeanPostProcessor.java | 18 +++++++++++++----- .../it/pubsub/http/SubscriberController.java | 2 +- 4 files changed, 22 insertions(+), 9 deletions(-) diff --git a/examples/src/main/java/io/dapr/examples/pubsub/http/README.md b/examples/src/main/java/io/dapr/examples/pubsub/http/README.md index 1e7fe7062..90c6e4885 100644 --- a/examples/src/main/java/io/dapr/examples/pubsub/http/README.md +++ b/examples/src/main/java/io/dapr/examples/pubsub/http/README.md @@ -51,13 +51,18 @@ public class Subscriber { ``` `DaprApplication.start()` Method will run an Spring Boot application that registers the `SubscriberController`, which exposes the message retrieval as a POST request. The Dapr's sidecar is the one that performs the actual call to the controller, based on the pubsub features. -This Spring Controller handles the message endpoint, Printing the message which is received as the POST body. The topic subscription in Dapr is handled automatically via the `@Topic` annotation. See the code snippet below: +This Spring Controller handles the message endpoint, printing the message which is received as the POST body. + +The subscription's topic in Dapr is handled automatically via the `@Topic` annotation - which also supports the same expressions in +[Spring's @Value annotations](https://docs.spring.io/spring-framework/docs/current/reference/html/core.html#beans-value-annotations). + +The code snippet below shows how to create a subscription using the `@Topic` annotation showcasing expression support. In this case, `myAppProperty` is a Java property that does not exist, so the expression resolves to the default value (`messagebus`). ```java @RestController public class SubscriberController { ///... - @Topic(name = "testingtopic", pubsubName = "messagebus") + @Topic(name = "testingtopic", pubsubName = "${myAppProperty:messagebus}") @PostMapping(path = "/testingtopic") public Mono handleMessage(@RequestBody(required = false) byte[] body, @RequestHeader Map headers) { diff --git a/examples/src/main/java/io/dapr/examples/pubsub/http/SubscriberController.java b/examples/src/main/java/io/dapr/examples/pubsub/http/SubscriberController.java index 5dba0a074..83de3cba0 100644 --- a/examples/src/main/java/io/dapr/examples/pubsub/http/SubscriberController.java +++ b/examples/src/main/java/io/dapr/examples/pubsub/http/SubscriberController.java @@ -26,7 +26,7 @@ public class SubscriberController { * @param cloudEvent The cloud event received. * @return A message containing the time. */ - @Topic(name = "testingtopic", pubsubName = "messagebus") + @Topic(name = "testingtopic", pubsubName = "${myAppProperty:messagebus}") @PostMapping(path = "/testingtopic") public Mono handleMessage(@RequestBody(required = false) CloudEvent cloudEvent) { return Mono.fromRunnable(() -> { diff --git a/sdk-springboot/src/main/java/io/dapr/springboot/DaprBeanPostProcessor.java b/sdk-springboot/src/main/java/io/dapr/springboot/DaprBeanPostProcessor.java index 22e2f4d55..9cb0c56c0 100644 --- a/sdk-springboot/src/main/java/io/dapr/springboot/DaprBeanPostProcessor.java +++ b/sdk-springboot/src/main/java/io/dapr/springboot/DaprBeanPostProcessor.java @@ -11,6 +11,8 @@ import io.dapr.Topic; import org.springframework.beans.BeansException; import org.springframework.beans.factory.config.BeanPostProcessor; +import org.springframework.beans.factory.config.ConfigurableBeanFactory; +import org.springframework.beans.factory.config.EmbeddedValueResolver; import org.springframework.stereotype.Component; import org.springframework.web.bind.annotation.PostMapping; @@ -26,6 +28,12 @@ public class DaprBeanPostProcessor implements BeanPostProcessor { private static final ObjectMapper MAPPER = new ObjectMapper(); + private final EmbeddedValueResolver embeddedValueResolver; + + DaprBeanPostProcessor(ConfigurableBeanFactory beanFactory) { + embeddedValueResolver = new EmbeddedValueResolver(beanFactory); + } + /** * {@inheritDoc} */ @@ -35,7 +43,7 @@ public Object postProcessBeforeInitialization(Object bean, String beanName) thro return null; } - subscribeToTopics(bean.getClass()); + subscribeToTopics(bean.getClass(), embeddedValueResolver); return bean; } @@ -52,12 +60,12 @@ public Object postProcessAfterInitialization(Object bean, String beanName) throw * Subscribe to topics based on {@link Topic} annotations on the given class and any of ancestor classes. * @param clazz Controller class where {@link Topic} is expected. */ - private static void subscribeToTopics(Class clazz) { + private static void subscribeToTopics(Class clazz, EmbeddedValueResolver embeddedValueResolver) { if (clazz == null) { return; } - subscribeToTopics(clazz.getSuperclass()); + subscribeToTopics(clazz.getSuperclass(), embeddedValueResolver); for (Method method : clazz.getDeclaredMethods()) { Topic topic = method.getAnnotation(Topic.class); if (topic == null) { @@ -71,8 +79,8 @@ private static void subscribeToTopics(Class clazz) { route = mapping.path()[0]; } - String topicName = topic.name(); - String pubSubName = topic.pubsubName(); + String topicName = embeddedValueResolver.resolveStringValue(topic.name()); + String pubSubName = embeddedValueResolver.resolveStringValue(topic.pubsubName()); if ((topicName != null) && (topicName.length() > 0) && pubSubName != null && pubSubName.length() > 0) { try { TypeReference> typeRef diff --git a/sdk-tests/src/test/java/io/dapr/it/pubsub/http/SubscriberController.java b/sdk-tests/src/test/java/io/dapr/it/pubsub/http/SubscriberController.java index 478c1dde3..eb387a8bf 100644 --- a/sdk-tests/src/test/java/io/dapr/it/pubsub/http/SubscriberController.java +++ b/sdk-tests/src/test/java/io/dapr/it/pubsub/http/SubscriberController.java @@ -77,7 +77,7 @@ public Mono handleBinaryMessage(@RequestBody(required = false) CloudEvent }); } - @Topic(name = "anothertopic", pubsubName = "messagebus") + @Topic(name = "#{'another'.concat('topic')}", pubsubName = "${pubsubName:messagebus}") @PostMapping(path = "/route3") public Mono handleMessageAnotherTopic(@RequestBody(required = false) CloudEvent envelope) { return Mono.fromRunnable(() -> {