Step 8: Implement the Synchronous Inbound Handler for the Echo Adapter

Step 8 of 9

Time to complete: 45 minutes

In this step, you implement the inbound capability of the echo adapter. This capability allows the adapter to listen for data or events from the target system. According to the WCF LOB Adapter SDK, you only need to implement the Microsoft.ServiceModel.Channels.Common.IInboundHandler interface, when your adapter supports inbound capability. The Adapter Development Wizard automatically generates the derived class called EchoAdpterInboundHandler for you.

In the following section, you update the EchoAdpterInboundHandler class to get a better understanding on how to implement this interface. When you complete this step, you have a working inbound handler for the echo adapter.

Prerequisites

Before you begin this step, you must have successfully completed Step 7: Implement the Synchronous Outbound Handler for the Echo Adapter. A basic familiarity with Microsoft.ServiceModel.Channels.Common.IInboundHandler is also helpful.

The IInboundHandler Interface

The Microsoft.ServiceModel.Channels.Common.IInboundHandler is defined as:

public interface IInboundHandler : IConnectionHandler, IDisposable  
{  
          void StartListener(string[] actions, TimeSpan timeout);  
          void StopListener(TimeSpan timeout);  
          bool TryReceive(TimeSpan timeout, out Message message, out IInboundReply reply);  
          bool WaitForMessage(TimeSpan timeout);  
}  

The method descriptions are:

Method Description
StartListener Starts listening to messages with the provided WS-Addressing Actions. If none is specified, it listens to all or the default actions.
StopListener Stops listening.
TryReceive Tries to receive an inbound message from the target system.
WaitForMessage Waits for an inbound WCF message from the target system.

For more details on the description for each method parameters, see the documentation on the Microsoft.ServiceModel.Channels.Common.IInboundHandler interface.

Implementing the EchoAdpterInboundHandler

The echo adapter uses the System.IO.FileSystemWatcher to simulate the target system. In the following, you implement each method within the Microsoft.ServiceModel.Channels.Common.IInboundHandler interface, StartListener, StopListener, TryReceive and WaitForMessage.

To implement IInboundHandler interface in the EchoAdpterInboundHandler class

  1. In Solution Explorer, double-click the EchoAdapterInboundHandler.cs file.

  2. In the Visual Studio editor, add the following lines to the existing set of using directives.

    using System.IO;  
    using System.ServiceModel.Channels;  
    using System.Xml;  
    using System.Diagnostics;  
    
  3. Now add class level variables to the EchoAdapterInboundHandler class. These variables are used to monitor the file system for file activity. Copy the declarations below into the class before the constructor.

    private Queue<Message> inboundQueue;  
    private FileSystemWatcher inboundWatcher;  
    private Object inboundQueueSynchronizationLock;  
    private string path;  
    private string filter;  
    
  4. Inside the EchoAdapterInboundHandler constructor method, add the following code to initialize the file watching infrastructure and to capture the monitoring path and filter.

    inboundWatcher = null;  
    inboundQueueSynchronizationLock = new Object();  
    path = connection.ConnectionFactory.Adapter.InboundFileSystemWatcherFolder;  
    filter = connection.ConnectionFactory.Adapter.InboundFileFilter;  
    
  5. Now add the following code to the StartListener method. The code implements logic to verify parameters and start monitoring for file activity.

    // if no actions are provided, log an error in the trace log  
    // and throw an exception  
    if (actions.Length == 0)  
    {  
        EchoAdapterUtilities.Trace.Trace(TraceEventType.Error, "http://echoadapterv2/startlistener/noactions", "No operation actions were received for listener to do specific processing.", this);  
        throw new AdapterException("Unable to receive any actions for inbound handler to start listening.");  
    }  
    
    inboundQueue = new Queue<Message>();  
    foreach (string action in actions)  
    {  
        // for the OnReceiveEcho action listen for a new file created event  
        if ("Echo/OnReceiveEcho".Equals(action))  
        {  
            if (inboundWatcher == null)  
            {  
                inboundWatcher = new FileSystemWatcher(path);  
                inboundWatcher.Filter = filter;  
                // Begin monitoring  
                inboundWatcher.EnableRaisingEvents = true;  
            }  
            inboundWatcher.Created += new FileSystemEventHandler(FileMonitor_Created);  
            EchoAdapterUtilities.Trace.Trace(TraceEventType.Information, "http://echoadapterv2/startlistener", "Listening for file created event for " + filter + " in path " + path, this);  
        }  
    }  
    
  6. Continue by adding an implementation for the StopListener method.

    if (inboundWatcher != null)  
    {  
        // End monitoring  
        inboundWatcher.EnableRaisingEvents = false;  
        inboundWatcher = null;  
    }  
    lock (inboundQueueSynchronizationLock)  
    {  
        inboundQueue.Clear();  
        inboundQueue = null;  
    }  
    
  7. Now provide an implementation for the TryReveive method. This method retrieves the most recent file receive message from the internal queue if one is available.

    reply = new EchoAdapterInboundReply();  
    message = null;  
    TimeoutHelper timeoutHelper = new TimeoutHelper(timeout);  
    while (true)  
    {  
        lock (inboundQueueSynchronizationLock)  
        {  
            if (inboundQueue == null)  
            {  
                //listener has been closed  
                return false;  
            }  
            if (inboundQueue.Count != 0)  
            {  
                message = inboundQueue.Dequeue();  
                if (message != null)  
                {  
                    return true;  
                }  
            }  
        }  
        if (timeoutHelper.IsExpired)  
        {  
            return false;  
        }  
        //wait for sometime, and check again  
        System.Threading.Thread.Sleep(500);  
    }  
    
  8. Continue by adding an implementation for the WaitForMessage method.

    while (inboundQueue.Count == 0) { };  
    Message msg = inboundQueue.Peek();  
    if (msg != null)  
    {  
        return true;  
    }  
    else  
    {  
        return false;  
    }  
    
  9. Now supply the callback for the file watcher. To do so, add the new method FileMonitor_Created to the EchoAdapterInboundAdapter class.

    private void FileMonitor_Created(object sender, FileSystemEventArgs e)  
    {  
        lock (inboundQueueSynchronizationLock)  
        {  
            if (e.ChangeType == WatcherChangeTypes.Created)  
            {  
                // wait for file to close - should do this in a better manner  
                System.Threading.Thread.Sleep(500);  
                try  
                {  
                    EchoAdapterUtilities.Trace.Trace(TraceEventType.Information, "http://echoadapterv2/FileMonitorCreated", "File " + e.FullPath + " created.", this);  
                    FileInfo fileInfo = new FileInfo(e.FullPath);  
                    // Create WCF message to send to the inbound service  
                    // that is listening for messages from adapter  
                    String xmlData = String.Format(@"<OnReceiveEcho xmlns=""{0}""><path>{1}</path><length>{2}</length></OnReceiveEcho>", EchoAdapter.SERVICENAMESPACE, e.FullPath, fileInfo.Length);  
                    // set action string  
                    XmlReader reader = XmlReader.Create(new StringReader(xmlData));  
                    // create WCF message  
                    Message requestMessage = Message.CreateMessage(MessageVersion.Default  
                                , "Echo/OnReceiveEcho"  
                                , reader);  
                    requestMessage.Headers.To = new Uri(path);  
                    inboundQueue.Enqueue(requestMessage);  
                }  
                catch (Exception ex)  
                {  
                    String message = String.Format("An exception was thrown while trying to open file {1}.", e.FullPath);  
                    EchoAdapterUtilities.Trace.Trace(System.Diagnostics.TraceEventType.Error, "http://echoadapterv2/FileMonitorCreated", message, this, ex);  
                    throw new AdapterException(message, ex);  
                }  
            }  
        }  
    }  
    
  10. Now you must remove the NotImplementedException exceptions thrown by the internal EchoAdapterInboundReply class. To do this, delete the following statement from the Abort and Reply methods.

    throw new NotImplementedException("The method or operation is not implemented.");  
    

    Your Abort and Reply methods should be similar to the following.

    /// <summary>  
    /// Abort the inbound reply call  
    /// </summary>  
    public override void Abort()  
    {  
    }  
    
    /// <summary>  
    /// Reply message implemented  
    /// </summary>  
    public override void Reply(System.ServiceModel.Channels.Message message  
        , TimeSpan timeout)  
    {  
    }  
    
  11. To complete the implementation for the inbound handler, add the following class to EchoAdapterOutboundHandler.cs. This class provides timeout support for the inbound handler implementation.

    /// <summary>  
    /// Utility class containing helper functions for measuring timeout   
    /// </summary>  
    class TimeoutHelper  
    {  
        private TimeSpan timeout;  
        private DateTime creationTime;  
        private Boolean isInfinite;  
    
        /// <summary>  
        /// Constructor  
        /// </summary>  
        /// <param name="timeout"></param>  
        public TimeoutHelper(TimeSpan timeout)  
        {  
            this.creationTime = DateTime.Now;  
            this.timeout = timeout;  
            if (timeout.Equals(Infinite)) this.isInfinite = true;  
        }  
    
        /// <summary>  
        /// Value of infinite timespan  
        /// </summary>  
        public static TimeSpan Infinite  
        {  
            get { return TimeSpan.MaxValue; }  
        }  
    
        /// <summary>  
        /// Value indicating remaining timeout  
        /// </summary>  
        public TimeSpan RemainingTimeout  
        {  
            get  
            {  
                if (this.isInfinite) return Infinite;  
                return this.timeout.Subtract(DateTime.Now.Subtract(this.creationTime));  
            }  
        }  
    
        /// <summary>  
        /// Get remaining timeout value and throw an exception if the timeout  
        /// has expired.  
        /// </summary>  
        /// <param name="exceptionMessage"></param>  
        /// <returns></returns>  
        public TimeSpan GetRemainingTimeoutAndThrowIfExpired(String exceptionMessage)  
        {  
            if (this.isInfinite) return Infinite;  
            if (RemainingTimeout < TimeSpan.Zero)  
            {  
                throw new TimeoutException(exceptionMessage);  
            }  
            return RemainingTimeout;  
        }  
    
        /// <summary>  
        /// Throw an exception if the timeout has expired.  
        /// </summary>  
        /// <param name="exceptionMessage"></param>  
        public void ThrowIfTimeoutExpired(String exceptionMessage)  
        {  
            if (RemainingTimeout < TimeSpan.Zero)  
            {  
                throw new TimeoutException(exceptionMessage);  
            }  
    
        }  
    
        /// <summary>  
        /// Value indicating whether timeout has expired.  
        /// </summary>  
        public Boolean IsExpired  
        {  
            get  
            {  
                if (this.isInfinite) return false;  
                return RemainingTimeout < TimeSpan.Zero;  
            }  
        }  
    }  
    
  12. In Visual Studio, on the File menu, click Save All.

  13. On the Build menu, click Build Solution. It should compile without errors. If not, ensure that you have followed every step above.

Note

You saved your work. You can safely close Visual Studio at this time or go to the next step, Step 9: Build and Deploy the Echo Adapter.

What Did I Just Do?

In this step of the Echo Adapter tutorial, you provided an implementation for the Inbound Handler. This implementation provides file watching capabilities for the Echo Adapter using the FileSystemWatcher class of the .NET Framework.

Next Steps

In the next step, you deploy the adapter.

See Also

Step 9: Build and Deploy the Echo Adapter
Step 7: Implement the Synchronous Outbound Handler for the Echo Adapter