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:
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.
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:
|
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
|
statusId |
The current state of the request:
|
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.
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:
|
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:
|
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.
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.
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.
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.