/wsEvent

comment(s) 

WS-Eventing with SQL Notification Services

http://www.brains-N-brawn.com/wsEvent 1/26/04 casey chesnut

i was subscribed to somebody's blog, and saw a mention of WS-Eventing. started reading the spec, and the keywords looked really familiar. then it hit me ... SQL Notification Services (NS)! since the WSE has been in tech preview FOREVER, and i wanted to play around with some new web service stuff, decided to go ahead and put together my own implementation. actually, i had done an ad hoc (read fragile) implementation of this same concept for the /noSink app many moons ago. for this article, all i'm basically going to do is wrap the WS-Eventing spec around SQL NS, specifically the Stock sample that comes with NS

SQL Notification Services (NS)

NS development is a little different. it's a couple of XML files that describes your schema for subscribers, subscriptions, notifications and rules for event-based or scheduled messaging. then you build those XML files and it pushes the table layouts and stored procs (rules) into SQL Server. then you populate the subscription tables, possibly through a web app or whatever. next, you have to insert events into the table as well; possibly through an API they provide, or a file system watcher, etc... there are many ways to hook NS. anyways, when an event (possibly scheduled) occurs, then the rules are executed and notifications are generated based on those messages. the notifications are then formatted and dispatched however you want (SMTP, HTTP, SOAP ...). so lots of flexibility and all that good jazz. now the clencher is that it is made to scale, and can handle ALOT of notifying

NS comes with a couple samples. the defacto one is the Stock sample. it has users subscribe through a web app. they provide a stock that they are interested in, and what price they want to be notified if the price of said stock goes over. it also has a simple web interface for inputting events, e.g. a stock name, and a new price for that stock. when that input occurs, then the subscription rules are run against the new stock price, and notifications are generated for people who subscribed at a price lower than the current value. those notifications are then HTTP POST'ed to a simple web page as the delivery mechanism. not much to it, so i'm just going to modify it and put a WS-Eventing layer on top of this sample

first, i had to install NS. actually, i hate DB work, so i had to install SQL Server too :) regardless, NS has a great quick start article on MSDN that shows you how to get installed, and get a sample running. yes, i felt dirty installing the SOAP Toolkit

WS-Eventing

this is a standard subscription model ... but for web services (WS). it lets a web service (sink) subscribe for notifications from another web service (source). this subscription is leased, and expires over time if the sink does not renew its subscription. the messages are:

some stuff i found interesting: most of this spec happens in the Soap:Body, where most other specs are entirely in Soap:Header or split between both, like XML-Encryption. most of the spec is optional, but should be implemented. the source can entirely ignore your requested / renewal lease date; so your client should pay attention to when it needs to renew. if you want to subscribe forever, you just set a really long lease date and hope the server accepts it :) your subscription can filter for notifications by passing a filter, which defaults to an XSLT dialect. which brings up, isn't it time for XQuerty yet, now that i finally have a decent working knowledge behind the voodoo magic of XSLT? really think that the filter section should be expanded to support other means, such as passing parameters with name/value pairs, possibly scheduled events, etc... Notifications, which seem like the most important part, aren't in the specification. reason being is that notifications can be any type of message, so they are left open. also, notifications are pointed out as being 1-way in the spec. the source can send the SubscriptionEnd Header with a code of ServiceCancelling to mean that the sink should go to UDDI and find somebody else that offers the service to subscribe to instead ... cool! the spec uses much of WS-Addressing, briefly mentions WS-ReliableMessaging (wish it said more), and has a section on how to tie-in WS-Security as well. the spec. finally, it also contained an XML Schema as well as a WSDL file!

WSDL

also known as What a Shitty Description Language. with the WSDL, i decided to go the WSDL-1st route. wanted to generate my server stub, and then create the web service from that. figured that the WSDL would need some tweaking, but not that it would kick my ass. serious. started out using Christian Weyer's Contract First tool, which i had been looking for an excuse to try. it kept coming up with a false positive, saying it was successful, but creating an empty stub with no content (i'm sure he would know how to instantly mod the WSDL to get it to work though). also tried the command line wsdl.exe, as well as wseWsdl.exe with WSE2. all of them were just beating me down. i did the pattern matching between a WSDL file that i knew would work, and then would look for differences and try to fix them. even went on to the XSD to validate it. everything looked great, but my stubs kept coming up empty. or i would get great error info such as "Warning: no classes were generated". ok, deep breath (i preferred the false positive to the true negative BTW). that was taking the top down approach, so i decided to go the other way, and build up from nothing, aka the Don Box approach. made a stupid .ASMX web service to generate the most basic WSDL file and would run wsdl.exe /sever against it to make sure it worked. then i would cut / paste from my other WSDL into this one, until something broke. the only obvious problem that came up involved this line in the schema for the Code element: <xs:restriction base= 'xs:QName' >. normally, i would research the problem to see what was really happening, but at this point i said F*** it, and changed it to xs:string ... and it worked. i hope Whidbey fixes this crap. once i had the stub, all i did was subclass it and override the webMethods. took a quick peek at the auto-generated help page to make sure the example SoapRequest / Responses looked somewhat right. even looked at the auto gen'd WSDL for a second ... with disgust in my eyes. that gave me the WS-Eventing skeleton, but i still needed WS-Addressing

you can check out the WS itself (WSDL and message envelope previews) here: EventingSource.asmx [it wont work from this link though, because i dont have NS installed on this server]

WS-Addressing

opted not to use WSE2, and decided to use my own bits from the /spWse  article for maximum flexibility. since my WS-Addressing bits were only to allow .NETcf apps to call a WSE2 server, i had to extend them to support the server side formatting. i did not however build up the WS-Referral logic to make it actually do anything behind the scenes on the server-side, but only made the request and responses look appropriate. such as respond with the correct Action and make sure the RelatesTo header corresponds with the MessageID from the request

the WS-Eventing Subscribe request carries some Addressing-like elements. NotifyTo specifies where notifications should ultimately be sent, and does not have to correspond to the requesting URL. it also has an EndTo element, so that if the source needs to send out a SubscriptionEnd header, then that message can go that specific address. for this exercise, all i want to do is store the NotifyTo address along with the subscription in NS, along with the lease expire time. now that i have the Addressing and Eventing message structures, i can move on to actually tie it to NS

Stock Mods

first thing i did was rip out the web app code for feeding events into the sample, and added them to my EventSource web service. next, i ripped out the subscription code from the web app and hooked that up into the Subscribe method. then, i added minimal code for Renew and Unsubscribe. Renew just uses NS to find the subscription, and updates its lease time. Unsubscribe just uses NS to find the subscription, and then it deletes itself. i did have to extend the appADF.xml file minimally. the 1st change involved adding 2 new fields to the Subscription: NotifyTo and LeaseTime. NotifyTo is where corresponding notifications should be sent. also added this field to the Notification schema, so that it could be transformed into whatever message type before being send over the wire. since WS-Eventing mentions that notifications could be any message, i entirely left that part up to NS, since it is already allows extreme flexibility for messaging. finally, i had to update the rule, so that it only generated notifications for subscriptions that were still leased. to make it complete it should generate SubscribeEnd notifications for the ones that are expired, but i did not take it that far. finally, i made a little winForms test app. all it does is call Subscribe, Renew, and Unsubscribe. i would then use the web service test page to submit a new stock quote, and make sure that notifications were generated within about a minute. just let the notifications be posted to the HttpLogger page. it would be simple to mod them with XSLT and have the notifications turned into WS calls to be sent to the event sink, or do a custom delivery channel if you wanted to get back to an object model

Client code to subscribe:

string symbol = symbolBox.Text.Trim(); 
string strVal = valueBox.Text.Trim(); 
int val = Int32.Parse(strVal); 
EsProxy.EventingSource es = new EsProxy.EventingSource(); 
es.Url = "http://localhost:8080/wsEvent/EventingSource.asmx";

EsProxy.Subscribe s = new EsProxy.Subscribe(); 
s.NotifyTo = new EsProxy.EndpointReferenceType(); 
s.NotifyTo.Address = new EsProxy.AttributedURI(); 
s.NotifyTo.Address.Value = "fake@bake.com";

s.NotifyTo.ReferenceProperties = new EsProxy.ReferencePropertiesType(); 
XmlDocument xd = new XmlDocument(); 
XmlElement xe = xd.CreateElement("bNb", "MySubscription", "http://www.brains-N-brawn.com/"); 
xe.InnerText = "KC";
XmlElement [] xea = new XmlElement [] {xe}; 
s.NotifyTo.ReferenceProperties.Any = xea;

DateTime dtExpire = DateTime.Now.AddSeconds(60); 
s.Expires = dtExpire.ToString("s") + ".000-00:00"; 
s.Filter = new EsProxy.MessagePredicateAssertion(); 
s.Filter.Dialect = "SqlNs";
s.Filter.Value = symbol + ":" + strVal; 

EsProxy.SubscribeResponse sr = es.Subscribe(s); //web service call
subIdBox.Text = sr.Id;
expBox.Text = sr.Expires;

WS code to renew:

if(Renew1.Id == null || Renew1.Id == String.Empty)
   throw new SoapException("bad identifier", new XmlQualifiedName("Client"));
string subscriptionId = Renew1.Id;
string strExpires = Renew1.Expires;
DateTime dtExpires = Convert.ToDateTime(strExpires);
NSUtility nsUtil = new NSUtility(instanceName, applicationName);
Subscription subscription = nsUtil.GetSubscription(subscriptionId, subscriptionClassName);
if(subscription == null)
   throw new SoapException("unknown subscription", new XmlQualifiedName("Client"));
			
subscription["LeaseTime"] = dtExpires;
subscription.Update();
			
RenewResponse rr = new RenewResponse();
rr.Expires = strExpires;
return rr;

WS code to unsubscribe:

string subscriptionId = Unsubscribe1.Id;
NSUtility nsUtil = new NSUtility(instanceName, applicationName);
nsUtil.DeleteSubscription(subscriptionClassName, subscriptionId);
return null;

SoapExtension for WS-Addressing:

ActionHeader actionHeader = new ActionHeader(message.Action + "Response");
//else check what the inAction is
RelatesToHeader relatesToHeader = null;
if(inMessageId != null)
   relatesToHeader = new RelatesToHeader(inMessageId);
ToHeader toHeader = null;
if(inReplyTo != null)
   toHeader = new ToHeader(inReplyTo);
message.Headers.Add(actionHeader);
if(relatesToHeader != null)
   message.Headers.Add(relatesToHeader);
if(toHeader != null)
   message.Headers.Add(toHeader);

Traces of the messages:

Subscribe - subReq.xml / subRes.xml
Renew - renReq.xml / renRes.xml
Unsubscribe - unsubReq.xml / unsubRes.xml

Conclusion

after this little bit of work (2 days), i still feel that SQL Notification Services is a perfect match for WS-Eventing. can't wait until we get some new bits from MS to let us start playing around with the higher-level web services stack

Updates

already kicking around a follow up article idea ...

Future

i finally jumped on the blogwagon. my home page (brains-N-brawn.com) has my moblog and RSS feed. i'm building it from scratch so that i can tweak it out, specifically for mobile blogging. right now it supports multiple usages scenarios with cell phones (MMS, VoiceXml, Wireless Web) and ink blogging with a tablet. the funniest messages involve the text generated from speech recognition! got alot of features to add, and will get better at adding content to it