Sunday, June 15, 2008

Metastorm BPM 7.6 and Windows Workflow part 3 - Using Visual Studio

In the first two parts of this series I've given a brief introduction to the integration of Windows Workflow into Metastorm BPM 7.6. For this final part I'll be discussing what interests me, being able to execute workflows authored in Visual Studio from Metastorm BPM.

The first question was, is it even possible? There is no UI for publishing workflows to the Metastorm database, other than those authored in the Metastorm WF Composer. But looking at the database, it looked like the only tables that needed populating were eMSWorkflow and eMSWorkflowDefinition. After some playing around I came up with the following code, a simple command-line tool. This is by no means bulletproof, it will fail if the workflow has already been published (the next version of the FreeFlow Administrator will let you delete workflows) and will likely fail for a host of other reasons. I was using it purely as a proof of concept.

using System;
using System.Data;
using System.Data.Odbc;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Workflow.Activities;

namespace PublishWorkflow
{
class Program
{
static void Main(string[] args)
{
if (args.Length < 4)
{
Console.WriteLine("PublishWorkflow usage:");
Console.WriteLine("PublishWorkflow [ODBC DSN] [ODBC username] [ODBC password] [Workflow DLL] ");
}
else
{
string fileName = args[3];

// get the assembly
Assembly assembly = Assembly.LoadFrom(fileName);

// get the workflow type
Type workflowType = null;
Type[] types = assembly.GetTypes();
for (int i=0; i<types.Length; i++)
{
if (types[i].IsSubclassOf(typeof(SequentialWorkflowActivity)))
{
workflowType = types[i];
}
}

if (workflowType == null)
throw new Exception("Can't find a workflow in the assembly");

string connectionString =
string.Format("UID={0};PWD={1};DSN={2}", args[1], args[2], args[0]);
using (OdbcConnection connection = new OdbcConnection(connectionString))
{
connection.Open();

Guid guid = Guid.NewGuid();

// write to eMSWorkflow
using (OdbcCommand command = connection.CreateCommand())
{
command.CommandText = string.Format(
"INSERT INTO eMSWorkflow (eWorkflowName, eWorkflowGuid) VALUES " +
"('{0}', '{1}')",
workflowType.FullName, guid.ToString());
command.ExecuteNonQuery();
}

// write to eMSWorkflowDefinition
using (OdbcCommand command = connection.CreateCommand())
{
string fileVersion = FileVersionInfo.GetVersionInfo(assembly.Location).FileVersion.ToString();

byte[] buffer;
using (FileStream stream = new FileStream(fileName, FileMode.OpenOrCreate, FileAccess.Read))
{
buffer = new byte[stream.Length];
stream.Read(buffer, 0, Convert.ToInt32(stream.Length));
stream.Close();
}

command.CommandText = string.Format(
"INSERT INTO eMSWorkflowDefinition (eWorkflowGuid, eWorkflowName, eFileVersion, eFullyQualifiedTypeName, " +
"eFullyQualifiedAssemblyName, eWorkflowDefinitionType, eRulesDefinitionName, eLoadedTime, " +
"eWorkflowDefinition, eWorkflowProject) " +
"VALUES ('{0}', '{1}', '{2}', '{3}', '{4}', 'A', '', GETDATE(), ?, NULL)",
guid.ToString(), workflowType.FullName, fileVersion, workflowType.AssemblyQualifiedName,
assembly.FullName);
OdbcParameter parameter = new OdbcParameter();
parameter.Value = buffer;
parameter.DbType = DbType.Binary;
parameter.Direction = ParameterDirection.Input;
command.Parameters.Add(parameter);
command.ExecuteNonQuery();
}
}
}
}
}
}

So I published my simple Visual Studio authored workflow using this code and was able to execute it. It would appear the process context activity provided as part of the Metastorm activities isn't required, although I assume it will be needed if you need to get folder information into your workflow.


The next step was to see if I could execute a workflow that contained custom activities. Obviously the problem here is where will the engine pick up the required assemblies (assuming the custom activities are in their own assembly)? I tried putting the DLL in the engine directory, dotnetbin directory and even the System32 directory (since this is where dllhost lives) but none of them seemed to work. All I got was the following useful error message


Microsoft Workflow evaluation (Instance ID 963fc00a-f2f9-40a8-a0df-7111b0706cd7)
For more information refer to the workflow tracking and event tables.

In fact the tracking and event tables contained no useful information. This error message seems to be the default error message when something goes wrong with a workflow and I've never found any useful information in the database. Debugging the engine through Visual Studio seems to be the only way of getting hold of the real error.


In the end the only way I found to get the engine to pick up my custom activity assembly was to install it in the GAC. Once I did that the workflow executed correctly.


So yes it is possible to execute a VS authored workflow in Metastorm, although some frigging around is required. However I have one concern. One problem I encountered whilst researching this was that workflows would abort after a certain time. Once again, the error above was shown in the Designer Log. Debugging the engine showed the problem was the workflow was timing out. I found a registry setting that controls this timeout, which by default is set to 60 seconds. But how does this timeout work? Is a thread kept alive waiting for the workflow to terminate? If so, this will be a problem for scalability if you wish to execute long running workflows. And that is what Windows Workflow is all about, running some code, sleeping for a week, executing some more code etc. I don't have the answer to this question yet, but will investigate further.


Part 1 - The basics

Part 2 - The database tables

Part 4 - Using your own activities

Part 5 - Long running workflows

Part 6 - State machines

3 comments:

Phil McGaw said...

Hi Doogal,

I was just reading through your blog on Windows Workflow and Metastorm as I have a keen interest in both, and was interested to know if you had tried using a State Machine workflow rather than the Sequential workflow to get around the timeout issue you seem to be experiencing?

I would have thought this would do the job, as by it's very nature the state machine will wait for an event to move it through the process, similar to that of a user action in Metastorm. (although I'm certain you already know this :)

Good article anyway.

Cheers
Phil

Doogal Bell said...

Hi Phil

I haven't tried a state machine workflow as of yet, thought it's on my list of things to do!

To be clear, the timeout error can be fixed by changing the registry setting to 0 instead of the default 60. My main concern was whether a thread was kept alive for each workflow instance to take care of the timeout. I haven't looked too closely at this but I think this may not be the case, so it shouldn't be a problem. Obviously you need to execute the workflow asynchronously though if you do expect it take a long time to execute or else it will hold up the execution of your BPM workflow.

Hope this helps

Doogal Bell said...

Hmm, actually it looks like calling a workflow asynchronously will timeout after 2 minutes whatever the timeout registry setting is set at. Might be there's another timeout setting for asynchronous workflows, I will have to investigate further. Again this is with sequential workflows rather than state machines, but I'm guessing it will be the same for them.