Click here to Skip to main content
15,894,907 members
Articles / Programming Languages / C#
Article

Implementing Dynamic Escalations Using Timers to Augment K2 Workflow

Rate me:
Please Sign up or sign in to vote.
3.00/5 (6 votes)
10 Jun 2007CPOL4 min read 25.6K   409   13  
Implementing dynamic escalations using Timers to augment K2 Workflow

Introduction

This article shows how to implement a basic timer / escalation framework. This framework allows you to set a due date for something and have some code run when the due date is missed. The framework supports modifying an existing due-date entry so that its due date can be pushed back or forward. The due-date entry can be removed when it is no longer applicable (the work has been completed on time).

Background

I've been developing BPM software using K2 Workflow for a while now. They have the concept of an Escalation on tasks that have been assigned to people so that when the task has not been completed by such a date/time, there is an escalation event. The escalation event could run some code to send an email to the person's manager, for example.

One of the limitations of K2 Workflow's framework is that it does not support changing the due-date of an existing escalation. One of my projects had the requirement of modifying an escalation after the initial due date had been set.

Using the Code

There are only a few classes used in this framework.

Screenshot - escalation_architecture.jpg

The web methods here are the important entry point to the framework.

C#
[WebMethod] 
public void AddEscalation(EscalationActionEventArgs args) 
{ 
EscalationManager eMgr = new EscalationManager();
eMgr.AddEscalationEvent(args);
} 

[WebMethod] 
public void UpdateEscalation(EscalationActionEventArgs args) 
{
EscalationManager eMgr = new EscalationManager();
eMgr.UpdateEscalationEvent(args);
}

[WebMethod] 
public void RemoveEscalationEvent(EscalationActionEventArgs args) 
{
EscalationManager eMgr = new EscalationManager();
eMgr.RemoveEscalationEvent(args);
} 

When an escalation is created, the AddEscalation method is called, passing an EscalationActionEventAgs object.

The EscalationActionEventAgs object has the necessary information for the framework to:

  1. Set the Timer's due date
  2. Set other data specific to your business problem
  3. Set the Type of your custom class to invoke when the timer is fired/due date is reached

There is an interface defined, IEscalationAction, with one method, ProcessEscalationAction(). Objects of this interface are invoked when the timer is fired, due date is reached. When the due date is reached, you want to do something, that something is defined in your custom class that implements the IEscalationAction interface. You set the System.Type information in the EscalationActionEventAgs object (IEscalationActionFullTypeName and IEscalationActionAssemblyName). The class diagram shows an example of a custom class, TestActionOne. The implementation of the TestActionOne class will run when the due date is reached.

Updating an Existing Timer

In the situation where a Timer has already been set but later, some business rules dictate that the due date be changed, then a Web service call to UpdateEscalation() is made. The escalationID of new due date is set in the EscalationActionEventAgs object that is passed in. The framework will update the Timer to reflect the changes.

Removing an Existing Timer

In the case where the work has been done and the due date is no longer applicable, the Web method, RemoveEscalation is called with the EscalationActionEventAgs argument. The EscalationID property is used to lookup the existing escalation and the timer is removed.

Creating Your Own Client

  1. Create a new project for your custom Escalation Action classes.
  2. Add a reference to the EscalationFramework assembly. This contains the IEscalationAction Interface and the EscalationActionEventAgs class.
  3. Optionally modify the EscalationActionEventAgs class so that it has the fields that are required for your escalation framework. In my example, I've included fields like ActivityName and EventName.
    • In a robust version of this framework, the EscalationActionEventAgs object would be serialized to a database, so make sure that your EscalationActionEventAgs is serializable.
  4. Create your implementation of the IEscalation interface, providing your custom logic for what to do when the escalation fires in the ProcessEscalationAction() method.
  5. Compile your project and add the build output DLLs' to the bin directory of the EscalationService Web service bin folder. The EscalationService will load your assembly in order to run your EscalationAction class when the Timer fires.

Example using K2 Workflow

In this example, the activity, SetInitialDueDate calls the AddEscalation() Web method that sets a due date. If that activity is finished, the due-date timer needs to be removed. In that case, the K2 Succeeding Rule would have some code to call the RemoveEscalation() method.

In the case where that activity is not completed and still pending, the due date can be adjusted or removed.

In this example, the ExtendDueDate activity has some logic that can check some business logic and modify the due date for the SetInitialDueDate activity. This would be done by calling the UpdateEscalation() Web method providing the new due-date and the EscalationID.

Another reason this framework is compelling is that because it is exposed as a Web Service, any application makes a call to the framework to add, modify or remove an escalation. All that is needed is the EscalationID, which has the value of whatever you initialized it with.

Screenshot - k2EscalationDiagram.jpg

Additional Details

One class that is not shown on the class diagram is the EscalationManager. This class is my implementation of the details of the timer framework.

Below are the details of the AddEscalation() and RemoveEscalation() methods:

C#
public void AddEscalationEvent(EscalationActionEventArgs args) 
{ 
TimeSpan ts = args.DueDate.Subtract(DateTime.Now); 
System.Threading.Timer t = new System.Threading.Timer
		(ProcessRecord, args, ts, new TimeSpan(-1));
Timer_Args ta = new Timer_Args(); 
ta.timer = t; 
ta.args = args;
timerStructList.Add(ta);
}

Remove Escalation

C#
public void RemoveEscalationEvent(EscalationActionEventArgs args)
{
for (int i = timerStructList.Count - 1; i >= 0; i++)
{
Timer_Args ta = (Timer_Args)timerStructList[i];
if (ta.args.EscalationId == args.EscalationId)
{
ta.timer.Change(System.Threading.Timeout.Infinite, -1); 
ta.timer.Dispose();
timerStructList.RemoveAt(i);
break;
}
}
} 

If the framework service dies, all the timers will be lost. Though this is a simple example, a robust solution should store the active timer data in a database so that if the service crashes, there is a way to reinitialize all the timers.

Conclusion

I believe this framework can provide a useful part in a BPM solution where notifications must be managed with a BPM server that has limitations in the flexibility of their notification framework.

History

  • 10th June, 2007: Initial post

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
United States United States
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
-- There are no messages in this forum --