This site contains the documentation that is relevant to older WSO2 product versions and offerings.
For the latest WSO2 documentation, visit https://wso2.com/documentation/.

Inter Gadget Communication with Pub & Sub

The WSO2 Gadget Server supports inter-gadget communication with the help of publisher and subscriber gadgets allowing gadgets to communicate with each other while running in the Portal. Let's investigate the messaging paradigm known as Publisher/Subscriber (pub/sub) and how the WSO2 Gadget Server uses it to facilitate communication between Gadgets originating from any source.

To follow this tutorial, you are advised to be familiar with the WSO2 Gadget Server and writing Google Gadgets using the Gadgets API. You can find relevant tutorials in the Gadget Server Knowledge Base.

Introduction

Google Gadgets are a nice way to develop user interfaces for distributed services. The fact that they can be hosted anywhere over a network, not necessarily in the very portal server they eventually run in makes them re-usable and allows users to quickly assemble sets of Gadgets as and when required to create a unified view. This is especially useful in enterprise scenarios where the IT department maintains a central Gadget Repository using the WSO2 Gadget Server and end-users use these Gadgets in a layout most productive for their tasks. They can also supplement these enterprise Gadgets with other useful ones found in public Gadget Containers such as iGoogle.

A common requirement encountered by Gadget developers, especially when trying to satisfy Business Intelligence needs of an enterprise, is the ability for a set of Gadgets to work as a team. For instance, when the user changes an input parameter in one Gadget, a set of others should respond to the change by displaying matching data or graphs. To implement such requirements, Gadgets should be able to communicate with each other.

This tutorial explains the pub/sub based communication mechanism exposed by the WSO2 Gadget Server to Gadget developers.

What is pub/sub?

Publisher/Subscriber (or pub/sub) is a messaging paradigm where a sender (or a Publisher) sends messages without specifically targeting one receiver. A receiver (or a Subscriber) declares interest in one or more messages published by a publisher or a set of publishers. Keeping track of publishers, subscribers and their subscriptions is done by a Message Broker. In addition to keeping track of publishers and their subscribers, the Message Broker will also take responsibility of routing messages, commonly by utilising a store-and-forward mechanism. In the context of the WSO2 Gadget Server these roles are as follows:

  • Publisher - Any Gadget importing the "pubsub" feature and publishing to a one or more named Channels
  • Subscriber - Any Gadget importing the "pubsub" feature and subscribing to one or more named Channels
  • Message Broker - The WSO2 Gadget Server

How Does it work?

Gadgets are self contained applications. Since they can originate from different sources in a network, they can't directly communicate with each other or with the Gadget Container (the Portal page of the WSO2 Gadget Server) using Javascript. The way Gadgets usually communicate with the Container is via Features (http://code.google.com/apis/gadgets/docs/reference.html#feature-libs) and their APIs. The Features required by a Gadget are declared in the gadget.xml within the ModulPrefs section. At runtime, the Gadget Server will attach the necessary libraries needed for a specified Feature to function.

In the WSO2 Gadget Server, the Message Broker mentioned above is written in Javascript and therefore works completely on the Browser. No calls to the Server are made. Message storage and routing happens much faster because of this. It should be noted that this pub/sub mechanism can be termed unmanaged pub/sub as far as the container is concerned. Because the container's role is to just store and forward messages to all subscribers irrelevant of their origin.

The way Gadgets communicate their interest to either publish or subscribe to a Channel is via the gadgets.pubsub.* API. In order to load the necessary API fragments for the Gadget to function you need to import the feature titled "pubsub". This can be declared in the ModulePrefs section of your Gadget.

Adding <Require feature="pubsub"/> will work for both Publisher and Subscriber scenarios.

A Publisher Gadget

Here's a simple Publisher Gadget. Once the "pubsub" feature is included in your Gadget, publishing can be done by the Javascript method call gadgets.pubsub.publish("random-number", message);

Here, "random-number" is the name of the Channel to publish what is contained in the variable "message".

<Module>
  <ModulePrefs title="Sample PubSub Publisher" height="250">
   <Require feature="pubsub"/>
  </ModulePrefs>
  <Content type="html">
    <![CDATA[
      <script>
        function publish() {
           var message = Math.random();
           gadgets.pubsub.publish("random-number", message);
           document.getElementById("output").innerHTML = message;
         }
      </script>
      <div>
        <input type="button" value="Publish a random number" onclick="publish()"/>
      </div>
      <div id="output"> </div>
    ]]>
  </Content>
</Module>
A Subscriber Gadget

A Subscriber Gadget should declare "pubsub" as a required feature just as before. Once done, calling the method gadgets.pubsub.subscribe("random-number", callback);, will activate the subscription to the "random-number" channel. One additional thing to do in a Subscriber Gadget is to pass a Javascript callback function to be invoked when a message to the channel is received by the Message Broker. This callback function will be in the form as below:

function callback(sender, message) {
  document.getElementById("output").innerHTML =
    "message : " + gadgets.util.escapeString(message + "") + "<br/>" +
    "sender : " + gadgets.util.escapeString(sender);
}

A sample Subscriber Gadget is given below.

<Module>
<ModulePrefs title="Sample PubSub Subscriber" height="250">
  <Require feature="pubsub"/>
</ModulePrefs>
<Content type="html">
  <![CDATA[
     <script>
       function callback(sender, message) {
          document.getElementById("output").innerHTML =
          "message : " + gadgets.util.escapeString(message + "") + "<br/>" +
          "sender : " + gadgets.util.escapeString(sender);
       }

       function subscribe() {
          gadgets.pubsub.subscribe("random-number", callback);
       }

       function unsubscribe() {
         gadgets.pubsub.unsubscribe("random-number");
         document.getElementById("output").innerHTML = "";
       }

     </script>
     <div>
       <input type="button" value="Subscribe" onclick="subscribe()"/>
       <input type="button" value="Unsubscribe" onclick="unsubscribe()"/>
     </div>
     <div id="output"> </div>
  ]]>
</Content>
</Module>

As you can see in the above code, a Subscriber can unsubscribe using the method call gadgets.pubsub.unsubscribe("random-number");

A real world scenario with code

Let's have a look at a simple, real world use case for this technology. We ship two search Gadgets with the WSO2 Gadget Server downloadable distribution, which search e-bay and Amazon for a given query respectively. What if we can provide a third Gadget that takes the query from a user and publishes that query so that both e-bay and Amazon gadgets can simultaneously do searches? Instead of entering the search query twice, the user will now enter it in a single "data gatherer" Gadget.

The same pattern can be used in Gadgets displaying graphs and charts, where one Gadget acts as the input gatherer for others displaying data. So when a user changes data in the input gathering Gadget, the whole view changes to match the new inputs.

Our Query Publisher Gadget

The code for the Publisher is given below. It's a slight modification to the sample Publisher Gadget we've looked at earlier. This Gadget takes the user input via a text box and publishes the value to the channel named "search-query".

<?xml version="1.0" encoding="UTF-8"?>
<Module>
<ModulePrefs title="Query Publisher">
<Require feature="pubsub"/>
</ModulePrefs>
   <Content type="html">
      <![CDATA[
          <script>
             function publish() {
                 var message = document.getElementById("query").value;
                 gadgets.pubsub.publish("search-query", message);
                 document.getElementById("output").innerHTML = "Current active query is '" + message + "'.";
             }
          </script>

          <div>
              <input id="query" type="text" value=""/>
              <input type="button" value="Search" onclick="publish()"/>
          </div>

         <div id="output"></div>
      ]]>
  </Content>
</Module>
Our Subscriber Gadgets

These Gadgets are the one already shipped with WSO2 Gadget Server, slightly modified to handle channel messages as well. The first change I did was to add a callback function to be invoked when a message is received as listed below.

// Callback to handle messages received from channel.
function callback(sender, message) {
  document.getElementById("query").value = gadgets.util.escapeString(message + "")
  search(document.queryForm);
}

The above callback function uses the message string to populate the text box in the search Form found in e-bay and Amazon Gadgets and then calls the "search(form);" method, which does its usual task. 

The next change is to add a "subscribe" function and register it to be executed at Gadget load. This will ensure that the Gadget is subscribed automatically to the "search-query" channel when it's loaded in the portal.

// Function to subscribe to the channel. Registered to be executed at Gadget load.
function subscribe() {
  gadgets.pubsub.subscribe("search-query", callback);
}

Registering as an on-load handler to our "subscrib" can be done as follows.

// Registering subscription to be handled when the Gadget is loaded.
gadgets.util.registerOnLoadHandler(subscribe);

Adding and Testing the Gadgets

Now we can add these three Gadgets to the portal. As usual you can either store them in the Registry of WSO2 Gadget Server or in any HTTP server in your network. Once you have added these to your portal page, type a query to the Publisher Gadget and you will see both e-bay and Amazon Gadgets pick your input up and display their respective results as shown below.

Best Practices and Security

Several best practices need to be noted. First, since Channel names are not restricted by the container and are not tracked, it's better to stick follow a naming convention. For instance, the above "search-query" Channel name can be renamed to "org.wso2.gadgets.channels.searchquery". This will prevent Gadgets from accidentally publishing or subscribing Channels, which brings us to the second best practice considering security.

Since these Channels are public and easily discoverable by looking at the code inside a gadget.xml, sensitive data should not be published using the pub/sub mechanism. Sharing such information should happen in the back end with proper authentication and authorisation procedures in place. It's also possible to have a back end service generate a random channel name for each session and allow the gadgets to retrieve this name for that session. Although this may seem like an extreme scenario, if channel privacy is of utmost importance, this can be considered as a workaround.

Summary

In this tutorial we had a brief look at the inner workings of a typical publisher/subscriber mechanism and how it's used in the WSO2 Gadget Server to facilitate inter Gadget communication. We had a look at how the gadget.pubsub.* API is used to establish a channel in order to pass messages. The functionality is used to implement a simple, practical scenario where a set of search Gadgets provid a user a single point of entry for a search query. Best practices and security are discussed to ensure safe usage of this feature in development.