Long running tasks

Created by Vidar Langberget

In Webnodes, we have integrated Windows Workflow Foundation deeply into the system, and it is used to handle long running processes.

In Webnodes, we have integrated Windows Workflow Foundation deeply into the system, and it is used to handle long running processes. For developers not familiar with Windows Workflow Foundation, it can be a bit intimidating. For situations where all you want to do is to run a long running task outside a request, in a separate thread, it is too complex.

We have therefore created a wrapper around WWF that makes creating long running processes that run in WWF a breeze. All you have to do is to is to create a class that inherits from the abstract class WorkflowMethod, and implement it. This is the bare minimum of how such a class can look like:

public class Test1WorkflowMethod : WorkflowMethod {

    public override NextCall Invoke(WorkflowMethod invoker) {
        throw new NotImplementedException();
    }
}

Inside the Invoke method you can write all the code you want, and not worry about timeouts or other web related stuff. The code runs in a separate thread managed by WWF. To create a workflow that doesn’t create an exception, you can do something like this:

public class Test1WorkflowMethod : WorkflowMethod {
    public override NextCall Invoke(WorkflowMethod invoker) {
        WFContext.Notify("Hello from workflow!");
        return null;
    }
}

While it brings many benefits to run in a separate thread, there are also a number of complications. Not a problem if you are aware of them and plan around them. The main thing to remember is that the separate thread isn’t associated with a web request, so you don’t have access to any web stuff. If you need information from the web context, you need to send that information to the workflow thread when you start it.

This brings us to another topic: starting the workflow method from the web context. This is extremely simple. It’s only two lines of code:

Test1WorkflowMethod workflowMethod = new Test1WorkflowMethod();
WAFContext.Session.StartWorkflowMethod(workflowMethod);

If you want to send parameters to the workflow method, the best way is to create the desired properties on the workflow method class first. In the workflow method we created above, we have simply created a property called SiteUrl:

public class Test1WorkflowMethod : WorkflowMethod {
    public string SiteUrl {
        get;
        set;
    }
    public override NextCall Invoke(WorkflowMethod invoker) {
        WFContext.Notify("Hello from workflow! SiteUrl: " + SiteUrl);
        return null;
    }
}

To send a parameter to the workflow method, we can now do the following:

Test1WorkflowMethod workflowMethod = new Test1WorkflowMethod();
workflowMethod.SiteUrl = WAFContext.UrlFromRootToApp;
WAFContext.Session.StartWorkflowMethod(workflowMethod);

Workflow Context

If you have looked closely at the code examples, you can see that the workflow method code uses WFContext and not WAFContext that you normally use when working with Webnodes. WFContext means WorkFlowContext, and you must use WFContext and not WAFContext when you’re accessing Webnodes from a workflow method. This is due to the fact that it is not running in a web context as mentioned earlier. WFContext doesn’t have any of the web specific functionality of WAFContext, but it also adds a few new things. The most commonly used are:

WFContext.InBackgroundMode
If you run the workflow in one of the edit modules, the workflows can automatically tie into the dialogue system. InBackgroundMode determines if the workflow should show a window when it runs, or if it should run silently in the background.


WFContext.SetProgress
It is considered good practice to give feedback to users about how long it will take to complete when the process takes more than a few seconds. The SetProgress method changes the workflow window to show a progress bar, and set the how far from completetion the process is.

WFContext.BreakExecution
If you want to stop the workflow method, there must be communication between the UI and the separate workflow thread. Check the BreakExecution property at regular intervals in the workflow method to allow the workflow system to terminate the workflow method, if it has been requested from the UI. 

WorkflowMethod chaining

If you look at the Inovoke method signature from the WorkflowMethod class, you see that the return type is NextCall. The NextCall class has several different constructors, but all of them take a workflow or workflow method as one of the parameters. This is can be used to chain together several independent workflow methods.

This is used in Webnodes several places, for example in the content module, where you create new contents. First you select the content class, then you select the position of the content(if applicable). This is using two completely separate workflow methods.

Similarly, in the shop module, when you create a new product, you first select which product category it belongs to, then the new product is opened in a dialogue window. That is actually two workflow methods chained together.  The last workflow method is the EditContent workflow method which is used extensively throughout the whole edit interface (it is used to edit content in a dialogue window).

Starting workflows from JavaScript

It is also possible to start a workflow method from JavaScript. The function below fetches the selected node in a ContentList control and adds the key of the selected node as a parameter to the EditProduct workflow method.

<script type="text/javascript">
function listDblClick(lstControl) {
var key = lstControl.GetFlaggedValues()[0];
var input = new Object();
input.Key = key;
WAF.StartWorkflowMethod("EditProduct", input);
}
</script>

Note that this is unsecure input, so the workflow method must validate the input before using it. We’ll cover this in more detail in a later article.

Security and how to control who starts a workflow method

Many of the long running processes that you normally have in a Webnodes site are not something everyone should be allowed to start. Since the workflows also can be started from JavaScript etc, the security must be maintained in the workflow itself. But people often forget to write the code, or leave a bug that makes it possible to exploit in some situations. A simple fix is to add  an attribute where you describe who should be allowed to start the workflow method. The attribute in question is called ClientActivation. It’s used like this:

clientactivation