K2 Web Services - Part 1

This blogpost is outdated. Please see K2 SmartObject Services – Configuration update static endpoint if you have any problems with the K2HostServer.config file changes.

In the 1200 series, a few new web services are available out of the box. In this article I’ll tell you what they are for, how you can use them and give some insight on the technical details behind the services to make you understand them better.

There are two services new to K2 in the 1200 series:

  • K2 Services
  • K2 SmartObject Services

The K2 Services were first introduced in the KB001200 Update which contains a WS and WCF endpoint.  A later release will bring REST-enabled endpoints for the K2 Services.  The SmartObject Services are released in KB001230.

This post will cover the KB001200 and KB0001230 release functionality.  The additional REST-enabled endpoints for K2 Services will be discussed in a separate post once released.

K2 Services

These services provide you with the ability to participate and interact with process instances and worklist items via standard WS or WCF interfaces. The WS and WCF endpoint provide the same functionality only with a different interface.  Looking at the functionality, it’s roughly the same as the SourceCode.Workflow.Client namespace provides. Obviously, not all functionality is included in this first release.

The WCF version exposes the IWorkflow and  IWorklist interfaces. The WS endpoint has the same methods, provided by one interface.  There are some small differences, but those are just because it’s one object and not two interfaces.  Both WS and WCF expose objects like ProcessInstance, WorklistItem and related objects.

The Workflow Service is hosted in IIS and connects to the K2 server. This means it does a double hop. Normally, this would require Kerberos to be configured. To eliminate this need, K2 is using its own impersonation mechanism to connect to the K2 server. The impersonation is done by the API, and the IIS application pool needs K2’s Impersonation permissions to work correctly.

Examples

This wouldn’t be a real developer blogpost without code, so here are some examples.

The WCF examples are created using a console application in VS2010. Simple add a Service Reference to the K2 Services WCF.svc and you’ll be good to go.

The WS and WCF endpoints are hosted inside the same web application as the K2 workspace, so the URL’s look something like this:

  • http://[workspace host]/K2Services/WCF.svc
  • http://[workspace host]/K2Services/WS.asmx

If you want to use the WS endpoint in VS2010, make sure you use the legacy way of adding the endpoint. You can do this by clicking advanced in the ‘add service reference’ wizard and then clicking ‘add web reference’. If you use Add Service Reference on the WS endpoint, it will be peer-shaped. If you add it the correct way, the WS endpoint will code nearly identical to the WCF endpoint.

Starting a process

K2Services.IWorkflow workflow = new K2Services.WorkflowClient() as K2Services.IWorkflow;
K2Services.ProcessInstance procInst = new K2Services.ProcessInstance();

procInst.FullName = "K2Project1\\K2Contacts";

procInst.DataFields = new K2Services.DataField[1];
procInst.DataFields[0] = new K2Services.DataField();
procInst.DataFields[0].Name = "District";
procInst.DataFields[0].Value = 9;
procInst.Folio = "Process 1";

workflow.StartNewProcessInstance(procInst, false);

In this example only the IWorkflow interface is used and a process is started. You can pass DataField values if needed. You’ll notice that i simply create a ProcessInstance using its constructor, the FullName property is now important because that identifies which process should be started. There’s also a StartNewProcessInstanceScalar method which accepts a string as the process name. The boolean value specifies if the process should be started synchronous or asynchonous

Getting the worklist items

K2Services.IWorklist worklist = new K2Services.WorklistClient() as K2Services.IWorklist;

K2Services.WorklistItem[] worklistItems = worklist.OpenWorklist(true);
foreach (K2Services.WorklistItem worklistItem in worklistItems)
{
         Console.WriteLine("{0} - {1} - {2}", worklistItem.SerialNumber, worklistItem.ProcessInstance.Folio, worklistItem.ProcessInstance.DataFields[0].Value);
}

Console.ReadLine();

The IWorklist interface is used to get WorklistItems. The parameter in the OpenWorklist defines if DataFields should be loaded. Not loading them increases performance.

Executing an action

K2Services.IWorklist worklist = new K2Services.WorklistClient() as K2Services.IWorklist;

// Get the worklistitem by serialnumber and allocate it.
K2Services.WorklistItem worklistItem = worklist.OpenWorklistItem("1_9", true);

// Print some info.
Console.WriteLine("Process Folio: {0}", worklistItem.ProcessInstance.Folio);
Console.WriteLine("Process DataField: {0} - {1}", worklistItem.ProcessInstance.DataFields[0].Name, worklistItem.ProcessInstance.DataFields[0].Value);
Console.WriteLine("WorklistItem status: {0}", worklistItem.Status);

// Print the possible actions
foreach (string action in worklistItem.Actions) {
      Console.WriteLine("Possible action: {0}", action);
}

//Execute action
worklist.ExecuteActionByWorklistItem(worklistItem, "Approve", false);
//worklist.ExecuteActionBySerial("1_9", "Approve", true);

Actioning a worklistitem is possible in two ways. This example uses the WorklistItem object, but there’s also a ExecuteActionBySerial so you don’t need the WorklistItem object. The boolean parameter is to make it synchronous or asynchronous.

K2 SmartObject Services

K2 SmartObjects have always been an important integration mechanism for the K2 platform. SmartObjects allow you to integrate data in processes, reports and other systems. It is the mechanism to integrate data in and around processes. In the beginning SmartObjects had some issues (mainly performance). The 4.5 release clearly showed improvements on that front. Having SmartObjects really can make integration with your workflow or other systems very easy. Using SmartObjects in your project also means you had to use the K2 assemblies to store or retrieve information from SmartObjects. This implies a dependency. I’m sure you’ll be able to think of a lot of reasons not to want a dependency. The SmartObject Service allows you to utilize SmartObjects using a WCF or REST endpoint, removing this dependency.

The WCF endpoint allows you to use the full functionality that SmartObjects give you. Currently the REST endpoint only has the ability to use SmartObject list methods, so  you can only read a list of data. The reason for that limitation is because you’ll need to pass (a lot) or parameters when doing other SmartObject methods.

Apart from the integration abilities that this service provides. The service was also created to provide a WCF endpoint for SharePoint 2010 BCS entities. The PowerPivot also allows for integration of different data sources and the SmartObject service also allows this to be done.

Configuring SmartObject Services

The K2 Server hosts the endpoints for the SmartObject Service. The configuration for the service is therefore located in the K2HostServer.config. It’s very important to understand the configuration correctly as it’s going to influence which SmartObjects are exposed and how they are exposed!

Out of the box, this service “just works” after enabling it using the enableService parameter, but there are some good reasons not to turn it on right now on your production system.
By default the SmartObject Service is disabled, you can enable it by changing the enableService parameter to true. Doing so will cause the K2 server to create endpoints for every SmartObject in your environment. Available endpoints can be viewed using this url:

http://[k2 server]:8888/SmartObjectServices/endpoints/endpoints.xml

On my machine, the page looks like this:

Because this is a change in your K2HostServer.config, you’ll need to restart the K2 server. Starting up the K2 server now takes a little more time because it is generating the endpoints for every SmartObject you have. It is through hosting the endpoints in the K2 Server that allows the generation to be dynamic.

K2 SmartObject security is still applied for all the SmartObject methods that are executed. You may require more granular control to ensure that SmartObjects aren’t exposed at all via the K2 SmartObject Services. The exclude configuration parameter allows you to exclude categories of SmO’s. In my example (below) I’ve excluded every category and used an static endpoint to expose just one.

<smoServices enableEndpoints="true" enableEvents="true" enableCrossDomainPolicy="false" baseUri="http://dlx:8888/SmartObjectServices" binding="WsHttpBinding" specialCharacterReplacement="_">
 <managedEndpoints> 
<static>
 <endpoints> 
<endpoint categoryPath="K2Contacts/" smartobjectName="Contacts" alias="ContactsV1" isolationLevel="single" /> 
</endpoints>
</static>
 <excluded all="true" /> 
</managedEndpoints>
 </smoServices> 

The static endpoint gives it a unique (short-ish) URL which (in this case) exposes just that one endpoint. The endpoint will be listed in the EndPoints.xml.  Static endpoints also provide the ability to bind to a specific version of a SmartObject definition (using the smartobjectVersion parameter). This enables you to upgrade your SmartObjects without changing your client application. It might be a good idea to add a version number to the URL when you’re working in a SOA environment like the ‘V1’ I’ve added in the above example.

All that config is nice and all, but I don’t really like restarting the K2 server. That’s where the enableEvents might come in handy. This allows you to specify if the service should listen for SmartObject deployment events. If that event occurs, the service will refresh the endpoint(s). The static endpoint also has an isolationLevel parameter which can be “Shared” or “Isolated”. Shared means it is part of one shared application domain. Isolated means it has its own application domain. When the SmartObject deployment event is received, only that single application domain that the SmartObject is in is refreshed.

Consuming the SmartObject Services endpoints

REST

Showing how the REST interface works is pretty easy to do using a browser. Simply go to the endpoint listed in the Endpoints.xml. After that, you can write the method name you want to execute. In my example that will be:

http://[k2 server]:8888/SmartObjectServices/rest/ContactsV1/Get List

Note the space in the URL (or %20) and note there’s no slash at the end. The REST endpoint also takes parameters, an example of this:

http://[k2 server]:8888/SmartObjectServices/rest/ContactsV1/Get List?$skip=0&$top=2&$format=atom

The $skip and $top allows you to define a section of the information you need. The format used here is ATOM. Default is XML, but JSON is also supported. Remember, at this time, only the list methods are supported.

WCF

For the WCF endpoint, it’s best to simply show you an example. A small console application built in VS2010 and .NET 4.0 using the following URL in the Add Service Reference dialog:

http://[k2 server]:8888/SmartObjectServices/wcf/ContactsV1

And there’s the example code:

K2SOServices.ContactsSvc contactService = new K2SOServices.ContactsSvcClient() as K2SOServices.ContactsSvc;

// Create a new contact - I named by smartObject Contacts (with the S) - very bad naming :-)
K2SOServices.Contacts contact = new K2SOServices.Contacts();
contact.Name = "Capgemini Nederland B.V.";
contact.Phone = "+31 (0)30 689 0000";
contact.Address = "Papendorpseweg 100";
contact.Zipcode = 3528;
contact.City = "Utrecht";
contact.Country = "The Netherlands";

// Call the service and get the new contact back (with autogenerated ID property)
K2SOServices.Contacts newContact = contactService.ContactsSvc_Create(contact);

Console.WriteLine("New contact ID: {0}", newContact.Id);

// Create a 'query'-contact object.
K2SOServices.Contacts loadContact = new K2SOServices.Contacts();
loadContact.Id = newContact.Id;

// query and show the contact
K2SOServices.Contacts loadedContact = contactService.ContactsSvc_Load(loadContact);
Console.WriteLine("{0} - {1}", loadedContact.Id, loadedContact.Name);

//Query all contacts
K2SOServices.Contacts[] allContacts = contactService.ContactsSvc_GetList(null);
foreach (K2SOServices.Contacts contactItem in allContacts)
{
	Console.WriteLine("{0} - {1}", contactItem.Id, contactItem.Name);
}

The generated methods are nearly identical to the actual SmartObject methods. Special characters, which are not allowed in a .NET method, are replaced by whatever is specified in the “specialCharacterReplacement” option inside the configuration. In this case, I left it to the default underscore.

All the methods you can call on the WCF service have a SmartObject as a parameter. They also return a SmartObject (or list of them).  The parameters are used for filtering. The Load() method will need some parameters to uniquely identify one SmartObject to return it. On the list method you can use the parameter to apply filtering. In the example I simply pass NULL to get the full list.
When I first started playing with this service I tried the delete method and found that it also returns a SmartObject. That seemed a bit weird. To understand why, think about what you do when you design your SmartObject. You map SmartObject properties to Service Object properties. If you do the mapping for the delete method, you can specify a return mapping. Those return values will end up in the returning SmartObject.

This shows that the WCF endpoint really provides you with the same functionality as the K2 API does.

Conclusion

K2 has really made a good effort to provide new ways of integrating with the K2 platform. From my developer perspective, it really is a good to have fully typed access to K2 worklist and process data (K2 Services) and SmartObjects (K2 SmartObject Services) without having to build that myself.  Every SmartObject I deploy can automatically have a WCF endpoint that I can use in my applications.  I can even have no-code access to SmartObject data, and soon Worklist and Process Instances, via REST-enabled endpoints.  Remember to check back after the next K2 update to learn about the Worklist and Process REST-enabled endpoints along with something called ‘sync’.