Home
Interests
Photos
Favorites

Apache WSRF, Pubscribe, and Muse

By Ian Springer

September 26, 2005

(Apache WSRF, Pubscribe, and Muse :  Page 1 of 1 )



You may have heard about some new standards, or soon-to-be-standards, on the "WS-*" block—WS-ResourceFramework (WSRF), WS-Notification (WSN), and WS-DistributedManagement (WSDM). Although the alphabet soup of acronyms can be confusing, these specifications open up interesting potential applications: they define standard ways to use web services to expose stateful resources, publish and subscribe for notifications, and perform management functions.

In November 2004, HP contributed Java implementations of early drafts of WSRF, WSN, and WSDM to the Apache Software Foundation. At the same time, the Globus Alliance contributed the WSRF and WSN implementations that it had developed as the foundation for the next generation of the Globus Toolkit for building grid computers and applications. For both WSRF and WSN, HP and Globus agreed to combine the best of both of their implementations into one new and improved implementation. To host the donated code and the ongoing development efforts, three new subprojects were formed within the Apache incubator (a staging area for new projects to mature before becoming fully endorsed Apache projects). The projects were named Apollo, Hermes, and Muse. Apollo would implement the WSRF specifications, Hermes would extend Apollo to implement the WSN specifications, and Muse would extend both Apollo and Hermes to implement the WSDM MUWS specification.

WSRF, WSN, and WSDM

WSRF, WSN, and WSDM are sets of specifications being cultivated within the OASIS standards body and are driven by such industry leaders as IBM, HP, Oracle, and the Globus Alliance. OASIS is responsible for several popular web services standards, including UDDI, WS-Security, and WS-Reliability. WSDM 1.0 was ratified as an OASIS standard in March 2005. WSRF and WSN are on track to become standards sometime in late 2005; shortly thereafter, a new version of WSDM will be released that is based upon the standard versions of WSRF and WSN.

In addition to residing in OASIS, WSRF, WSN, and WSDM have much in common. All three consist of sets of interrelated specifications. WSRF consists of WS-Resource, WS-ResourceProperties, WS-ResourceLifetime, WS-ServiceGroup, and WS-BaseFaults. WSN consists of WS-BaseNotification, WS-BrokeredNotification, and WS-Topics. The last, WSDM, consists of Management Using Web Services (MUWS) and Management Of Web Services (MOWS). All three are built upon the three core Web services specifications that are produced by the World Wide Web Consortium (W3C)—SOAP, WSDL, and the newcomer, WS-Addressing (WSA). They also build upon each other; WSN builds upon WSRF as its foundation, and WSDM builds upon both WSRF and WSN.

Apache WSRF, Pubscribe, and Muse

Since the Apache projects were initiated, much work has been done. The code bases have greatly matured, a community of users and developers has begun to form, and 1.0 beta versions have been released. On June 3, 2005, all three projects officially exited incubation. Due to trademark concerns, Apollo was recently renamed to WSRF, and Hermes was renamed to Pubscribe. To avoid confusion with the WSRF specifications, I will usually refer to the WSRF project as "Apache WSRF" instead of simply "WSRF."

There are a number of core design goals that Apache WSRF, Pubscribe, and Muse share:

  1. No assumptions should be made about the nature of the entity, or entities, being exposed as a WS-Resource. For example, you should be able to tie a WS-Resource to anything from a POJO, to a JMX MBean, to a device that is accessed via some proprietary protocol. The WSRF/Pubscribe/Muse platform simply provides the hooks for connecting the WS-Resource's operations and properties to the actual entity being exposed as a WS-Resource.
  2. Writing and deploying a WSRF/WSN/WSDM service using the WSRF/Pubscribe/Muse platform should be as painless as possible. To this end, the platform provides implementations of all specification-defined operations and a code generator that, given a WSDL, generates all of the Java classes and configuration files necessary to expose (via the platform) all services found in the WSDL. For any WSRF/WSN/WSDM specification-defined operations found in the WSDL, code is generated to connect them with platform-provided implementations. For any other operations, empty methods are generated; the bodies of these methods must then be written by the service developer. In addition, the service developer must add code in the init method of the generated resource class to initialize the resource's properties, and topic set if it is a notification producer.
  3. Services built on WSRF, Pubscribe, and Muse should be deployable to any Java-based SOAP platform with minimal effort. Although Apache Axis is bundled with the WSRF, Pubscribe, and Muse distributions, it is not a requirement. In fact, porting to another SOAP platform should be simply a matter of extending two abstract classes in WSRF's resource invocation framework. I use the word "should" because the projects currently utilize the Apache Addressing project, which has a dependency on Axis. It is planned for the near future to refactor Apache Addressing to remove this dependency.

Apache WSRF

Apache WSRF can be divided into three parts: a resource invocation framework; APIs for WSRF resource properties, base faults, etc.; and implementations of the operations defined by the WS-ResourceProperties and WS-ResourceLifetime specifications. APIs and operation implementations are not currently provided for the WS-ServiceGroup specification.

The resource invocation framework revolves around several core interfaces. WsrfService represents a WSRF service (i.e. a service that represents the external interface for a set of WS-Resource instances of a particular type). Resource represents a WS-Resource as defined by the WS-Resource specification. ResourceHome provides a way to instantiate and lookup Resource instances (think EJB Home); there is one ResourceHome object per type of Resource. ResourceContext provides request context information to the WSRF service.

At runtime, the entry point to the framework is the ResourceHandler. The ResourceHandler is implemented as a JAX-RPC 1.1 handler to allow it to run inside of any JAX-RPC-based SOAP engine. Since it acts as a request dispatcher, it belongs either as the pivot point of the handler chain or as the last handler in the chain. For each incoming SOAP request, the ResourceHandler performs the following steps:

  1. It deserializes the contents of the request message body to an XMLBeans XmlObject and then validates this XmlObject according to its schema type as it was defined in the types section of the service's WSDL.
  2. It creates a ResourceContext and populates it with vital information associated with the request, such as the service name, the service URL, and the JAX-RPC SOAPMessageContext.
  3. Based on the name of the service to which the request was sent, it instantiates the appropriate type of service object, passing it the ResourceContext.
  4. Based on either the request body element or the wsa:Action header (this is configurable on a per-operation basis), maps the request to a particular method of the service object, invoking that method with the request XmlObject as a parameter;
  5. It serializes the XmlObject returned by the method and adds it to the body of the response message.

As described above, when the ResourceHandler creates a service instance, it passes it a ResourceContext object. From the context, methods in the service can obtain the specific resource instance that was targeted by the current request; this is accomplished by simply calling the getResource method on the context. Under the hood, the context uses the service name as a key to lookup the resource home object from JNDI. It then extracts a resource key Object from the header portion of the SOAP request and uses this key to lookup a resource instance from the home. If there is no resource registered in the home with the specified key, a ResourceUnknownException is thrown, which ultimately is propagated back to the client as a WSRF-defined ResourceUnknownFault.

Schema validation of incoming requests is a powerful feature. When validation fails, a fault is returned to the client that contains a detailed description of exactly what is wrong with the XML. Request validation is not only powerful, but it is also efficient, since it is performed via XMLBeans, which uses an in-memory binary schema store. Nevertheless, if the utmost performance is required, request validation can be disabled.

Wsdl2Java

Wsdl2Java is an Ant task that can be used to generate, from a WSDL file, all of the classes and configuration files required to expose, via Apache WSRF, the services defined in the WSDL. Wsdl2Java ties in all specification-defined operations (e.g. GetResourceProperty or Destroy) it recognizes in the WSDL to built-in implementations of these operations. This allows the service developer to focus on setting the values of any resource properties and implementing any custom operations. Wsdl2Java will add TODO comments in all places where the service developer needs to add code; each TODO comment will include a brief description of what needs to be done.

Pubscribe

Pubscribe builds on top of Apache WSRF and can also be divided into three parts: APIs for WSN topic spaces, subscriptions, etc.; implementations of the operations defined by the WS-BaseNotification specification; and a SubscriptionManager service that exposes WS-Resources that represent subscriptions. Again, APIs and operation implementations are not currently provided for the WS-BrokeredNotification specification.

A resource that acts as a notification producer (i.e. its WSDL implements the WSN NotificationProducer portType) has a TopicSet object associated with it. This topic set represents the set of topics to which the resource may publish notifications and to which consumers may subscribe. As the service developer of the producer resource, you are responsible for adding the topics you wish to support to the topic set using Pubscribe APIs. This would typically be done in the init method of the resource. Pubscribe provides built-in implementations of the Subscribe and GetCurrentMessage operations. When a Subscribe request is received, Pubscribe creates a new Subscription Resource which is exposed via the built-in SubscriptionManager service. The Subscription Resource provides operations which allow clients to cancel, pause, or resume the associated subscription. To publish a notification to a topic, you must call the publish method on the Topic object, passing it a DOM Node or XMLBeans XmlObject that represents the XML message to be published. Pubscribe will then take care of wrapping the message in a SOAP envelope and sending it to all consumers who are subscribed to the associated Topic.

Notification Consumer

A resource that acts as a notification consumer is one whose most-derived WSDL portType implements the NotificationConsumer portType. This portType includes only one operation—Notify. Producers invoke the Notify operation when they send notifications to the consumer resource. In addition to the actual notification message, the Notify operation also includes the topic expression to which the message was published and the EPR of the producer resource that generated the message. These additional components allow the consumer to correlate the message with a particular subscription.

Topic expressions are XPath-like expressions that refer to one or more topics. The Subscribe operation includes a topic expression that specifies which topic(s) the client would like to subscribe to. The WS-Topics specification defines three topic expression dialects of varying complexity—Simple, Concrete, and Full. Pubscribe includes full support for all three dialects, which means that any producer Resources you expose via Pubscribe can advertise to clients support for all three dialects.

Muse

Muse builds on top of both Apache WSRF and Pubscribe and can, once more, be divided into three parts: APIs for MUWS management events, relationships, etc.; implementation of the QueryRelationshipsByType operation defined by the MUWS specification; and an Advertiser service that exposes the MUWS-defined ManageableResourceCreation and ManageableResourceDestruction topics and publishes notifications to those topics whenever any Resource instances are added or removed to any deployed MUWS services.

The MUWS specification defines a capability as a set of related operations, properties, and topics that a management resource may implement. It then defines about a dozen standard capabilities. Most of the standard capabilities include properties and/or topics, but not operations. There is only one capability that defines any operations, and it only defines one. Hence, what Muse primarily provides is not implementations of operations, but rather implementations of the specification-defined topics that will automatically publish notifications when the corresponding situation occurs. It also provides helper utility methods that can be used to easily add the various MUWS-defined topics to a producer resource's topic set.

Building a WSRF/WSN/WSDM Service Using Muse

Now that you are familiar with Apache WSRF, Pubscribe, and Muse, let's walk through using them to build a service that exposes some resource properties, some notification topics, and a few of the standard management capabilities defined by WSDM-MUWS. We will write a service that can expose one or more WS-Resources that each represents a physical server. We'll start out by deciding which operations and properties that we want each server resource to expose. We'll then write a WSDL file that defines these operations and properties. We'll generate service and resource classes from our WSDL and then add code to the resource to initialize its properties and notification topics. In the end, with very little work, we will have created a standards-based stateful Web-service that exposes the ability to monitor and manage multiple servers. We'll be able to retrieve configuration and status properties and subscribe for notifications of updates or errors.

Since we will be leveraging features from WSRF, WSN, and WSDM, we will use Muse, which includes WSRF and Pubscribe, and hence provides support for all three specifications. Instructions on downloading and installing Muse are provided at http://ws.apache.org/muse/ within the documentation section. To use Muse, you will also need to download and install JDK 1.4.x, Apache Tomcat 5.0.x, and Apache Ant 1.6.x. Note: the Muse web application should also run in servlet containers other than Tomcat, but, for the purposes of this article, we will assume Tomcat is being used.

Writing the Service's WSDL

Once you've installed Muse and its prerequisites, the next step is to write a WSDL that defines our Server service. To make it easier to write a WSDM WSDL, Muse provides a template WSDL that can be used as a starting point; using the template will save you a good deal of time and is less error-prone than starting from scratch. The template WSDL is part of a template application directory located in the template subdirectory of the Muse install directory.

Begin by copying the entire template directory to a directory named Server. Then, within the Server directory, rename _TEMPLATE_.wsdl to Server.wsdl. Open up Server.wsdl in your favorite XML editor. Notice that many blocks of lines in the WSDL are commented out. Based on the operations and properties we want our Server resource to expose, we will uncomment a number of these commented-out blocks.

Look at the definition of the ResourceProperties element within the types section of the WSDL. This element defines the format of the resource property document for this resource. Notice that the only uncommented property reference within the property document definition is muws-p1-xs:ResourceId. This is because the MUWS specification requires all MUWS resources to provide this property. In addition, the WSRF GetResourceProperty operation is required, because the WS-ResourceProperties specification requires any resource with an associated resource properties document to provide this operation.

We will start by making our service implement the WSRF GetMultipleResourceProperties portType, which consists of a single operation: GetMultipleResourceProperties. It is generally a good idea to provide this operation, as it allows clients to retrieve the values of multiple resource properties using a single SOAP request, thereby improving performance. To implement GetMultipleResourceProperties, uncomment the GetMultipleResourceProperties operation within both the portType and binding sections.

Next we will make our service implement the WSN NotificationProducer portType. This will make our resource capable of advertising notification topics and allowing clients to subscribe to these topics. To implement this portType, uncomment the three NotificationProducer property references (wsnt:Topic, wsnt:FixedTopicSet, and wsnt:TopicExpressionDialects) within the types section of the WSDL. Next, uncomment the two NotificationProducer operations (Subscribe and GetCurrentMessage) within both the portType and binding sections.

Now we'll add support for a couple MUWS capabilities. Our service already supports the required Identity capability, since we provide the muws-p1-xs:ResourceId property. In addition, let's add support for the OperationalStatus capability. This capability consists of a single property: muws-p2-xs:OperationalStatus. This property allows clients to ascertain from a WS-Resource instance whether the entity being managed via the resource is currently operationally available. In the case of our Server resource, OperationalStatus will be set to "Available" when the server being managed is up, and it will be set to "Unavailable" when it is down.

Finally let's add a couple custom properties and one custom operation. We'll define properties for the server's IP address and hostname and an operation that shuts down the server.

Our completed Server.wsdl should look as follows (extraneous comments have been removed):

<?xml version="1.0"?>

<definitions name="ServerDefinition"
             targetNamespace="http://abc.com/wsdl/Server.wsdl"
             xmlns="http://schemas.xmlsoap.org/wsdl/"
             xmlns:tns="http://abc.com/wsdl/Server.wsdl"
             xmlns:xsd="http://www.w3.org/2001/XMLSchema"
             xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
             xmlns:wsrp="http://docs.oasis-open.org/wsrf/2004/06/wsrf-WS-ResourceProperties-1.2-draft-01.xsd"
             xmlns:wsrpw="http://docs.oasis-open.org/wsrf/2004/06/wsrf-WS-ResourceProperties-1.2-draft-01.wsdl"
             xmlns:wsntw="http://docs.oasis-open.org/wsn/2004/06/wsn-WS-BaseNotification-1.2-draft-01.wsdl"
             xmlns:muws-p2-wsdl="http://docs.oasis-open.org/wsdm/2004/12/muws/wsdm-muws-part2.wsdl"
             xmlns:wsa04="http://schemas.xmlsoap.org/ws/2004/08/addressing">

   <import namespace="http://docs.oasis-open.org/wsrf/2004/06/wsrf-WS-ResourceProperties-1.2-draft-01.wsdl"
           location="../spec/wsrf/WS-ResourceProperties-1_2-Draft_01.wsdl"/>

   <import namespace="http://docs.oasis-open.org/wsn/2004/06/wsn-WS-BaseNotification-1.2-draft-01.wsdl" 
           location="../spec/wsn/WS-BaseNotification-1_2-Draft_01.wsdl"/>

   <import namespace="http://docs.oasis-open.org/wsdm/2004/12/muws/wsdm-muws-part2.wsdl" 
           location="../spec/wsdm/MUWS-Part2-1_0.wsdl"/>

   <types>
      <schema elementFormDefault="qualified"
              targetNamespace="http://abc.com/wsdl/Server.wsdl"
              xmlns="http://www.w3.org/2001/XMLSchema"
              xmlns:xsd="http://www.w3.org/2001/XMLSchema"
              xmlns:wsrl="http://docs.oasis-open.org/wsrf/2004/06/wsrf-WS-ResourceLifetime-1.2-draft-01.xsd"
              xmlns:wsbf="http://docs.oasis-open.org/wsrf/2004/06/wsrf-WS-BaseFaults-1.2-draft-01.xsd"
              xmlns:wsnt="http://docs.oasis-open.org/wsn/2004/06/wsn-WS-BaseNotification-1.2-draft-01.xsd"
              xmlns:muws-p1-xs="http://docs.oasis-open.org/wsdm/2004/12/muws/wsdm-muws-part1.xsd"
              xmlns:muws-p2-xs="http://docs.oasis-open.org/wsdm/2004/12/muws/wsdm-muws-part2.xsd">
 
         <xsd:import namespace="http://docs.oasis-open.org/wsn/2004/06/wsn-WS-BaseNotification-1.2-draft-01.xsd" 
                     schemaLocation="../spec/wsn/WS-BaseNotification-1_2-Draft_01.xsd"/>

         <xsd:import namespace="http://docs.oasis-open.org/wsdm/2004/12/muws/wsdm-muws-part1.xsd" 
                     schemaLocation="../spec/wsdm/MUWS-Part1-1_0.xsd"/>

         <xsd:import namespace="http://docs.oasis-open.org/wsdm/2004/12/muws/wsdm-muws-part2.xsd" 
                     schemaLocation="../spec/wsdm/MUWS-Part2-1_0.xsd"/>
         
         <element name="IPAddress" type="xsd:string" />
         <element name="HostName" type="xsd:string" />
         
         <!-- Resource Properties Document Schema -->        
         <element name="ServerResourceProperties">
            <complexType>
               <sequence>
               
                  <element ref="wsnt:Topic" maxOccurs="unbounded" /> 
                  <element ref="wsnt:FixedTopicSet" /> 
                  <element ref="wsnt:TopicExpressionDialects" maxOccurs="unbounded" />     
                  
		  <element ref="muws-p1-xs:ResourceId"/>		  
		  <element ref="muws-p2-xs:OperationalStatus"/>

                  <element ref="tns:IPAddress" />
                  <element ref="tns:HostName" />                                                       
                                    
               </sequence>
            </complexType>
         </element>

         <element name="Shutdown">
            <complexType />
         </element>

         <element name="ShutdownResponse">
            <complexType />
         </element>         
         
      </schema>
   </types>

   <message name="ShutdownRequest">
      <part name="ShutdownRequest" element="tns:Shutdown"/>
   </message>

   <message name="ShutdownResponse">
      <part name="ShutdownResponse" element="tns:ShutdownResponse"/>
   </message>   
   
   <portType name="ServerPortType" wsrp:ResourceProperties="tns:ServerResourceProperties">
         
      <operation name="GetResourceProperty">
         <input name="GetResourcePropertyRequest" message="wsrpw:GetResourcePropertyRequest"/>
         <output name="GetResourcePropertyResponse" message="wsrpw:GetResourcePropertyResponse"/>
         <fault name="ResourceUnknownFault" message="wsrpw:ResourceUnknownFault"/>
         <fault name="InvalidResourcePropertyQNameFault" message="wsrpw:InvalidResourcePropertyQNameFault"/>
      </operation>

      <operation name="GetMultipleResourceProperties">
         <input name="GetMultipleResourcePropertiesRequest" message="wsrpw:GetMultipleResourcePropertiesRequest"/>
         <output name="GetMultipleResourcePropertiesResponse" message="wsrpw:GetMultipleResourcePropertiesResponse"/>
         <fault name="ResourceUnknownFault" message="wsrpw:ResourceUnknownFault"/>
         <fault name="InvalidResourcePropertyQNameFault" message="wsrpw:InvalidResourcePropertyQNameFault"/>
      </operation>

      <operation name="Subscribe">
         <input message="wsntw:SubscribeRequest" /> 
         <output message="wsntw:SubscribeResponse" /> 
         <fault name="ResourceUnknownFault" message="wsntw:ResourceUnknownFault" /> 
         <fault name="SubscribeCreationFailedFault" message="wsntw:SubscribeCreationFailedFault" /> 
         <fault name="TopicPathDialectUnknownFault" message="wsntw:TopicPathDialectUnknownFault" /> 
      </operation>
      
      <operation name="GetCurrentMessage">
         <input message="wsntw:GetCurrentMessageRequest" /> 
         <output message="wsntw:GetCurrentMessageResponse" /> 
         <fault name="ResourceUnknownFault" message="wsntw:ResourceUnknownFault" /> 
         <fault name="InvalidTopicExpressionFault" message="wsntw:InvalidTopicExpressionFault" /> 
         <fault name="TopicNotSupportedFault" message="wsntw:TopicNotSupportedFault" /> 
         <fault name="NoCurrentMessageOnTopicFault" message="wsntw:NoCurrentMessageOnTopicFault" /> 
      </operation>       
      
      <operation name="Shutdown">
         <input  name="ShutdownRequest" message="tns:ShutdownRequest" />
         <output name="ShutdownResponse" message="tns:ShutdownResponse" />         
      </operation>          

   </portType>

   <binding name="ServerSoapHttpBinding" type="tns:ServerPortType">

      <soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"/>
   
      <operation name="GetResourceProperty">
         <soap:operation style="document"/>
         <input>
            <soap:body use="literal"/>
         </input>
         <output>
            <soap:body use="literal"/>
         </output>
         <fault name="ResourceUnknownFault">
            <soap:fault name="ResourceUnknownFault" use="literal"/>
         </fault>
         <fault name="InvalidResourcePropertyQNameFault">
            <soap:fault name="InvalidResourcePropertyQNameFault" use="literal"/>
         </fault>
      </operation>

      <operation name="GetMultipleResourceProperties">
         <soap:operation style="document"/>
         <input>
            <soap:body use="literal"/>
         </input>
         <output>
            <soap:body use="literal"/>
         </output>
         <fault name="ResourceUnknownFault">
            <soap:fault name="ResourceUnknownFault" use="literal"/>
         </fault>
         <fault name="InvalidResourcePropertyQNameFault">
            <soap:fault name="InvalidResourcePropertyQNameFault" use="literal"/>
         </fault>
      </operation>

      <operation name="Subscribe">
         <soap:operation style="document"/>
         <input>
            <soap:body use="literal"/>
         </input>
         <output>
            <soap:body use="literal"/>
         </output>
         <fault name="ResourceUnknownFault">
            <soap:fault name="ResourceUnknownFault" use="literal"/>
         </fault>
         <fault name="SubscribeCreationFailedFault">
            <soap:fault name="SubscribeCreationFailedFault" use="literal"/>
         </fault>
         <fault name="TopicPathDialectUnknownFault">
            <soap:fault name="TopicPathDialectUnknownFault" use="literal"/>
         </fault>         
      </operation>            

      <operation name="GetCurrentMessage">
         <soap:operation style="document"/>
         <input>
            <soap:body use="literal"/>
         </input>
         <output>
            <soap:body use="literal"/>
         </output>
         <fault name="ResourceUnknownFault">
            <soap:fault name="ResourceUnknownFault" use="literal"/>
         </fault>
         <fault name="InvalidTopicExpressionFault">
            <soap:fault name="InvalidTopicExpressionFault" use="literal"/>
         </fault>
         <fault name="TopicNotSupportedFault">
            <soap:fault name="TopicPathDialectUnknownFault" use="literal"/>
         </fault>         
         <fault name="NoCurrentMessageOnTopicFault">
            <soap:fault name="NoCurrentMessageOnTopicFault" use="literal"/>
         </fault>         
      </operation>            
      
      <operation name="Shutdown">
         <soap:operation style="document"/>
         <input>
            <soap:body use="literal"/>
         </input>
         <output>
            <soap:body use="literal"/>
         </output>         
      </operation>  
      
   </binding>

   <service name="ServerService">
      <port name="Server" binding="tns:ServerSoapHttpBinding">
         <soap:address location="http://localhost:8080/muse/services/Server" />
      </port>
   </service>

</definitions>

Generating and Updating Service Classes

After creating a WSDL for your service, the next step is to run the MuwsWsdl2Java tool on it to generate the set of classes that will represent the service. To do so, open a command prompt and change directories to the Server directory, then execute the command "ant generate." Source files for the service classes will be created in the generated/Server/src/java/com/abc/wsdl/serverWsdl subdirectory. A jar containing the XMLBeans classes representing the schema types defined in the WSDL is created in the generated/.xmlbeans/lib subdirectory; these classes will be used by the Muse runtime to serialize and validate XML in incoming requests, and to deserialize responses to outgoing XML.

Before compiling the service classes, we need to make updates to three of them—the resource class, the service class, and the home class.

In ServerResource.java, we need to add code to the init method to initialize our resource properties and notification topics. For each resource property, we must initialize its value by calling the appropriate setter on the XMLBean that represents the property. We must then add a single topic to the resource's topic set—the OperationalStatusCapability topic which is part of the OperationalStatus capability. Clients will be able to subscribe to this topic to be notified whenever the value of a resource's muws-p2-xs:OperationalStatus property changes. Finally, we'll invoke the initNotificationProducerProperties utility method provided by Muse, which will take care of the three properties from the NotificationProducer portType. Our updated init method should look something like the following:

public void init()
{
   super.init();               

   try {
   org.apache.ws.resource.properties.ResourcePropertySet resourcePropertySet = getResourcePropertySet();
   org.apache.ws.resource.properties.ResourceProperty resourceProperty = null;

   // init the {http://docs.oasis-open.org/wsdm/2004/12/muws/wsdm-muws-part1.xsd}ResourceId Resource Property 
   resourceProperty = resourcePropertySet.get(ServerPropertyQNames.RESOURCEID);
   org.oasisOpen.docs.wsdm.x2004.x12.muws.wsdmMuwsPart1.ResourceIdDocument    
   prop_resourceid = org.oasisOpen.docs.wsdm.x2004.x12.muws.wsdmMuwsPart1.ResourceIdDocument.Factory.newInstance();
   prop_resourceid.setResourceId("urn:" + getID());
   resourceProperty.add(prop_resourceid);

   // init the {http://docs.oasis-open.org/wsdm/2004/12/muws/wsdm-muws- part2.xsd}OperationalStatus Resource Property
   resourceProperty = resourcePropertySet.get(ServerPropertyQNames.OPERATIONALSTATUS);
   org.oasisOpen.docs.wsdm.x2004.x12.muws.wsdmMuwsPart2.OperationalStatusDocument  prop_operationalstatus =    
	org.oasisOpen.docs.wsdm.x2004.x12.muws.wsdmMuwsPart2.OperationalStatusDocument.Factory.newInstance();
   prop_operationalstatus.setOperationalStatus(org.oasisOpen.docs.wsdm.x2004.x12.muws.wsdmMuwsPart2.OperationalStatusDocument.OperationalStatus.AVAILABLE);
   resourceProperty.add(prop_operationalstatus);

   // init the {http://abc.com/wsdl/Server.wsdl}HostName Resource Property
   resourceProperty = resourcePropertySet.get(ServerPropertyQNames.HOSTNAME);
   com.abc.wsdl.serverWsdl.HostNameDocument prop_hostname =      com.abc.wsdl.serverWsdl.HostNameDocument.Factory.newInstance();
   prop_hostname.setHostName(java.net.InetAddress.getLocalHost().getHostName());	
   resourceProperty.add(prop_hostname);		
	
   // init the {http://abc.com/wsdl/Server.wsdl}IPAddress Resource Property
   resourceProperty = resourcePropertySet.get(ServerPropertyQNames.IPADDRESS);
   com.abc.wsdl.serverWsdl.IPAddressDocument prop_ipaddress = com.abc.wsdl.serverWsdl.IPAddressDocument.Factory.newInstance();
   prop_ipaddress.setIPAddress(java.net.InetAddress.getLocalHost().getHostAddress());		
   resourceProperty.add(prop_ipaddress);   

   // add the MUWS "OperationalStatusCapability" topic to our topic set
   org.apache.ws.notification.topics.TopicSpace muwsTopicSpace = 
	new org.apache.ws.notification.topics.impl.TopicSpaceImpl(org.apache.ws.muws.v1_0.MuwsConstants.NSURI_MUWS_PART2_TOPICS);
   org.apache.ws.muws.v1_0.topics.ManagementEventTopic operationalStatusCapability = 
	new org.apache.ws.muws.v1_0.topics.impl.XmlBeansManagementEventTopicImpl(org.apache.ws.muws.v1_0.capability.OperationalStatusCapability.TOPIC_NAME);
   muwsTopicSpace.addTopic(operationalStatusCapability);
   getTopicSpaceSet().addTopicSpace(muwsTopicSpace);
   org.apache.ws.notification.topics.util.TopicUtils.initNotificationProducerProperties(getTopicSpaceSet(), getResourcePropertySet());
   } catch ( Exception e ) {
      throw new RuntimeException( "Failed to initialize ServerResource instance.", e );
   }
}

In ServerService.java, add code in the Shutdown method that changes the value of the muws-p2-xs:OperationalStatus property to "Unavailable" and then publishes a WSRF ResourcePropertyValueChangeNotification, wrapped in a MUWS ManagementEvent, to the MUWS-defined OperationalStatusCapability topic to notify subscribers that the resource's status has changed. If this were a real application, we would also add code in Shutdown that would shut down the physical server being managed. Since, in this case, the server is your personal box, we're probably better off skipping this step! The modified Shutdown method should look like this:

public com.abc.wsdl.serverWsdl.ShutdownResponseDocument Shutdown( com.abc.wsdl.serverWsdl.ShutdownDocument requestDoc )
{
   org.apache.ws.notification.base.NotificationProducerResource resource;
   try {
      resource = (org.apache.ws.notification.base.NotificationProducerResource)getResourceContext().getResource();
   } catch (Exception e) {
      throw new RuntimeException("Unable to obtain resource instance from context.", e);
   }
   org.apache.ws.resource.properties.ResourceProperty statusProp = resource.getResourcePropertySet().get(ServerPropertyQNames.OPERATIONALSTATUS);
   org.oasisOpen.docs.wsdm.x2004.x12.muws.wsdmMuwsPart2.OperationalStatusDocument.OperationalStatus status = 
	(org.oasisOpen.docs.wsdm.x2004.x12.muws.wsdmMuwsPart2.OperationalStatusDocument.OperationalStatus)statusProp.get(0);
       if (!status.enumValue().equals(org.oasisOpen.docs.wsdm.x2004.x12.muws.wsdmMuwsPart2.OperationalStatusDocument.OperationalStatus.UNAVAILABLE))
   {     
      XmlObject oldStatus = org.apache.ws.util.XmlBeanUtils.copyXmlBean( status );
      status.set(org.oasisOpen.docs.wsdm.x2004.x12.muws.wsdmMuwsPart2.OperationalStatusDocument.OperationalStatus.UNAVAILABLE);
      org.apache.ws.notification.topics.TopicSpace muwsTopicSpace = resource.getTopicSpaceSet().getTopicSpace(org.apache.ws.muws.v1_0.MuwsConstants.NSURI_MUWS_PART2_TOPICS);
      org.apache.ws.muws.v1_0.topics.impl.XmlBeansManagementEventTopicImpl statusTopic = 
		(org.apache.ws.muws.v1_0.topics.impl.XmlBeansManagementEventTopicImpl)muwsTopicSpace.getTopic(org.apache.ws.muws.v1_0.capability.OperationalStatusCapability.TOPIC_NAME);
      org.apache.ws.resource.properties.ResourcePropertyValueChangeEvent statusChangeEvent = 
		new org.apache.ws.resource.properties.v2004_06.impl.XmlBeansResourcePropertyValueChangeEvent(new Object[]{oldStatus}, new Object[]{status});
      statusTopic.propertyChanged(statusChangeEvent);  // publish the event
   }
   com.abc.wsdl.serverWsdl.ShutdownResponseDocument responseDocument = com.abc.wsdl.serverWsdl.ShutdownResponseDocument.Factory.newInstance();
   com.abc.wsdl.serverWsdl.ShutdownResponseDocument.ShutdownResponse  response = responseDocument.addNewShutdownResponse();
   return responseDocument;
}

In ServerHome.java, two changes are required. Override the init method as follows to create a ServerResource instance with the identifier "MyComputer" and add it to the home:

public void init() throws Exception
{
   super.init();
   org.apache.ws.resource.ResourceKey key = new org.apache.ws.resource.impl.SimpleTypeResourceKey(new QName("http://abc.com/wsdl/Server.wsdl", "ResourceIdentifier"), "MyComputer");
   org.apache.ws.resource.Resource resource = createInstance(key);
   add(key, resource);
}

And replace the default implementation of the getInstance method with the following:

public Resource getInstance( ResourceContext resourceContext ) throws ResourceException, ResourceContextException, ResourceUnknownException
{
   ResourceKey key = resourceContext.getResourceKey();
   ServerResource resource = (ServerResource)find( key );
   if ( resource.getEndpointReference() == null )
   {
      EndpointReference epr = getEndpointReference(resourceContext.getBaseURL(  ) + "/" + getServiceName().getLocalPart(), key, SPEC_NAMESPACE_SET.getAddressingNamespace());
      resource.setEndpointReference(epr);
   }
   return resource;
}

Compiling, Deploying, and Testing the Service

Now that we've updated the generated classes, we're ready to compile them and deploy them to the Muse web application. To deploy the Muse web application to Tomcat, copy the webapps/muse subdirectory of your Muse installation to the webapps subdirectory of your Tomcat installation. The build.xml file that was generated for the Server service includes targets for compiling the service and deploying it to the Muse web application. Before executing these targets, edit the generated build.properties file and set the value of the muse.webapp.dir property to the webapps/muse subdirectory of your Tomcat installation (i.e. C:/opt/tomcat-5.0.31/webapps/muse). This property is what the deploy target uses to determine where the Server service should be deployed. Now we're ready to compile and deploy the service. To do so, open a command prompt and change directories to the generated/Server subdirectory of the directory containing Server.wsdl, then execute the command "ant compile deploy."

Now, start Tomcat. Once Tomcat has started, verify that our Server service has been deployed by opening the URL http://localhost:8080/muse/services in your Web browser and ensuring the Server service is included in the list of deployed services.

Finally, we will send some test requests to our service to verify that it is working as intended. We will target the WS-Resource instance with the identifier "MyComputer" by including this identifier in a SOAP header element in the request. To send the requests we will use the sendRequest Ant target that is in the build.xml that was generated for the Server service. This target expects a parameter in the form of a system property named xml whose value is the location of an XML file containing the SOAP envelope of the request to be sent. For example, to send the request contained in the file myrequest.soap, you would open a command prompt and change directories to the generated/Server subdirectory of the directory containing Server.wsdl, then execute the command "ant sendRequest –Dxml=myrequest.soap." The sendRequest target will output the response from the service.

First, we will invoke the GetResourceProperty operation to retrieve the muws-p2-xs:OperationalStatus property. To do so, save the following XML to a file named get_status.soap:

<Envelope xmlns="http://schemas.xmlsoap.org/soap/envelope/">
   <Header xmlns:wsa03="http://schemas.xmlsoap.org/ws/2003/03/addressing">
      <wsa03:To mustUnderstand="1">http://localhost:8080/wsrf/services/Server</wsa03:To>
      <wsa03:Action mustUnderstand="1">operation:GetResourceProperty</wsa03:Action>
      <server:ResourceIdentifier xmlns:server="http://abc.com/wsdl/Server.wsdl" mustUnderstand="1">MyComputer</server:ResourceIdentifier>
   </Header>
   <Body>
      <wsrp:GetResourceProperty xmlns:wsrp="http://docs.oasis-open.org/wsrf/2004/06/wsrf-WS-ResourceProperties-1.2-draft-01.xsd" /
			xmlns:muws-p2="http://docs.oasis-open.org/wsdm/2004/12/muws/wsdm-muws-part2.xsd">muws-p2:OperationalStatus</wsrp:GetResourceProperty>
   </Body>
</Envelope>

Now send the get_status.soap request using the sendRequest target as described above. The response should indicate that the value of the property is "Available," since that is what we initialized it to in ServerResource's init method.

Next we will subscribe to be notified whenever the value of the muws-p2-xs:OperationalStatus property changes. In order to do this, we'll subscribe to the muws-p2-topics:OperationalStatusCapability topic. Save the following XML to a file named subscribe_status_change.soap:

<Envelope xmlns="http://schemas.xmlsoap.org/soap/envelope/"  xmlns:wsa03="http://schemas.xmlsoap.org/ws/2003/03/addressing">
   <Header>
      <wsa03:To mustUnderstand="1">http://localhost:8080/wsrf/services/Server</wsa03:To>
      <wsa03:Action mustUnderstand="1">operation:Subscribe</wsa03:Action>
      <server:ResourceIdentifier xmlns:server="http://abc.com/wsdl/Server.wsdl" mustUnderstand="1">MyComputer</server:ResourceIdentifier>
   </Header>
   <Body>
      <wsnt:Subscribe xmlns:wsnt="http://docs.oasis-open.org/wsn/2004/06/wsn-WS-BaseNotification-1.2-draft-01.xsd">
         <wsnt:ConsumerReference>            
           <wsa03:Address>http://localhost:9090/</wsa03:Address>
         </wsnt:ConsumerReference>         
         <wsnt:TopicExpression muws-p2-topics="http://docs.oasis-open.org/wsdm/2004/12/muws/wsdm-muws-part2-events.xml" /
			Dialect="http://docs.oasis-open.org/wsn/2004/06/TopicExpression/Simple">muws-p2-topics:OperationalStatusCapability</wsnt:TopicExpression>
      </wsnt:Subscribe>
   </Body>
</Envelope>

Send the subscribe_status_change.soap request using the sendRequest target as described above. The response should contain an EPR for a resource that represents your newly created subscription. Requests can be sent to this EPR to cancel, pause, or modify the subscription.

We will use the Axis tcpmon tool, which is included with Muse, as a notification consumer. Tcpmon is a small Swing application that listens on a specified port for incoming HTTP requests, prints them out, and optionally forwards them on to another HTTP server. In our subscribe request, we specified http://localhost:9090/ as the address in the consumer EPR, so we will tell tcpmon to listen on port 9090. Start tcpmon by opening a command prompt, changing directories to the generated/Server subdirectory of the directory containing Server.wsdl, and executing the command "ant tcpmon –Dtcpmon.listenPort=9090." Select the "XML Format" checkbox at the lower left of the tcpmon window; this configures tcpmon to pretty-print any XML contained in incoming requests.

Now we'll invoke the Shutdown operation on our resource. This will change the value of the OperationalStatus property to "Unavailable" and cause a property-changed management event to be published to the muws-p2-topics:OperationalStatusCapability topic. Save the following XML to a file named shutdown.soap:

<Envelope xmlns="http://schemas.xmlsoap.org/soap/envelope/" xmlns:server="http://abc.com/wsdl/Server.wsdl">
   <Header xmlns:wsa03="http://schemas.xmlsoap.org/ws/2003/03/addressing">
      <wsa03:To mustUnderstand="1">http://localhost:8080/wsrf/services/Server</wsa03:To>
      <wsa03:Action mustUnderstand="1">operation:Shutdown</wsa03:Action>
      <server:ResourceIdentifier mustUnderstand="1">MyComputer</server:ResourceIdentifier>
   </Header>
   <Body>
      <server:Shutdown />                                  
   </Body>
</Envelope>

Send the shutdown.soap request using the sendRequest target as described above. Shortly after sending the request, tcpmon should receive and print a SOAP request containing a property-changed management notification. And, concluding our example, you'll have used Muse to expose a MUWS resource that exposes a couple standard MUWS capabilities, as well as a couple custom properties and a custom operation; you'll have interacted with the resource using the sendRequest Ant target; and you'll have utilized the tcpmon tool as a notification consumer.

Conclusion

The new WSRF, WSN, and WSDM specifications from OASIS define powerful standardized ways to use Web services to represent stateful resources, support the publish/subscribe notification pattern, and expose management properties and events. The Apache WSRF, Pubscribe, and Muse projects provide toolkits that greatly simplify the process of developing and deploying Web services that implement these specifications. By providing a resource invocation framework and robust implementations of all of the specification-defined operations, the Apache projects allow you to focus on defining your resource's properties and topics, and writing any custom operations.

Copyright © 2005 CMP Media LLC

Questions or problems regarding this web site should be directed to abeckman@outdoorssite.com.

Copyright © 2008 Art Beckman. All rights reserved.

Last Modified: March 9, 2008