ITSM Outbound Web Services and Workflows

LiveTime v6.5 introduces the ability to add customized extensions to request workflow state transitions and item lifecycle state transitions. In order to implement custom functionality on these state transitions, there are several steps a user must follow.

1. Build the extension
2. Make the extension available to LiveTime & configure the system to enable the calls

Building the extension

Two java interfaces are provided, in a standalone jar file ‘livetime-listen.jar’, which can be found in:

{LiveTime Install Folder}/LiveTime.woa/Contents/Resources/Java/

This jar file contains two interfaces, each of which has two methods:

WorkflowListener
	public Map<String, String> stateEntered(Map<String, Object> argsMap) throws Exception
	public Map<String, String> stateExited(Map<String, Object> argsMap) throws Exception

LifecycleListener
	public Map<String, String> stateEntered(Map<String, Object> argsMap) throws Exception
	public Map<String, String> stateExited(Map<String, Object> argsMap) throws Exception

Naturally the WorkflowListener is used for integrating with the Request Workflow, whilst the LifecycleListener is used for integrating with the Item Lifecycle.

The task for the developer is to create a java class that implements the appropriate interface to achieve the integration objective. The two methods exist to provide flexibility to the developer implementing the integration, by allowing them to perform tasks based on a state being ‘exited’ and then a state being ‘entered’.

The implementing class is required to return a Map for all methods. Non-implemented interfaces can return null, and this will be treated as a no-op internally. Methods that provide functionality should return a map which will be parsed for two parameters:

‘success’ : ‘true’ or ‘false’
‘message’ : Description to be stored against the history of the request or item

Each method is passed a Map of parameters that relate to the request or item being updated. LiveTime v6.5 will pass a String for all values, the method takes for future extensions that may require the use of Objects. Implementations should check the object is a String prior to casting to future-proof the implementation.

WorkflowListener Arguments

The parameter Map passed in to the WorkflowListener interface methods consists of:

Parameter Description
triggerStatusId The ID of the status that has triggered the listener event
statusExited: originalStatus
statusEntered: newStatus
triggerStatusName The name of the status that triggered the listener event
requestId The ID of the request being updated
requestType The type of request being updated
1000 = Incident
2000 = Problem
3000 = Change Request
7000 = Service Request
9000 = Deployment Task
statusId The current state of the request:
statusExited: newStatus
statusEntered: same as triggerStatus
statusName The name of the state defined by statusId above
classificationId The ID of the classification assigned to the request
customerId The ID of the customer assigned to the request
customerFirstName The first name of the customer assigned to the request
customerLastName The last name of the customer assigned to the request
customerEmail The email address of the customer assigned to the request
orgUnitId The ID of the Org Unit associated with the request
orgUnitName The name of the Org Unit associated with the request
itemNumber The item number of the CI associated with the request

As this is a first iteration of the call-out interface, these parameters have been deemed sufficient to allow a developer to perform non-trivial tasks like update an external system that may require request updates.

LifecycleListener Arguments

The parameter Map passed in to the LifecycleListener interface methods consists of:


Parameter Description
triggerStatusId The ID of the status that has triggered the listener event
statusExited: originalStatus
statusEntered: newStatus
triggerStatusName The name of the status that triggered the listener event
itemNumber The Item Number assigned to the CI
itemStatusId The current state of the request:
statusExited: newStatus
statusEntered: same as triggerStatus
itemStatusName The name of the state defined by statusId above
itemtypeId The Item Type ID
itemtypeName The name of the Item Type the Item is an instance of
categoryId The Category ID
categoryName The name of the Category the Item Type belongs to

These are the initial parameters deemed appropriate to allow a developer to perform non-trivial tasks like (for example) to feed state changes into a monitoring tool to reset an alert trigger.

Implementing a Listener

This requires some Java knowledge, to either define the entire functionality, using these entry points, or to create a JNI wrapper to page out to code written in an alternate language, although this does have platform implications. A JNI (Java Native Interface) implementation is outside the scope of this document, and the following sample focuses on a Java Implementation.

The user needs to create a class, for example ‘MyWorkflowListener’ which implements the WorkflowListener Interface, or MyLifecycleListener, which implements the LifecycleListener interface. These methods then need to be made to perform some work. A non-trivial example would be one where the listener calls a web service to an external system. Consider the following usage scenario.

A company has (for whatever reason) two LiveTime instances, and they are needing to, on occasion feed updates to the secondary system on request state changes. LiveTime has an inbound web services interface, and now, an outgoing interface for communicating changes to third parties.

The SOAP WSDL’s can be used to generate Java classes to make calls into the secondary system, which can now be called from the outgoing interface. Generating the SOAP equivalent java classes, and calling them from a WorkflowListener might yield a listener class that looks something like the following.


package com.livetime.sample;

import com.livetime.ws.listen.WorkflowListener;
import java.util.Map;

public class WorkflowListenerImpl implements WorkflowListener{

public WorkflowListenerImpl() {}

	public Map<String, String> statusEntered(Map<String, Object> argsMap) throws Exception 	{
		BaseRequest baseRequest = new BaseRequest();

		String requestId = (String)argsMap.get("requestId");
		String statusName = (String)argsMap.get("triggerStatusName");

		Map temp = new HashMap();
		temp.put("subject", "Request Created via Outbound WebServices Call");
		temp.put("description", "Request #" + requestId + " has entered status " + statusName);

		return baseRequest.createRequest(temp);
	}

	public Map<String, String> statusExited(Map<String, String> argsMap) throws Exception
	{
		BaseRequest baseRequest = new BaseRequest();

		String requestId = (String)argsMap.get("requestId");
		String statusName = (String)argsMap.get("triggerStatusName");

		Map temp = new HashMap();
		temp.put("subject", "Request Created via Outbound WebServices Call");
		temp.put("description", "Request #" + requestId + " has exited status " + statusName);

		return baseRequest.createRequest(temp);
	}
}

With the createRequest method in the class BaseRequest looking like this:


public Map<String, String> createRequest(Map<String, String> properties) {
	// try and connect, if successful, create request and logout
	if(connect()) {
		java.net.URL requestURL = null;
		HashMap response = new HashMap();

		try {
			// Service endpoint
			requestURL = new java.net.URL(props.getRequestServiceURL());

			// Get handle to the service
			Request_Service service = new Request_ServiceLocator();
			Request_PortType port = service.getRequest(requestURL);

			// Call BaseClient method to populate persistent headers
			populateHeaders((javax.xml.rpc.Stub)port);

			String subject = (String)properties.get("subject");
			String description = (String)properties.get("description");

			response = port.createIncident(props.getTargetItemNumber(),
				props.getTargetClassificationId(), subject,
				description, new HashMap());
			}
			catch(Exception ex) {
				return buildErrorMessage("An error occurred whilst creating”);
			}

			if(disconnect()) {
				return response;
			}
			else {
				return buildErrorMessage("An error occurred whilst disconnecting");
			}
		}
	else {
		return buildErrorMessage("An error occurred whilst connecting");
	}
}

In this example, the listener simply creates a new request in the second system, stating the nature of the change, but this highlights some of the possibilities of outbound functionality.

Note this example has been heavily truncated to illustrate the key functionality.

Making the extension available to LiveTime

This is a rather simple process for users with an install on their own infrastructure. Users of the virtual appliance and SaaS customers on version 6.5 will need to contact LiveTime Support (support@livetime.com) to have their extensions made available. Version 6.6 allows users to upload these directly.

In order for the class to be accessible to LiveTime, the compiled code needs to be on the LiveTime classpath. In this case, this means the compiled jar, along with any associated components, need to be copied into the lib folder of the LiveTime installation ({LiveTime Path}/WEB-INF/lib), and the LiveTime instance needs to be restarted, so these resources are picked up by the classloader.

A note about JNI implementations

The earlier reference to JNI code having platform implications is of particular relevance at this point. If a native interface is being developed, it must suit the deployment platform. The LiveTime appliance and SaaS instances are UNIX based. It is not possible to provide a dll written for windows on these platforms as it simply won’t be loaded.

This outbound interface is written primarily for Java as that is the only case where platform considerations can be (mostly) ignored.

Configuring LiveTime to use the extension

At this point a new class (or classes) exist and have been loaded, now LiveTime simply needs to be configured to use them. This is a two part process. Firstly the option needs to be enabled by a system administrator to enable outbound web services. This option can be found in the Administrator portal, under Setup > Privileges > System (Outbound Web Services).

Once set to ‘On’ and saved, a new field appears in both the Workflow State and Lifecycle State Editors respectively. The ‘Listener Class’ can now be populated per workflow (or lifecycle) state, allowing each state to call the same, or different implementations as necessary. This allows different workflows to behave per the designers’ requirements.