Compartir vía


Paso 8: Implementar el controlador de entrada sincrónico para el adaptador de Echo

Paso 8 de 9

Tiempo de finalización: 45 minutos

En este paso, implementará la funcionalidad de entrada del adaptador de eco. Esta funcionalidad permite al adaptador escuchar datos o eventos desde el sistema de destino. Según el SDK del adaptador de LOB de WCF, solo tiene que implementar la interfaz cuando el adaptador admite la Microsoft.ServiceModel.Channels.Common.IInboundHandler funcionalidad entrante. El Asistente para desarrollo de adaptadores genera automáticamente la clase derivada denominada EchoAdpterInboundHandler automáticamente.

En la sección siguiente, actualizará la clase EchoAdpterInboundHandler para comprender mejor cómo implementar esta interfaz. Cuando complete este paso, tendrá un controlador de entrada en funcionamiento para el adaptador de eco.

Requisitos previos

Antes de comenzar este paso, debe haber completado correctamente el paso 7: Implementar el controlador de salida sincrónico para el adaptador de eco. Una familiaridad básica con Microsoft.ServiceModel.Channels.Common.IInboundHandler también es útil.

Interfaz IInboundHandler

Microsoft.ServiceModel.Channels.Common.IInboundHandler se define como:

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);  
}  

Las descripciones del método son:

Método Descripción
StartListener Comienza a escuchar mensajes con las acciones de WS-Addressing proporcionadas. Si no se especifica ninguno, escucha todas o las acciones predeterminadas.
StopListener Deja de escuchar.
TryReceive Intenta recibir un mensaje entrante del sistema de destino.
WaitForMessage Espera un mensaje WCF entrante desde el sistema de destino.

Para obtener más información sobre la descripción de cada parámetro de método, consulte la documentación de la Microsoft.ServiceModel.Channels.Common.IInboundHandler interfaz.

Implementación de EchoAdpterInboundHandler

El adaptador de eco usa para System.IO.FileSystemWatcher simular el sistema de destino. En lo siguiente, se implementa cada método dentro de la Microsoft.ServiceModel.Channels.Common.IInboundHandler interfaz, StartListener, StopListener, TryReceive y WaitForMessage.

Para implementar la interfaz IInboundHandler en la clase EchoAdpterInboundHandler

  1. En Explorador de soluciones, haga doble clic en el archivo EchoAdapterInboundHandler.cs.

  2. En el editor de Visual Studio, agregue las líneas siguientes al conjunto existente de directivas using.

    using System.IO;  
    using System.ServiceModel.Channels;  
    using System.Xml;  
    using System.Diagnostics;  
    
  3. Ahora agregue variables de nivel de clase a la clase EchoAdapterInboundHandler. Estas variables se usan para supervisar el sistema de archivos para la actividad de archivos. Copie las declaraciones siguientes en la clase antes del constructor.

    private Queue<Message> inboundQueue;  
    private FileSystemWatcher inboundWatcher;  
    private Object inboundQueueSynchronizationLock;  
    private string path;  
    private string filter;  
    
  4. Dentro del método de constructor EchoAdapterInboundHandler, agregue el código siguiente para inicializar la infraestructura de inspección de archivos y capturar la ruta de acceso y el filtro de supervisión.

    inboundWatcher = null;  
    inboundQueueSynchronizationLock = new Object();  
    path = connection.ConnectionFactory.Adapter.InboundFileSystemWatcherFolder;  
    filter = connection.ConnectionFactory.Adapter.InboundFileFilter;  
    
  5. Ahora agregue el código siguiente al método StartListener . El código implementa lógica para comprobar los parámetros e iniciar la supervisión de la actividad de archivos.

    // 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. Continúe agregando una implementación para el método StopListener .

    if (inboundWatcher != null)  
    {  
        // End monitoring  
        inboundWatcher.EnableRaisingEvents = false;  
        inboundWatcher = null;  
    }  
    lock (inboundQueueSynchronizationLock)  
    {  
        inboundQueue.Clear();  
        inboundQueue = null;  
    }  
    
  7. Ahora proporcione una implementación para el método TryReveive . Este método recupera el mensaje de recepción de archivos más reciente de la cola interna si hay uno disponible.

    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. Continúe agregando una implementación para el método WaitForMessage .

    while (inboundQueue.Count == 0) { };  
    Message msg = inboundQueue.Peek();  
    if (msg != null)  
    {  
        return true;  
    }  
    else  
    {  
        return false;  
    }  
    
  9. Ahora proporcione la devolución de llamada para el monitor de archivos. Para ello, agregue el nuevo método FileMonitor_Created a la clase EchoAdapterInboundAdapter .

    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. Ahora debe quitar las excepciones NotImplementedException producidas por la clase interna EchoAdapterInboundReply . Para ello, elimine la siguiente instrucción de los métodos Abort y Reply .

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

    Los métodos Abort y Reply deben ser similares a los siguientes.

    /// <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. Para completar la implementación del controlador de entrada, agregue la siguiente clase a EchoAdapterOutboundHandler.cs. Esta clase proporciona compatibilidad con el tiempo de espera para la implementación del controlador de entrada.

    /// <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. En Visual Studio, en el menú Archivo , haga clic en Guardar todo.

  13. En el menú Compilar , haga clic en Compilar solución. Debe compilarse sin errores. Si no es así, asegúrese de que ha seguido todos los pasos anteriores.

Nota

Ya ha guardado su trabajo. Puede cerrar Visual Studio de forma segura en este momento o ir al paso siguiente, Paso 9: Compilar e implementar el adaptador de eco.

¿Qué hice?

En este paso del tutorial del adaptador de eco, proporcionó una implementación para el controlador de entrada. Esta implementación proporciona funcionalidades de inspección de archivos para el adaptador de eco mediante la clase FileSystemWatcher de .NET Framework.

Pasos siguientes

En el paso siguiente, implementará el adaptador.

Consulte también

Paso 9: Compilar e implementar el adaptador de Echo
Paso 7: Implementar el controlador de salida sincrónico para el adaptador de Echo