In terms of keeping this blog up-to-date on my goings on, the month of May has been a dismal one. There has been a lot going on, but I've simply not made the time to log my activities and/or code snippets and it's eating at me.
Recently, I've been writing some code that needed to run in response to certain activities that would be triggered by another application. These applications would not be connected, per se, in the sense that one would 'hook' into the other and respond to events. Rather, one application would set a flag, send a message, etc such that the second application would recognize the notification and run some activity in response.
The logical choice for such an architecture is a messaging-based system. In this case, I decided upon MSMQ/Queued Components and boy am I happy about that! I hadn't seriously worked in MSMQ for years (though I used to do quite a bit of it) and it was fun to romp around my old playing grounds, but now with a .NET engine helping out. Tons of fun!
This particular application is structured such that I have a web-based application (ASP.NET in this case) that will send certain messages to a message queue when the user completes some task. Then I have a Windows Service running that will read messages from the queue and run a configurable response. As part of the setup, I have written a few installer classes that will 1) create the message queue and 2) install the Windows Service and 3) run it automatically after install.
The task of creating a message queue installer is quite simple and straightforward. I wanted my installer to detect the existance of the queue and create it if it wasn't found. Upon rollback or uninstall, the queue would be deleted. Here's an example from the Install method of my Installer class:
public override void Install(IDictionary stateSaver) {
base.Install(stateSaver);
try {
const QueueName = @".\Private$\TestQueue";
if ( !MessageQueue.Exists(QueueName) ) {
MessageQueue queue = MessageQueue.Create(QueueName, true);
Trustee trustee = new Trustee(@"NT AUTHORITY\NETWORK SERVICE");
MessageQueueAccessRights rights = MessageQueueAccessRights.FullControl;
MessageQueueAccessControlEntry ace = new MessageQueueAccessControlEntry(trustee, rights);
queue.SetPermissions(ace);
}
}
catch ( Exception er ) {
// log the error to the event log
throw;
}
}
At this point, I'm ready to start interacting with the message queue by sending and receiving messages.
To simplify this process and to make it more structured, I decided to create a simple class called QueueItem. This class provides the structure and data needed by the recipient application so that it can effectively run in response to the website's activity. This class is also decorated with the appropriate attributes that enable it to be serialized to XML. To further simplify the process of interacting with the message queue, the QueueItem class contains a method that allows the caller to send the message to the queue; all of the logic necessary is enclosed therein:
[XmlRoot("queueItem")]
public sealed class QueueItem {
public QueueItem() { /* parameterless ctor for serialization support */ }
public QueueItem(string id, string action) {
_id = id;
_action = action;
}
private string _id, _action
[XmlElement("id")]
public string Id {
get { return _id; }
set { _id = value; }
}
[XmlElement("action")]
public string Action {
get { return _action; }
set { _action = value; }
}
public void SendToQueue(string label) {
QueueItem.SendToQueue(this, label);
}
public static void SendToQueue(QueueItem item, string label) {
MessageQueue queue = new MessageQueue(@".\Private$\TestQueue", QueueAccessMode.Send);
queue.Formatter = new XmlMessageFormatter(new Type[] {typeof ( QueueItem )});
using ( MessageQueueTransaction trans = new MessageQueueTransaction() ) {
trans.Begin();
queue.Send(item, label, trans);
trans.Commit();
}
}
}
The Windows Service, now, is responsible for watching the queue and running the appropriate response to messages that find their way into the queue. To accomplish this, I decided to have the service spin off a secondary thread at start up. It's this thread that is responsible for processing messages off of the queue.
protected override void OnStart(string[] args) {
try {
Thread th = new Thread(new ThreadStart(processMessages));
th.IsBackground = true;
th.Name = "QueueListener";
th.Start();
}
catch ( Exception er ) {
EventLog.WriteEntry(Service.ActualName,
string.Format("Unable to start service\n\n{0}", er),
EventLogEntryType.Error);
this.ExitCode = 1;
this.Stop();
return;
}
}
private void processMessages() {
try {
using ( MessageQueue queue = new MessageQueue(@".\Private$\TestQueue", QueueAccessMode.Receive) ) {
queue.Formatter = new XmlMessageFormatter(new Type[] {typeof ( QueueItem )});
while ( true ) {
using ( MessageQueueTransaction trans = new MessageQueueTransaction() ) {
trans.Begin();
Message msg = queue.Receive();
trans.Commit();
QueueItem item = msg.Body as QueueItem;
if ( null != item ) {
EventLog.WriteEntry(Service.ActualName,
string.Format("Message received: {0}, {1}", item.Id, item.Action),
EventLogEntryType.Information);
}
else {
EventLog.WriteEntry(Service.ActualName,
string.Format("Invalid message datatype received: {0}. Expected: QueueItem.", msg.Body.GetType().FullName),
EventLogEntryType.Warning);
}
}
}
}
}
catch ( Exception er ) {
EventLog.WriteEntry(Service.ActualName,
string.Format("Critical shutdown\n\n{0}", er),
EventLogEntryType.Error);
this.ExitCode = 1;
this.Stop();
return;
}
}
The beauty here is that I don't have to poll. Rather, I have the background thread call .Receive() on the queue. This will effectively block the thread indefinitely or until a message is pushed into the queue. At that point, it unblocks and begins processing the message. Once finished it calls .Receive() again. If there's a message, it gets processed, if not, it blocks. And thus the cycle repeats itself.
Using MSMQ can be very rewarding and a lot of fun, and this little project has reminded me of that fact.