IgorShare Thoughts and Ideas

Consulting and Training

Workflows are not only for business logic, or how to define an SMTP protocol state machine in a Workflow.

Posted by Igor Moochnick on 01/07/2008

clip_image001

[ Update: 

]

Since my first written line of code (Fall 1987, yes – 20 years ago), after I’ve learned about the block programming, I thought that the most natural way for people to interact with the outer world is via the building blocks. The idea of lego-like development always haunts me. To my opinion, the way where people drop a set of actions on the table and connect them in some logical ways, is the most natural one. The produced sequence of actions can be visually comprehended by any person who has a very basic logic understanding. So no advanced training is nessesary.

The described above, is the ideal world which is far from reality. The reality teaches us that this way of development is a very restrictive one and you can use it in a very limited domain of problems. But all this means that the block development is the most effective way of logic definition in a certain group of problems.

Recently released Microsoft Workflow Foundation is mainly targeted to define the bushiness workflows, but nobody restricts you to experiment with it and to find another killer-up for workflows. The Foundation is very flexible and extensible, so you can do with it anything you’d like.

In this post I’m going to show you how to use Workflows to define a protocol state machine. Note, that the Workflow Foundation will not generate the fastest code in the world, so you can’t use this state machine in the CPU-hungry servers, but it is a perfect example. Check the picture on the right – what can be easier than this to describe a state machine?

To show you how to use the workflows, I’ve translated the SMTP state machine, shown in my previous post . You can see the workflow on the picture above.

The following instructions can be applied to any situation where it makes sense to describe some logic as a workflow.

In order to use workflows in your application, don’t forget to initialize and start WorkflowRuntime.

   1: // Initialize workflow runtime
   2: workflowRuntime = new WorkflowRuntime();
   3: ...
   4: workflowRuntime.StartRuntime();

Each time when a new connection is detected, the SMTP Server initializes a new SMTP Session with a new Workflow that will be in charge of managing it’s state machine:

WorkflowInstance workflowInstance = workflowRuntime.CreateWorkflow(typeof(SmtpSessionWorkflow), properties);
workflowInstance.Start();

In many cases a Workflow instance will want to know something about it’s surroundings and a reason why it was instantiated. To do this, you can pass extra parameters to the Workflow during it’s initialization. This is very similar to calling a class constructor with extra parameters. Have you noticed the extra properties parameter, passed to the CreateWorkflow method above? This is how you can create parameters collection:

// Construct workflow parameters
Dictionary<string, object> properties = new Dictionary<string, object>();
properties.Add("SessionID", session.SessoinID);
properties.Add("DomainName", "testserver.com");

And don’t forget to add corresponding public properties (it should have get and set methods) with exactly the same names and the same letters case to your workflow. You’re welcome to use the new .NET 3.0 auto-generated default property handlers notation like this:

public Guid SessionID { set; get; }
public string DomainName { set; get; }

So far so good. Here comes more interesting stuff.

There are a lot of different ways to let a Workflow to communicate with the outside world. It can call a Web Service, CallExternalMethod or a custom Activity. I’ll talk about custom Activities in some future posts, but meanwhile I’d like to cover the simplest way – CallExternalMethod. In order to let your Workflow to be aware of your external service you need to do 2 simple things:

  1. Inherit your service (external object) from an interface with [ExternalDataExchange] attribute
  2. Announce your service (external object) to the WorkflowRuntime

This is how you do the second part:

   1: ExternalDataExchangeService svc = new ExternalDataExchangeService();
   2: workflowRuntime.AddService(svc);
   3: svc.AddService(this);

And this is the interface the SMTP service inherits in my example:

 

   1: [ExternalDataExchange]
   2: public interface ISmtpEvents
   3: {
   4:   event EventHandler<ExternalDataEventArgs> OnStop;
   5:   event EventHandler<ExternalDataEventArgs> OnError;
   6:   event EventHandler<SmtpEventData> OnCommand;
   7:   void Write(Guid SessionID, string message);
   8: }

clip_image001[5]

 

Here you can see a snapshot of the event handler that handles MAIL command. When you drop a CallExternalCommand activity (marked by the rectangle with the blue background), you are given an opportunity to select what method and with what parameters will be called. In our case we need to call the Write method with a string parameter message (see the interface definition). [Note: all the methods called by the workflow will have an extra Guid parameter, carrying the calling workflow’s instance unique ID]

clip_image001[7]

This is how the properties of the CallExternalCommand activity should look like:

Note that message property references a workflow’s internal CommandMessage field. You can create this field manually or press on “…” (triple dot) button, that will appear to the right side of the message property value. You have a choice of creating a Field or a property. In any case the wizard will clip_image001[9]generate a code for you, but, if you don’t need to write any special logic, choose a Field. The generated code will be as simple as this:

public String CommandMessage = default(System.String);

But before the Write method will be called the Workflow should assign some value to the CommandMessage. In a simple way it can be done in the method that can be called before the Write. You can define such a method in MethodInvoking property of the CallExternalCommand activity. If you double click the property the wizard will create a code stub that you can fill in. This is how the method can look like:

private void AcceptMailActivity_MethodInvoking(object sender, EventArgs e)
{
    CommandMessage = "250 OK";
}

imageThe power of the Workflow foundation is in declaration-based development. You can define the Workflow on the fly and, even, during the runtime. You can supply rules and rules parameters in a separate external file that can be changed or generated by some external process (we’ll talk about this ]in future posts). So, if you’d like to leverage such power, you can use policies. 

You can describe a policy as a series of if-then-else statements. In the case when you’ll want a policy to ALWAYS execute some set of actions, you can set the condition to True. So the following policy (on the right) will be EXACTLY identical to the AcceptMailActivity_MethodInvoking method (described above) – note the red rectangles.

clip_image001[3]

A flow that will use such a trick can look like the one, shown on the left. Note that the Workflow will execute the policy “policyAcceptHELO” activity before it’ll execute the “callWrite2” external method activity. The result will be identical, but, having the action defined in a policy, allows us to change it later without recompiling our application.

As you can see the technique of building basic workflows is extremely straightforward – you just grab simple building blocks and drop them on the playground. Draw the arrows and define the parameters. A walk in a park!

Obviously there are a lot of tips and tricks you should know if you want to build something smart and powerful, but it comes in time – just get your hands dirty and don’t be afraid of experimenting.

You can start by taking my sample project and creating your own workflow which will use the underlying infrastructure. You can use the Outlook Express to point to this server – it works. Just configure the SMTP server IP and port correctly (I guess you know how to do this). Compose an e-mail, hit Send and watch the Windows Workflows doing wonders.

For the curios minds out there:

clip_image001[11]

QUESTION: Try to answer my small Quiz without peeping into the answer – what are these 2 events (see the picture on the right) doing at the top of the SMTP state machine workflow?

ANSWER: These are global events. This allows the state machine to react to such events independently of what state it is hanging right now. So, if an Error event comes,  the state machine will abandon the current execution state (there are a lot of tricks here as well) and will start executing the eventOnError logic.

The Workflows theme is vast, so we have to limit our excitement for this post. Stay tuned – more will come. Send me your questions and your comments!

As usual the full source code can be found on my web site http://igor.moochnick.googlepages.com/.

 

 

6 Responses to “Workflows are not only for business logic, or how to define an SMTP protocol state machine in a Workflow.”

  1. […] emailed me for my thoughts about his SMTP server built with Windows Workflow. A link for the source code is at the bottom of his post. As Igor points out, WF is well suited to […]

  2. Ali said

    Excellent work and great article!

  3. […] emailed me for my thoughts about his SMTP server built with Windows Workflow. A link for the source code is at the bottom of his post. As Igor points out, WF is well suited to […]

  4. […] business (…Exposing dynamic dat… on RSS is not only for the news…K. Scott Allen : A F… on Workflows are not only for bus…Dynamically Composin… on CodeDom extensions and dynamic… Daily Bits – Janua… on […]

  5. […] Part 1: How to define an SMTP protocol state machine in a Workflow […]

  6. asp.net, c#,javascript…

    […]Workflows are not only for business logic, or how to define an SMTP protocol state machine in a Workflow. « IgorShare Weblog[…]…

Leave a comment