apache > ws.apache
Apache Muse
 

Apache Muse - Writing Code for Custom Capabilities

The Capability API

All of the capability classes that are aggregated to create a resource implementation are based on the Muse interface org.apache.muse.core.Capability. This interface exposes two important features: the capability URI and lifecycle events. The former is used by the Muse engine to determine the role of each capability, while the latter provides you the opportunity to implement essential initialization and shutdown tasks.

The capability's URI can be retrieved using the Capability.getCapabilityURI() method. This value will be filled in by the Muse engine, so you do not have to worry about hardcoding it or making it match with the value in muse.xml. The lifecycle events are represented by the four states listed below:

  • Initialization - Capability.initialize() - This is when the capability can perform all self-contained startup tasks, things that do not involve the use of the other capabilities found in the resource.

  • Post-initialization - Capability.initializeCompleted() - This is when the capability can perform all remaining startup tasks; specifically, it can query and manipulate other capabilities with the knowledge that they are in a stable state.

  • Pre-shutdown - Capability.prepareShutdown() - This is when the capability can perform all shutdown tasks that involve querying and manipulating other capabilities. This is the proverbial last call - a chance for the capability to get what it needs from other components before they are shutdown and their stability is not guaranteed.

  • Shutdown - Capability.shutdown() - Armed with all of the data it needs from pre-shutdown, the capability can now perform all self-contained shutdown tasks, including persistence, configuration, or notifications.

You can implement or override these methods to change how your capability modules behave during startup and shutdown. All of the capabilities in our sample (both standard and custom) derive from the org.apache.muse.core.AbstractCapability class, which provides all of the basics that a capability class needs so that users can concentrate on writing the logic of their properties and operations. With regard to the lifecycle methods, most of the implementations in AbstractCapability are trivial or even no-ops. The API documentation for this class provides more detail on the common tasks that can be completed by using its public and protected methods.

Methods for Properties and Operations

The MyCapability class that was generated by WSDL2Java extends from AbstractCapability and adds the following methods:

  • String getServerName()
  • void setServerName(String serverName)
  • int getMessageInterval()
  • void setMessageInterval(int messageInterval)


These methods represent the resource properties defined by the custom capability: ServerName and MessageInterval. The actual code looks like this:

package org.apache.ws.muse.test.wsrf;

public interface MyCapability
{
    String PREFIX = "tns";

    String NAMESPACE_URI = "http://ws.apache.org/muse/test/wsrf";

    public int getMessageInterval();

    public void setMessageInterval(int param0);

    public String getServerName();

    public void setServerName(String param0);
}

Notice that in addition to the getters and setters, Wsdl2Java has created constants for the capability URI that was specified in the XML schema.

Implementation Code

WSDL2Java generates a skeleton implementation class for each of your custom capabilities. These classes are referenced in muse.xml so that the Muse engine can load and aggregate your capabilities into resource types. The generated code for our custom capability (MyCapability) looks like this:

package org.apache.ws.muse.test.wsrf;

import org.apache.muse.ws.resource.impl.AbstractWsResourceCapability;
import javax.xml.namespace.QName;

public class MyCapabilityImpl extends AbstractWsResourceCapability implements MyCapability
{
    private static final QName[] _PROPERTIES = new QName[] {
        new QName(NAMESPACE_URI, "MessageInterval", PREFIX),
        new QName(NAMESPACE_URI, "ServerName", PREFIX)
    };

    public QName[] getPropertyNames()
    {
        return _PROPERTIES;
    }

    private int _MessageInterval;

    private String _ServerName;

    public int getMessageInterval()
    {
        return _MessageInterval;
    }

    public void setMessageInterval(int param0)
    {
        _MessageInterval = param0;
    }

    public String getServerName()
    {
        return _ServerName;
    }

    public void setServerName(String param0)
    {
        _ServerName = param0;
    }
}

Notice that there are private fields for both properties. If you were implementing a capability for a real IT resource, it is likely that these property values would not need to be stored in fields but would be read and written using a resource-specific API. As an example, if we were creating a WSDM interface for a relational database product, we would use that product's management API to get the property values for a particular instance of the database and return them to callers. For this simple scenario, we will store our properties in memory so that we can see how the application works without getting bogged down in resource-specific implementation details.

Both of our properties have a cardinality of one, and the MessageInterval property is mutable by remote clients. Both have a setter method, but we can restrict write access to the ServerName property using metadata (WSRMD). We can also provide some initial values using metadata, but for the sake of simplicity, let's just hardcode some values into our implementation:

    private int _MessageInterval = 10;

    private String _ServerName = "test-muse.apache.org";

The above code sets the message interval to ten seconds and the server name to test-muse.apache.org.

Now that we've handled the state of our capabilities, let's add some logic to publish messages on the given interval. This will require us to access the WSN NotificationProducer capability used by the resource and ask it to publish messages on a given WSN topic. Because we want to publish these messages asynchronously, we'll need to spawn a thread. The best place to do this is in the capability's initializeCompleted() method - here we can access the WSN NotificationProducer capability with the assurance that it has been initialized and is ready to handle our requests.

Note
You can see the completed version of this class in the wsn-producer sample that ships with the Muse distribution. The additions to the code are included here for illustrative purposes.

The code below overrides the default implementation of initializeCompleted() to create a new WSN topic and start a thread that publishes messages on that topic. The thread reads the latest value of the MessageInterval property to determine how long it should wait between messages. If a remote client were to use the WSRP SetResourceProperties operation to change MessageInterval to, say, twenty, the code would only publish messages every twenty seconds.

private static final QName _TOPIC_NAME = new QName(NAMESPACE_URI, "MyTopic", PREFIX);

public void initializeCompleted()
    throws SoapFault
{
    super.initializeCompleted();

    //
    // access resource's WSN capability and create a new topic
    //
    final NotificationProducer wsn = (NotificationProducer)getResource().getCapability(WsnConstants.PRODUCER_URI);
    wsn.addTopic(_TOPIC_NAME);

    Thread producer = new Thread() {
        public void run()
        {
            //
            // for this example, reuse one payload for every notification
            //
            QName messageName = new QName(NAMESPACE_URI, "MyMessage", PREFIX);
            String message = "This is a message from " + getServerName();
            Element payload = XmlUtils.createElement(messageName, message);

            while (true)
            {
                try
                {
                    //
                    // read current value - property is mutable
                    //
                    int currentInterval = getMessageInterval();

                    getLog().info("Waiting " + currentInterval + " seconds before sending message...");
                    Thread.currentThread().sleep(currentInterval * 1000);

                    //
                    // use WSN capability to send message to any subscribers
                    //
                    getLog().info("Sending message to consumers...");
                    wsn.publish(_TOPIC_NAME, payload);
                }

                catch (Throwable error)
                {
                    error.printStackTrace();
                }
            }
        }
    };
    producer.start();
}

Our custom capability is able to use the WSN NotificationProducer capability without being tied to a specific WSN implementation; this code uses the publish() method from the WSN API to send messages to subscribers without knowing anything about how subscriptions are maintained or who is currently listening. Our capability is also able to read and write its own property values without using the generic, XML-based WSRP methods. Finally, we see how the getLog() method is used to write messages to the log file for debugging or status reports. You can find more how-tos and details on common capability tasks in the reference manual.


< Back      Next (Axis2 users) >      Next (OSGi users) >