Partager via


Creating a SIP Application for Live Communications Server

 
Microsoft Office Live Communications Server 2005 with SP1

Creating a SIP Application for Live Communications Server

A SIP application is defined as a managed code executable that:

The following sections detail the basic steps for creating a SIP application.

Setting up a Server Agent

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

The first step in setting up a Live 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);
	}
}

 

This ServerAgent class is instantiated by calling the class constructor and supplying the ApplicationManifest object that contains the compiled application manifest, as seen 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 Live 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);

Processing 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 Live 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 Live 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 Live Communications server generated event (calls to Dispatch in the message filter, namely) being added to a work queue obtained from the thread pool.

Handling Messages from the Live Communications Server

To receive messages from the Live 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 Live 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.

Creating and Managing Transactions

The Microsoft.Rtc.Sip namespace defines a base type, Transaction, to represent the basic properties of a SIP transaction. This includes the request that originated the transaction, any responses handled by the transaction, and events for catching transaction status changes (such as time-outs or cancellations).

Two types derive from Transaction: ClientTransaction, which represents a client transaction; and ServerTransaction, which represents a server transaction.

The ServerTransaction instance is obtained by referencing the RequestReceivedEventArgs.ServerTransaction property, available when a RequestReceivedEventArgs object is dispatched to the designated method. The request it was created to service can be obtained in two ways: one, as the RequestReceivedEventArgs.Request property; or two, as the ServerTransaction.Request property.

Client transactions must be created programmatically by calling ServerTransaction.CreateBranch. The UAC case, in which a client transaction originates a request and does not have a parent server transaction, is not supported.

To fork a message, ServerTransaction.CreateBranch can be called as many times as forks are needed. To send a request to each fork, call ClientTransaction.SendRequest on each branch.

Responses to serviced requests are obtained by registering a handler for the ClientTransaction.ResponseReceived event. When this event is raised, any response received by the Live Communications Server that matches the originating request will be sent as a ResponseReceivedEventArgs object to the method registered using the ResponseReceivedEventHandler delegate.

Responses can also be raised by dispatches from the message filter script when it is set to filter responses. In this case, the response will be out-of-context—there is no ClientTransaction object on the server that originated the request. In this case, the new ClientTransaction object is obtained by referencing the ResponseReceivedEventArgs.ClientTransaction property. The ServerTransaction to forward the response is obtained by referencing the ClientTransaction.ServerTransaction property on this ClientTransaction, which contains the new ServerTransaction instance to handle it.

If a CANCEL method is received for a transaction, the Transaction.Canceled event is raised. When the transaction is closed, Transaction.Terminated is raised. If a transaction times out, Transaction.TimedOut is raised.

SIP Application Behaviors

Applications can behave only as proxies or user agent servers (UAS) in the current version of this API. For more information pertinent to the specific types of application, see the following documentation:

  
  What did you think of this topic?
  © 2008 Microsoft Corporation. All rights reserved.