Self-Updating Windows Service Infrastructure with Command Pattern Message Queue Invoker Service
By Peter A. Bromberg, Ph.D.

Peter Bromberg

" Right now, we are just at the very beginning of a change in great magnitude in the way people
get, access and exchange information." --
Tom Brokaw, 2004

I'm a big fan of "building blocks". The whole idea of quality OOP - oriented programming, to me, is to build reusable infrastructure pieces that can be assembled in various ways and will inter operate to produce new business logic that is easy to create, extensible, and fast. That's why I'm pleased to highlight two of what I consider to be very high-quality "pieces" that I've made work together in this manner as a sort of proof of concept you can use in your own development scenarios. These are the "Perfect Service" infrastructure produced by J. Ambrose Little, and the Command Pattern Invoker, by Brad King.



First, a little history on how and why I've selected these two pieces for customization, improvement, and utilization. I am working right now in a heavy-duty telephony environment. Software that runs $30,000 telephony board hardware with commercial customers that need to be up 24/7/365 doesn't have the luxury of being "turned off" while some developer deploys a revised Windows Service or DLL into production. So obviously, I did plenty of research on not having to "reinvent the wheel", and found MVP Ambrose's "Perfect Service" idea was the only implementation out there that really fit this scenario. So, I took it and added some new features of my own, and also fixed a bug in the process.

Second, I'm also a fan of interface - based programming and dynamic invocation of interface-based method calls as an extensible way to run a "pluggable" business deployment architecture that is easy to change or add to as business scenarios dictate.

Finally, I've found that I am using MSMQ more and more in loosely-coupled, sometimes connected business development. Hence, the use of Brad King's excellent Command Pattern idea with MSMQ. MSMQ has matured remarkably in version 3.0, kudos to the people at Redmond, into a full-scale reliable messaging infrastructure. Until SQL Server 2005 and Service Broker come out, Message Queue is often the best solution for this type of need.

Many people don't fully understand the power of MSMQ until they come to realize that a message can contain an entire class with properties that have values, with routing information, and methods that can be invoked by a calling infrastructure when the message is received, opened, and deserialized. If the class contained in this message conforms to a particular GOF Pattern interface, its interface allows the assembly required to be dynamically loaded and invoked to perform the unit of business work, with no more knowledge necessary than the assembly name, method name, and any parameters required. So as a result, the same dynamic "Invoker" service can handle twenty different types of operations with 20 different custom assemblies, and the above three items are all it needs to receive. That's a pretty good example of extensibility.

Now that we've laid the groundwork, let's take a look at how the pieces of the infrastructure interrelate:

As can be seen above, Client Applications (lower right) send instances of the ICommand - derived classes to the Receiver Queue as MSMQ Binary Formatted messages. If desired, acknowledgement messages can be deposited by the Receiver Queue into an Acknowledgement Queue to be picked up and processed by the Client.

The Dynamic Service Manager (upper right), which is the only actual "real" Windows Service, and can run on any machine in the network, handles the management of one or more Pluggable Service assemblies that listen for incoming messages on the Receiver Queue. Whenever a message arrives, it is deserialized by the Pluggable Service ("Invoker") class, any routing or other metadata is read, and its ICommand Execute method is called. In this example case I've added a simple SQLHelper - assisted method that just inserts notification records into a SQL Server Notifications table, but your particular implementation(s) of the ICommand interface can do any type of business logic you want -- it could send out notification emails, print reports, update web pages, you name it.

The Dynamic Service Manager Infrastructure (a la "Perfect Service")

While I am not going to review the Service Manager in great detail since J. Ambrose Little has two complete articles and downloadable sample code for his creation at the link above, I will try to summarize what it does, and the changes I've made to enhance it. The ServiceManager is a "regular" Windows Service that does basically two things:

1) It creates an instance of the ServiceBroker class.
2) It creates a FileSystemWatcher to monitor File Changed events on the current directory, for dlls and config files.

The ServiceBroker class maintains HybridDictionaries of various Services assemblies, their last modified dates, and so on, in order to dynamically load or unload your custom Pluggable Service assemblies that derive from the IService interface. It sports a RemoteServiceHandler that spins these up each into its own AppDomain, loads their "PluggableServiceXXX.dll.config" configuration files, and starts or stops each service. You never have to stop the Service Manager service. To start a new pluggable service (or a revised one or a changed configuration file) you only need to drop a new copy into the ServiceManager's execution path folder. The infrastructure takes care of all the rest. To stop a service, just delete the dll. Everything is shadow-copied, nothing is locked. The ServiceBroker is in charge of starting your implementation and from there, your "Pluggable Service" can be written to do virtually anything you want it to, provided it implements the simple IService interface, which consists of only two methods, StartService and StopService.

What I have done is add a few enhancements that I felt were either desirable or necessary:

1) I made the ServiceManager a "self-installing" service a- la Craig Andera's excellent example code. You only need to run "ServiceManager.exe /install" or "ServiceManager.exe /uninstall" and you are good to go. This is relatively easy to do, and it eliminates the need to rely on that nasty InstallUtil.exe thing.

2) I added an eventing mechanism that enables your service to be notified if its configuration file has been reloaded; it's not sufficient just to be able to reload the configuration based on FileSystemWatcher events; your service may need to do things like change property or other values and so it must have a way of knowing that it should do this. A sample implementation of this is shown below:

public event EventHandler NotifyService;

private void ConfigChanged(object source, FileSystemEventArgs e)
{
// wait for any locks to be released
System.Threading.Thread.Sleep(1500);
// reload settings
this.LoadSettings();
this.OnNotifyService();
}

protected void OnNotifyService()
{
if(NotifyService !=null)NotifyService(true,EventArgs.Empty);
}

-- and your service would subscribe to this event like so:

ServiceBroker.Config cfg = new ServiceBroker.Config();
cfg.NotifyService+=new EventHandler(cfg_NotifyService);

private void cfg_NotifyService(object source, EventArgs e)
{
// do work here
ServiceBroker.Logger.WriteToLog("Config for Invoker Service was reloaded at "+DateTime.Now.ToLongTimeString(),
System.Diagnostics.EventLogEntryType.Information);
// reload properties, etc. here if necessary because of config file changing
}

3) There is a fairly well-known bug in the FileSystemWatcher that makes it fire events more than once based on a single file change event, which can not only be annoying, but could possibly even mess up your business logic in some cases, so I added a workaround for this like so:

private string FullPathString = String.Empty;
private DateTime TimeFired;
private void ConfigChanged(object source, FileSystemEventArgs e)
{
// nasty bug in FileSystemWatcher fires twice (in about 4 ms) on changed file. This is a workaround...
if(e.FullPath.ToUpper()==FullPathString && TimeFired.Subtract(DateTime.Now).TotalMilliseconds < 50) return;
// set the values of the fullpath and time of the event fired to check / prevent dupe firings
FullPathString =e.FullPath.ToUpper();
TimeFired=DateTime.Now;
// wait for any locks to be released
System.Threading.Thread.Sleep(1500);
// reload settings
this.LoadSettings();
// Trigger our notification event
this.OnNotifyService();

}

MSMQ Command Pattern Invoker Service

The rest of my implementation centers squarely on a best-practices implementation of the ICommand Command Pattern interface for my MSMQ Invoker Service, which is what we will cover next.

The ICommand Interface

The key to the Command Pattern is the ICommand Interface, which is extremely simple:

using System;
namespace MSMQInvokerService
{

public interface ICommand
{
void Execute();
}
}

Provided that your ICommand - derived Invoker class implements the above Execute method, it is free to do "whatever it wants" at that point, and as long as the required assembly is available to by dynamically invoked, that's really about all you need to know!

My reference implementation has a lot of logging built in, using the MS Exception Management Application Block (included) so that if you want to use this as a starting point, you will be seeing plenty of "stuff" in your Event log:

using System;
using System.Messaging;
using System.Net;

namespace MSMQInvokerService
{
 /// <summary>
 /// MSMQ Invoker Service using Command Pattern and IService Interface
 /// NOTE: AssemblyInfo file MUST have ServiceBroker.ServiceEntryPoint attribute
 /// </summary>
 public class Invoker : ServiceBroker.IService
 {
  #region Class-level fields
  MessageQueue mqReceive=null;
  internal static ServiceBroker.Config AppSettings =new ServiceBroker.Config();
  string QueuePath =  AppSettings["QueuePath"].ToString();
  #endregion
  
  #region IService Members
  public void StartService()
  {
   ServiceBroker.Logger.WriteToLog("HOLA! "+AppSettings["StartText"],
    System.Diagnostics.EventLogEntryType.Information);   
   ServiceBroker.Config cfg = new ServiceBroker.Config();
   cfg.NotifyService+=new EventHandler(cfg_NotifyService);

   if (!MessageQueue.Exists(QueuePath))MessageQueue.Create(QueuePath);  
   mqReceive = new MessageQueue(QueuePath);
   MessageQueue.EnableConnectionCache = false;    
   // Control Queue permissions here:
   mqReceive.SetPermissions("Everyone",
    MessageQueueAccessRights.FullControl, AccessControlEntryType.Allow);
   mqReceive.Formatter = new BinaryMessageFormatter();
     
   try
   {      
    IAsyncResult ar = mqReceive.BeginReceive(TimeSpan.FromSeconds(5),
                         mqReceive,new AsyncCallback(OnMessageArrival));
    ServiceBroker.Logger.WriteToLog("BeginReceive Called",
                          System.Diagnostics.EventLogEntryType.Information);

   }
   catch (MessageQueueException e)
   {
    if (e.MessageQueueErrorCode != MessageQueueErrorCode.IOTimeout)
    {
     ServiceBroker.Logger.WriteToLog(e.Message,System.Diagnostics.EventLogEntryType.Information);
    }
   }
   catch (Exception e)
   {
    ServiceBroker.Logger.WriteToLog(e.Message,System.Diagnostics.EventLogEntryType.Information); 
   }
   }

 private void OnMessageArrival(IAsyncResult ar)
  {
   MessageQueue mq = (MessageQueue)ar.AsyncState;
  ServiceBroker.Logger.WriteToLog("OnMessageArrival Called",
   System.Diagnostics.EventLogEntryType.Information);
   try
   {
    Message msg=mq.EndReceive(ar);
    ICommand cReceived = (ICommand)msg.Body;
    ServiceBroker.Logger.WriteToLog("ICommand Object Casted.",
        System.Diagnostics.EventLogEntryType.Information);
    cReceived.Execute();
    ServiceBroker.Logger.WriteToLog("Executed Command at "+DateTime.Now.ToLongTimeString(),
        System.Diagnostics.EventLogEntryType.Information);
   }
   catch
   {
    ServiceBroker.Logger.WriteToLog("Timeout!",
        System.Diagnostics.EventLogEntryType.Information);
   }
   finally
   {
    mq.BeginReceive(TimeSpan.FromSeconds(5),mq,new AsyncCallback(OnMessageArrival));
   }           
  }

  private void cfg_NotifyService(object source, EventArgs e)
  {
   ServiceBroker.Logger.WriteToLog("Config file for Invoker Service was reloaded at "
+DateTime.Now.ToLongTimeString(), System.Diagnostics.EventLogEntryType.Information); // reload properties, etc. here if necessary because of config file changing } public void StopService() { ServiceBroker.Logger.WriteToLog(AppSettings["StopText"], System.Diagnostics.EventLogEntryType.Information); // Closa dee Q! mqReceive.Close(); } #endregion } }

Since the implementation above is more or less self-explanatory, I leave the reader to walk through the code. As can be seen, I am using a familiar asynchronous Receive method on my Queue, which calls back to the OnMessageArrival method to cast my ICommand out of the IASyncResult.AsyncState object:

Message msg=mq.EndReceive(ar);
ICommand cReceived = (ICommand)msg.Body;

This automatically uses a ThreadPool under the hood, and is generally an efficient way to process large numbers of incoming messages quickly on reusable background threads, provided that they don't tie up their threads for too long. Of course, there are many other ways to "skin a queue". At this point you've got an ICommand object on which you can call the expected Execute Method.

Calling the Invoker

Ok, what does the "Invoker" invoke? Well, it invokes an instance of the LateBoundCommand class:

using System;
using System.Reflection;

namespace MSMQInvokerService
{
 /// <summary>
 /// Late-Bound Command Pattern Class
 /// </summary>
 [Serializable()]
 public class LateBoundCommand : ICommand
 {
  protected string AssemblyName;
  protected string ClassName;
  protected string MethodName;
  protected object[] Parameters;

  /// <summary>
  /// Required no-parameter ctor for serializer
  /// </summary>
  protected LateBoundCommand()
  {
  }
  // constructor with assembly, class, and method arguments
  public LateBoundCommand(string assemblyName, string className, string methodName)
  { 
   AssemblyName = assemblyName;
   ClassName = className;
   MethodName = methodName;
  }

  // sets all parameter values
  public void SetParameters(params object[] myParameters)
  {
   Parameters = myParameters;
  }

  private DateTime executionTime;
  public DateTime ExecutionTime
  {
   get
   {
    return  DateTime.Now;
   }
   set
   {
    executionTime=value;

   }
  }
        /// <summary>
        /// Implement the ICommand Execute method
        /// </summary>
  public virtual void Execute()
  {
   ServiceBroker.Logger.WriteToLog("Attempting Execute" + ExecutionTime.ToLongTimeString(), 
System.Diagnostics.EventLogEntryType.Information); try { // Load the assembly Assembly a = Assembly.LoadWithPartialName(AssemblyName); // Get the class we are interested in from the assembly Type TargetType = a.GetType(ClassName); // Get the method we are interested in MethodInfo TargetMethod = TargetType.GetMethod(MethodName); // Call the method TargetMethod.Invoke(null, Parameters); ServiceBroker.Logger.WriteToLog("LateBoundCommand.Execute complete" +
Parameters[0].ToString() + " " +Parameters[1].ToString()+": "
+ExecutionTime.ToLongTimeString(),System.Diagnostics.EventLogEntryType.Information); } catch(Exception ex) { ServiceBroker.Logger.WriteToLog("LateBoundCommand.Execute Exception: "
+ex.Message+": " +ExecutionTime.ToLongTimeString(), System.Diagnostics.EventLogEntryType.Information); } } } }

The Receiver Class

Finally, once the correct ICommand Object has been dynamically loaded and its TargetMethod has been invoked, it has to have a CommandReceiver to do the actual work:

using System;
using System.Data;
using System.Data.SqlClient;
using Microsoft.ApplicationBlocks.Data ;

namespace MSMQInvokerService
{
/// <summary>
/// Sample target class / method to be invoked from ICommand Execute interface method
/// </summary>
public class CommandReceiver
{
public static void LogMessage(string connectionString, string spName, object[] parms)
{
SqlHelper.ExecuteNonQuery(connectionString,spName,parms);
ServiceBroker.Logger.WriteToLog("executed: " + spName, System.Diagnostics.EventLogEntryType.Information);
}
}
}

In this case, my CommandReceiver simply calls the named stored procedure given to it, using the connection string and object array of procedure parameters passed to it. You can use this type of infrastructure to pass a Message body that calls any stored procedure on any database, with any parameters, all with the same infrastructure. In this proof - of - concept demo app, it just writes the entry into the Notifications table, with an ID, Name, Email and EntryDateTime, the idea being that this table will be polled, perhaps, by another service that sends out notifications or some similar work. Note also that my Receiver class uses the MS Data Application Blocks "SqlHelper" class to make the actual process of taking a connection string, a proc name, and an object array of parameters complete childsplay. The overloads that accept the object array of parameter values also automatically cache the SqlParameters to be re-used, based on the passed-in connection string and stored proc name.

That covers the basics of my Self-Updating Windows Service Infrastructure with Command Pattern Message Queue Invoker Service, and how to use it. In the downloadable VS.NET 2003 solution zip file below, you'll find a Readme.txt file that covers all the steps to build, install, and test this infrastructure all on a single machine. Once you've gotten that done and seen that it works properly, I encourage you to "use your noodle" to find new and innovative ways to adapt and extend the concept for your own business needs.

Download the Visual Studio.NET Solution that accompanies this article

 



Peter Bromberg is a C# MVP, MCP, and .NET consultant who has worked in the banking and financial industry for 20 years. He has architected and developed web - based corporate distributed application solutions since 1995, and focuses exclusively on the .NET Platform.
Article Discussion: