7.4. Notifying changes in a resource property

We will see how we can add notifications to a service so clients can be notified each time a certain RP is modified. As we did in and , our example will be based, for simplicity, on the SimpleResourceHome resource home.

7.4.1. Create a New Site with a NotificationProducer Service

Our portType will need to extend from a standard WS-Notifications portType called NotificationProducer, which exposes a Subscribe operation that consumers can use to subscribe themselves to a particular topic. Since this examples takes an existing resource property and exposes it as a topic, no additional WSDL code is required beyond extending the NotificationProducer portType. Copy & paste the WSDL at the end of this chapter to "/tmp/MathNPService.wsdl".


% mkdir /tmp/MathNPSite
% mv MathNPService.wsdl /tmp/MathNPSite
% cd /tmp/MathNPSite
% wsdl2web.py --script=client.py --rpy=MathService.rpy MathNPService.wsdl
% cat /tmp/MathLTSite/services/MathService.rpy | sed s/MathLTService/MathNPService/g > services/MathService.rpy

Note

Site is finished!

7.4.2. Generated WSRF Service: generated.MathNPService.services.MathNPService.MathService.MathServiceWSRF

Note

This class has been generated for you, it implements the WS-BaseNotification NotificationProducer operations.


class MathServiceWSRF(MathService):

    def GetResourceContext(ps, address):
        """get a resource context"""
        return ManagerHome.getInstance().getResourceContext(ps, address)
    GetResourceContext = staticmethod(GetResourceContext)

    ...
    ...

    def wsa_Subscribe(self, ps, address, **kw): (1)
        #http://docs.oasis-open.org/wsn/2004/06/wsn-WS-BaseNotification-1.2-draft-01.wsdl SubscribeRequest
        request,response = MathService.wsa_Subscribe(self, ps, address) (2)
        ctx = self.GetResourceContext(ps, address) (3)
        assert SubscriptionManager._isInstantiated(),"SubscriptionManager is not instantiated"
        manager = SubscriptionManager.getInstance() (4)
        response._SubscriptionReference = manager.subscribe(request, ctx) (5)
        return request,response (6)
(1)
Subscribe to Notifications via the SubscriptionManager/NotificationProducer
(2)
Call lower-level service binding stub to return SubscribeRequest and SubscribeResponse instances.
(3)
Retrieve the ResourceContext of the service instance.
(4)
Retrieve the SubscriptionManager Singleton.
(5)
Use the SubscriptionManager to subscribe, returning an endpoint reference which is used as the SubscriptionReference.
(6)
Return python object representing the SubscribeResponse

7.4.3. Using the NotificationConsumer Client

Note

Copy & Paste example..


#!/usr/bin/env python
############################################################################
# Automatically generated by wsdl2web.py
# See LBNLCopyright for copyright notice!
###########################################################################
from twisted.python import log
from twisted.internet import reactor

import ZSI
from ZSI.fault import Fault
from pyGridWare.utility.scripts.client import GetBasicOptParser, GetPortKWArgs, SetUp

from pyGridWare.wsrf.notification import SubscribeUtility
from pyGridWare.wsrf.notification.NotificationConsumer import \
    NotificationConsumer, NotificationConsumerService

from pyGridWare.wsrf.faults.PropertiesFaults import ResourceUnknownFault

from generated.MathNPService.stubs import MathNPService as CLIENT


def failure(iport):
    print "Attempt to access Destroyed Resource"
    try: 
        msg = iport.add(CLIENT.AddInputMessage(1))
    except Fault, ex:
        print "RESOURCE DESTROYED..", ex (1)
        print '\tcode   -- ', ex.code
        print '\tstring -- ', ex.string
        print '\tdetail'
        for d in ex.detail:
            print '\t\tstring -- %s'  %d.string
            print '\t\ttrace  -- %s'  %d.trace

    except Fault, ex: 
        print "Unexpected SOAP Fault...", ex.__class__

    reactor.stop()


def main(**kw):
    target = 10
    locator = CLIENT.MathServiceLocator()
    port = locator.getMathPortType(**kw)
    msg = port.createResource(CLIENT.CreateInputMessage()) (2)
    print 'Created instance.'

    iport = locator.getMathPortType(endPointReference=msg.EndpointReference, **kw) (3)

    class _NCService(NotificationConsumerService):(4)

        def wsa_Notify(self, ps, address, **kw):(5)
            request,response = NotificationConsumerService.wsa_Notify(self, ps, address)
            pyobj = request.NotificationMessage[0].Message
            value = pyobj.ResourcePropertyValueChangeNotification.NewValue.Any

            print "NewValue", value
            if pyobj.ResourcePropertyValueChangeNotification.NewValue.Any != target:(6)
                msg = iport.add(CLIENT.AddInputMessage(1))
                return request,response

            print 'Destroy instance.'
            msg = iport.Destroy(CLIENT.DestroyRequest())
            reactor.callLater(0.1, failure, iport)
            return request,response


    consumer = NotificationConsumer(notificationClass=_NCService) (7)
    consumer.start()

    request = CLIENT.SubscribeRequest() 
    SubscribeUtility.SimpleTopicExpression(request, consumer, (8)
        "http://www.globus.org/namespaces/examples/core/MathService_instance_rp", "Value")
    msg = iport.Subscribe(request) (9)
    msg = iport.add(CLIENT.AddInputMessage(1)) (10)


if __name__ == '__main__':
    op = GetBasicOptParser()
    (options, args) = op.parse_args()
    SetUp(options)
    kw = GetPortKWArgs(options)
    reactor.callWhenRunning(main, **kw)
    reactor.run()

(2)
Create the service instance.
(3)
Create a port for communicating with the service instance.
(4)
Define a callback class for the notification consumer.
(7)
Create a NotificationConsumer, pass in the callback class as a parameter.
(8)
Use the SubscribeUtility to set up a SimpleTopicExpression. This subscription specifies that the consumer be notified whenever the Value resource property changes.
(9)
Use the port to invoke the Subscribe operation.
(10)
Use the port to invoke the add operation. Control is passed to the reactor.
(5)
The Notify message arrives.
(6)
Does the NewValue of the Value resource property match the target value? If not invoke the add operation again, else invoke Destroy and queue up the failure function.
(1)
The service instance is destroyed, so when the add operation is invoked a SOAP:Fault is caught.

7.4.4. Run the client:

Note

Make sure to start the server first...


./client.py -u http://127.0.0.1:9080/wsrf/services/MathService -d 0
FtWarning: Creation of InputSource without a URI
/Library/Frameworks/Python.framework/Versions/2.4/lib/python2.4/site-packages/ZSI-2.0_rc2-py2.4.egg/ZSI/__init__.py:187: SyntaxWarning: The null string should be None, not empty.
Created instance.
NewValue 1
NewValue 2
NewValue 3
NewValue 4
NewValue 5
NewValue 6
NewValue 7
NewValue 8
NewValue 9
NewValue 10
Destroy instance.
Attempt to access Destroyed Resource
'HTTP Error 500'
RESOURCE DESTROYED.. Processing Failure
pyGridWare.wsrf.faults.PropertiesFaults:ResourceUnknownFault
<pyGridWare.wsrf.faults.PropertiesFaults.pyGridWare.wsrf.faults.PropertiesFaults.ResourceUnknownFault instance 178baf8 
<ns2:ResourceUnknownFault xmlns:ns1="http://docs.oasis-open.org/wsrf/2004/06/wsrf-WS-BaseFaults-1.2-draft-01.xsd" xmlns:ns2="http://docs.oasis-open.org/wsrf/2004/06/wsrf-WS-ResourceProperties-1.2-draft-01.xsd"><ns1:Timestamp xmlns:ns3="http://www.w3.org/2001/XMLSchema" xmlns:ns4="http://www.w3.org/2001/XMLSchema-instance" ns4:type="ns3:dateTime">2006-04-15T19:40:37Z</ns1:Timestamp></ns2:ResourceUnknownFault>>
[trace: build/bdist.darwin-8.5.0-Power_Macintosh/egg/ZSI/twisted/WSresource.py:341:render_POST
build/bdist.darwin-8.5.0-Power_Macintosh/egg/ZSI/twisted/WSresource.py:254:processRequest
/Library/Frameworks/Python.framework/Versions/2.4/lib/python2.4/site-packages/pyGridWare-1.2.0rc3-py2.4.egg/pyGridWare/utility/web/resource.py:77:processRequest
/private/tmp/MathNPSite/services/MathService.rpy:11:wsa_add
/private/tmp/MathNPSite/generated/MathNPService/services/MathNPService/MathService.py:20:GetResourceContext
/private/tmp/MathNPSite/generated/MathNPService/resource/MathNPService/MathService.py:24:getResourceContext
/Library/Frameworks/Python.framework/Versions/2.4/lib/python2.4/site-packages/pyGridWare-1.2.0rc3-py2.4.egg/pyGridWare/resource/ResourceHome.py:52:get]
        code   --  (u'http://schemas.xmlsoap.org/soap/envelope/', u'Server')
        string --  Processing Failure
        detail
                string -- pyGridWare.wsrf.faults.PropertiesFaults:ResourceUnknownFault
<pyGridWare.wsrf.faults.PropertiesFaults.pyGridWare.wsrf.faults.PropertiesFaults.ResourceUnknownFault instance 178baf8 
<ns2:ResourceUnknownFault xmlns:ns1="http://docs.oasis-open.org/wsrf/2004/06/wsrf-WS-BaseFaults-1.2-draft-01.xsd" xmlns:ns2="http://docs.oasis-open.org/wsrf/2004/06/wsrf-WS-ResourceProperties-1.2-draft-01.xsd"><ns1:Timestamp xmlns:ns3="http://www.w3.org/2001/XMLSchema" xmlns:ns4="http://www.w3.org/2001/XMLSchema-instance" ns4:type="ns3:dateTime">2006-04-15T19:40:37Z</ns1:Timestamp></ns2:ResourceUnknownFault>>
                trace  -- build/bdist.darwin-8.5.0-Power_Macintosh/egg/ZSI/twisted/WSresource.py:341:render_POST
build/bdist.darwin-8.5.0-Power_Macintosh/egg/ZSI/twisted/WSresource.py:254:processRequest
/Library/Frameworks/Python.framework/Versions/2.4/lib/python2.4/site-packages/pyGridWare-1.2.0rc3-py2.4.egg/pyGridWare/utility/web/resource.py:77:processRequest
/private/tmp/MathNPSite/services/MathService.rpy:11:wsa_add
/private/tmp/MathNPSite/generated/MathNPService/services/MathNPService/MathService.py:20:GetResourceContext
/private/tmp/MathNPSite/generated/MathNPService/resource/MathNPService/MathService.py:24:getResourceContext
/Library/Frameworks/Python.framework/Versions/2.4/lib/python2.4/site-packages/pyGridWare-1.2.0rc3-py2.4.egg/pyGridWare/resource/ResourceHome.py:52:get

7.4.4.1. What has just happened?

After subscribing the notification consumer is ready to receive notifications about changes to the Value resource property. After the initial add, control is released to the reactor. Each add will cause a Notify message to be sent to the notification consumer. In this example I keep adding one to the service instance until I'm notified that the Value resource property matches my target value (10). Once this target has been reached, the service instance is destroyed and the function failure is queued to test if the service instance is still available.