I must admit that while teaching the UCMA API, I have been learning about it myself.  Today, my hope was to tell you how to create a client that sends a SUBSCRIBE message to the server and then receives NOTIFY messages from it.  However, as I’ll explain shortly, I ran into a small problem.

 

For those of you not familiar with SUBSCRIBE…NOTIFY (also called a “subnot” dialog) it functions somewhat like events in other programming languages.  The idea is you send a SUBSCRIBE message to an endpoint specifying that you are interested in a particular event and the endpoint sends you NOTIFY messages whenever that event occurs.  For completeness sake, another message type exists that is called BENOTIFY that stands for “best effort notify” and is the same as a NOTIFY message except the client does not need to send an OK response.

 

There are a number of cases where this is very useful and we use this in several areas of Microsoft Office Communications Server internally.  The main issue I came across, though, is UCMA only allows for sending SUBSCRIBE messages and receiving NOTIFY messages.  There is no provision to receive SUBSCRIBE messages and send NOTIFY messages.  Therefore, we can add this functionality to our client but we cannot add it to our server.

 

I have therefore decided against adding this functionality to our IDKStudioClient since we will never get a response from the server but it is still worth noting how one can implement just in case there is a server (written with something other than UCMA) that can correspond with it.

 

A SUBSCRIBE…NOTIFY series is called a subnot dialog for a purpose – it is a dialog itself and therefore an INVITE is not necessary here, so a SignalingSession is not necessary here.  Instead you will use the SipSubscription class and the ISipSubscriptionProcessor interface.  In order to create a SipSubscription object, you will need the following.

 

-          A RealTimeEndpoint

-          The address of the server you are sending the SUBSCRIBE message to

-          A string containing the event package name (similar to the event you are subscribing to in most programming languages)

-          An ISubscriptionProcessor object

 

The following piece of code could be used to create a SipSubscription in the client.

 

_subscription = new SipSubscription(_endPoint,

                                    new RealTimeAddress(Settings.OurServerName),

                                    "packagename",

                                    this);

 

The ISubscriptionProcessor interface contains the following methods.

 

·         GetExtensionHeaders – Depending on the request type, this allows you to add any other headers you wish.

·         GetMessageBody – The name should explain it.

·         ProcessErrorResponse – You can ignore this for now.

·         ProcessNotification – This method is called whenever a NOTIFY message is received.

·         SubscriptionStateChanged – This method is called whenever the state of the subscription has been altered.

 

If you wish to add this code to the IDKStudioClient, feel free to implement ISubscriptionProcessor and add the following code.

 

/// <summary>

/// Gets the headers for the message depending on the request type

/// </summary>

/// <param name="requestType"></param>

/// <param name="extensionHeaders"></param>

public void GetExtensionHeaders(SipSubscription.RequestType requestType, out IEnumerable<SignalingHeader> extensionHeaders)

{

    // We do not require any special headers

    extensionHeaders = null;

}

 

/// <summary>

/// Gets the body for the message depending on the request type

/// </summary>

/// <param name="requestType"></param>

/// <param name="contentType"></param>

/// <param name="messageBody"></param>

public void GetMessageBody(SipSubscription.RequestType requestType, out ContentType contentType, out byte[] messageBody)

{

    // If we are subscribing, we need to send the keywords

    if (requestType == SipSubscription.RequestType.Subscribe)

    {

        // reate a list of the keywords separated by semicolons

        RecordProgress("Getting the message body.");

        string message = "interesting stuff";

 

        // set the content type to text

        contentType = new ContentType("text/plain");

 

        // Set the body to the keywords

        messageBody = Encoding.UTF8.GetBytes(message);

    }

    else

    {

        contentType = null;

        messageBody = null;

    }

}

 

/// <summary>

/// Not currently used by the SipSubscription class

/// </summary>

/// <param name="message"></param>

public void ProcessErrorResponse(SipResponseData message)

{

    RecordProgress("We received an error response that we should not have.");

}

 

/// <summary>

/// Occurs when we get a notification. In our case it means we have found

/// a keyword.

/// </summary>

/// <param name="message"></param>

public void ProcessNotification(SipMessageData message)

{

    RecordProgress("We received a notification.");

 

    // Make sure we can read the message

    if (message.ContentType.MediaType == "text/plain")

    {

        RecordProgress("The message does not have a text body.");

    }

    else

    {

        // The message is the keyword

        string messageText = message.GetMessageBodyString();

        RecordProgress("Received the notification {0}", messageText);

    }

}

 

/// <summary>

/// Indicates a change in the subscription state has occurred

/// </summary>

/// <param name="eventArg"></param>

public void SubscriptionStateChanged(SubscriptionStateChangedEventArgs eventArg)

{

    switch (eventArg.NewState)

    {

        case SubscriptionSignalingState.Subscribed:

            RecordProgress("New subscription state = Subscribed");

            break;

        case SubscriptionSignalingState.Subscribing:

            RecordProgress("New subscription state = Subscribing");

            break;

        case SubscriptionSignalingState.Terminated:

            RecordProgress("The subscription has terminated.");

            break;

        case SubscriptionSignalingState.Terminating:

            RecordProgress("The subscription is terminating.");

            break;

        case SubscriptionSignalingState.WaitingForRetry:

            RecordProgress("Subscription is waiting for retry.");

            break;

    }

}

 

In order to actually send the SUBSCRIBE message, you will need to call the BeginSubscribe method in SipSubscription.  To cancel the subscription, call BeginTerminate.  The default implementation of SipSubscription will refresh the subscription every 15 minutes but there are provisions to take this over yourself.

 

Tomorrow I think it is time to cover SipPeerToPeerEndpoint and see what we can accomplish with it.