General Guidelines
This section describes general guidelines and tips for all developers of applications built using Microsoft Unified Communications Managed API (UCMA) 3.0.
The UCMA 3.0 platform uses queues to dispatch events and callback methods. There is a queue for events and callbacks on the CollaborationPlatform class. The endpoint has a queue for its events and callbacks. Each conversation has its own queue for all components that are part of the conversation such as calls, conference session, and media flows. Incoming calls are exposed in the queue of the corresponding conversation rather than in the queue of the endpoint so that the calls are not serialized in a single queue. Queue processing is done using a thread pool queue.
If an application performs a long-lasting synchronous operation in a queue-processing thread, it is blocking queue operations. For this reason, the application will not receive subsequent events and callbacks from that queue until the thread returns control to the platform. If the synchronous operation being performed on that thread depends on subsequent events or callbacks, the application might end up creating a deadlock situation. As a result, it is highly recommended for the application to ensure that the queue thread is not blocked. The application should process queue events quickly. One way to do this is to use the .NET Framework thread pool queue for work that must be done based on the callback or event.
The UCMA 3.0 platform can throw InvalidOperationException for BeginXxx operations when the internal state of the component does not permit the operation. It is important for the application to catch this exception whenever these operation APIs are called. The EndXxx operations can throw any exception derived from RealTimeException. For specific exceptions, see UCMA 3.0 Core Exception Model. If the application is intended to handle specific exceptions, it should catch them before catching RealTimeException. The last catch block is normally used for catching exceptions of type RealTimeException. Note that any API can throw standard exceptions such as ArgumentException, that should be caught during development or when the application passes data from other sources (such as Web or IM messages) without validating them first.
While every effort has been taken not to throw exceptions other than those mentioned in the documentation, under certain unusual conditions, other exceptions might be thrown. If an exception escapes a worker thread, such as when the application throws an exception that the platform does not catch, the process crashes. The UCMA 3.0 platform exposes the UnhandledExceptionManager class for such cases so that the application can decide between allowing the application to crash or logging the event and ignoring the exception. This is a valuable mechanism to use when the application is being tested. After testing, this code in the production release can be removed or scoped down (by ignoring a small number of exceptions such as RealTimeException, InvalidOperationException, or ArgumentException) after the error cases are discovered and fixed. Note that the .NET Framework guidelines do not recommend blindly catching all exceptions to prevent application crash.
Some UCMA 3.0 components can terminate themselves under certain conditions. For example, a registered endpoint can be terminated when the server sends a notification to deregister the endpoint for reasons such as “Too many endpoints”, “User disabled”, or “Endpoint removed”. A conversation terminates when either the last call in the conversation is terminated or the conference session is terminated. A flow terminates if it detects that there is no media data or control flow for an extended period of time. A call terminates when the remote sends a BYE message. Applications can detect these events based on state transitions and can ensure that the corresponding objects are cleaned up and released. The application should not use references to these objects after clean up so that they can be garbage collected. There is no harm in terminating a component that is already terminated. The terminate operation in various components should not throw InvalidOperationException or any other exception other than ArgumentException when invalid arguments are passed.
Many components expose events that can be raised when some operations are invoked or relevant network messages are received. To ensure that the application does not miss these events due to race conditions, event registration should be done before invoking the relevant operations. For example, an application should register for endpoint discovery events before starting the platform, register for state change events before establishing an endpoint, register for participant change events in a conversation before making or accepting calls, and so on. Many components provide API to obtain a snapshot of data as well, but there should be no need to use them if the application registers for these data events and monitors them from the beginning. For example, if the application registers for the PropertiesChanged event on the Conversation class, it is not necessary to use the GetActiveMediaTypes() method.
It is important to ensure that a long-lived object does not refer to a short-lived object when the lifetime of short-lived object is over. Otherwise, these objects will not be garbage collected. For example, if a long-lived object has a callback registered on a short-lived object, then it should be unregistered when the short-lived object is terminated. Using anonymous events might avoid these problems but the code can become harder to read. One advantage of using anonymous delegates is the ability to easily refer to variables in the enclosed parent blocks or method parameters.
An application endpoint (ApplicationEndpoint) is designed to be resilient to network issues. For example, this endpoint type has retry mechanisms for failures such as registration refresh, in-band provisioning data request, and media relay server token requests that are needed for audio calls. If the endpoint is in the Established state, there is no known reason for communications with other endpoints to fail. If the endpoint is in the Reestablishing state, incoming communications might be affected, although it is possible for outgoing communications to work when the state is Reestablishing. It is the responsibility of the application to retry some operations when they fail. For example, when a session is in recovery mode due to SIP dialog resiliency, operations on the session can fail. Similarly, if the next-hop proxy is down or unreachable, outgoing calls can fail to establish or a conference join operation can fail to join the focus. The application can either report these failures to the user or retry them if the operations are critical.
The UCMA 3.0 platform stores incoming calls in internal queues to be delivered to the application. When there is a heavy load on the system, new incoming calls that have not yet been delivered to the application can cause the internal buffers to grow in size. As a result, applications will likely need more time to process the queued calls. An application that must control the number of such messages in internal queues can set high-water and low-water limits for the messages on the collaboration platform (setting the ConnectionThrottlingHighMark and ConnectionThrottlingLowMark properties on RealTimeConnectionManager, accessible through the ConnectionManager() property on the CollaborationPlatform instance). When the number of incoming messages (calls or conference invitations) exceeds the high-water limit, new messages are automatically rejected by the platform until the number of incoming messages falls below the low-water limit. Obviously, the low-water limit should be smaller than the high water mark. This is an advanced API that should be used only after some experimentation with normal cases has been performed. For example, an application can set a very large high-water limit and monitor the number of messages in internal buffers during normal or high-volume load to determine the appropriate levels for the high-water and low-water limits.
Applications that are primarily based on serving audio calls typically use speech technology and workflows. The number of audio calls that can be handled by such applications can be measured based on memory usage and CPU load on a given machine configuration. It is recommended that an application of this type should have mechanisms in place to prevent situations in which the application must process more calls than it is able to handle. To mitigate this situation, the application can play music for overflow calls until the load declines, or it can reject the overflow calls.
The UCMA 3.0 platform does not permit two or more application endpoints to have the same owner URI. If the application is able to shut down and restart application endpoints, it should ensure that the first application endpoint is fully terminated before it creates another application endpoint with the same owner URI.
Application developers ordinarily should develop a mechanism to test their application for functionality, stress, and long haul. The stress can be useful to ensure that the application does not crash or cause memory leaks that can occur due to bugs in the application. Applications can use the UCMA 3.0 Core SDK itself to build test clients that provide the load to their application. If there are many application instances (running on different machines using DNS load balancing), it might be necessary to increase the number of clients targeting the application. The application developer can use numerous instances of their own stress client simulation tools for this purpose.
The UCMA 3.0 platform is designed and tested using .NET Framework 3.5. It has not been tested on .NET Framework 4.0, and so is not supported. Application developers who use UCMA 3.0 with .NET Framework 4.0 do so at their own risk.
There are a number of quick start samples and end-to-end samples shipped with UCMA 3.0 Core SDK that might be useful to application developers. There also are numerous code snippets provided in this documentation for selected methods. These samples are written by a number of people, so there are some style differences in these samples, even though the sample writers used a set of common guidelines. Application developers can determine the appropriate style that is best suited for their intended use. The end-to-end samples are highly recommended as starting points before embarking on application development using the UCMA 3.0 platform.
When it is not clear how to handle a particular situation using UCMA 3.0, or when there are questions that are not answered in this documentation, application developers are encouraged to post any such questions on the UCMA 3.0 forums.