Creating a SIP Managed Application for Office Communications Server

This content is no longer actively maintained. It is provided as is, for anyone who may still be using these technologies, with no warranties or claims of accuracy with regard to the most recent product version or service release.

Creating a SIP Application for Office Communications Server

When writing a SIP managed application, start by writing code to do the following.

  • Create and Compile an Application Manifest

  • Set up a Server Agent

  • Process Server Events

  • Define the Message Event Handler

Create and Compile an Application Manifest

The first step in setting up a Office Communications Server agent is to create an application manifest and compile it within your application. The application manifest contains a message filter script that will run on the server and dispatch only those SIP messages your application is interested in receiving. This action is usually performed in the Main method of the application object, as follows:

using System.Threading;
using System.Resources;
using Microsoft.Rtc.Sip;
...



// Obtain an XML string that contains your application manifest.
// In the sample below, the XML string is contained with the
// application assembly as a resource, and retrieved using the
// ResourceManager.GetString(resourceName) method.

ResourceManager myResourceManager = new ResourceManager(myApplicationClass);

// The "appManifest" string should be the name you gave your application
// manifest XML file when you added it as a resource to your application's
// assembly.

string myAppManifestXmlString = myResourceManager.GetString("appManifest");

ApplicationManifest myAppManifest = new ApplicationManifest(myAppManifestXmlString);

try {
myAppManifest.Compile();
}
catch (CompilerErrorException cee) {
Console.WriteLine("The following errors occurred during compilation:");
foreach (string errorMessage in cee.ErrorMessages) {
   Console.WriteLine("--- {0}", errorMessage);
}
}

Set up a Server Agent

The server agent is the common point of communication between the Office Communications Server and your application. This object, defined as the ServerAgent class, sends and receives messages through the Office Communications Server on behalf of your application.

This ServerAgent object is instantiated by calling the class constructor and supplying the ApplicationManifest object that contains the compiled application manifest, as illustrated in the following code example.

try {
ServerAgent myAppServerAgent = new ServerAgent(myAppManifest);
}
catch (NullReferenceException nre) {
Console.WriteLine("The application manifest is uncompiled and ServerAgent cannot be instantiated.");
}
catch (ServerNotFoundException snfe) {
Console.WriteLine("The Office Communications Server is not available.");
}

The following line appears after the application manifest has been successfully compiled. If the application manifest has not been compiled or has generated errors, the ServerAgent object cannot be correctly instantiated and will generate an exception. Therefore, your code should always ensure that the application manifest compiled successfully before instantiating a ServerAgent.

Your application must handle messages dispatched from the message filter. To do this, you must create a class with the methods that handle those messages. For example, if you have a call in your message filter like

Dispatch("OnInviteReceived");

you must have the corresponding OnInviteReceived method defined on a class that will be registered with the server agent.

With the handlers defined, you create an instance of the ServerAgent class by calling the constructor, passing in an instance of the class with the previously defined dispatch handler methods and the ApplicationManifest object containing the compiled application manifest. For example:

ServerAgent myServerAgent = new ServerAgent(myClassWithDispatchMethods, myAppManifest);

If your application is script-only, and all of the processing logic is included in the message filter (such that Dispatch is never called), you can use the constructor that takes only the application manifest as a parameter.

ServerAgent myScriptOnlyServerAgent = newServerAgent(myAppManifest);

Process Server Events

When Dispatch is called from the MSPL script, a server event is raised. It is the responsibility of the application author to consume this event.

The signal for a server event is raised over the wait handle provided by the ServerAgent.WaitHandle property. This signal is consumed by calling the WaitOne (or WaitAny, if other application signals are being handled) method on the ServerAgent instance. If this method returns true (or a positive integer containing the index to the signaled callback in your handle array, in the case of WaitAny), the ProcessEvent method must be called on the ServerAgent instance. ProcessEvent passes the dispatched message to the event handler specified in the call to Dispatch.

Each application has its own process, and is responsible for creating its own threads. While the Office Communications Server Application API suite does not impose any specific threading model, the .NET Framework provides a ThreadPool class that can be used for this particular purpose. Using the ThreadPool and creating a queue for events is the simplest mechanism, as illustrated in this code example, which services a single input (the Office Communications Server):

ManualResetEvent autoResetEvent = new ManualResetEvent(false);
WaitHandle[] handleArray = new WaitHandle[] {
                              myAppServerAgent.WaitHandle,
                              manualResetEvent
                           };

WaitCallback waitCallback = new WaitCallback(myAppServerAgent.ProcessEvent);

while (true)
{
   int signaledEvent = WaitHandle.WaitAny(handleArray);

   if (signaledEvent == 0)  // The server event wait handle (index = 0) in handleArray was signaled
   {

       // Schedule a worker thread to process the server event.
       try
       {
          if (!ThreadPool.QueueUserWorkItem(waitCallBack))
          {
              Console.WriteLine("QueueUserWorkItem fails, quitting.");
              return;
          }

       }
       catch (Exception e)
       {
          Console.WriteLine("Unexpected exception: {0}\n{1}",
                            e.Message,
                            e.StackTrace);
       }
    }
    else // Manual reset event handle (index = 1) in handle array was signaled
    {
       Console.WriteLine("Quit handle signaled, worker will quit now\n");
       break;
    }
}

Note that the ServerAgent class has a callback property used specifically for event signaling: ServerAgent.ProcessEvent. Passing this callback to a WaitCallback delegate results in each Office Communications Server generated event (calls to Dispatch in the message filter, namely) being added to a work queue obtained from the thread pool.

Define the Message Event Handler

To receive messages from the Office Communications Server, your application manifest must specifically dispatch them to the application. Within the application manifest, the MSPL built-in function Dispatch is used to send the current message to a supplied handler in your application. For example, in your application manifest, the following script might appear:

if (sipRequest.Method == "MESSAGE")
Dispatch("OnMessageReceived");
Respond(200);
}

This script causes the Office Communications Server to dispatch any message with a SIP method type of "MESSAGE" to the "OnMessageReceived" event handler in your application, passing the request as a RequestReceivedEventArgs object. You define the handler as follows:

public void OnMessageReceived(object sender, RequestReceivedEventArgs requestEventArgs) {

// Obtain the Request object to process from RequestReceivedEventArgs.
Request request = requestEventArgs.Request;

// Obtain the new server transaction for this request.
ServerTransaction myServerTransaction = requestEventArgs.ServerTransaction;

// Processing logic here.
// ...

// Create a branch on the server transaction to forward the request.
try
{
   // Attempt to send the request.
   requestEventArgs.ServerTransaction.CreateBranch();
   requestEventArgs.SendRequest(request);
}
catch (Exception e)
{
   Console.WriteLine("Unexpected exception: {0}\n{1}",
                            e.Message,
                            e.StackTrace);
}
}

Note that the method signature of your message handler must match that of the RequestReceivedEventHandler delegate (taking an object as the first parameter, and a RequestReceivedEventArgs reference as the second).

Again, to physically dispatch the methods, ServerAgent.ProcessEvent must be called when a server event is signaled with ServerAgent.WaitHandle. This method passes the dispatched message to the event handler registered with the corresponding MSPL Dispatch call.