The Journey of the Lunch Launcher: Part 3 - Managing the Transport

Part 1 - The origins of the 'lunch launcher'
Part 2 - MEDC 2007

In the first two installments (linked above), I introduced the lunch launcher and talked about getting ready for its debut at MEDC 2007 in Las Vegas.  Now it's time to start looking at how the .NET Compact Framework  v3.5 Store and Forward Messaging feature makes this application possible.

At the heart of the lunch launcher are the Windows Communication Foundation channel pairs (input and output).  You may ask 'what do you mean channel pairs, isn't the one channel that sends and receives'?  Excellent question!  The answer is no.  The Store and Forward Messaging feature is built upon one-way communications channels (IInputChannel and IOutputChannel).  This is necessary due to the nature of email as a transport.  Email has, what I term, variable latency.  By variable latency, I mean that once a message has been sent, the time it will take for it to be received cannot be determined.  It may be received in a few seconds, a few hours, a few days or never depending on when and if the recipient checks their email.  This leads to some very interesting challenges in writing applications which leverage Store and Forward Messaging.  I plan on discussing these challenges in a future post (separate from this series).

Back to the topic at hand... How does the lunch launcher manage the transport?  All objects required to send and receive messages, for all three of the lunch launcher channel pairs (invitations, replies and notifications) are contained in a TransportObjects class, as shown below.  This example is taken directly from the MEDC 2007 demo project.

Please note that specific details of property implementation and object construction have been removed for space reasons and that the following disclaimer covers all examples contained herein.
//-----------------------------------------------------------------------//THIS CODE AND INFORMATION ARE PROVIDED AS IS WITHOUT WARRANTY OF ANY//KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE//IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A//PARTICULAR PURPOSE.//-----------------------------------------------------------------------/// <summary>/// Object to encapsulate and simplify working with /// Windows Communication Framework objects/// </summary>public class TransportObjects{    /// <summary>    /// The binding used to create the channels    /// </summary>    public WindowsMobileMailBinding Binding    { get; }            /// <summary>    /// The name of the input channel (ex: "MyServiceChannel")    /// </summary>    public String ChannelName    { get; }            /// <summary>    /// The input channel    /// </summary>    public IInputChannel InputChannel    { get; }            /// <summary>    /// The channel factory used to create output channels    /// </summary>    public IChannelFactory<IOutputChannel> OutputChannelFactory    { get; }}
The application engine creates the TransportObjects object and then initialize them (create the necessary Windows Communication Foundation objects) by calling Initialize().
/// <summary>/// Initializes the Windows Communication Foundation objects/// </summary>public void Initialize(){    try    {        this.m_Binding = new WindowsMobileMailBinding();        this.m_Binding.AcceptExistingMessages = true;                        // create the binding parameters used by our channels        BindingParameterCollection bpc = new BindingParameterCollection();                        // NOTE: input channel gets the email address from the binding        Uri inputChannelUri = MailUriHelper.CreateUri(this.m_ChannelName, "");

        // create the input channel listener
        IChannelListener<IInputChannel> listener =
            this.m_Binding.BuildChannelListener<IInputChannel>(inputChannelUri, bpc);
        listener.Open();
               
        // create the input channel
        this.m_InputChannel = listener.AcceptChannel();
        this.m_InputChannel.Open();
               
        // now that we have the input channel,
        //  it is safe to close the listener
        listener.Close();
        listener = null;
               
        // create the output channel factory
        this.m_OutputChannelFactory = this.m_Binding.BuildChannelFactory<IOutputChannel>(bpc);
        this.m_OutputChannelFactory.Open();
    }
    catch
    {
        // initialization failed, make sure we cleanup
        Uninitialize();
        throw;
    }
}
       
When the application engine is done with a channel pair (at shutdown) or if anything bad occurs (exception on initialization, for example) Uninitialize() is called.
/// <summary>/// Uninitializes and cleans up the Windows Communication Foundation objects/// </summary>public void Uninitialize(){    if(null != this.m_InputChannel)    {        this.m_InputChannel.Close();        this.m_InputChannel = null;    }    if(null != this.m_OutputChannelFactory)    {        this.m_OutputChannelFactory.Close();        this.m_OutputChannelFactory = null;    }    if(null != this.m_Binding)    {        this.m_Binding.Close();        this.m_Binding = null;    }}
In the event that the application engine encounters a faulted state on the transport, recovery is performed by the Recover() method.  Recover calls Uninitialize and Initialize.  If both succeed, success is indicated by the method returning true.  Upon a true return, the application logic continues to send and receive messages.  A false return aborts the channel pair's functionality.
/// <summary>/// Recovers from a transport fault by uninitializing and reinitializing/// the Windows Communication Foundation objects/// </summary>/// <returns>/// True if recovery was successful, false otherwise/// </returns>public Boolean Recover(){    Boolean recovered;                try    {        Uninitialize();        Initialize();                        // successful recovery        recovered = true;    }    catch    {        // unsuccessful recovery        recovered = false;                        Uninitialize();                    }                return recovered;}
Take care,
-- DK

Disclaimer(s):
This posting is provided "AS IS" with no warranties, and confers no rights.
The information contained within this post is in relation to beta software. Any and all details are subject to change.