UCMA 2.0 - Part 1.5 Setting up a session
In our next iteration, we will add to our commandlet to create a signaling session. In essence we will send an INVITE to the other party so that we can send a message later. There are a number of steps involved in setting up a session but for now we will just send the INVITE. When running this sample, it is best if you are not signed in as the user account that is receiving the INVITE as we do not have the media negotiation piece in place so the INVITE will fail.
If you have no idea what an INVITE is, I suggest you spend some time perusing the many SIP resources on the Internet. For example, see the SIP RFC at https://tools.ietf.org/html/rfc3261. This series is meant to help others learn the UCMA API, which assumes a decent knowledge of SIP.
SignalingSession is basically an INVITE dialog for our purposes. The constructor for SignalingSession accepts an endpoint (which we already created) and a RealTimeAddress. RealTimeAddress takes a string constructor with the SIP address of the other party in the dialog. To obtain the SIP address of the other party, we will need to add an extra parameter to the commandlet called “SendTo”.
SignalingSession has a number of constructors. The one we are interested in here accepts a RealTimeEndpoint and a RealTimeAddress. The endpoint is the one we are registered as, which was covered in yesterday’s blog. The address is the SIP address of the party we want to INVITE. This is the simplest constructor and will meet our needs.
_session = new SignalingSession(_endPoint, new RealTimeAddress(_sendTo));
Other constructors have a string callId parameter. In SIP parlance, each “conversation” (I call you – we talk – then hang up) is called a “dialog”. The dialog is identified by a callId. Therefore by using a callId that is identical to one in a different INVITE, you are stating that this INVITE is part of the same conversation. A typical use for this is in forking scenarios – where you send the same INVITE to multiple locations where the user may be found.
Finally, there is a constructor that accepts a “localIdentityUri” and “requestUri” parameters. The “localIdentityUri” parameter allows you to set the identity in the local header of the SIP URI. RequestUri can be null if it is not different from the target address specified with the RealTimeAddress.
To action create the session, you must call BeginParticipate. As this commandlet is synchronous, I will again perform the taboo operation of calling EndParticipate(BeginParticipate()). As I have previously stated, never do this in a server or a client. I placed the logic to create the session in a separate method.
private void CreateSession()
{
// Send an INVITE
try
{
_session.EndParticipate(_session.BeginParticipate(null, null));
}
catch (OperationTimeoutException ote)
{
Error(
ote,
"The Notification could not be received by {0} because the session establishment timed out.",
_sendTo);
}
catch (FailureResponseException e)
{
if (e.ResponseData != null)
{
Error(e,
"The Notification could not be received by {0}; Error Code={1} Error Text={2}.",
_sendTo,
e.ResponseData.ResponseCode,
e.ResponseData.ResponseText);
}
else
{
Error(e, "The Notification could not be received by {0}.", _sendTo);
}
}
catch (ConnectionFailureException cfe)
{
Error(
cfe,
"The Notification could not be received by {0}; the connection could not be established.",
_sendTo);
}
catch (RealTimeException e)
{
Error(
e,
"The Notification could not be received by {0}; the following problem occurred {1}.",
_sendTo,
e.Message);
}
}
The code for the Error routine, which just lets us output a Powershell error, is the following.
private void Error(Exception ex, string message, params object [] arguments)
{
string formattedMessage = String.Format(
CultureInfo.CurrentUICulture,
message,
arguments);
ErrorRecord record = new ErrorRecord(
ex,
ex.GetType().Name,
ErrorCategory.OpenError,
null);
record.ErrorDetails = new ErrorDetails(formattedMessage);
WriteError(record);
}
So what do all of these exceptions mean?
· RealTimeException is a catch all for any other type of RTC exception.
· A ConnectionFailureException will occur if the URI and/or port you specified do not exist.
· A FailureResponseException could mean several things. Generally it means that the other party is unavailable or cannot respond.
· The ServerPolicyException (not caught explicitly here) derives from it and indicates this message is not acceptable per the server policy. There are several other descendents of FailureResponseException (such as PublishSubscribeException which is not relevant here) but in general they all indicate that the INVITE failed for a particular reason explained in the exception.
· OperationTimeoutException can occur if it took too long for the other party to accept the invite.
When you test this, I recommend that you do not sign in as the other party, which will generate a FailureResponseException. We will fix this in the future when we talk about SDP media negotiation.
To terminate the session once we are done with it, we will start with a cleanup method.
private void Cleanup()
{
// Terminate the session if we have one
if (null != _session)
{
_session.Terminate();
WriteVerbose("Closed session");
}
}
As you can see it is as easy as calling _session.Terminate. A BeginTerminate/EndTerminate version also exist. Also note that Terminate should not throw.
Finally, this is our simple ProcessRecord method now.
protected override void ProcessRecord()
{
// Create the session
try
{
CreateSession();
}
finally
{
Cleanup();
}
}
That’s it for now. In the next blog, we’ll start negotiating a media session so we can send the message.