Publish & Subscribe

GreenLightning allows behaviors to communicate with each other using PubSub (short for Publish and Subscribe). One behavior can subscribe to a topic and thus listen to any messages coming from other behaviors about the same topic.

GreenLightning also has the concept of Private Topics - topics between behaviors are discovered at start, and a direct private connection between those behaviors is established, preventing other behaviors from listening if they do not subscribe to the same topic.

PubSub Example

Below is a very simple PubSub example. The GreetingBehavior will send out a greeting message to the topic "Greet" and publish another greeting if it receives a "SayHello" message. The ExampleBehavior will listen to the greeting message and send out the initial "SayHello" message.

We demonstrate usage of the default PubSubListener (filter topics manually using if-else or switch-case) and PubSubMethodListener (direct method call, no branching logic needed).

In our main file, we will simply register the behaviors and add their corresponding subscriptions:

PubSub.java
public class PubSub implements GreenApp
{
    @Override
    public void declareConfiguration(Builder c) {
    }

    @Override
    public void declareBehavior(GreenRuntime runtime) {
       // Create a pubSub listener that will broadcast a message if prompted
       runtime.addPubSubListener(new GreetingBehavior(runtime, "Greet"))
                           // We want to know when someone is asking for a greeting.
                            .addSubscription("SayHello");
       
       // Create another behavior that will ask for a Hello on start up and 
       // listen to it.
       ExampleBehavior example = new ExampleBehavior(runtime, "SayHello");
       runtime.registerListener(example)
                           // We want to know when someone greets us, so subscribe
                           .addSubscription("Greet", example::listenToGreeting)
                           // This just demonstrates multiple subscriptions are simpler using
                           // PubSubMethodListener instead of PubSubListener
                           .addSubscription("AnExample", example::anotherMessage);
    }
}

In GreetingBehavior.java, we create a PubSubFixedTopicService to publish our message:

GreetingBehavior.java
public class GreetingBehavior implements PubSubListener {
   private final PubSubFixedTopicService channel;
   
   public GreetingBehavior(GreenRuntime runtime, CharSequence publishTopic) {
      // Create a channel based on topic passed in that we will publish to
      this.channel = runtime.newCommandChannel().newPubSubService(publishTopic.toString()); 
   }

   // We have receive someone asking for a greeting, so send it!
   @Override
   public boolean message(CharSequence topic, ChannelReader payload) {
      //Note if this behavior is subscribed to more than 1 topic we will need
      //to branch here based on the value of topic.    
      return channel.publishTopic(w -> w.append("Hello World!"));
   }
}

In ExampleBehavior.java, we create another PubSubFixedTopicService and use it to publish our given topic on start (we are asking for a Greeting). Then, we have the listenToGreeting method as defined in PubSub.java listening to any new greetings:

ExampleBehavior.java
public class ExampleBehavior implements PubSubMethodListener, StartupListener {
    private final PubSubFixedTopicService channel;
    
    public ExampleBehavior(GreenRuntime runtime, CharSequence publishTopic) {
       // Create a new command channel that we will use to ask for a greeting (SayHello topic).
       this.channel = runtime.newCommandChannel().newPubSubService(publishTopic.toString());
    }
    
    @Override
    public void startup() {
        // On startup, let's ask for a greeting!
        channel.publishTopic();
    }
    
    public boolean listenToGreeting(CharSequence topic, ChannelReader payload) {
        // We received a greeting, so listen to it
        System.out.println("Received a greeting: " + payload.readUTF());
        return true; // if you were to return false, you will receive the same message again next time
    }
    
     public boolean anotherMessage(CharSequence topic, ChannelReader payload) {
         // Do nothing here, it's just an example.
         return true;
     }
}

Private Topics

Private Topics (always enabled) allow direct communication between behaviors without having to dispatch through a central router, improving performance and security.

However, you may not want Private Topics for certain topics (see warning below). You can use the method definePublicTopics on Builder:

@Override
public void declareConfiguration(Builder config) {
   // For our example above, this would disable dirct communication between
   // ExampleBehavior and GreetingBehavior. The messages would first go through
   // a grand dispatch before arriving at targeted subscribers.
   config.definePublicTopics("Greet", "SayHello");
}

Note that subscribing to a private topic at runtime is not possible. You will have to explicitly declare this topic public if you know that it will be dynamically subscribed to at a later point.

Last updated