Posted by: rcosic | 26/03/2012

Creating simple UCMA application endpoint

After you have learned more about UCMA applications, and how to provision server-side, application endpoint on Lync server, it’s time to finally dwelve into real application development.

You will typically start new .NET project in Visual Studio, albeight a console application, NT service, WCF service, or similar. And yes, you can and you will develop on the Lync server directly (or some dedicated app server, if you decided to do so).

For sake of simplicity, I’ll create a windows service which will present one UCMA application endpoint. This endpoint will be known to Lync server, since it’s been previously registered by provisioning. The connection data I will provide in configuration settings, since it’s the easiest way to get it modifiable later. Next, I’ll create a logging class to be able to see what’s happening in the application during its execution. Windows service code will be available to other, console app, which I will create to be able to debug and run it easier. I think this is quite straightforward environment and I will not present it here in a whole. The remaining part is UCMA-related and this is what we have here:

Application Endpoint wrapper class

You will end up designing such a class if you want to manage application endpoints. It will serve to establish and manage a UCMA application endpoint. In this case, for sending and receiving instant messages only, so I’ve named it ImApplicationEndpoint. It contains CollaborationPlatform object, together with ApplicationEndpoint underlying object, some variables and wait handles for signalling of execution:

// underlying UCMA objects used
private CollaborationPlatform m_collaborationPlatform;
private ApplicationEndpoint m_appEndpoint;

// manual provisioning specific variables
private string m_strContactUri;
private string m_strProxyServerFqdn;

// auto provisioning specific variables
bool m_bIsAutoProvisioning;
int _endpointsDiscovered = 0;

private ILogger m_logger;

// a wait handles for startup and shutdown
private ManualResetEvent _startupWaitHandle = new ManualResetEvent(false);
private ManualResetEvent _shutdownWaitHandle = new ManualResetEvent(false);

/// <summary>
/// Gets the underlying UCMA application endpoint.
/// </summary>
internal ApplicationEndpoint ApplicationEndpoint
{
get
{
return m_appEndpoint;
}
}

I’ve exposed couple of methods (Start, Drain, Shutdown), together with blocking methods (WaitForStartup, WaitForShutdown), since all UCMA methods are asynchronous, and typically there is a need for sychronous flow in the applications. Depending on sort of provisioning (automatic or manual) I offered two Start methods. They both initialize CollaborationPlatform object with provided settings (either ProvisionedApplicationPlatformSettings for auto or ServerPlatformSettings for manual provisioning). Also, they both start up the platform, although in case of auto-provisioning, there’s an extra step – to register for application settings auto-discovery. In manual provisioning, these settings will be created on startup being ended:

/// <summary>
/// Starts the initialization process of app endpoint with auto provisioning.
/// </summary>
/// <param name=”strApplicationUserAgent”>application name</param>
/// <param name=”strApplicationId”>application identifier</param>
internal void Start(string strApplicationUserAgent, string strApplicationId)
{
m_logger.Log(“Begin establishing application endpoint (auto provisioning) …”);

m_bIsAutoProvisioning = true;

// auto-provisioning path

ProvisionedApplicationPlatformSettings settings = new ProvisionedApplicationPlatformSettings(strApplicationUserAgent, strApplicationId);

m_collaborationPlatform = new CollaborationPlatform(settings);

m_collaborationPlatform.RegisterForApplicationEndpointSettings(OnApplicationEndpointSettingsDiscovered);

m_collaborationPlatform.BeginStartup(OnPlatformStartupCompleted, null);         }

/// <summary>
/// Starts the initialization process of app endpoint with manual provisioning.
/// </summary>
/// <param name=”strApplicationUserAgent”>application name</param>
/// <param name=”nPort”>listening port</param>
/// <param name=”strGruu”>GRUU</param>
/// <param name=”strApplicationPoolFqdn”>application pool FQDN</param>
internal void Start(string strApplicationUserAgent, int nPort, string strGruu, string strApplicationPoolFqdn,
string strContactUri, string strProxyServerFqdn)
{
m_logger.Log(“Begin establishing application endpoint (manual provisioning) …”);
m_bIsAutoProvisioning = false;

// store manual provisioning parameters for later use
m_strContactUri = strContactUri;
m_strProxyServerFqdn = strProxyServerFqdn;

// manual provisioning path

string localhost = Dns.GetHostEntry(“localhost”).HostName;

ServerPlatformSettings settings = new ServerPlatformSettings(strApplicationUserAgent, localhost, nPort, strGruu,
CertificateHelper.GetLocalCertificate(strApplicationPoolFqdn));

m_collaborationPlatform = new CollaborationPlatform(settings);

m_collaborationPlatform.BeginStartup(OnPlatformStartupCompleted, null);
}

/// <summary>
/// A delegate to receive discovered application endpoint settings.
/// </summary>
/// <param name=”sender”></param>
/// <param name=”args”></param>
private void OnApplicationEndpointSettingsDiscovered(object sender, ApplicationEndpointSettingsDiscoveredEventArgs args)
{
Interlocked.Increment(ref _endpointsDiscovered);

if (_endpointsDiscovered > 1)
{                 return;             }

m_appEndpoint = new ApplicationEndpoint(m_collaborationPlatform, args.ApplicationEndpointSettings);

m_appEndpoint.BeginEstablish(OnApplicationEndpointEstablishCompleted, null);
}

/// <summary>
/// A callback for collaboration platform startup method.
/// </summary>
/// <param name=”result”></param>
private void OnPlatformStartupCompleted(IAsyncResult result)
{
try
{
m_collaborationPlatform.EndStartup(result);
m_logger.Log(“Collaboration platform started.”);

if (!m_bIsAutoProvisioning)
{
EstablishApplicationEndpoint();
}
}
catch (RealTimeException ex)
{
m_logger.Log(“Platform startup failed: {0}”, ex);
}
}

/// <summary>
/// Establishes an application endpoint (in case of manual provisioning).
/// </summary>
private void EstablishApplicationEndpoint()
{
int tlsPort = 5061;
ApplicationEndpointSettings settings = new ApplicationEndpointSettings(m_strContactUri, m_strProxyServerFqdn, tlsPort);

settings.IsDefaultRoutingEndpoint = true;
settings.AutomaticPresencePublicationEnabled = true;
settings.Presence.PresentityType = “automaton”;
settings.Presence.Description = “AlwaysOnlineBot”;

PreferredServiceCapabilities capabilities = settings.Presence.PreferredServiceCapabilities;
capabilities.InstantMessagingSupport = CapabilitySupport.Supported;
capabilities.AudioSupport = CapabilitySupport.UnSupported;
capabilities.ApplicationSharingSupport = CapabilitySupport.UnSupported;
capabilities.VideoSupport = CapabilitySupport.UnSupported;

m_appEndpoint = new ApplicationEndpoint(m_collaborationPlatform, settings);

m_appEndpoint.StateChanged += new EventHandler<LocalEndpointStateChangedEventArgs>(_appEndpoint_StateChanged);

m_logger.Log(“Establishing application endpoint…”);
m_appEndpoint.BeginEstablish(OnApplicationEndpointEstablishCompleted, null);
}

/// <summary>
/// A callback for application endpoint establish method.
/// </summary>
/// <param name=”result”></param>
private void OnApplicationEndpointEstablishCompleted(IAsyncResult result)
{
try
{
m_appEndpoint.EndEstablish(result);

m_logger.Log(“Application endpoint established.”);
m_logger.Log(“Contact URI: {0}”, m_appEndpoint.OwnerUri);
m_logger.Log(“Endpoint URI: {0}”, m_appEndpoint.EndpointUri);

// register for incoming instant messaging calls
m_appEndpoint.RegisterForIncomingCall<InstantMessagingCall>(OnIncomingInstantMessagingCallReceived);

_startupWaitHandle.Set();
}
catch (RealTimeException ex)
{
m_logger.Log(“Application endpoint establishment failed: {0}”, ex);
}
}

Let me point out some things around here..

When auto-provisioning is chosen, there are no settings provided to the UCMA except for application name and identifier (urn address of the application). UCMA will try to connect to the application endpoint and return the result in the provided handler. There you have to do nothing, except for initializing application endpoint with returned settings, and calling BeginEstablish method.  Au contraire, if you choose to manually configure endpoint, you’ll have to provide all the necessary parameters to construct ServerPlatformSettings object (localhost address, port, GRUU, certificate, etc.) and then, after startup of platform was successful, establish application endpoint on yourself. But then you have freedom to choose many of the options UCMA apps have, such as: auto-presence publication, defining types of presentity and capabilities chosen. In any case, this is a good place to subscribe to endpoint’s StateChanged event, and to notify consumer that the setup was completed.

In this particular case, we want to be able to send and receive IM messages back and forth. So, there is also a delegate, an event, and a registration to this event on application endpoint’s establish completed handler:

internal delegate void InstantMessagingCallReceived(InstantMessagingCall call);

/// <summary>
/// Occurs when a new instant messaging call is received.
/// </summary>
internal event InstantMessagingCallReceived OnInstantMessagingCallReceived;

/// <summary>
/// A delegate to handle incoming calls for a specific modality.
/// </summary>
/// <param name=”sender”></param>
/// <param name=”e”></param>
private void OnIncomingInstantMessagingCallReceived(object sender, CallReceivedEventArgs<InstantMessagingCall> e)
{
try
{
// accept the incoming call
e.Call.BeginAccept(result =>
{
try
{
e.Call.EndAccept(result);
m_logger.Log(“Accepted incoming IM call from {0}”, e.RemoteParticipant.Uri);

// notify consumers about incoming IM call
if (OnInstantMessagingCallReceived != null)
{
OnInstantMessagingCallReceived(e.Call);
}
}
catch (RealTimeException ex)
{
m_logger.Log(“Failed accepting incoming IM call.”, ex);
}
}, null);
}
catch (InvalidOperationException ex)
{
m_logger.Log(“Failed accepting incoming IM call.”, ex);
}
}

What is left is to correctly close the connection to the endpoint. There are two ways on how to achieve this: by terminating or draining the endpoint. The difference between those two methods is that in terminating all active connections are being immediately shut down. So, if there are some calls active, they would be ended. In case of draining, UCMA will wait for all connections to terminate themselves, and then the termination will follow. Here are the methods, together with blocking methods code:

/// <summary>
/// Shuts down application endpoint.
/// </summary>
internal void ShutDown()
{
m_logger.Log(“Terminating application endpoint…”);

m_appEndpoint.BeginTerminate(OnApplicationEndpointTerminateCompleted, null);
}

/// <summary>
/// Waits for startup to finish.
/// </summary>
internal void WaitForStartup()
{
_startupWaitHandle.WaitOne();
}

/// <summary>
/// Waits for shutdown to finish.
/// </summary>
internal void WaitForShutdown()
{
_shutdownWaitHandle.WaitOne();
}

/// <summary>
/// ‘Drains’ application endpoint
/// (waits for any pending connection to be shut down before it is shut down).
/// </summary>
internal void Drain()
{
m_logger.Log(“Draining endpoint…”);

try
{
m_appEndpoint.BeginDrain(OnDrainingCompleted, null);
}
catch (InvalidOperationException ex)
{
m_logger.Log(“Failed draining endpoint.”, ex);
}
}

Next time, more about creating UCMA wrapper class for defining calls…

Kind regards,
Ratko.

Advertisements

Responses

  1. I have met a problem, could you give me a hand?
    I try to make a windows service application, but it can’t start.
    I found it blocked at ” ProvisionedApplicationPlatformSettings settings = new ProvisionedApplicationPlatformSettings(“application”, _appID);”

    Does there need any other operations?
    Thanks ^_^

    • This way doesn’t work on my environment either. I am effectively using the manual provisioning, since I also set some other parameters and this way works for sure. Sorry if this answer doesn’t suits your needs.

      Kind regards,
      Ratko.

      • I sloved that issue 🙂 My ucma runtime is based on x64 while my project is built for x86, so the problem came. But after sloving this, I got an UnauthorizedException when create a ServerAgent object and my service is registed as ‘local system’, do you have any ideas about this?

        Best regards,
        Yancheng.


Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Categories

%d bloggers like this: