Chuck Conway

In The craft

Simple Polling Messaging Queue

July 2, 2012 · 3 minute read

The following code is a simple implementation of a Polling service. It has not been optimized for speed nor has it been tested much. It works and shows the concept of a message queue that dynamically loads plugins(dlls) and processes messages.

using System;
using System.Collections.Generic;
using System.Configuration;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Xml;
using System.Xml.Serialization;
using DA.Api;

namespace My.Worker.Core
{
    public class QueueService
    {
        /// <summary> Process this object. </summary>
        public void Process(string url)
        {
            IApi api = new Api.Api(url);

            string error = null;
            bool success = true;

            //Retrieving the messages from the Queue
            var messages = api.List("get_message_queue_to_process", new { });

            //Load all processors found in the /plugins directory
            var processors = GetProcessors();

            //Process each method
            foreach (var message in messages)
            {
                int messageId = message.Id;

                try
                {
                    ProcessMessage(processors, message);
                }
                catch (Exception ex)
                {
                    error = string.Format("Message:{0}", ex.Message);
                    success = false;
                }
                finally
                {
                    //Record success or failure
                    api.Put("queue/update", new { id = messageId, success, message = error });
                }
            }
        }

        /// <summary> Process the message. </summary>
        ///  Thrown when an exception error condition occurs. 
        ///  The processors. 
        ///     The message. 
        private void ProcessMessage(IEnumerable processors, Message message)
        {
            Type processor = null;
            Type messageType = null;

            foreach (var type in processors.Where(t=&gt; t.FullName == message.MessageProcessor))
            {
                //discover the processor type
                Type single = type.GetInterfaces().Single(i =&gt; i.IsGenericType &amp;&amp; i.GetGenericTypeDefinition() == typeof (IMessageProcessor));

                //Discover the message type
                messageType = single.GetGenericArguments().FirstOrDefault();
                processor = type;
            }

            if (processor != null)
            {
                //Create an instance of the processor type
                var instance = Activator.CreateInstance(processor);

                //Create an instance of the message type and hydrate it with the data from the queue
                var imp = GetMessage(messageType, message.Payload);

                //Call the process method on the IMessageProcessor and pass in the message
                processor.InvokeMember("Process", BindingFlags.InvokeMethod, null, instance, new[] {imp});
            }
            else
            {
                throw new Exception(string.Format("Could not find processor {0} implementation. Message Id {1}", message.MessageProcessor, message.Id));
            }
        }

        /// <summary> Gets a message. </summary>
        ///  Type of the full. 
        ///      The payload. 
        ///  The message. 
        public object GetMessage(Type messageType, string payload)
        {
            object implementation = null;

            //If message type is null badness has happened
            if (messageType != null)
            {
                //deserialize the message.
                implementation = new XmlSerializer(messageType).Deserialize(new XmlTextReader(new StringReader(payload)));
            }

            return implementation;
        }

        /// <summary> Enumerates get processors in this collection. </summary>
        ///  An enumerator that allows foreach to be used to process get processors in this
        /// collection. 
        private static IEnumerable GetProcessors()
        {
            List implementations = new List();

            //Get executing path
            Uri assemblyUri = new Uri(Assembly.GetExecutingAssembly().CodeBase);
            string path = Path.GetDirectoryName(assemblyUri.LocalPath);

            //Find all files in the executing path that reside in the child plugins directory
            var files = Directory.GetFiles(path + "plugins", "*.dll", SearchOption.AllDirectories);

            foreach (var file in files)
            {
                var assembly = Assembly.LoadFrom(file);
                Type[] types = assembly.GetTypes();

                var r = types.Where(type =&gt; type.GetInterfaces().Any(i =&gt; i.IsGenericType &amp;&amp; 
                    i.GetGenericTypeDefinition() == typeof (IMessageProcessor)));
                implementations.AddRange(r);
            }

            return implementations;
        }
    }
}