Thursday, December 20, 2007

Actions leads to Outcomes

In enterprise solutions it’s impossible to keep logic in one place. We are faced with BPM rules engines, line of business applications with their own schemas, input and output limitations, business rules, UI rules, drinking rules and speed cameras! Although it would be great to have them all in one place, as with red tape in governing organs we cope with adapting our senses to read and adhere to this logic in an easy way. K2 blackpearl makes a few effective attempts at centralizing logic and offering us tools to use logic from elsewhere.

In K2 2003 a user task was a good example where we often had routing logic in the workflow that had to be duplicated in the UI. For workflow routing our UI has to know what options the user had when auctioning a task and then the succeeding rule and all line rules out of the activity had to know about this as well. Maintenance was a nightmare.

In order to centralize routing logic, my team built a complete framework on K2.net 2003 that allowed us to automatically render the user interface for a workflow client event by looking at the available lines out of an activity that hosted the client event (if you are not bothered about the battles we had in 2003 or are a K2 blackpearl only client, skip to ‘In K2 blackpearl” sections).

In K2 2003

Here is the sample of the framework in action:


Logic of the workflow is automatically available in our business entities by purely inheriting from a specific base class (Customer in this example with an associated Customer Approval workflow instance) so accessing the logic defined in the workflow via client UI code looks like this:

Public Sub LookAtCustomerActionsAvailable()

' load a biz entity
Dim cust As Customer = _
RemoteServer.GeneralManager.Load(Of Model.Customer)(1)

' get the workflow context using the framework
RemoteServer.WorkflowManager.PopulateContext(cust)

' get the list of actions from the biz entity
For Each action As Action In cust.ActionsAvailable
Assert.IsNotNull(action, "bollocks test")
Next


' take the first action
cust.ActionToBeTaken = cust.ActionsAvailable(0)

' save the entity and action the workflow using framework

RemoteServerHelper.GeneralManager.SaveEntityObject(cust);

End Sub

Powerful feature, but because it is our own framework extensions on top of K2 2003, it is hard to maintain. For example, all our client events, lines, succeeding rules and line rules have to adhere to our framework requirements, i.e. “what action will lead to what line and what line represents a specific outcome”. We have to add and maintain XML stubs on all these entities, i.e. here is a line telling the framework what action will lead to its outcome:


Now that K2 blackpearl is available with the concept of being able to take a client action that leads to an outcome build into the product, life is much easier.

In K2 blackpearl

Blackpearl defines the concept of a client event in the workflow being aware of what actions can be taken on it. This is defined when creating the client even in the process designer. The wizard will also allow you to define outcomes and link the action that a user can take with a specific outcome based on your own rule set. Once the client receives the task at run-time, the task contains the available actions and the UI can automatically render this.

Here is a sample process:


When I added the client event, I automatically added two actions with two outcomes, North and South. I also asked the client event wizard to create a line for each outcome… not a single line of code needed for this.

Once I’ve started a new process instance and look at the allocated client task, I can see the two actions available: - this picture from the Workspace work list:



Job done! I can link these outcomes to my UI using either the various UI integration events available (InfoPath Client Event and Form Generation Client Event) – zero code required.

Alternatively I could render my own UI using the Client API. This sample code opens a connection to the K2 server, opens a task based on its serial number and then loops the available actions. It then takes the first action on the task in order to complete the client event:

static void LoadActions() {
// SourceCode.Workflow.Client.Connection now implements
// IDisposable...

using (Connection con = new Connection()) {
try {
// open connection and load task
con.Open("localhost");
WorklistItem task = con.OpenWorklistItem("SERIALNUMBER");

// loop the actions
foreach (Action action in task.Actions) {
Console.WriteLine(action.Name);
}

// action task (assume there is at least one)
task.Actions[0].Execute();

}
catch (Exception exception) { throw exception; }
finally { con.Close(); }
}
}

Thanks to the ambitions of the development team at Dynamyx that inspired the creation of the Dynamyx K2 framework that’s made life so much easier for us and our clients. I dedicate this blog entry to them. Rob, Ian, Trent, Kenny, Carlos and JT.

Tuesday, December 18, 2007

K2 blackpearl SP1 features realtime viewflow



I've had access to some beta's of K2 blackpearl SP1. I could not wait for it to hit the streets and now that it’s been unleashed, I'll highlight one of the features included.

One of the most powerful reasons for adoption of K2 is its ability to visualise a process at run-time. Although I've heard varied opinions about this in the industry, everything from 'please don't show that to the user, it will confuse the hell out of them' to 'that’s why we bought K2', the viewflow has always been a hit. And now it’s just been given a dose of concentrated Brazilian guarana... real time capability!

Here is a simple process I created in the K2 blackpearl Visual Studio designer:


I exported it to the K2 server and started a new instance. The first activity will generate a client task, so I head straight for the Workspace, and click on the dropdown of my task.





From there I can open the viewflow of the process. Look at this:



There is also the option to view the viewflow in realtime, so I enable it, go back to my worklist and complete my pending task. Within a few seconds the viewflow automatically updates the state:



Monday, December 17, 2007

K2 blackpearl SP1 released

From Technical Bulletin #7:

K2 blackpearl™, the centerpiece of the new K2 platform, was released in August, and Service Pack 1 is now available.

More than 300 customers and partners have already downloaded K2 blackpearl, and a US-based oil and gas company was the first organization to go live with an enterprise implementation for the management of wells. More than a dozen additional companies expect to deploy K2 blackpearl to production in the coming months.

K2 blackpearl SP1 includes many fixes and enhancements to K2 blackpearl, based on extensive customer and internal feedback. Some of the key enhancements include:

• Out-of-box reports have been enhanced.
• A Web-based ViewFlow has been included with auto-refresh and click-through for additional details and steps.
• Users can now access the task lists of people that report to them.
• Roles are now resolved in real-time.
• AD groups can now serve as destinations and are resolved at runtime.
• Memory and performance fixes have reduced the memory footprint and increased the stability and
responsiveness of K2 Designer for Visual Studio and the K2 Web Designer for SharePoint.
• A Server Logging Framework and Server Performance Monitoring, with Windows Performance Monitor
integration, have been added.

Monday, July 09, 2007

K2 Underground

Dear Blog-surfer,

Most of my blog has always been dedicated to articles and write-ups on K2.net. As I recently joined the K2 Insider community, my K2.net efforts will be directed to the new K2.net community portal, www.k2underground.com.

I'll keep this blog up to date with other articles and stories.

The Naked Programmer

Monday, May 07, 2007

Using a K2.net 2003 process as a 'function call'

Although this kind of thing will be much easier in K2.net [BlackPearl] using SmartFunctions, sometime we would like to run a K2.net 2003 process to determine the outcome of a set of rules only. Normally this process will not have any client interaction as it mearly takes some input, apply a set of rules using the K2.net line rules, and output some result. Doing this we get the K2.net visual representation of our business rules and the outcome of it.

The calling code treat the K2.net process instance as a method call, creating a new process instance, passing the initial data into the process via datafields and then wait for the process instance to reach an 'answer point', then read the answer from the process instance.

The assumption is that there is alway only one outcome from the business rules.

Firstly, our process. This is a very simple process that will set some datafield called Result to true.

The code inside the True server event looks like this:

Sub Main(ByVal K2 As ServerEventContext)
 ' make this server event wait
 K2.Synchronous = False
 ' store the server event's serial in a process level datafield
 K2.ProcessInstance.DataFields("WaitingServerEventSerial").Value = K2.SerialNumber
 ' set the result of this process, i.e. the business rule
 K2.ProcessInstance.DataFields("Result").Value = True
End Sub

Note that we set the Synchronous property to False. This will make this server event wait until we instruct it to complete. We need to do this so that we can grab the outcome of the business rules (True in this example, stored in the datafield called Result). For the calling code to get to this server event to complete it, the server event store its serial number in a process level datafield called WaitingServerEventSerial.

The calling code looks this this:

Sub Main()
 Dim myConn As SourceCode.K2ROM.Connection = Nothing
 Try
 ' this opens a connection to the K2 server
 myConn = New SourceCode.K2ROM.Connection
myConn.Open("localhost")

 ' this is the rules based process
 Dim myProc As SourceCode.K2ROM.ProcessInstance
 Dim piString As String = "Async\Rules"

 ' this creates the process instance
 myProc = myConn.CreateProcessInstance(piString)

 ' this starts the process instance but does not return control to this code
 ' until the process hits a waiting point. in this case the server event thats
 ' been marked to be K2.Synchronous = False

 myConn.StartProcessInstance(myProc, True)

 ' save the process instance ID and use it to open the process again
 Dim myProcID = myProc.ID
 myProc = myConn.OpenProcessInstance(myProcID)

 ' get the result
 Dim result As Boolean = CType(myProc.DataFields("Result").Value, Boolean)

 ' and very important, tell the server event to complete (by getting the waiting server
 ' event's serial number from the process instance datafield. if we don't do this then the
 ' process instance will stay active

 Dim waitingServerEventSerial As String = myProc.DataFields("WaitingServerEventSerial").Value.ToString()
 Dim waitingServerEvent As SourceCode.K2ROM.ServerItem = myConn.OpenServerItem(waitingServerEventSerial)
waitingServerEvent.Finish()

 Console.WriteLine("Result is " & result.ToString())

Catch ex As Exception
 Console.WriteLine(ex.ToString)
Finally
 If Not myConn Is Nothing Then
 myConn.Close()
 End If
End Try
Console.ReadLine()
End Sub

The client code creates and starts a process instance. Note the Sync property is set to True when starting the process instance. This will make sure that the StartProcessInstance method will not return control to the calling code until the process hits a client event for Async server event. In our example this Async server event is the outcome of the business logic that we are waiting for.

Once this point is reached and control is passed back to our client code we reload the process instance, get the outcome of the business rules via the Result datafield and then instruct the waiting server event to finish by picking its serial number up from the datafield WaitingServerEventSerial.

PS. Thanks Richard Neal and Mark Jones who's questions prompted me to do this article.

PSS. Often clients ask me to code in the language they use, so I've decided to at last also post a sample in VB.NET. Also note that I always use a simple pattern where I always make sure that the connection to the K2.net server is closed.

Wednesday, April 11, 2007

Microsoft Virtual PC 2007 and me Dell Inspiron 6000

What a battle... took me days to find a solution to this; installed VPC 2007 on my Dell Inspiron (its got the Centrino chipset). The virtual machine would keep on being jittery, slow, and unresponsive. Poor performance all over.

This solved it:
http://codebetter.com/blogs/jeff.lynch/archive/2006/11/28/VPC-2007-Beta_3A00_-Centrino-915-Chipset-Issues.aspx

Like noted, not the perfect fix and eats away nicely at the battery, but it works.

Monday, January 15, 2007

This version or that version?

It might be required to create an instance of a specific version of a process. Some reasons:

0. When exporting a new version of a process, the K2 server makes it the default version automatically. This is a bit of a pain as new instances of the new exported process will be automatically created by clients that don't spesify a spesific version.

1. Export process that's passed QA in development to production and run final pre-production testing before making the process live. In this scenario your user interface will always specify the version of the process when creating an instance.

2. Sometimes changes to a process require changes to the user and/or system interfaces. In this scenario the user interface can create a process on a specific version but should also render itself correctly for a specific version of the process (more about that in the next blog).
Using the K2ROM.dll library one can specify what version of the process to create using the overloaded .CreateProcessinstance method.

SourceCode.K2ROM.Connection con = new SourceCode.K2ROM.Connection();
con.Open("127.0.0.1");

SourceCode.K2ROM.ProcessInstance pi;
pi = con.CreateProcessInstance(@"Project\Process", 2);

This will create an instance of the Process process using version 2.