1.15 ZSD Extensions

ZENworks Service Desk (ZSD) has 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.15.1 Building the Extension

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

{ZSD Installation 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. ZSDZSD 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.

1.15.2 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

  • 1000 = Incident

  • 2000 = Problem

  • 3000 = Change Request

  • 7000 = Service Request

  • 9000 = Deployment Task

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.

1.15.3 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.

1.15.4 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 ZSD instances, and they are needing to, on occasion feed updates to the secondary system on request state changes. ZSD 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.

1.15.5 Making the Extension Available to ZSD

This is a rather simple process for users with an install on their own infrastructure.

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

1.15.6 Configuring ZSD to Use the Extension

At this point a new class (or classes) exist and have been loaded, now ZSD 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.