MessageListenerAdapter.cs :  » Inversion-of-Control-Dependency-Injection » Spring.net » Spring » Messaging » Listener » C# / CSharp Open Source

Home
C# / CSharp Open Source
1.2.6.4 mono .net core
2.2.6.4 mono core
3.Aspect Oriented Frameworks
4.Bloggers
5.Build Systems
6.Business Application
7.Charting Reporting Tools
8.Chat Servers
9.Code Coverage Tools
10.Content Management Systems CMS
11.CRM ERP
12.Database
13.Development
14.Email
15.Forum
16.Game
17.GIS
18.GUI
19.IDEs
20.Installers Generators
21.Inversion of Control Dependency Injection
22.Issue Tracking
23.Logging Tools
24.Message
25.Mobile
26.Network Clients
27.Network Servers
28.Office
29.PDF
30.Persistence Frameworks
31.Portals
32.Profilers
33.Project Management
34.RSS RDF
35.Rule Engines
36.Script
37.Search Engines
38.Sound Audio
39.Source Control
40.SQL Clients
41.Template Engines
42.Testing
43.UML
44.Web Frameworks
45.Web Service
46.Web Testing
47.Wiki Engines
48.Windows Presentation Foundation
49.Workflows
50.XML Parsers
C# / C Sharp
C# / C Sharp by API
C# / CSharp Tutorial
C# / CSharp Open Source » Inversion of Control Dependency Injection » Spring.net 
Spring.net » Spring » Messaging » Listener » MessageListenerAdapter.cs
#region License

/*
 * Copyright 2002-2008 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#endregion

using System.Collections;
using System.Messaging;
using Common.Logging;
using Spring.Context;
using Spring.Expressions;
using Spring.Messaging.Core;
using Spring.Messaging.Support;
using Spring.Messaging.Support.Converters;
using Spring.Objects.Factory;
using Spring.Reflection.Dynamic;

namespace Spring.Messaging.Listener{
    /// <summary>
    /// Message listener adapter that delegates the handling of messages to target
    /// listener methods via reflection <see cref="DynamicReflectionManager"/>, 
    /// with flexible message type conversion.
    /// Allows listener methods to operate on message content types, completely
    /// independent from the MSMQ API.
    /// </summary>
    /// <remarks>
    /// <para>
    /// By default, the content of incoming MSMQ messages gets extracted before
    /// being passed into the target handler method, to let the target method
    /// operate on message content types such as String or business object instead of
    /// <see cref="Message"/>. Message type conversion is delegated to a Spring
    /// <see cref="IMessageConverter"/>  By default, an <see cref="XmlMessageConverter"/> 
    /// with TargetType set to System.String is used.  If you do not want such automatic 
    /// message conversion taking place, then be sure to set the
    /// MessageConverter property to null.
    /// </para>
    /// <para>
    /// If a target handler method returns a non-null object (for example, with a
    /// message content type such as <code>String</code>), it will get
    /// wrapped in a MSMQ <code>Message</code> and sent to the response destination
    /// (either using the MSMQ Message.ResponseQueue property or 
    /// <see cref="DefaultResponseQueue"/>) specified default response queue
    /// destination).
    /// </para>
    /// <para>
    /// Find below some examples of method signatures compliant with this adapter class.
    /// This first example uses the default <see cref="XmlMessageConverter"/> that can
    /// marhsall/unmarshall string values from the MSMQ Message.
    /// </para>
    /// <example>
    /// public interface IMyHandler
    /// {
    ///   void HandleMessage(string text);
    /// }
    /// </example>
    /// <para>
    /// The next example indicates a similar method signature but the name of the
    /// handler method name has been changed to "DoWork", using the property
    /// <see cref="DefaultHandlerMethod"/>
    /// </para>
    /// <example>
    /// public interface IMyHandler
    /// {
    ///   void DoWork(string text);
    /// }
    /// </example>
    /// <para>If your <see cref="IMessageConverter"/> implementation will return multiple object
    /// types, overloading the handler method is perfectly acceptible, the most specific matching
    /// method will be used.  A method with an object signature would be consider a 
    /// 'catch-all' method
    /// </para>
    /// <example>
    /// public interface IMyHandler
    /// {
    ///   void DoWork(string text);
    ///   void DoWork(OrderRequest orderRequest);
    ///   void DoWork(InvoiceRequest invoiceRequest);
    ///   void DoWork(object obj);
    /// }
    /// </example>   
    /// <para>
    /// The last example shows how to send a message to the ResponseQueue for those
    /// methods that do not return void.
    /// <example>
    /// public interface MyHandler
    /// {
    ///   string DoWork(string text);
    ///   OrderResponse DoWork(OrderRequest orderRequest);
    ///   InvoiceResponse DoWork(InvoiceRequest invoiceRequest);
    ///   void DoWork(object obj);
    /// }
    /// </example>
    /// </para>
    /// </remarks>
    /// <author>Mark Pollack</author>
    public class MessageListenerAdapter : IMessageListener, IApplicationContextAware, IInitializingObject
    {
        #region Logging

        private static readonly ILog logger = LogManager.GetLogger(typeof (MessageListenerAdapter));

        #endregion

        /// <summary>
        /// Out-of-the-box value for the default listener method: "HandleMessage"
        /// </summary>
        public static readonly string ORIGINAL_DEFAULT_LISTENER_METHOD = "HandleMessage";

        #region Fields

        private IApplicationContext applicationContext;

        private object handlerObject;

        private string defaultHandlerMethod = ORIGINAL_DEFAULT_LISTENER_METHOD;

        private IExpression processingExpression;

        private string defaultResponseQueueName;

        private string messageConverterObjectName;

        private MessageQueueTemplate messageQueueTemplate;

        private IMessageQueueFactory messageQueueFactory;

        #endregion

        #region Constructors

        /// <summary>
        /// Initializes a new instance of the <see cref="MessageListenerAdapter"/> class.
        /// </summary>
        public MessageListenerAdapter()
        {
            InitDefaultStrategies();
            handlerObject = this;
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="MessageListenerAdapter"/> class.
        /// </summary>
        /// <param name="handlerObject">The handler object.</param>
        public MessageListenerAdapter(object handlerObject)
        {
            InitDefaultStrategies();
            this.handlerObject = handlerObject;
        }

        #endregion

        /// <summary>
        /// Gets or sets the handler object to delegate message listening to.
        /// </summary>
        /// <remarks>
        /// Specified listener methods have to be present on this target object.
        /// If no explicit handler object has been specified, listener
        /// methods are expected to present on this adapter instance, that is,
        /// on a custom subclass of this adapter, defining listener methods.
        /// </remarks>
        /// <value>The handler object.</value>
        public object HandlerObject
        {
            get { return handlerObject; }
            set { handlerObject = value; }
        }

        /// <summary>
        /// Gets or sets the default handler method to delegate to,
        /// for the case where no specific listener method has been determined.
        /// Out-of-the-box value is "HandleMessage".
        /// </summary>
        /// <value>The default handler method.</value>
        public string DefaultHandlerMethod
        {
            get { return defaultHandlerMethod; }
            set { 
                defaultHandlerMethod = value;
                processingExpression = Expression.Parse(defaultHandlerMethod + "(#convertedObject)");
            }
        }

        #region IInitializingObject Members

        /// <summary>
        /// Invoked by an <see cref="Spring.Objects.Factory.IObjectFactory"/>
        /// after it has injected all of an object's dependencies.
        /// </summary>
        /// <remarks>
        ///   <p>
        /// This method allows the object instance to perform the kind of
        /// initialization only possible when all of it's dependencies have
        /// been injected (set), and to throw an appropriate exception in the
        /// event of misconfiguration.
        /// </p>
        ///   <p>
        /// Please do consult the class level documentation for the
        /// <see cref="Spring.Objects.Factory.IObjectFactory"/> interface for a
        /// description of exactly <i>when</i> this method is invoked. In
        /// particular, it is worth noting that the
        /// <see cref="Spring.Objects.Factory.IObjectFactoryAware"/>
        /// and <see cref="Spring.Context.IApplicationContextAware"/>
        /// callbacks will have been invoked <i>prior</i> to this method being
        /// called.
        /// </p>
        /// </remarks>
        /// <exception cref="System.Exception">
        /// In the event of misconfiguration (such as the failure to set a
        /// required property) or if initialization fails.
        /// </exception>
        public void AfterPropertiesSet()
        {
            if (messageQueueFactory == null)
            {
                DefaultMessageQueueFactory mqf = new DefaultMessageQueueFactory();
                mqf.ApplicationContext = applicationContext;
                messageQueueFactory = mqf;
            }
            if (messageConverterObjectName == null)
            {
                messageConverterObjectName = QueueUtils.RegisterDefaultMessageConverter(applicationContext);
            }
            if (messageQueueTemplate == null)
            {
                messageQueueTemplate = new MessageQueueTemplate();
                messageQueueTemplate.ApplicationContext = ApplicationContext;
                messageQueueTemplate.AfterPropertiesSet();
            }
        }

        #endregion

        #region IApplicationContextAware Members

        /// <summary>
        /// Gets or sets the <see cref="Spring.Context.IApplicationContext"/> that this
        /// object runs in.
        /// </summary>
        /// <remarks>
        /// <p>
        /// Normally this call will be used to initialize the object.
        /// </p>
        /// <p>
        /// Invoked after population of normal object properties but before an
        /// init callback such as
        /// <see cref="Spring.Objects.Factory.IInitializingObject"/>'s
        /// <see cref="Spring.Objects.Factory.IInitializingObject.AfterPropertiesSet"/>
        /// or a custom init-method. Invoked after the setting of any
        /// <see cref="Spring.Context.IResourceLoaderAware"/>'s
        /// <see cref="Spring.Context.IResourceLoaderAware.ResourceLoader"/>
        /// property.
        /// </p>
        /// </remarks>
        /// <exception cref="Spring.Context.ApplicationContextException">
        /// In the case of application context initialization errors.
        /// </exception>
        /// <exception cref="Spring.Objects.ObjectsException">
        /// If thrown by any application context methods.
        /// </exception>
        /// <exception cref="Spring.Objects.Factory.ObjectInitializationException"/>
        public IApplicationContext ApplicationContext
        {
            get { return applicationContext; }
            set { applicationContext = value; }
        }

        #endregion

        /// <summary>
        /// Gets or sets the message queue factory.
        /// </summary>
        /// <value>The message queue factory.</value>
        public IMessageQueueFactory MessageQueueFactory
        {
            get { return messageQueueFactory; }
            set { messageQueueFactory = value; }
        }

        /// <summary>
        /// Sets the name of the default response queue to send response messages to.
        /// This will be applied in case of a request message that does not carry a
        /// "ResponseQueue" value.
        /// <para>Alternatively, specify a response queue via the property
        /// <see cref="DefaultResponseQueue"/>.</para>
        /// </summary>
        /// <value>The name of the default response destination queue.</value>
        public string DefaultResponseQueueName
        {
            get { return defaultResponseQueueName; }
            set { defaultResponseQueueName = value; }
        }

        /// <summary>
        /// Sets the default destination to send response messages to. This will be applied
        /// in case of a request message that does not carry a "ResponseQueue" property
        /// Response destinations are only relevant for listener methods that return
        /// result objects, which will be wrapped in a response message and sent to a
        /// response destination.
        /// <para>
        /// Alternatively, specify a "DefaultResponseQueueName" 
        /// to be dynamically resolved via the MessageQueueFactory.
        /// </para>
        /// </summary>
        /// <value>The default response destination.</value>
        public MessageQueue DefaultResponseQueue
        {
            get
            {
                if (DefaultResponseQueueName != null)
                {
                    return messageQueueFactory.CreateMessageQueue(DefaultResponseQueueName);
                }
                else
                {
                    return null;
                }
            }
        }


        /// <summary>
        /// Gets or sets the name of the message converter object used to resolved a <see cref="IMessageConverter"/>
        /// instance.
        /// </summary>
        /// <value>The name of the message converter object.</value>
        public string MessageConverterObjectName
        {
            get { return messageConverterObjectName; }
            set { messageConverterObjectName = value; }
        }

        /// <summary>
        /// Gets message converter that will convert incoming MSMQ messages to
        /// listener method arguments, and objects returned from listener
        /// methods back to MSMQ messages.
        /// </summary>
        /// <remarks>
        /// <para>The converter used is the one returned by CreateMessageConverter on MessageQueueFactory.
        /// </para>
        /// </remarks>
        /// <value>The message converter.</value>
        public IMessageConverter MessageConverter
        {
            get
            {
                return messageQueueFactory.CreateMessageConverter(MessageConverterObjectName);
            }
        }

        /// <summary>
        /// Sets the message queue template.
        /// </summary>
        /// <remarks>If not set, will create one for it own internal use whne MessageListenerAdapter is constructed.
        /// It maybe useful to share an existing instance if you have an extensively configured MessageQueueTemplate.
        /// </remarks>
        /// <value>The message queue template.</value>
        public MessageQueueTemplate MessageQueueTemplate
        {
            set { messageQueueTemplate = value; }
        }

        #region IMessageListener Members

        /// <summary>
        /// Called when message is received.
        /// </summary>
        /// <param name="message">The message.</param>
        public virtual void OnMessage(Message message)
        {
            object convertedMessage = ExtractMessage(message);

            IDictionary vars = new Hashtable();
            vars["convertedObject"] = convertedMessage;

            //Invoke message handler method and get result.
            object result = processingExpression.GetValue(handlerObject, vars);
            if (result != null)
            {
                HandleResult(result, message);
            }
            else
            {
                logger.Debug("No result object given - no result to handle");
            }
        }

        #endregion

        /// <summary>
        /// Gets the name of the handler method.
        /// </summary>
        /// <param name="originalMessage">The original message.</param>
        /// <param name="extractedMessage">The extracted message.</param>
        /// <returns>The name of the handler method.</returns>
        protected virtual string GetHandlerMethodName(Message originalMessage, object extractedMessage)
        {
            return DefaultHandlerMethod;
        }

        /// <summary>
        /// Initialize the default implementations for the adapter's strategies.
        /// </summary>
        protected virtual void InitDefaultStrategies()
        {
            processingExpression = Expression.Parse(defaultHandlerMethod + "(#convertedObject)");
        }

        /// <summary>
        /// Extracts the message body from the given message.
        /// </summary>
        /// <param name="message">The message.</param>
        /// <returns>the content of the message, to be passed into the
        /// listener method as argument</returns>
        protected virtual object ExtractMessage(Message message)
        {
            IMessageConverter converter = MessageConverter;
            if (converter != null)
            {
                return converter.FromMessage(message);
            }
            return message;
        }

        private void HandleResult(object result, Message request)
        {
            if (logger.IsDebugEnabled)
            {
                logger.Debug("Listener method returned result [" + result +
                             "] - generating response message for it");
            }
            Message response = BuildMessage(result);
            PostProcessResponse(request, response);
            MessageQueue destination = GetResponseDestination(request, response);
            SendResponse(destination, response);
        }

        /// <summary>
        /// Sends the given response message to the given destination.
        /// </summary>
        /// <param name="destination">The destination to send to.</param>
        /// <param name="response">The outgoing message about to be sent.</param>
        protected virtual void SendResponse(MessageQueue destination, Message response)
        {
            //Will send with appropriate transaction semantics 
            if (logger.IsDebugEnabled)
            {
                logger.Debug("Sending response message to path = [" + destination.Path + "]");
            }
            messageQueueTemplate.Send(destination, response);
        }

        /// <summary>
        /// Builds a MSMQ message to be sent as response based on the given result object.
        /// </summary>
        /// <param name="result">The result.</param>
        /// <returns>the MSMQ <code>Message</code> (never <code>null</code>)</returns>
        /// <exception cref="MessagingException">If no messgae converter is specified.</exception>
        protected virtual Message BuildMessage(object result)
        {
            IMessageConverter converter = MessageConverter;
            if (converter != null)
            {
                // This is the default Message converter registered in QueueUtils.RegisterDefaultMessageConverter
                // and used by MessageQueueTemplate and the MessageListenerAdapter if no other Message converage is 
                // set via the property MessageConverteryObjectName.
                if (messageConverterObjectName.Equals("__XmlMessageConverter__"))
                {
                    return converter.ToMessage(result.ToString());
                }
                else
                {
                    return converter.ToMessage(result);
                }
            }
            else
            {
                Message msg = result as Message;
                if (msg == null)
                {
                    throw new MessagingException("No MessageConverter specified - cannot handle message [" + result +
                                                 "]");
                }
                return msg;
            }
        }

        /// <summary>
        /// Post-process the given response message before it will be sent. The default implementation
        /// sets the response's correlation id to the request message's correlation id.
        /// </summary>
        /// <param name="request">The original incoming message.</param>
        /// <param name="response">The outgoing MSMQ message about to be sent.</param>
        protected virtual void PostProcessResponse(Message request, Message response)
        {
            response.CorrelationId = request.CorrelationId;
        }

        /// <summary>
        /// Determine a response destination for the given message.
        /// </summary>
        /// <remarks>
        /// <para>The default implementation first checks the MSMQ ResponseQueue
        ///  of the supplied request; if that is not <code>null</code>
        /// it is returned; if it is <code>null</code>, then the configured
        /// <see cref="DefaultResponseQueue"/> default response destination}
        /// is returned; if this too is <code>null</code>, then an
        /// <see cref="MessagingException"/>is thrown.
        /// </para>
        /// </remarks>
        /// <param name="request">The request.</param>
        /// <param name="response">The response.</param>
        /// <returns></returns>
        protected virtual MessageQueue GetResponseDestination(Message request, Message response)
        {
            MessageQueue replyTo = request.ResponseQueue;
            if (replyTo == null)
            {
                replyTo = DefaultResponseQueue;
                if (replyTo == null)
                {
                    throw new MessagingException("Cannot determine response destination: " +
                                                 "Request message does not contain ResponseQueue destination, and no default response queue set.");
                }
            }
            return replyTo;
        }
    }
}
www.java2v.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.