Chapter 14. Advanced Messaging:Reliability and Sessions

Customers of web services often request several advanced messaging features. The most requested feature, of course, is message-based security (refer to Chapter 13); however, reliable messaging, sessions, and eventing protocols are also needed.

Reliability includes a continuum of features. The Web service architecture offers several specifications that together can form a reliable messaging architecture. These range from WS-Routing for specifying routes that SOAP messages should take, to specifications for reliability (not yet released as of this writing). This chapter covers features that deliver sessions, reliability, and even eventing, and which you can use individually or in combination. Because SOAP is so modular, you can create a lightweight application by using only the pieces you need.

Because there are no specifications or even drafts of specifications available at this time, this chapter focuses on the ideas behind potential specifications. Over time, these concepts will continue to be applicable, but the syntax introduced here is only for this book, and does not reflect any company's specific ideas.

Note too that this chapter is in no way a complete discussion of what it takes to build a reliable messaging system. Rather, it presents the basic ideas. The specific implementation details are left to the reader, as they vary according to the type of application being built. In the near future, look for the release of generic reliable messaging systems that provide most of this functionality.

Sessions

The concept of sessions is difficult to define. Some people use the word to define any conversation among a set of messaging endpoints. But I prefer a more liberal definition: A session is the logical and unique name given to a set of semantically and temporally related messages. It's a way to let a message receiver state that a particular message is part of the same conversation, or session.

Examples of a session might be the series of messages

  • Signing up a new customer.

  • Doing online shopping, including checking out.

  • Submitting a purchase order.

HTTP Sessions

Most typically, sessions are seen in Web applications in which there is a need to tie a particular browser instance together. This is usually done in one of two ways: either with an HTTP cookie; or with a special URL value, either in the URL or as a POST value that is carried forward from each Web page. In both instances, the value is a GUID (Global Unique Identifier) that eventually times out.

With Active Server Pages (ASPs), and ASP.NET, there is a Session object that is available only on the server side and which can place custom application data. The correct Session object is then presented to the server code each time the HTTP GET or POST request contains the session cookie, as based on the value. This also can work well for Web services, and as a result, ASP.NET Web services support the exact same programming mode, using the same cookie-based Session key.

To take advantage of this server-side feature, the client that is sending the request message must obey the common rules for HTTP cookie handling. To enable this with the .NET Web services proxy class, you need to create a CookieContainer object and set the CookieContainer property of the class to this instance:

MyClientProxyClass clientProxy = new MyClientProxyClass(); 
clientProxy.CookieContainer = new CookieContainer();

Now, whenever you make a method call to a server, the correct cookies, including HTTP-based session cookies, will be sent to this client proxy class and accepted by it. Because this container is programmatically accessible, you can also persist it to disk or to a database. In fact, you will have to manage this collection of cookies in between client-proxy-class lifetimes, because the underlying .NET runtime won't do this for you the same way the browser does.

On the server side, the session is initiated by code that tries to access it. This requires no explicit call to create a cookie. Instead, you need only to access a session value. The ASP.NET infrastructure will automatically do the right thing in terms of looking for a cookie, and creating one if none exists yet. Listing 14.1 shows exactly how easy session initiation can be with ASP.NET Web services. Similar levels of ease exist in most HTTP-aware SOAP stacks.

Example 14.1. Sessions with ASP.NET Web Services

<%@ WebService Class="SessionTest" Language="C#" %>
using System.Web.Services;
using System.Web;
public class SessionTest : WebService
{
     [WebMethod]
     public String AddToSession( String text )
     {
          String s = (String)Session["text"];
          S = s + text;
          Session["text"] = s;
        return s;
    }
}

Message-Level Sessions

As of this writing, there is no SOAP-based specification for sessions that has gained wide acceptance in the industry. But there has been enough discussion to make some good guesses about how a session specification would work.

The first thing to decide is whether session information should be header based, body based, or some combination. It seems logical that sessions are exactly the kind of out-of-band information that is best kept in the header. We'll keep that as a working hypothesis.

With that in mind, you can imagine a very simple session header, called <Session>, with a single child element, called <Id>. We'll need a namespace as well, so we'll use http://keithba.com/2002/05/Session. Listing 14.2 shows the resulting session header.

Example 14.2. An Example Session Header

<soap:Envelope
   xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Header>
 <Session xmlns="http://keithba.com/2002/05/Session">
     <Id>uuid:1111-1111-1111-1111</Id>
 </Session>
</soap:Header>
 <soap:Body>
    ...
 </soap:Body>
</soap:Envelope>

It's interesting that the <Id> can take as a value any URI. In the schema for this, it would be of type <xsd:anyURI>. This allows the session's identifier to be a GUID, as in Listing 14.2, or even a more “normal” URI such as http://keithba.com/purchaseOrder/20202198. It would be unusual not to have a unique session identifier; therefore, anything but a GUID would be uncommon.

With ASP.NET's HTTP cookies, there is no specific way to initiate a session, and the session is truly understood only on the server's end. In effect, there is no client side of a session. With a SOAP-based session protocol, on the other hand, it makes sense to want some additional features:

  • Explicit initiation and expiration

  • Expiration date and time

  • Ability for the client or server to contain, initiate, and expire a session

With this in mind, we need to add an <Initiate> element—which also contains the expiration date and time of the session—as well as a <Terminate> element for ending the session. Listings 14.3 and 14.4 show a session initiation and termination, respectively.

Example 14.3. An Example of Session Initiation

<soap:Envelope
  xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Header>
  <Session xmlns="http://keithba.com/2002/05/Session">
      <Initiate>
           <Expires>4/4/2002 12:00:00PM PST</Expires>
      </Initiate>
      <Id>uuid:1111-1111-1111-1111</Id>
  </Session>
</soap:Header>
  <soap:Body>
    ...
  </soap:Body>
</soap:Envelope>

Example 14.4. An Example of Session Termination

<soap:Envelope
  xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Header>
 <Session xmlns="http://keithba.com/2002/05/Session">
     <Terminate />
     <Id>uuid:1111-1111-1111-1111</Id>
 </Session>
</soap:Header>
 <soap:Body>
   ...
 </soap:Body>
</soap:Envelope>

The classes for building this session header are fairly straightforward. Listing 14.5 shows the class for the <Session> header itself.

Example 14.5. The <Session> Header Class

[XmlRoot(Namespace="http://keithba.com/2002/05/Session")]
public class Session : SoapHeader
{
     private String _Id;
     public String Id
     {
          get
          {
               return _Id;
          }
          set
          {
               _Id = value;
          }
     }
     private Initiate _Initiate;
     public Initiate Initiate
     {
          get
          {
               return _Initiate;
          }
          set
          {
               _Initiate = value;
          }
     }
     private Terminate _Terminate;
      public Terminate Terminate
     {
          get
          {
               return _Terminate;
          }
          set
          {
               _Terminate = value;
          }
     }
}

Notice a couple of things from Listing 14.5:

  • The [XmlRoot] attribute is used to set the namespace of the <Session> element and its children.

  • The Initiate and Terminate elements are classes.

The Initiate class contains an expiration date and time as well, as shown in Listing 14.6.

Example 14.6. The Initiate Class

public class Initiate
{
     private DateTime _Expires;
     public DateTime Expires
     {
          get
          {
               return _Expires;
          }
          set
          {
               _Expires = value;
          }
     }
}

The Terminate class is entirely uninteresting, but it must be a class in order to be a child element. Here is the code:

public class Terminate 
{
}

Listing 14.7 shows the schema for the session header.

Example 14.7. The Session Header Schema

<xs:schema
     targetNamespace="http://keithba.com/2002/05/Session"
     xmlns:tns="http://keithba.com/2002/05/Session"
     xmlns:xs="http://www.w3.org/2001/XMLSchema"
     elementFormDefault="qualified"
     attributeFormDefault="unqualified">
     <xs:element name="Session" type="tns:Session"/>
     <xs:complexType name="Session">
          <xs:sequence>
               <xs:element
                    name="Id"
                    type="xs:string"
                    minOccurs="0"/>
               <xs:element
                    name="Initiate"
                    type="tns:Initiate"
                    minOccurs="0"/>
               <xs:element
                    name="Terminate"
                    type="tns:Terminate"
                    minOccurs="0"/>
               <xs:any namespace="##other"/>
          </xs:sequence>
</xs:complexType>
<xs:complexType name="Initiate">
        <xs:sequence>
               <xs:element name="Expires" type="xs:dateTime"/>
               <xs:any namespace="##other"/>
        </xs:sequence>
</xs:complexType>
<xs:complexType name="Terminate">
      <xs:sequence>
          <xs:any namespace="##other"/>
     </xs:sequence>
</xs:complexType>
</xs:schema>

Notice that all of the elements have an <any> element defined for them. This allows for extending this schema without needing to version it— which will come in handy later, when we extend this session to handle reliability features. Figure 14.1 diagrams this schema.

A Graphical Look at the Session Schema

Figure 14.1. A Graphical Look at the Session Schema

At this point, using this session class within a service would be very simple:

public class Service1 : WebService 
{
     public Session Session;
     [WebMethod]
      [SoapHeader("Session",
                Direction=SoapHeaderDirection.InOut)]
     public void SubmitPO( String PO )
     {
          //code would go here
     }
}

The client side would work similarly to any header. Of course, having access to these headers isn't enough, because the application needs to be able to tie any particular sessions together. However, it's outside the scope of this book to fully explain how this could happen. In general, a property bag or named object dictionary would be more than enough. The only code then needed would be another dictionary to contain the specific session's dictionary.

Message Reliability

One hundred percent availability is never attainable. That said, there is a lot you can do to get a very high degree of reliability in any messaging system.

With SOAP and Web services, the usual way to get reliability is to use a session, but session technology alone won't give you everything you need to know. What's needed is a way to label messages inside of a session and get acknowledgments that the intended recipient has received certain labeled messages (or all messages). Figure 14.2 shows the basic idea: Several messages are sent, and an acknowledgment for each message is received.

Typical Message Flows

Figure 14.2. Typical Message Flows

The basic idea is that, within a session, messages are sent and acknowledgments are received. So, at the very least, we need to add to the session header the ability to give a message a number, and also to add acknowledgments. This would look something like Listing 14.8.

Example 14.8. Session Header with Message Numbers

<soap:Envelope
  xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Header>
 <Session xmlns="http://keithba.com/2002/05/Session">
<Id>uuid:1111-1111-1111-1111</Id>
<MessageNumber
           xmlns="http://keithba.com/2002/04/Reliable">
          4
     </MessageNumber>
     <Ack xmlns="http://keithba.com/2002/05/Reliable">
          3
      </Ack>
      <Ack xmlns="http://keithba.com/2002/05/Reliable">
          1
     </Ack>
 </Session>
</soap:Header>
  <soap:Body>
    ...
  </soap:Body>
</soap:Envelope>

Notice that in Listing 14.8, both message 1 and message 3 are being acknowledged at the same time. There is nothing to stop this from occurring. In fact, in a long running conversation between two message endpoints, it would be unusual for there always to be a pair set of messages that could be acknowledged.

Notice that message numbering and acknowledgment are achieved by extending the session header that we defined earlier. The schema says that these extensions need to be in a namespace other than the session name-space; therefore, in this case, we are using http://keithba.com/2002/05/.

In addition to the <MessageNum> and <Ack> elements, it would be useful to be able to request an acknowledgment. You can accomplish this by adding one more element, <RequestAck>, whose value is a message number. Listing 14.9 shows the addition of this element.

Example 14.9. Adding the <RequestAck> Element to the Session Header

<soap:Envelope
  xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Header>
 <Session xmlns="http://keithba.com/2002/05/Session">
 <Id>uuid:1111-1111-1111-1111</Id>
<MessageNumber
           xmlns="http://keithba.com/2002/04/Reliable">
          4
     </MessageNumber>
     <Ack xmlns="http://keithba.com/2002/05/Reliable">
          1
     </Ack>
     <RequestAck
       xmlns="http://keithba.com/2002/05/Reliable">
          3
     </RequestAck>
 </Session>
</soap:Header>
  <soap:Body>
    ...
  </soap:Body>
</soap:Envelope>

Figure 14.3 illustrates the message flow with this capability. Notice how loosely coupled the entire system in Figure 14.3 appears to be. The request–response semantic of typical RPC systems is completely missing. Instead, the acknowledgment and request for acknowledgment semantic allows for a much more robust yet flexible system. It is more robust because now at least one process will always be aware of any errors or data loss.

A Message Flow with Acknowledgments and Requests for Acknowledgment

Figure 14.3. A Message Flow with Acknowledgments and Requests for Acknowledgment

The schema for these additional elements is simple:

<xs:schema 
     targetNamespace="http://keithba.com/2002/05/Reliable"
     xmlns:xs="http://www.w3.org/2001/XMLSchema"
     xmlns="http://keithba.com/2002/05/Reliable"
     elementFormDefault="qualified"
     attributeFormDefault="unqualified">
          <xs:element name="MessageNum" type="xs:integer"/>
          <xs:element name="RequestAck" type="xs:integer"/>
          <xs:element name="Ack" type="xs:integer"/>
</xs:schema>

The modified class for session would now look like Listing 14.10.

Example 14.10. Additional Classes

[XmlRoot(Namespace="http://keithba.com/2002/05/Session")]
public class Session : SoapHeader
{
     private String _Id;
     public String Id
{
     get
     {
         return _Id;
     }
     set
     {
         _Id = value;
     }
}

private Initiate _Initiate;
public Initiate Initiate
{
     get
     {
         return _Initiate;
     }
     set
     {
         _Initiate = value;
     }
}
private Terminate _Terminate;
public Terminate Terminate
{
     get
     {
         return _Terminate;
     }
     set
     {
         _Terminate = value;
     }
}
private int _MessageNum;
[XmlElement(
 Namespace="http://keithba.com/2002/05/Reliable")]
public int MessageNum
{
     get
     {
         return _MessageNum;
     }
     set
     {
         _MessageNum = value;
     }
}
private int[] _Ack;
     [XmlElement("Ack",
      Namespace="http://keithba.com/2002/05/Reliable")]
     public int[] Ack
     {
         get
         {
             return _Ack;
         }
         set
         {
             _Ack = value;
         }
     }
     private int[] _RequestAck;
     [XmlElement("RequestAck",
      Namespace="http://keithba.com/2002/05/Reliable")]
     public int[] RequestAck
     {
         get
         {
             return _RequestAck;
         }
         set
         {
             _RequestAck = value;
         }
     }
}

The application code for handling this varies depending on how durable the conversation needs to be, but typically you will want to involve a database of some kind. Using this database, store the messages received. As the application processes each one, send an acknowledgment. If the application receives a request for an acknowledgment, that message can then be processed immediately if at all possible.

Dialogues and Monologues

Two kinds of conversations can occur with Web services: dialogues and monologues. Dialogues are the conversations between exactly two parties with messages flowing to and from both parties. Monologues are conversations between one primary message sender and any number of message receivers.

Figure 14.4 shows a monologue in action. The idea is that a single message sender can send a series of messages all belonging to the same session. This series of messages can be related in any number of ways. It's important to note that there can be any number of receivers of this same message (in Figure 14.4 labeled as A, B, and C).

A Monologue

Figure 14.4. A Monologue

The uses for such a monologue are interesting. Backward communication to the sender from any of the receivers is not requisite for an interesting application. In fact, the receivers don't even need to be using the messages in the same manner.

For example, the messages being sent could contain the status of the server, including information such as CPU utilization, available memory, and size of the primary process. The first two receivers, A and B, could be management applications such as Hewlett Packard's OpenView or Microsoft's Operations Manager.

However, the last receiver, C, may be a backup server that is watching to see if it needs to start processing requests in order to replace the message sender. Or, C could be an application that will send out alerts via SMS (Short Message Service) or pure phone dialing of pagers once certain thresholds are seen.

Other applications of a monologue are for streaming information such as news, stock quotes, and media (e.g., music and video). Any of these could have a number of clients. Permitting an architecture in which the message sender doesn't have to worry about messages from a large number of receivers allows for a highly flexible design.

Dialogues are much more common than monologues; when Web services are deployed, they usually are dialogues. The Figure 14.5 illustrates the basic idea of a dialogue.

A Dialogue

Figure 14.5. A Dialogue

A dialogue is a conversation between two parties. Both parties send messages, and both parties receive them. The key characteristic is the presence of a long series of messages, not necessarily request–response message exchanges. These messages may be a part of several sessions, although generally any one conversation would equate to a single session.

Ultimately, the notion of a long running exchange of messages could yield some interesting scenarios, particularly if by long running we are talking about not just seconds or minutes, but hours or days—or perhaps even weeks. (That's not to say that a conversation couldn't last longer than weeks, but my mind can only believe so much.)

Examples of these scenarios include the purchase order example I've used throughout this book. Taking into account the actual time it may take to process a purchase order, and that the process may require human intervention, you can easily see how allowing days and weeks would help. The steps are easy to extrapolate:

  1. Mark in the tire department receives a notice that more tires are needed.

  2. He opens a custom business application, and orders tires from the current supplier.

  3. The application sends a message to the tire supplier. This message initiates a session and includes a purchase order (PO).

  4. The tire supplier's Web service receives the order. The tire supplier sends an acknowledgment for the order, either in the next message the tire supplier sends to the car company or in a separate message later. The acknowledgment includes a PO ticket number to identify this particular PO.

  5. The internal systems generate a PO request, which ultimately turns into an e-mail to Elizabeth in the tire sales department.

  6. After a few hours, Mark checks the status of his order.

  7. This order status becomes a message requesting status of the particular PO. This request message is within the same reliable session and includes the PO ticket.

  8. Mark receives a reply message indicating that the PO is in the sales department. A few moments later, Mark sees this on his computer screen.

  9. Elizabeth processes the order: She ensures that the correct number of tires can be delivered, checks the price, and enters this information into the computer, along with the anticipated ship date and shipper to be used.

  10. The tire supplier sends a message to the car company with the updated PO information. Again, the same reliable session ID is used.

  11. Mark receives an e-mail that the PO has been processed, along with the pertinent information.

  12. Once the tires have been shipped, the PO is updated with this information, along with the anticipated delivery date and shipping ID.

  13. A message is sent with the updated PO information.

This process could continue indefinitely, as status and other information is continually traded. In fact, this same dialogue could later include an invoice. The point is that we have two logical message senders and message receivers that engage in a dialogue of messages. That dialogue is long running, yet flexible and powerful enough to deliver everything required. Notice that e-mail integration is an obvious feature with this kind of system. If you require near real-time notification without history, an IM (instant messaging) system such as MSN Messenger would also be a likely candidate.

Summary

In any advanced messaging system, you will likely find the ability to create sessions and reliable dialogues. To review:

  • Sessions are sets of related messages.

  • Sessions contain a unique identifier (often a GUID) to name them.

It is possible to build reliable messaging systems on top of session technol ogy. These systems usually involve the following:

  • Numbering messages in a session

  • Acknowledging each individual message received

  • Requesting acknowledgments for specific messages

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.216.144.165