Search code examples
javadockerjakarta-eewildflydapr

Setting Dapr @Topic fields at runtime


Stack: Java Jakarta EE10 Wildfly27 Docker Dapr

The issue is configuration of Topics, dynamically. I have several environments TEST/DEV/PROD and i want to use different topics for the different environments, but the same image. Ideally i would like to set the topic via Environment variables.

So why not do this declarative? Well as i run Dapr on Azure Container Service (ACA) and it does NOT(!) support declarative config(!)

So the way to do this in my subscriber is via the Annotation @Topic

Like this@Topic(name = "cache_update", pubsubName = "${myAppProperty:messagebus}")

In the sample above "messagebus" is the default value and "myAppProperty" is the name of the application Property.

Had i used Spring this would have worked but we are using Jakarta EE10 running on Wildfly 27

How on earth can i get this to work?

UPDATE:

Ok, thank You very much for this, however i am still a bit unclear on how to write the Sub part (That will expose a POST endpoint? SO my Endpoint would look something like this instead?

(i.e No @Topic annotation needed?)

@Path("/handleTopicRoute")
@RequestScoped 
public class DaprServiceImpl extends AbstractCmsService implements DaprService { 

   public Response receiveMessage(CloudEvent<CacheUpdate> event) { 
   return getResponse(daprSubscriptionController.updateCache(event.getData())); 
}

So Dapr finds my mapping endpoint to topic via the endpoint you showed me?


Solution

  • Dapr Java SDK only provides a native integration for Spring Boot; where Dapr-specific annotations such as Topic gets processed and their configuration synthesized into the respective action taking into consideration property resolution, which in your case, would be the topic subscription creation.

    To have the a subscription created dynamically toward the config-wired topic values, you can use the Dapr SDK programmatically to create subscription topics by exposing a route over the /dapr/subscribe endpoint:

    @Path("/dapr")
    public class DaprController {
    
       private static final DefaultObjectSerializer SERIALIZER = new DefaultObjectSerializer();
    
       @GET
       @Produces(MediaType.APPLICATION_JSON)
       @Path("/subscribe")
       public Response subscribeTopics() {
           String topic = "TOPIC_NAME"; // you can resolve this value out of environment variables, system properties or MicroProfile config is you are using Wildfly-extras
           String pubsub = "PUBSUB"; // resolve this values as well through any external meaning
           TopicConfig config = new TopicConfig(pubsub, topic, "/handleTopicRoute", Collections.emptyList());
           return Response.ok(SERIALIZER.serialize(Collections.singleton(config))).build();
       }
    }
    

    Here is the Topic configuration template (Inspired from Spring Boot integration DaprTopicConfiguration):

    public class TopicConfig {
      private final String pubsubName;
      private final String topic;
      private final String route;
      private final Map<String, String> metadata;
    
      public TopicConfig(String pubsubName, String topic, String route, Map<String, String> metadata) {
        this.pubsubName = pubsubName;
        this.topic = topic;
        this.route = route;
        this.metadata = Collections.unmodifiableMap(metadata);
      }
    
      public String getPubsubName() {
        return pubsubName;
      }
    
      public String getTopic() {
        return topic;
      }
    
      public String getRoute() {
        return route;
      }
    
      public Map<String, String> getMetadata() {
        return metadata;
      }
    }