Skip to main content

skip to main content

developerWorks  >  Autonomic computing | SOA and Web services | XML  >

Define, configure, and process topics for notification producers

Learn to use WS-Notification filters in Apache Muse

developerWorks
Document options

Document options requiring JavaScript are not displayed

Discuss


Rate this page

Help us improve this content


Level: Intermediate

Dan Jemiolo (danjemiolo@us.ibm.com), Advisory Software Engineer, IBM 

29 May 2007

WS-Notification (WSN) is an OASIS standard that describes, among other things, a system for categorizing the types of notifications that are emitted from a manageable resource. By grouping notifications into categories, the designer of a resource's Web service interface makes it much easier for clients to find the data they need while ignoring data that is irrelevant. The Apache Muse project contains an implementation of WS-Notification, including all of the topic data structures and processing logic described in the specification. This article reviews how to define and configure topics for your notification producers, as well as how to process topic-based notifications in your notification consumers.

In this article, I'll review how to define and configure topics for your notification producers, as well as how to process topic-based notifications in your notification consumers. Let's get started.

Using topics to advertise and publish notifications

Your first job as author of a resource interface that includes topics is to define what your topics are. Topics can be part of a hierarchy (tree) that allows clients to subscribe for different levels of information depending on how broad the consumer's powers are. Each topic has a name in the form of a qualified path -- it looks like a UNIX® directory path, but it is qualified with an XML prefix. An example would be myns:server/lifecycle/restart in which myns is the prefix that maps to the topic namespace and server/lifecycle/restart is the concrete topic path. In this example, myns:server/lifecycle would also be a valid topic name -- it represents all notifications for restart as well as any of its sibling topics.

After you have defined your topic hierarchy on pencil and paper (or whatever design tool you're using), it is then time to translate that hierarchy into configuration data that is consumable by the Muse framework.

The collection of topics that a resource publishes is called its topic set. The topic set is advertised to clients using WS-ResourceProperties (WSRP); more specifically, each topic is represented by an instance of the WSN wsnt:TopicExpression property and a client can retrieve all of the wsnt:TopicExpression values using the WSRP GetResourceProperty() operation. Any value in a resource's topic set can be used to create a topic filter and included in a WSN Subscribe request. If the client specifies a valid topic in the topic filter, the subscription created will only be for notifications published with the given topic name; if the client specifies an invalid topic, a fault occurs and no subscription is created. The sample resource in the wsn-producer project already has the wsnt:TopicExpression property defined, so adding your topic hierarchy is simply a matter of adding values to that property. There are two ways to do this:

  • Declaratively (using WS-ResourceMetadata)
  • Programmatically (using the Muse API)

I'll assume that you are familiar with building Web service endpoints with Apache Muse and that you understand the general ideas behind WS-Notification (WSN) and WS-ResourceFramework (WSRF). You should also read the companion to this article ("Optimal message processing with WS-Notification filters"), which reviews the different types of WSN subscription filters and the benefits of each (see Resources). I'm assuming that you already know why you want to use topics -- now you just need to know the details behind how to make them work in your Muse-based application.

In order to test the instructions and code presented in this article, you will need two test applications that leverage WS-Notification: a notification producer and a notification consumer. Fortunately, the Apache Muse project includes two sample projects that fill those roles exactly -- they are called wsn-producer and wsn-consumer. You should be able to build and run these samples successfully before trying to modify them with the code and artifacts presented here. By building off of these samples, you will not have to create your own WSDLs, cutting and pasting from existing WSDL files to get the required XML definitions for WSN compliance. I will refer to files in these sample projects when instructing you on how to leverage topics in Apache Muse.

Creating topics declaratively using WS-ResourceMetadata

The easiest way to define topics is to use WS-ResourceMetadata and specify your topic names in your resource metadata (RMD) files. If you are unfamiliar with RMD files and how to create them, please see Resources for links that can bring you up to speed. In this case, the resource defined in the wsn-producer project already has an RMD file with metadata for all of its properties, so all you have to do is add the wsnt:TopicExpression values for each of your topics.

In the wsn-producer RMD file, located at /wsdl/WsResource.rmd, find the <Property/> element for wsnt:TopicExpression. It should look like this:

<Property xmlns:wsnt="http://docs.oasis-open.org/wsn/b-2" 
          name="wsnt:TopicExpression" 
          modifiability="read-only" 
          mutability="constant"/>

To add topics to your resource when the application starts, you need to add your topic names as static values or initial values (using the <StaticValues/> or <InitialValues/> elements, respectively). The only difference between static values and initial values is that, after the resource has been created, initial values can be deleted and static values cannot. Because topics are usually constant across the lifetime of a resource, I'll use static values to declare the topic names. The required changes to the RMD files are shown in bold in Listing 1.


Listing 1. RMD files
<Property xmlns:wsnt="http://docs.oasis-open.org/wsn/b-2" 
          name="wsnt:TopicExpression" 
          modifiability="read-only" 
          mutability="constant">
  <StaticValues>
                    <wsnt:TopicExpression xmlns:myns="http://my/topic/namespace">
                      myns:MyFirstTopic
                    <wsnt:TopicExpression>
                    <wsnt:TopicExpression xmlns:myns="http://my/topic/namespace">
                      myns:MySecondTopic
                    <wsnt:TopicExpression>
                    <wsnt:TopicExpression xmlns:myns="http://my/topic/namespace">
                      myns:MySecondTopic/MyChildTopic
                    <wsnt:TopicExpression>
                    <!-- insert as many topics as you like -->
                  </StaticValues>
</Property>

There are a few things to note here. First, you need to replace the sample topic paths (myns:MyFirstTopic, and so on) with your actual topic paths. Second, you have to declare each member of the topic hierarchy separately; you cannot, for example, declare myns:MySecondTopic/MyChildTopic and assume that the WSN implementation will add both the parent and child topics. Each part of the hierarchy is a separate topic and a separate wsnt:TopicExpression value, so each needs a separate entry under <StaticValues/>. This may not be as convenient as you'd like, but the Muse framework requires it in order to load your topics correctly. Finally, the syntax and semantics are the same if you choose to use <InitialValues/> instead of <StaticValues/>.

To see your new topics in action, all you have to do is rebuild the sample project (with your modified RMD file) by running ant from the wsn-producer project directory. You can then deploy the wsn-producer.war file on your application server. Next, I'll discuss the methods for creating topics programmatically.

Creating topics programmatically using the Muse API

Defining topics declaratively using metadata is easy, but occasionally you will want to create topics in a more dynamic way. When you come across a scenario where you won't decide on the final list of topics you're advertising until run time, you can use the Muse API to access the WSN NotificationProducer capability and add topics programmatically.

To load topics dynamically from any of your custom capability classes, just access the NotificationProducer capability and use the addTopic() method to add your topic values:


Listing 2. Adding topic values
//
// in a real application, you would discover these dynamically
//
QName topicNames = new QName[]{
    new QName("http://my/topic/namespace", "MyFirstTopic", "myns"), 
    new QName("http://my/topic/namespace", "MySecondTopic", "myns")
};

//
// look up the NotificationProducer capability and add the topics
//
NotificationProducer wsn = getResource().getCapability(WsnConstants.PRODUCER_URI);

for (int n = 0; n < topicNames.length; ++n)
    wsn.addTopic(topicNames[n]);

That's it! Another convenient feature of the Muse WSN API is that the addTopic() method returns an org.apache.muse.ws.notification.topics.Topic object that has setter methods that you can use to further configure the topic. For example, if you know that each topic has a unique WSDM Event Format situation, you can associate message patterns with the topics that give clients further insight as to what types of notifications they'll receive if they subscribe to the topic. Listing 3 illustrates topic customization using the Topic API:


Listing 3. Topic customization using Topic API
//
// in a real application, you would discover these dynamically
//

QName topicNames = new QName[]{
    new QName("http://my/topic/namespace", "LifecycleTopic", "myns"), 
    new QName("http://my/topic/namespace", "ConfigurationChangeTopic", "myns")
};

QName situationTypes = new QName[]{
    WefConstants.START_SITUATION_QNAME, 
    WefConstants.CONFIGURE_SITUATION_QNAME
};

//
// look up the NotificationProducer capability and add the topics
//
NotificationProducer wsn = getResource().getCapability(WsnConstants.PRODUCER_URI);

for (int n = 0; n < topicNames.length; ++n)
{
    Topic topic = wsn.addTopic(topicNames[n]);
    
    //
    // create an XPath based on the situation type and associate 
    // it with the topic
    //
    String xpath = "/*/*/*/muws2:Situation/" + XmlUtils.toString(situationTypes[n]);
    topic.setMessagePattern(xpath);
}

Creating topics with the Muse API isn't harder than defining them with RMD files, but it will probably be easier to maintain the project if you use RMD files as much as possible; this way, whenever you want to change the topic set, you can just modify an XML file rather than having to change code, rebuild the project, and test to make sure nothing was broken by mistake. Finally, note that you don't have to limit yourself to one way or the other -- you can mix the use of RMD files and Muse API calls if some topics are known ahead of time and others are not.

If you've added the above code to the custom capability that is part of the wsn-producer sample (MyCapabilityImpl.java), you can rebuild and redeploy just as you did in the previous section -- by running ant and installing the wsn-producer.war file on your application server.

Using topics to subscribe to notifications

For services that act as notification subscribers (or orchestrators), you can use the NotificationProducerClient class to invoke the WSN Subscribe operation with the desired filters. The Filter API makes it easy to create a topic filter and add it to the Subscribe request. Listing 4 illustrates how to add a topic filter when invoking Subscribe:


Listing 4. Adding a topic filter when invoking Subscribe
import java.net.URI;

import javax.xml.namespace.QName;

import org.apache.muse.ws.addressing.EndpointReference;
import org.apache.muse.ws.notification.impl.TopicFilter;
import org.apache.muse.ws.notification.remote.NotificationProducerClient;

public static void main(String[] args)
{
    URI address = URI.create("http://localhost:8080/my-producer");
    EndpointReference producerEPR = new EndpointReference(address);
    
    URI address = URI.create("http://localhost:8080/my-consumer");
    EndpointReference consumerEPR = new EndpointReference(address);
    
    NotificationProducerClient producer = new NotificationProducerClient(producerEPR);
    
    QName topicName = new QName("http://my/topic/namespace", "MyTopicName", "myns");
    TopicFilter topicFilter = new TopicFilter(topicName);
    
    producer.subscribe(consumerEPR, topicFilter, null);
}

Similar code is found in the WsnTestClient.java file that is part of the wsn-consumer project. You can modify that test client to use topic filters as shown above and run the client by executing ant run -Dmain=org.apache.muse.test.wsn.WsnTestClient from the wsn-consumer project directory.

Using topics to process notifications

The last area where you need to concern yourself with topics is in the consumer applications that are processing your notifications. For Muse-based consumers, this means using the TopicListener API to both filter and react to incoming notifications. Muse also has a more generic NotificationMessageListener API for when you want to process messages that aren't associated with a topic, but that is beyond the scope of this article. In this section, I'll show you how to create a new TopicListener and register it with the WSN NotificationConsumer capability so that it can start processing messages.

The resource in the wsn-consumer project is set up to print all notifications to the console, but you can easily change it to print only those of a given topic. Open the ConsumerCapabilityImpl.java file and find the initialize() method -- this is where the current code registers a message listener with the NotificationConsumer capability. The listener has just two methods: accepts() and process(). The former returns a true or false value that indicates whether it is interested in the current message; the latter does whatever processing logic is necessary on those messages that are "accepted" (in this case, printing them to the console). You are going to change this code to register a different listener.

At the bottom of the same file, create a new class that implements the TopicListener interface. Like the previous listener, this one is going to print messages to the console, but unlike the previous listener, it will only print those that belong to a certain topic. Listing 5 includes the entire definition of the new listener class:


Listing 5. New listener class
class MyTopicListener implements TopicListener
{
    static final QName MY_TOPIC = new QName("http://my/topic/namespace", "MyFirstTopic");
    
    public QName getTopic()
    {
        return MY_TOPIC;
    }
    
    public boolean accepts(NotificationMessage message)
    {
        QName topic = message.getTopic();
        return topic != null && topic.equals(MY_TOPIC);
    }
    
    public void process(NotificationMessage message)
    {
        System.out.println("Received message:\n");
        System.out.println(message);
    }
}

After this class is in place, all you need to do is instantiate it and register it with the NotificationConsumer capability. The code that you need to add to initialize() is:

NotificationConsumer wsn = getResource().getCapability(WsnConstants.CONSUMER_URI);
wsn.addTopicListener(new MyTopicListener());

After initialization is complete, the NotificationConsumer will pass all incoming notifications through its registered listeners and for those whose accepts() method returns "true," it will invoke process(). In the accepts() implementation, check to make sure the incoming message has a topic (topic != null) and then match it against the name of the topic you care about (topic.equals(MY_TOPIC)). The process() method does not have to concern itself with ensuring the message is valid.

Rebuilding and deploying the project is done just like it was in the wsn-producer project -- run ant and add the resulting wsn-consumer.war file to your application server. The wsn-consumer test client is still the best way to test your new code.

Share this...

digg Digg this story
del.icio.us Post to del.icio.us
Slashdot Slashdot it!

In your own projects, you can create as many listener classes as you want for a topic -- as you add more, the NotificationConsumer implementation will optimize the message processing so that the topic name comparison is done only once per message. This is another example of the efficiency of topic filters discussed in the precursor to this article (see Resources). You can also mix in more generic message listeners (using NotificationMessageListener instead of TopicListener) if you want a listener to process both topic and non-topic-based messages. The NotificationConsumer's listener API is very flexible in the options it gives you for responding to incoming notifications.

In conclusion

This article has reviewed all aspects of WSN topics, including how they are implemented in Apache Muse and how you can configure your applications to take advantage of them. You now know how to define topics both declaratively and programmatically and you can publish notifications associated with those topics. From a client and consumer perspective, you know about those parts of the Muse framework that can help you subscribe to topics and split up your notification handling logic by topics.



Resources

Learn

Get products and technologies

Discuss


About the author

Dan Jemiolo is an Advisory Software Engineer on IBM's Autonomic Computing team in Research Triangle Park, NC. He led the design and development of Apache Muse 2.0 and continues to work on the project today. Dan also participates in the WS-RF TC as editor of the WS-ResourceMetadata specification and is involved in IBM's strategy for increasing adoption of Web services standards. He came to IBM just over two years ago after earning his Master of Science degree in Computer Science from Rensselaer Polytechnic Institute.




Rate this page


Please take a moment to complete this form to help us better serve you.



YesNoDon't know
 


 


12345
Not
useful
Extremely
useful
 


Back to top