I have run the sample Subscribe presenceview which worked great, but I need to do the same with an Application endpoint against target users.
So I modified the Manual provisioning sample and added the code for subscribing to presence for a target user, the code is the same as the working presenceview example.
The problem is that I only receive the initial presence notification when I use Persistent mode. No further notifications after that. (Polling mode with 5 minutes interval works though), but persistent does not notify me when I change the presence of the target.
The only difference from the working Presenceview example is that I am using an Application Endpoint in a trusted application pool in my example:
********************************************************
*
*
* Copyright (C) Microsoft. All rights reserved. *
*
*
********************************************************/
// .NET namespaces
using System;
using System.Collections.Generic;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
// UCMA namespaces
using Microsoft.Rtc.Collaboration;
using Microsoft.Rtc.Collaboration.Presence;
using Microsoft.Rtc.Signaling;
// UCMA samples namespaces
using Microsoft.Rtc.Collaboration.Sample.Common;
namespace Microsoft.Rtc.Collaboration.Sample.ManualProvisioning
{
public class UCMASampleManualProvisioning
{
#region Locals
#region UCMA 3.0 Core Classes
/// <summary>
/// The settings typically used for creating a CollaborationPlatform
/// instance to be used for manually creating ApplicationEndpoints.
/// </summary>
private ServerPlatformSettings _platformSettings;
/// <summary>
/// The highest-level API in UCMA, responsible for managing connections
/// between servers, establishing trust with other servers and
/// providing control over all the endpoints bound to it.
/// </summary>
private CollaborationPlatform _platform;
/// <summary>
/// The settings typically used for creating an ApplicationEndpoint.
/// </summary>
private ApplicationEndpointSettings _endpointSettings;
/// <summary>
/// The endpoint designed to be globally trusted by other server
/// components.
/// </summary>
private ApplicationEndpoint _endpoint;
#endregion
#region Configuration Settings
/// <summary>
/// The part of the user agent string that identifies the application.
/// </summary>
private const string _applicationUserAgent = "UCMASampleApp";
/// <summary>
/// Fully Qualified Domain Name (FQDN) of the computer in the trusted
/// application pool.
/// </summary>
private string _applicationHostFQDN;
/// <summary>
/// Port assigned to the trusted application.
/// </summary>
private int _applicationHostPort;
/// <summary>
/// GRUU assigned to this computer for the trusted application.
/// </summary>
private string _computerGRUU;
/// <summary>
/// Friendly name of the certificate identifying this computer.
/// </summary>
/// <remarks>
/// See
http://msdn.microsoft.com/en-us/library/ms788967.aspx for help
/// viewing certificates with the MMC snap-in.
/// </remarks>
private string _certificateFriendlyName;
/// <summary>
/// Certificate identifying this computer.
/// </summary>
private X509Certificate2 _certificate;
/// <summary>
/// SIP URI assigned to the trusted application endpoint.
/// </summary>
private string _endpointOwnerURI;
/// <summary>
/// FQDN of the registrar pool to which this trusted application
/// endpoint is assigned.
/// </summary>
private string _registrarFQDN;
/// <summary>
/// Port of the registrar pool to which this trusted application
/// endpoint is assigned.
/// </summary>
private int _registrarPort;
private RemotePresenceView _persistentView;
private RemotePresenceView _pollingView;
private RemotePresentitySubscriptionTarget _target;
#endregion
#endregion
#region Methods
/// <summary>
/// Instantiate and run the ManualProvisioning quickstart.
/// </summary>
/// <param name="args">unused</param>
public static void Main(string[] args)
{
UCMASampleManualProvisioning ucmaSampleManualProvisioning
= new UCMASampleManualProvisioning();
ucmaSampleManualProvisioning.Run();
}
/// <summary>
/// Retrieves the application configuration and begins starting up the
/// platform.
/// </summary>
private void Run()
{
// Prompt for the settings necessary to initialize the
// CollaborationPlatform and the ApplicationEndpoint if they are
// not declared in App.config.
// TODO (Left to the reader): Input sanitization on the
// collected parameters.
_applicationHostFQDN = UCMASampleHelper.PromptUser(
"Please enter the FQDN assigned to this computer in the trusted application pool => ",
"TrustedAppComputerFQDN");
if (string.IsNullOrEmpty(_applicationHostFQDN))
{
UCMASampleHelper.WriteErrorLine(
"No FQDN was found in App.config or input by the user.");
}
string inputPort = UCMASampleHelper.PromptUser(
"Please enter the port assigned to this trusted application => ",
"TrustedAppPort");
if (!int.TryParse(inputPort, out _applicationHostPort))
{
UCMASampleHelper.WriteErrorLine(
"Port could not be parsed from App.config or from input by the user.");
}
_computerGRUU = UCMASampleHelper.PromptUser(
"Please enter the GRUU assigned to this computer for this trusted application => ",
"TrustedAppComputerGRUU");
if (string.IsNullOrEmpty(_computerGRUU))
{
UCMASampleHelper.WriteErrorLine("No GRUU was found in App.config or input by the user.");
}
_certificateFriendlyName = UCMASampleHelper.PromptUser(
"Please enter the friendly name of the certificate identifying this computer => ",
"CertificateFriendlyName");
if (string.IsNullOrEmpty(_certificateFriendlyName))
{
UCMASampleHelper.WriteErrorLine(
"No certificate friendly name was found in App.config or input by the user.");
}
_certificate = UCMASampleHelper.GetLocalCertificate(_certificateFriendlyName);
if (_certificate == null)
{
UCMASampleHelper.WriteErrorLine("Certificate with friendly name '" + _certificateFriendlyName
+ "' could not be found in computer account Personal certificate store.");
}
_endpointOwnerURI = UCMASampleHelper.PromptUser(
"Please enter the SIP URI assigned to this trusted application endpoint => ",
"TrustedAppEpOwnerURI");
if (string.IsNullOrEmpty(_endpointOwnerURI))
{
UCMASampleHelper.WriteErrorLine("No SIP URI was found in App.config or input by the user.");
}
_registrarFQDN = UCMASampleHelper.PromptUser(
"Please enter the FQDN of the registrar pool to which this endpoint is assigned => ",
"RegistrarFQDN");
if (string.IsNullOrEmpty(_registrarFQDN))
{
UCMASampleHelper.WriteErrorLine(
"No registrar pool FQDN was found in App.config or input by the user.");
}
string inputRegistrarPort = UCMASampleHelper.PromptUser(
"Please enter the port used by the registrar pool to which this endpoint is assigned => ",
"RegistrarPort");
if (!int.TryParse(inputRegistrarPort, out _registrarPort))
{
UCMASampleHelper.WriteErrorLine(
"Registrar port could not be parsed from App.config or from input by the user.");
}
try
{
// Create the CollaborationPlatform using the
// ServerPlatformSettings.
_platformSettings = new ServerPlatformSettings(_applicationUserAgent,
_applicationHostFQDN,
_applicationHostPort,
_computerGRUU,
_certificate);
_platform = new CollaborationPlatform(_platformSettings);
// Initialize and startup the platform. EndPlatformStartup()
// will be called when the platform finishes starting up.
UCMASampleHelper.WriteLine("Starting platform...");
_platform.BeginStartup(PlatformStartupCompleted, _platform);
}
catch (ArgumentNullException argumentNullException)
....
....
finally
{
// Wait for the sample to finish before shutting down the
// platform and returning from the main thread.
UCMASampleHelper.WaitForSampleFinish();
// It is possible the platform was never created due to issues
// collecting configuration parameters.
if (_platform != null)
{
// Shutdown the platform, thereby terminating any attached
// endpoints.
UCMASampleHelper.WriteLine("Shutting down the platform...");
_platform.BeginShutdown(PlatformShutdownCompleted, _platform);
}
}
}
/// <summary>
/// Acquire settings necessary for sample to run.
/// </summary>
private void AcquireSettings()
{
}
#endregion
#region Callback Delegates
/// <summary>
/// Callback from <code>BeginStartup</code> method on platform.
/// </summary>
/// <param name="result">
/// Status of the platform startup operation.
/// </param>
private void PlatformStartupCompleted(IAsyncResult result)
{
// Extract the platform that was passed in as the state argument to
// the BeginStartup() method.
CollaborationPlatform platform = result.AsyncState as CollaborationPlatform;
if (platform == null)
{
UCMASampleHelper.WriteErrorLine("CollaborationPlatform not passed into BeginStartup() method.");
UCMASampleHelper.FinishSample();
return;
}
try
{
// Determine whether the startup operation completed
// successfully.
platform.EndStartup(result);
UCMASampleHelper.WriteLine("Platform has been started.");
}
}
......
try
{
// Create the ApplicationEndpointSettings.
_endpointSettings = new ApplicationEndpointSettings(_endpointOwnerURI,
_registrarFQDN,
_registrarPort);
// Create the ApplicationEndpoint from the
// ApplicationEndpointSettings and bind it to the platform.
_endpoint = new ApplicationEndpoint(platform, _endpointSettings);
// Bind an event handler to notify when the endpoint changes
// state.
_endpoint.StateChanged += new EventHandler<LocalEndpointStateChangedEventArgs>(
_endpoint_StateChanged);
// Establish the endpoint so that it can receive incoming calls
// and conference invitations. EndEndpointEstablish() will be
// called when the endpoint finishes establishment.
_endpoint.BeginEstablish(EndpointEstablishmentCompleted, _endpoint);
UCMASampleHelper.WriteLine("Endpoint has been established.");
}
catch (ArgumentNullException argumentNullException)
.....
}
/// <summary>
/// Helper to wire up important event handlers for a RemotePreenceView.
/// </summary>
/// <param name="view">View to add event handlers to.</param>
private void WireUpHandlersForView(RemotePresenceView view)
{
Console.WriteLine("\nWiring up handlers for view: " + view.ApplicationContext);
view.SubscriptionStateChanged += RemotePresenceView_SubscriptionStateChanged;
view.PresenceNotificationReceived += RemotePresenceView_NotificationReceived;
}
private void subscribeToPresence(LocalEndpoint ep)
{
ep.RemotePresence.PresenceSubscriptionCategories =
new string[] { PresenceCategoryNames.ContactCard, PresenceCategoryNames.State };
var persistentSettings = new RemotePresenceViewSettings();
persistentSettings.SubscriptionMode = RemotePresenceViewSubscriptionMode.Default;
_persistentView = new RemotePresenceView(ep, persistentSettings);
_persistentView.ApplicationContext = "Persistent View";
// Wire up event handlers for the view
this.WireUpHandlersForView(_persistentView);
// Create a RemotePresenceView with a polling subscription mode
// This type of view can represent a list of people
// on the To: line of an e-mail, for example.
var pollingSettings = new RemotePresenceViewSettings();
pollingSettings.SubscriptionMode = RemotePresenceViewSubscriptionMode.Polling;
// The line below is not necessary; PollingInterval has a default
// (and minimum) value of 5 minutes.
pollingSettings.PollingInterval = TimeSpan.FromMinutes(5);
_pollingView = new RemotePresenceView(ep, pollingSettings);
_pollingView.ApplicationContext = "Polling View";
// Wire up event handlers for the view
this.WireUpHandlersForView(_pollingView);
Console.WriteLine("\nChanging Polling View's category filter to only include Note.");
_pollingView.SetPresenceSubscriptionCategoriesForPolling(
new string[] { PresenceCategoryNames.Note });
try
{
// This constructor does very basic validation on the uri
_target = new RemotePresentitySubscriptionTarget("sip:kari@tmos.local");
}
catch (ArgumentException argumentException)
{
// ArgumentException will be thrown if the parameter used to
// create the RemotePresentitySubscriptionTarget is an
// invalid sip Uri.
// TODO (Left to the reader): Error handling code to either
// retry creating the target with corrected parameters, log
// the error for debugging or gracefully exit the program.
Console.WriteLine(argumentException.ToString());
throw;
}
// Both Views will try to subscribe to the specified user
// Note: if the target is a not a real user, these operations will
// "succeed", but the StateChanged notifications will indicate the
// subscription went to a Terminated state.
_persistentView.StartSubscribingToPresentities(new RemotePresentitySubscriptionTarget[] { _target });
_pollingView.StartSubscribingToPresentities(new RemotePresentitySubscriptionTarget[] { _target });
// There is no callback for the StartSubscribingToPresentities
// operation because subscriptions to multiple targets can
// complete at different times. Completion or failure of the
// subscription can be monitored through the
// SubscriptionStateChanged event handler,
// RemotePresenceView_NotificationReceived.
UCMASampleHelper.PauseBeforeContinuing("Press ENTER to unsubscribe.");
Console.WriteLine("\nBoth Views are terminating any subscriptions to user: ");
}
private void RemotePresenceView_NotificationReceived(object sender, RemotePresentitiesNotificationEventArgs e)
{
// Extract the RemotePresenceView that received the notification.
RemotePresenceView view = sender as RemotePresenceView;
// A RemotePresentityNotification will contain all the
// categories for one user; Notifications can contain notifications
// for multiple users.
foreach (RemotePresentityNotification notification in e.Notifications)
{
Console.WriteLine("\nView: " + view.ApplicationContext
+ " Received a Notification for user "
+ notification.PresentityUri + ".");
// If a category on notification is null, the category
// was not present in the notification. This means there were no
// changes in that category.
if (notification.AggregatedPresenceState != null)
{
Console.WriteLine("Aggregate State = " + notification.AggregatedPresenceState.Availability + ".");
}
if (notification.PersonalNote != null)
{
Console.WriteLine("PersonalNote: " + notification.PersonalNote.Message + ".");
}
if (notification.ContactCard != null)
{
// A ContactCard contains many properties; only display
// some.
ContactCard contactCard = notification.ContactCard;
Console.WriteLine("ContactCard Company: " + contactCard.Company + ".");
Console.WriteLine("ContactCard DisplayName: " + contactCard.DisplayName + ".");
Console.WriteLine("ContactCard EmailAddress: " + contactCard.EmailAddress + ".");
}
}
}
private void RemotePresenceView_SubscriptionStateChanged(object sender, RemoteSubscriptionStateChangedEventArgs e)
{
// Extract the view that raised the event.
RemotePresenceView view = sender as RemotePresenceView;
// The event args can contain multiple StateChanged notifications
foreach (KeyValuePair<RealTimeAddress, RemotePresentityStateChange> stateChanged in e.SubscriptionStateChanges)
{
Console.WriteLine("\nView: " + view.ApplicationContext
+ "; SubscriptionState for user: "
+ stateChanged.Key /* uri of subscription target */
+ " has changed from: " + stateChanged.Value.PreviousState
+ " to: " + stateChanged.Value.State + ".");
}
}
/// <summary>
/// Callback from <code>BeginEstablish</code> method on endpoint.
/// </summary>
/// <param name="result">
/// Status of the endpoint establishment operation.
/// </param>
private void EndpointEstablishmentCompleted(IAsyncResult result)
{
// Extract the endpoint that was passed in as the state argument to
// the BeginEstablish() method.
ApplicationEndpoint endpoint = result.AsyncState as ApplicationEndpoint;
if (endpoint == null)
{
UCMASampleHelper.WriteErrorLine("ApplicationEndpoint not passed into BeginEstablish() method.");
UCMASampleHelper.FinishSample();
return;
}
try
{
// Determine whether the establish operation completed
// successfully.
endpoint.EndEstablish(result);
subscribeToPresence(endpoint);
Console.Write("Subscribed....enter to continue.");
Console.ReadKey();
_persistentView.StartUnsubscribingToPresentities(new string[] { "sip:kari@tmos.local" });
_pollingView.StartUnsubscribingToPresentities(new string[] { "sip:kari@tmos.local" });
// Endpoint state change event handler will log the successful
// establishment of the endpoint.
UCMASampleHelper.FinishSample();
}
catch (ConnectionFailureException connectionFailureException)
{
// ConnectionFailureException will be thrown when no connection
// can be made to the server.
// TODO (Left to the reader): Error handling code to either
// retry establishing the endpoint, log the error for debugging
// or gracefully exit the program.
UCMASampleHelper.WriteException(connectionFailureException);
UCMASampleHelper.FinishSample();
}
catch (OperationFailureException operationFailureException)
{
// OperationFailureException will be thrown if the retrieval of
// in-band provisioning data fails.
// TODO (Left to the reader): Error handling code to either
// retry establishing the endpoint, log the error for debugging
// or gracefully exit the program.
UCMASampleHelper.WriteException(operationFailureException);
UCMASampleHelper.FinishSample();
}
catch (RegisterException registerException)
{
// RegisterException will be thrown if the SIP REGISTER
// operation fails.
// TODO (Left to the reader): Error handling code to either
// retry establishing the endpoint, log the error for debugging
// or gracefully exit the program.
UCMASampleHelper.WriteException(registerException);
UCMASampleHelper.FinishSample();
}
catch (AuthenticationException authenticationException)
{
// AuthenticationException will be thrown if general
// authentication-related problem occur.
// TODO (Left to the reader): Error handling code to either
// retry establishing the endpoint, log the error for debugging
// or gracefully exit the program.
UCMASampleHelper.WriteException(authenticationException);
UCMASampleHelper.FinishSample();
}
catch (OperationTimeoutException operationTimeoutException)
{
// OperationTimeoutException will be thrown if the registrar
// server does not respond to REGISTER request.
// TODO (Left to the reader): Error handling code to either
// retry establishing the endpoint, log the error for debugging
// or gracefully exit the program.
UCMASampleHelper.WriteException(operationTimeoutException);
UCMASampleHelper.FinishSample();
}
catch (RealTimeException realTimeException)
{
// RealTimeException will be thrown if the endpoint establish
// operation fails due to some other issue.
// TODO (Left to the reader): Error handling code to either
// retry establishing the endpoint, log the error for debugging
// or gracefully exit the program.
UCMASampleHelper.WriteException(realTimeException);
UCMASampleHelper.FinishSample();
}
}
/// <summary>
/// Callback from <code>BeginShutdown</code> method on platform.
/// </summary>
/// <param name="result">
/// Status of the platform shutdown operation.
/// </param>
private void PlatformShutdownCompleted(IAsyncResult result)
{
// Extract the platform that was passed in as the state argument to
// the BeginShutdown() method.
CollaborationPlatform platform = result.AsyncState as CollaborationPlatform;
if (platform == null)
{
UCMASampleHelper.WriteErrorLine(
"CollaborationPlatform not passed into BeginShutdown() method.");
UCMASampleHelper.FinishSample();
return;
}
// Determine whether the shutdown operation completed
// successfully.
platform.EndShutdown(result);
UCMASampleHelper.WriteLine("The platform is now shut down.");
}
#endregion
#region Event Handlers
/// <summary>
/// Record the endpoint state transitions to the console.
/// </summary>
/// <param name="sender">Endpoint that saw its state change.</param>
/// <param name="e">Data about the endpoint state change event.</param>
private void _endpoint_StateChanged(object sender, LocalEndpointStateChangedEventArgs e)
{
// Extract the endpoint that sent the state change event.
ApplicationEndpoint endpoint = sender as ApplicationEndpoint;
UCMASampleHelper.WriteLine("Endpoint (" + endpoint.OwnerUri
+ ") has changed state. The previous endpoint state was '"
+ e.PreviousState + "' and the current state is '" + e.State + "'.");
}
#endregion
}
}