Programming, Policitcs, and uhhh Pineapples.
# Thursday, July 09, 2009

XMPP SASL Challenge-Response Using DIGEST-MD5 In C#

Thursday, July 09, 2009 3:46:19 PM UTC

I've been struggling mightily with implementing the SASL challenge-response portion of an XMPP client I've been working on. By far, this has been the hardest part to implement as it's been difficult to validate whether I've implemented the algorithm correctly as there doesn't seem to be any (easy to find) open source implementations of of SASL with the DIGEST-MD5 implementation (let alone in C#).

The trickiest part of the whole process is building the response token which gets sent back as a part of the message to the server.

RFC2831 documents the SASL DIGEST-MD5 authentication mechanism as such:

   Let { a, b, ... } be the concatenation of the octet strings a, b, ...

   Let H(s) be the 16 octet MD5 hash [RFC 1321] of the octet string s.

   Let KD(k, s) be H({k, ":", s}), i.e., the 16 octet hash of the string
   k, a colon and the string s.

   Let HEX(n) be the representation of the 16 octet MD5 hash n as a
   string of 32 hex digits (with alphabetic characters always in lower
   case, since MD5 is case sensitive).

      response-value  =
         HEX( KD ( HEX(H(A1)),
                 { nonce-value, ":" nc-value, ":",
                   cnonce-value, ":", qop-value, ":", HEX(H(A2)) }))

   If authzid is specified, then A1 is


      A1 = { H( { username-value, ":", realm-value, ":", passwd } ),
           ":", nonce-value, ":", cnonce-value, ":", authzid-value }

   If authzid is not specified, then A1 is


      A1 = { H( { username-value, ":", realm-value, ":", passwd } ),
           ":", nonce-value, ":", cnonce-value }

   where

         passwd   = *OCTET

   If the "qop" directive's value is "auth", then A2 is:

      A2       = { "AUTHENTICATE:", digest-uri-value }

   If the "qop" value is "auth-int" or "auth-conf" then A2 is:

      A2       = { "AUTHENTICATE:", digest-uri-value,
               ":00000000000000000000000000000000" }

Seems simple enough, right?  Not!  It took a bit of time to parse through it mentally and come up with an impelementation, but I was still failing (miserably).

The breakthrough came when I stumbled upon a posting by Robbie Hanson:

Here's the trick - normally when you hash stuff you get a result in hex values. But we don't want this result as a string of hex values! We need to keep the result as raw data! If you were to do a hex dump of this data you'd find it to be "3a4f5725a748ca945e506e30acd906f0". But remeber, we need to operate on it's raw data, so don't convert it to a string.

The most important part of his posting is the last line (and that it included the intermediate hexadecimal string results. Win! Now I finally had some sample data to compare against to figure out where I was going wrong).  At one critical junction in my implementation of the algorithm, I was converting the MD5 hash value to a hexadecimal string -- thank goodness for Robbie's clarification of that point!

Armed with this test data, I was finally able to get it all working.  Here is the test code:

using MbUnit.Framework;
using Xmpp.Client;

namespace Xmpp.Tests
{
    [TestFixture]
    public class SaslChallengeResponseTests
    {
        [Test]
        public void TestCreateResponse()
        {
            // See example here: http://deusty.blogspot.com/2007/09/example-please.html

            // h1=3a4f5725a748ca945e506e30acd906f0
            // a1Hash=b9709c3cdb60c5fab0a33ebebdd267c4
            // a2Hash=2b09ce6dd013d861f2cb21cc8797a64d
            // respHash=37991b870e0f6cc757ec74c47877472b

            SaslChallenge challenge = new SaslChallenge(
                "md5-sess", "utf-8", "392616736", "auth", "osXstream.local");

            SaslChallengeResponse response = new SaslChallengeResponse(
                challenge, "test", "secret", "05E0A6E7-0B7B-4430-9549-0FE1C244ABAB");

            Assert.AreEqual("3a4f5725a748ca945e506e30acd906f0", 
                response.UserTokenMd5HashHex);
            Assert.AreEqual("b9709c3cdb60c5fab0a33ebebdd267c4", 
                response.A1Md5HashHex);
            Assert.AreEqual("2b09ce6dd013d861f2cb21cc8797a64d", 
                response.A2Md5HashHex);
            Assert.AreEqual("37991b870e0f6cc757ec74c47877472b", 
                response.ResponseTokenMd5HashHex);
        }
    }
}

I modeled the SASL challenge like so:

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Text;

namespace Xmpp.Client
{
    /// <summary>
    /// Represents a SASL challenge in object code.
    /// </summary>
    public class SaslChallenge
    {
        private static readonly Dictionary<string, FieldInfo> _fields;

        private readonly string _rawDecodedText;
        private string _algorithm;
        private string _charset;
        private string _nonce;
        private string _qop;
        private string _realm;

        /// <summary>
        /// Initializes the <see cref="SaslChallenge"/> class.
        /// </summary>
        /// <remarks>
        /// Caches the properties which are set using reflection on <see cref="Parse"/>.
        /// </remarks>
        static SaslChallenge()
        {
            // Initialize the hash of fields.
            _fields = new Dictionary<string, FieldInfo>();

            FieldInfo[] fields = typeof (SaslChallenge).GetFields(
                BindingFlags.NonPublic | BindingFlags.Instance);

            foreach (FieldInfo field in fields)
            {
                // Trim the _ from the start of the field names.
                string name = field.Name.Trim('_');

                _fields[name] = field;
            }
        }

        /// <summary>
        /// Creates a specific SASL challenge message.
        /// </summary>
        public SaslChallenge(string algorithm, string charset, 
            string nonce, string qop, string realm)
        {
            _algorithm = algorithm;
            _charset = charset;
            _nonce = nonce;
            _qop = qop;
            _realm = realm;

            Debug.WriteLine("algorithm=" + _algorithm);
            Debug.WriteLine("charset=" + _charset);
            Debug.WriteLine("nonce=" + _nonce);
            Debug.WriteLine("qop=" + _qop);
            Debug.WriteLine("realm=" + _realm);
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="SaslChallenge"/> 
        /// class based on the raw decoded text.
        /// </summary>
        /// <remarks>
        /// Use the <see cref="Parse"/> method to create an instance from 
        /// a raw encoded message.
        /// </remarks>
        /// <param name="rawDecodedText">The raw decoded text.</param>
        private SaslChallenge(string rawDecodedText)
        {
            _rawDecodedText = rawDecodedText;

            string[] parts = rawDecodedText.Split(',');

            foreach (string part in parts)
            {
                string[] components = part.Split('=');

                string property = components[0];

                _fields[property].SetValue(this, 
                    components[1].Trim('"'));
            }

            Debug.WriteLine("algorithm=" + _algorithm);
            Debug.WriteLine("charset=" + _charset);
            Debug.WriteLine("nonce=" + _nonce);
            Debug.WriteLine("qop=" + _qop);
            Debug.WriteLine("realm=" + _realm);
        }

        public string Realm
        {
            get { return _realm; }
        }

        public string Nonce
        {
            get { return _nonce; }
        }

        public string Qop
        {
            get { return _qop; }
        }

        public string Charset
        {
            get { return _charset; }
        }

        public string Algorithm
        {
            get { return _algorithm; }
        }

        public string RawDecodedText
        {
            get { return _rawDecodedText; }
        }

        /// <summary>
        /// Parses the specified challenge message.
        /// </summary>
        /// <param name="response">The base64 encoded challenge.</param>
        /// <returns>An instance of <c>SaslChallenge</c> based on 
        /// the message.</returns>
        public static SaslChallenge Parse(string encodedChallenge)
        {
            byte[] bytes = Convert.FromBase64String(encodedChallenge);

            string rawDecodedText = Encoding.ASCII.GetString(bytes);

            return new SaslChallenge(rawDecodedText);
        }
    }
}

And finally, here is the challenge response class which contains the meat of the response building logic:

using System;
using System.Diagnostics;
using System.Security.Cryptography;
using System.Text;

namespace Xmpp.Client
{

    /// <summary>
    /// Partial implementation of the SASL authentication protocol 
    /// using the DIGEST-MD5 mechanism.
    /// </summary>
    /// <remarks>
    /// See <see href="http://www.ietf.org/rfc/rfc4422.txt"/> and 
    /// <see href="http://www.ietf.org/rfc/rfc2831.txt"/> for details.
    /// </remarks>
    public class SaslChallengeResponse
    {
        #region fields

        private static readonly Encoding _encoding;
        private static readonly MD5 _md5;
        private readonly SaslChallenge _challenge;
        private readonly string _cnonce;
        private readonly string _decodedContent;
        private readonly string _digestUri;
        private readonly string _encodedContent;
        private readonly string _password;
        private readonly string _realm;
        private readonly string _username;

        private string _a1Md5HashHex;
        private string _a2Md5HashHex;
        private string _responseTokenMd5HashHex;
        private string _userTokenMd5HashHex;

        #endregion

        #region properties

        /// <summary>
        /// Gets the final, base64 encoded content of the challenge response.
        /// </summary>
        /// <value>A base64 encoded string of the response content.</value>
        public string EncodedContent
        {
            get { return _encodedContent; }
        }

        /// <summary>
        /// Gets the unencoded content of the challenge response.
        /// </summary>
        /// <value>The response content in plain text.</value>
        public string DecodedContent
        {
            get { return _decodedContent; }
        }

        /// <summary>
        /// Gets the hexadecimal string representation of the user token MD5 
        /// hash value.
        /// </summary>
        /// <value>The hexadecimal representation of the user token MD5 hash 
        /// value.</value>
        public string UserTokenMd5HashHex
        {
            get { return _userTokenMd5HashHex; }
        }

        /// <summary>
        /// Gets the hexadecimal string representation of the response token 
        /// MD5 hash value.
        /// </summary>
        /// <value>The hexadecimal string representation of the response token 
        /// MD5 hash value.</value>
        public string ResponseTokenMd5HashHex
        {
            get { return _responseTokenMd5HashHex; }
        }

        /// <summary>
        /// Gets the hexadecimal string representation of the A1 MD5 hash 
        /// value (see RFC4422 and RFC2831)
        /// </summary>
        /// <value>The hexadecimal string representation of the A1 MD5 hash 
        /// value (see RFC4422 and RFC2831)</value>
        public string A1Md5HashHex
        {
            get { return _a1Md5HashHex; }
        }

        /// <summary>
        /// Gets the hexadecimal string representation of the A2 MD5 hash 
        /// value (see RFC4422 and RFC2831)
        /// </summary>
        /// <value>The hexadecimal string representation of the A2 MD5 hash 
        /// value (see RFC4422 and RFC2831)</value>
        public string A2Md5HashHex
        {
            get { return _a2Md5HashHex; }
        }

        #endregion

        /// <summary>
        /// Initializes the <see cref="SaslChallengeResponse"/> class.
        /// </summary>
        static SaslChallengeResponse()
        {
            _md5 = MD5.Create();
            _encoding = Encoding.UTF8;
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="SaslChallengeResponse"/> 
        /// class.
        /// </summary>
        /// <param name="challenge">The challenge.</param>
        /// <param name="username">The username.</param>
        /// <param name="password">The password.</param>
        public SaslChallengeResponse(SaslChallenge challenge, 
            string username, string password)
            : this(challenge, username, password, null, null, null)
        {
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="SaslChallengeResponse"/> 
        /// class.
        /// </summary>
        /// <param name="challenge">The challenge.</param>
        /// <param name="username">The username.</param>
        /// <param name="password">The password.</param>
        /// <param name="cnonce">A specific cnonce to use.</param>
        public SaslChallengeResponse(SaslChallenge challenge, string username, 
            string password, string cnonce)
            : this(challenge, username, password, null, null, cnonce)
        {
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="SaslChallengeResponse"/> 
        /// class.
        /// </summary>
        /// <param name="challenge">The challenge.</param>
        /// <param name="username">The username.</param>
        /// <param name="password">The password.</param>
        /// <param name="realm">A specific realm, different from the one in the 
        /// challenge.</param>
        /// <param name="digestUri">The digest URI.</param>
        /// <param name="cnonce">A specific client nonce to use.</param>
        public SaslChallengeResponse(SaslChallenge challenge, string username, 
            string password, string realm, string digestUri, string cnonce)
        {
            _challenge = challenge;
            _username = username;
            _password = password;

            if (string.IsNullOrEmpty(_challenge.Realm))
            {
                _realm = realm;
            }
            else
            {
                _realm = challenge.Realm;
            }

            if (string.IsNullOrEmpty(_realm))
            {
                throw new ArgumentException("No realm was specified.");
            }

            if (string.IsNullOrEmpty(cnonce))
            {
                _cnonce =
                    Guid.NewGuid().ToString().TrimStart('{').TrimEnd('}')
                        .Replace("-", string.Empty).ToLowerInvariant();
            }
            else
            {
                _cnonce = cnonce;
            }

            if (string.IsNullOrEmpty(digestUri))
            {
                _digestUri = string.Format("xmpp/{0}", _challenge.Realm);
            }
            else
            {
                _digestUri = digestUri;
            }

            // Main work here:
            _decodedContent = GetDecodedContent();

            byte[] bytes = _encoding.GetBytes(_decodedContent);

            _encodedContent = Convert.ToBase64String(bytes);
        }

        /// <summary>
        /// Gets the body of the response in a decoded format.
        /// </summary>
        /// <returns>The raw response string.</returns>
        private string GetDecodedContent()
        {
            // Gets the response token according to the algorithm in RFC4422 
            // and RFC2831
            _responseTokenMd5HashHex = GetResponse();

            StringBuilder buffer = new StringBuilder();

            buffer.AppendFormat("username=\"{0}\",", _username);
            buffer.AppendFormat("realm=\"{0}\",", _challenge.Realm);
            buffer.AppendFormat("nonce=\"{0}\",", _challenge.Nonce);
            buffer.AppendFormat("cnonce=\"{0}\",", _cnonce);
            buffer.Append("nc=00000001,qop=auth,");
            buffer.AppendFormat("digest-uri=\"{0}\",", _digestUri);
            buffer.AppendFormat("response={0},", _responseTokenMd5HashHex);
            buffer.Append("charset=utf-8");

            return buffer.ToString();
        }

        /// <summary>
        /// HEX( KD ( HEX(H(A1)), { nonce-value, ":" nc-value, ":", cnonce-value, 
        /// ":", qop-value, ":", HEX(H(A2)) }))
        /// </summary>
        private string GetResponse()
        {
            byte[] a1 = GetA1();
            string a2 = GetA2();

            Debug.WriteLine("a1=" + ConvertToBase16String(a1));
            Debug.WriteLine("a2=" + a2);

            byte[] a2Bytes = _encoding.GetBytes(a2);

            byte[] a1Hash = _md5.ComputeHash(a1);
            byte[] a2Hash = _md5.ComputeHash(a2Bytes);

            _a1Md5HashHex = ConvertToBase16String(a1Hash);
            _a2Md5HashHex = ConvertToBase16String(a2Hash);

            // Let KD(k, s) be H({k, ":", s})
            string kdString = string.Format("{0}:{1}:{2}:{3}:{4}:{5}",
                _a1Md5HashHex, _challenge.Nonce, "00000001",
                _cnonce, "auth", _a2Md5HashHex);

            Debug.WriteLine("kd=" + kdString);

            byte[] kdBytes = _encoding.GetBytes(kdString);

            byte[] kd = _md5.ComputeHash(kdBytes);
            string kdBase16 = ConvertToBase16String(kd);

            return kdBase16;
        }

        /// <summary>
        /// A1 = { H( { username-value, ":", realm-value, ":", passwd } ), 
        /// ":", nonce-value, ":", cnonce-value }
        /// </summary>
        private byte[] GetA1()
        {
            string userToken = string.Format("{0}:{1}:{2}",
                                             _username, _realm, _password);

            Debug.WriteLine("userToken=" + userToken);

            byte[] bytes = _encoding.GetBytes(userToken);

            byte[] md5Hash = _md5.ComputeHash(bytes);

            // Use this for validation purposes from unit testing.
            _userTokenMd5HashHex = ConvertToBase16String(md5Hash);

            string nonces = string.Format(":{0}:{1}",
                                          _challenge.Nonce, _cnonce);

            byte[] nonceBytes = _encoding.GetBytes(nonces);

            byte[] result = new byte[md5Hash.Length + nonceBytes.Length];

            md5Hash.CopyTo(result, 0);
            nonceBytes.CopyTo(result, md5Hash.Length);

            return result;
        }

        /// <summary>
        /// A2 = { "AUTHENTICATE:", digest-uri-value }
        /// </summary>
        private string GetA2()
        {
            string result = string.Format("AUTHENTICATE:{0}", _digestUri);

            return result;
        }

        /// <summary>
        /// Converts a byte array to a base16 string.
        /// </summary>
        /// <param name="bytes">The bytes to convert.</param>
        /// <returns>The hexadecimal string representation of the contents 
        /// of the byte array.</returns>
        private string ConvertToBase16String(byte[] bytes)
        {
            StringBuilder buffer = new StringBuilder();

            foreach (byte b in bytes)
            {
                string s = Convert.ToString(b, 16).PadLeft(2, '0');

                buffer.Append(s);
            }

            Debug.WriteLine(string.Format("Converted {0} bytes", 
                bytes.Length));

            string result = buffer.ToString();

            return result;
        }
    }
}

Happy DIGESTing!

# Wednesday, July 08, 2009

Leveraging The Windows Forms WebBrowser Control (For The Win)

Wednesday, July 08, 2009 1:28:32 PM UTC

I've been working on a little utility to experiment with the XMPP protocol.  The idea was to write a tool that would allow me to send, receive, and display the XML stream and XML stanza messages at the core of XMPP.

Of course, I could have implemented it using a simple multi-line text box for the XML entry, but that would mean that I wouldn't have nice things like syntax highlighting (for XML) and nice (auto) indentation.

On the desktop, I'm not familiar with any free Windows Forms editor controls that are capable of syntax highlighting.  But on the web side, there are several free, open source script libraries at our disposal.  For example, CodePress, EditArea, and CodeMirror.

I chose CodeMirror for this application as it was the simplest library that met my needs.

There really aren't any tricks to this aside from getting the URL correct.  In this case, I have my static HTML content in a directory in my project:

And I set the content files to "Copy always" in the properties pane for the files so that they get copied to the output directory of the project.  To set the correct path, I find the executing directory of the application and set the URL properly:

protected override void OnLoad(EventArgs e)
{
    if (!DesignMode)
    {
        string path = Path.Combine(
            Environment.CurrentDirectory, 
            "HTML/input-page.html");
        path = path.Replace('\\', '/');

        _inputBrowser.Url = new Uri(path);
    }

    base.OnLoad(e);
}

Note that I check if the control is in design mode (the designer throws an error if you don't do this since the path points to Visual Studio's runtime directory instead of your applications output).  Now all that's left is to get the script and style references correct in your HTML page:

<script src="../HTML/CodeMirror/js/codemirror.js" type="text/javascript"></script>
<link rel="stylesheet" type="text/css" href="../HTML/CodeMirror/css/docs.css" />  

And in your script:

<script type="text/javascript">
    var editor = CodeMirror.fromTextArea('code', {
        height: "210px",
        parserfile: "parsexml.js",
        stylesheet: "../HTML/CodeMirror/css/xmlcolors.css",
        path: "../HTML/CodeMirror/js/",
        continuousScanning: 500,
        lineNumbers: true,
        textWrapping: false
    });
</script>

The final part is getting your Windows forms code talking to the Javascript in the browser.  In this case, I've written some simple Javascript that interacts with the editor control:

<script type="text/javascript">
    var $g = document.getElementById;

    function resize(height) {
        var textArea = $g("code");

        // Get the iframe.
        var editor = textArea.nextSibling.firstChild;

        editor.style.height = (height - 1) + "px";
    }

    function reset() {
        editor.setCode("");
    }

    function addContent(data) {
        var code = editor.getCode();

        editor.setCode(code + data);

        editor.reindent();
    }

    function setContent(data) {
        editor.setCode(data);

        editor.reindent();

        // Scroll to bottom.
        editor.selectLines(editor.lastLine(), 0);
    }

    function getContents() {
        return editor.getCode();
    }
</script>

From the application code, we can call these script functions using the InvokeScript method:

private void AddStreamMessageInternal(string data)
{
    if (_streamBrowser.Document != null)
    {
        // Get the contents
        string code = (string) _streamBrowser.Document.InvokeScript(
            "getContents", new object[] {});

        code = code + data;

        code = code.Replace("><", ">\r\n<");

        _streamBrowser.Document.InvokeScript(
            "setContent", new object[] {code});
    }
}

private void AdjustSize()
{
    // Call a resize method to change the HTML editor size.
    if (_streamBrowser.Document != null)
    {
        _streamBrowser.Document.InvokeScript(
            "resize", new object[] {_streamBrowser.ClientSize.Height});
    }
}

public void RefreshBrowserContents()
{
    if (_streamBrowser.Document != null)
    {
        _streamBrowser.Document.InvokeScript(
            "reset", new object[] {});
    }
}

Awesome! You can see that I can both pass arguments into the Javascript functions and read the return data from the Javascript function as well.  The big win is that now you can take advantage of your favorite Javascript utilities in your Windows Forms applications.

# Monday, June 22, 2009

Where (Most?) Zombie Movies Go Wrong

Monday, June 22, 2009 1:14:45 PM UTC

As I was watching Diary of the Dead last night (I don't know why...I just have this thing for zombie movies), about half ways through the movie, it suddenly dawned upon me that none of the characters had uttered the word "zombie".  While the movie had many failings - the standard "Stupid Things People Do in Horror Movies" scenarios and taking itself too seriously - I felt that this was the most glaring one.  It's as if none of the characters had ever watched a zombie movie and didn't know the standard operating procedures with regards to zombie movies.

# Friday, June 19, 2009

I Learned A New Vocabulary Word Today...

Friday, June 19, 2009 3:32:47 PM UTC

Apophallation.

Apophallation is a technique resorted to by some species of air-breathing land slugs such as Limax maximus and Ariolimax spp. In these species of hermaphroditic terrestrial gastropod mollusks, after mating, if the slugs cannot successfully separate, a deliberate amputating of the penis takes place.

The slugs are hermaphroditic and have a full set of organs of both sexes. They have relatively large penises which, during mating, wrap around each other in a tight spiral. They sometimes have difficulty separating afterwards. When separating seems impossible, one slug gnaws off either its own, or its partner's penis, so that separation is then possible. No replacement penis grows, and the apophallated slug adopts a purely female function from that point onward.

:-O

(We've been having a pretty heavy slug infestation due to the inordinate amount of rainfall we've been getting...luckily, they don't seem to like pineapple)

As if that wasn't fascinating enough, their sex organs protrude from the side of their head.

And if that didn't boggle your mind, how about eating slugs (warning: cute and gag inducing at the same time)?

Youtube.

# Friday, June 12, 2009

Discovering System.Linq.Expressions

Friday, June 12, 2009 3:24:08 AM UTC

I'm officially a fan: System.Linq.Expressions is one of the coolest namespaces.

I've been working on a simple workflow engine as an example codebase for exploring different aspects of object oriented programming (inheritance, encapsulation, polymorphism, design patterns, etc.) for training purposes.

As a side note, I've found that it's actually very difficult to describe good object oriented code; I can just kind of feel it when I'm writing it and I know it when I see it...but it's really, really hard to describe.  I was asked by an associate consultant why it mattered.  Why bother with good object oriented design?  For me (at least), more than anything, it's about organization of code, readability, maintainability, and usability.  Good object oriented code makes it easy to think about the models and how the interactions between the different moving parts are executed.  But that's a bigger topic for a different post.

Back on topic :-D The basic design scenario that I was trying to solve in this case is that the simple workflow engine (SWE) would have the capability of hydrating a workflow template instance from an XML definition file (much like WF does with .xoml files, but on a much more basic level, of course).  I thought this would be a good exercise for teaching purposes as it would cover various aspects of reflection.  Somewhere along the line, inspired by a comment by Richard Deeming (see bullet #4) on Rick Strahl's DataContextFactory implementation, I decided to see if I could do it using expression trees instead.

Here is a sample XML template definition:

<WorkflowTemplate>
    <Actions>
        <Action Type="SimpleWorkflowEngine.Extensions.CustomActions.EmailAction, 
            SimpleWorkflowEngine.Extensions">
            <Parameters>
                <Parameter Name="ContinueOnError">true</Parameter>
                <Parameter Name="FriendlyName">Send Email</Parameter>
                <Parameter Name="RecipientAddress">cchen@domain.com</Parameter>
                <Parameter Name="Message">Hello, World! From SimpleWorkflowEngine!</Parameter>
                <Parameter Name="Subject">Test Email</Parameter>
            </Parameters>
        </Action>
    </Actions>
</WorkflowTemplate>

Here are the classes which this XML deserializes to:

using System;
using System.Collections.ObjectModel;
using System.Xml.Serialization;

namespace SimpleWorkflowEngine.Core.Common
{
    /// <summary>
    /// Models a workflow template.
    /// </summary>
    [Serializable]
    [XmlRoot("WorkflowTemplate")]
    public class WorkflowTemplate
    {
        private Collection<WorkflowTemplateAction> _actions;

        /// <summary>
        /// Gets or sets the actions for this workflow template.
        /// </summary>
        /// <value>The actions.</value>
        [XmlArray("Actions"), XmlArrayItem("Action", 
            typeof(WorkflowTemplateAction))]
        public Collection<WorkflowTemplateAction> Actions
        {
            get { return _actions; }
            set { _actions = value; }
        }
    }

    /// <summary>
    /// Represents an action in the workflow.
    /// </summary>
    [Serializable]
    public class WorkflowTemplateAction
    {
        private string _typeName;
        private Collection<WorkflowTemplateParameter> _parameters;

        /// <summary>
        /// Gets or sets the type name of the concrete 
        /// <see cref="WorkflowAction"/> class.
        /// </summary>
        /// <value>
        /// The type name of the concrete <see cref="WorkflowAction"/> class.
        /// </value>
        [XmlAttribute("Type")]
        public string TypeName
        {
            get { return _typeName; }
            set { _typeName = value; }
        }

        /// <summary>
        /// Gets or sets the parameters which map to property values on 
        /// the action.
        /// </summary>
        /// <value>The parameters.</value>
        [XmlArray("Parameters"), XmlArrayItem("Parameter", 
            typeof(WorkflowTemplateParameter))]
        public Collection<WorkflowTemplateParameter> Parameters
        {
            get { return _parameters; }
            set { _parameters = value; }
        }
    }

    /// <summary>
    /// Represents a property value on the <see cref="WorkflowAction"/>.
    /// </summary>
    [Serializable]
    public class WorkflowTemplateParameter
    {
        private string _name;
        private string _value;

        /// <summary>
        /// Gets or sets the name.  It must match the name of the property.
        /// </summary>
        /// <value>The name of the property.</value>
        [XmlAttribute("Name")]
        public string Name
        {
            get { return _name; }
            set { _name = value; }
        }

        /// <summary>
        /// Gets or sets the value.
        /// </summary>
        /// <value>The value.</value>
        [XmlText(typeof(string))]
        public string Value
        {
            get { return _value; }
            set { _value = value; }
        }
    }
}

I've done something similar in a workflow engine that I put together for Zorch Software before the days of WF. Back then I used reflection to assemble a workflow instance from an XML template as well. (In that case, the engine supported state machine workflows; for this training sample, I thought that just sequential was good enough)

Here is the invocation code to get a new instance of a workflow from a workflow template definition:

using System;
using MbUnit.Framework;
using SimpleWorkflowEngine.Core.Common;
using SimpleWorkflowEngine.Core.Contracts;
using SimpleWorkflowEngine.Core.Runtime;

namespace SimpleWorkflowEngine.Tests.Fixtures
{
    [TestFixture]
    public class WorkflowFactoryTests
    {
        [Test]
        public void TestCreateInstance()
        {
            IWorkflowTemplateProvider provider =
                ApplicationContext.Instance.Resolve<IWorkflowTemplateProvider>();

            WorkflowTemplate template =
                provider.FromTemplateId(
                new Guid("1FE5526C-D0E7-4c58-8644-3B3144AEED0E"));

            Workflow instance = WorkflowFactory.Create(template);

            Assert.AreEqual(1, instance.Actions.Count);
        }
    }
}

The basics are that the factory has to create an instance by iterating through each WorkflowTemplateAction and creating a concrete WorkflowAction instance and set property values using the WorkflowTemplateParameter associated with the WorkflowTemplateAction.

Here is the implementation of the factory method (cool stuff in bold):

using System;
using System.Collections.Generic;
using System.Linq.Expressions;
using System.Reflection;
using SimpleWorkflowEngine.Core.Common;

namespace SimpleWorkflowEngine.Core.Runtime
{
    /// <summary>
    /// Assembles a workflow instance based on an input template.
    /// </summary>
    public static class WorkflowFactory
    {
        private static string _nullPropertyError = 
            "The parameter \"{0}\" did not match a property on the class \"{1}\".";

        /// <summary>
        /// Assembles a workflow instance from a workflow template instance.
        /// </summary>
        /// <param name="template">The template.</param>
        /// <returns>A workflow instance based on the workflow template.</returns>
        public static Workflow Create(WorkflowTemplate template)
        {
            Workflow instance = new Workflow();

            foreach(WorkflowTemplateAction templateAction in template.Actions)
            {
                Type actionType = Type.GetType(templateAction.TypeName);               

                List<MemberBinding> bindings = new List<MemberBinding>();

                foreach(WorkflowTemplateParameter parameter in templateAction.Parameters)
                {
                    BindingFlags flags = BindingFlags.SetProperty 
                        | BindingFlags.Instance | BindingFlags.Public;

                    // Get the property matching the parameter name.
                    PropertyInfo property = actionType.GetProperty(
                        parameter.Name, flags);

                    if(property == null)
                    {
                        throw new NullReferenceException(string.Format(
                            _nullPropertyError, parameter.Name, actionType.Name));
                    }

                    // Convert the parameter value type.
                    object value = Convert.ChangeType(
                        parameter.Value, property.PropertyType);

                    // Create an assignment binding (set the property value).
                    MemberAssignment binding = Expression.Bind(
                        property, 
                        Expression.Constant(value, property.PropertyType));

                    bindings.Add(binding);
                }

                // Create the code expression and execute it to get a new instance.
                MemberInitExpression init = Expression.MemberInit(
                    Expression.New(actionType), bindings);

                WorkflowAction action = Expression.Lambda<Func<WorkflowAction>>(init)
                    .Compile().Invoke();

                // Add the action to the workflow.
                instance.Actions.Add(action);
            }

            return instance;
        }
    }
}

The outer for loop iterates the actions to create and the inner for loop collects the properties to set on the action.  Object creation and initializaiton is done in one shot using Expression.Lambda.

For reference, here is the target class for the XML in the sample above:

using System;
using SimpleWorkflowEngine.Core.Attributes;
using SimpleWorkflowEngine.Core.Common;
using SimpleWorkflowEngine.Core.Constants;
using SimpleWorkflowEngine.Extensions.Components;

namespace SimpleWorkflowEngine.Extensions.CustomActions
{
    /// <summary>
    /// A worflow action which sends an email to a recipient.
    /// </summary>
    public class EmailAction : WorkflowAction
    {
        private MailClientService _mailClientService;
        private string _message;
        private string _recipientAddress;
        private string _subject;

        /// <summary>
        /// Sets the mail client service.
        /// </summary>
        /// <value>The mail client service.</value>
        public MailClientService MailClientService
        {
            set { _mailClientService = value; }
        }

        /// <summary>
        /// Gets or sets the recipient address.
        /// </summary>
        /// <value>The recipient address.</value>
        [Parameter(Usage.IsRequired, "Recipient e-mail")]
        public string RecipientAddress
        {
            get { return _recipientAddress; }
            set { _recipientAddress = value; }
        }

        /// <summary>
        /// Gets or sets the message.
        /// </summary>
        /// <value>The message.</value>
        [Parameter(Usage.IsRequired, "Message")]
        public string Message
        {
            get { return _message; }
            set { _message = value; }
        }

        /// <summary>
        /// Gets or sets the subject.
        /// </summary>
        /// <value>The subject.</value>
        [Parameter(Usage.IsRequired, "Subject")]
        public string Subject
        {
            get { return _subject; }
            set { _subject = value; }
        }

        /// <summary>
        /// Gets a value indicating whether this instance 
        /// [requires input].
        /// </summary>
        /// <value>
        /// <c>true</c> if this instance [requires input]; 
        /// otherwise, <c>false</c>.
        /// </value>
        public override bool RequiresInput
        {
            get { return false; }
        }

        /// <summary>
        /// Inheriting member should override this method to implement 
        /// custom execution logic.
        /// </summary>
        /// <param name="context">The context.</param>
        protected override void OnExecute(ExecutionContext context)
        {
            if (_mailClientService == null)
            {
                throw new NullReferenceException(
                    "No mail service client is configured for this runtime.");
            }

            _mailClientService.SendMail(RecipientAddress, Subject, Message);
        }
    }
}

Admittedly, I didn't expect this to work...I had a hell of a time figuring out how to create the expression to set the property values.

# Tuesday, June 09, 2009

I Like Jeffrey Palermo's Prediction...

Tuesday, June 09, 2009 4:15:49 PM UTC

I've been playing around with ASP.NET MVC for about a week now and it does everything right that ASP.NET web forms did so wrong.  I've hated ASP.NET web forms with a passion as soon as I realized that the model was terrible for the majority of development shops in terms of managing unmanageable code jumbles -- huge methods written on event handlers...

Don't get me wrong, ASP.NET 1.0 was a huge step up from classic ASP, but it has exploded into this huge culture of control based RAD developers.  Not that you couldn't roll your own MVC pattern for ASP.NET, but having it as an officially supported development model should help bring more web developers out from the grips of the dark side.

Most consultants (even "senior" ones) simply take the quick-and-dirty route when it comes to ASP.NET web forms development, dragging and dropping controls all day long.  Not that it's a bad thing...for prototyping, but it quickly devolves into hard to manage, hard to maintain, and difficult to test codebases with very low levels of reusability.  This is especially true once you roll on junior or mid level developers to a web project.

Earlier in the year, Jeffrey Palermo predicted that MVC would replace web forms as the dominant web application development model on the .NET platform.  With the recent news of MVC 2, Palermo reasserts his prediction with even greater conviction.  I was already long convinced that ASP.NET web forms was a craptastic model for web application development, but I had some skepticism about MVC (after my pretty awesome experience with Django).  This past week has convinced me that MVC kicks ass and I hope that Jeffrey's prediction is right.

I wholeheartedly agree; I simply can't wait to dump ASP.NET web forms.

# Friday, June 05, 2009

T-SQL MERGE: My New Friend

Friday, June 05, 2009 3:49:08 PM UTC

As I was catching up on SQL 2008 this morning, I spent some time working with the T-SQL MERGE command, new in SQL 2008.  Most of the blogs and examples online are a bit terse, so I decided to write a simple sample.

Check it out:

--// Create our SOURCE table
DECLARE @t1 TABLE (
    id      int,
    name    nvarchar(16),
    email   nvarchar(32)
)

--// Create our TARGET table
DECLARE @t2 TABLE (
    id      int,
    name    nvarchar(16),
    email   nvarchar(32)
)

--// Insert values into SOURCE table
INSERT INTO @t1 VALUES (1, 'Charles Chen', 'charles.chen@domain.com')
INSERT INTO @t1 VALUES (2, 'Sandra Chen', 'sandra.chen@domain.com')
INSERT INTO @t1 VALUES (3, 'Brad Wright', 'brad.wright@domain.com')

--// Insert values into TARGET table
INSERT INTO @t2 VALUES (4, 'Brady Sines', 'brady.sines@domain.com')
INSERT INTO @t2 VALUES (1, '', '')

--// Select values from the TARGET table (just to verify)
SELECT * FROM @t2

--// Merge records from source to target
MERGE
    @t2 AS [target]
USING
    @t1 AS [source]
ON
    ([target].id = [source].id)
WHEN NOT MATCHED BY TARGET
    THEN
        INSERT(id, name, email)
        VALUES([source].id, [source].name, [source].email)
WHEN MATCHED
    THEN
        UPDATE SET
            [target].name = [source].name,
            [target].email = [source].email
--// Get the journal of actions
OUTPUT $action, inserted.*, deleted.*;

--// Grab the resultant table
SELECT * FROM @t2

And here are the results:

Awesome!  Admittedly, I'm still trying to come up with some legitimate uses for this in my day-to-day application programming :-D

# Thursday, June 04, 2009

QOTD

Thursday, June 04, 2009 6:16:25 PM UTC

A friend passed along a quote the other day:

So I just picked up this book today....and found this quote in the forward: "The truth of the matter is, if you need to “save” your job, I can’t help you. This book isn’t about struggling to maintain the level of mediocrity required not to get fired. It’s about being awesome. It’s about winning. You don’t win a race by trying not to lose. And you don’t win at life by trying not to suck. Fortunately, the content of the book has never been about trying not to suck. I can’t think that way, and neither should you."

Thanks, Dan!

# Thursday, May 28, 2009

Visitor Pattern In C# 4.0

Thursday, May 28, 2009 7:19:17 PM UTC

I've blogged about the Visitor pattern previously and using double dispatch to resolve .NET's inherent inability to resolve methods by parameter type at runtime.

As I was reading about C# 4.0's dynamic types, I started to wonder if this would mean that we could finally get a more concise implementation of the pattern.  My hunch was correct.

Here is the updated code listing (key changes bolded):

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections.ObjectModel;

namespace VisitorPatternConsole
{
    public class Program
    {
        private static void Main(string[] args)
        {
            // NOTE: Using dynamic collections.
            Collection<dynamic> visitors 
				= new Collection<dynamic>();
            Collection<dynamic> pets 
				= new Collection<dynamic>();
 
            // Initialize the pets
            pets.Add(new Fish());
            pets.Add(new Dog());

            // Initialize the visitors
            visitors.Add(new Feeder());
            visitors.Add(new Walker());

            // Visit each of the pets.
            foreach (dynamic pet in pets)
            {
                foreach (dynamic visitor in visitors)
                {
                    visitor.Visit(pet);
                }
            }

            // Check the results.
            foreach (Pet pet in pets)
            {
                Console.Out.WriteLine(pet.GetType().Name);
                foreach (String note in pet.Visitors)
                {
                    Console.Out.WriteLine("\t{0}", note);
                }
            }
        }
    }

    /// <summary>
    /// Handles the base cases.
    /// </summary>
    public abstract class AbstractVisitor
    {
        public void Visit(Pet pet)
        {
            pet.Visitors.Add(
                "Not supported for this type of pet...");
        }
    }

    /// <summary>
    /// Concrete visitor, a pet feeder.
    /// </summary>
    public class Feeder : AbstractVisitor
    {
        public void Visit(Dog dog)
        {
            // Feed the dog
            dog.Visitors.Add("Fed the dog");
        }

        public void Visit(Fish fish)
        {
            // Feed the fish
            fish.Visitors.Add("Fed the fish");
        }
    }

    /// <summary>
    /// Concrete visitor, a pet walker.
    /// </summary>
    public class Walker : AbstractVisitor
    {
        public void Visit(Dog dog)
        {
            dog.Visitors.Add("Walked the dog");
        }

        // Fish can't be walked!
    }

    /// <summary>
    /// Base class for pets.
    /// </summary>
    public abstract class Pet
    {
        private readonly Collection<string> visitors;

        public Collection<string> Visitors
        {
            get { return visitors; }
        }

        protected Pet()
        {
            visitors = new Collection<string>();
        }
    }

    /// <summary>
    /// A pet fish.
    /// </summary>
    public class Fish : Pet { }

    /// <summary>
    /// A pet dog.
    /// </summary>
    public class Dog : Pet { }
}

Here is the output:

Wow, just when I thought dynamic was going to suck :-D

What is somewhat interesting is that both collections have to be declared of type dynamic; I'm still mulling this over, but it's not clear why it doesn't work if only one of the collections is declared dynamic (I figured that it should have worked if the visitors collection alone was declared dynamic).

# Wednesday, May 27, 2009

Random DevTools Entry #017

Wednesday, May 27, 2009 10:27:01 PM UTC

In software development, it's incredibly useful to be able to visualize your code interactions using sequence diagrams and data flow diagrams and what not.  Not only is the visualization a plus, the act of generating one helps tremendously in terms of working out the outline of the logic that you need to implement.  One of the biggest problems I've come across in this area are the tools: they're simply too heavy and too complex for general diagram drawing tasks in addition to being generally rigid as well.

Today, as I was about to download and install Visio, I decided to spend a few minutes checking to see if there was a web alternative.  Enter websequencediagrams.  This is an all around awesome little tool to add to your toolbox.  Not only is it free (free is always awesome), it's text based.  At first blush, this seems terrible; there's a whole syntax to learn and lots of typing.  But the syntax is incredibly easy and simple while powerful and easy to understand.

Here's an example (I've bolded the syntax to make it easier to distinguish):

Browser->App: HandleSearchClickEvent()
App->Service: ExecuteSearch(keyword)
activate Service
Service-->App: (return results)
deactivate Service
App->App: RenderResults(reseults)
note right of App:
    render URL with
    keyword in query string.
end note
App-->Browser: HTML
Browser-->Office: click:
http://../name.docx?term=keyword
Office->AddIn: ThisAddIn_Startup()
AddIn->AddIn: Check for search term
note left of AddIn:
    ActiveDocument.FullName will
    contain the query string.  This
    can be extracted with a regular
    expression
end note
AddIn->AddIn: Execute find

And here is the result:

There are also a variety of pre-defined styles you can choose to render your diagram.  It's all sorts of awesome and a real time-saver compared to traditional tools.  I personally love that it's text based; I've found that when working with Visio (and other such tools), more than half of my time is spent arranging things just to get them to line up.  A text based approach works well for sequence diagrams and gets rid of that layer of unnecessary complexity.

RSS 2.0 Atom 1.0 CDF