Generic Web Service Push Adapter

The generic web service push adapter provides a UCMDB-initiated push of SOAP messages containing query data to a web service data receiver. The mapped results are sent in standard SOAP messages via the HTTP POST protocol to the data receiver. The data receiver must understand the SOAP messages produced by the push adapter. To facilitate the development of the proper data receiver, a WSDL is provided with this push adapter.

Custom processing of the SOAP message response XML is possible in the Jython script.

To understand the format of the incoming mapped data, the developer of the data receiver should communicate with the developer of the mappings file. An .xsd is currently not provided with this version of the web service push adapter, so data must be processed in a way reflective of the incoming data, which is a combination of the original TQL and the applied mappings.

The web service push adapter functions for pushing data to the client are shown below. The items in green are customized or supplied by the client to implement the adapter for a specific push target. The items in blue are out-of-the-box components.

An example of an implementation of the generic web service push adapter to an MDR-specific push adapter using an Enterprise Service Bus (ESB) is shown here:

WSDL

A WSDL is supplied to the client developer to create a data receiver capable of communicating with the UCMDB push adapter via a web service. The UCMDBDataReceiver.wsdl describes the SOAP messages that are used to communicate data from UCMDB to the data receiver. The design diagram from the WSDL is shown here:

The data receiver (which is in practice a server or “service endpoint" in SOAP terminology) should implement three methods: addData, deleteData, and updateData, corresponding to the data sets that the UCMDB pushes. The HTTP headers contain the correct SoapAction keyword that indicates the type of data that is being sent. The data receiver is responsible for implementing the business logic and processing the data.

The default WSDL URL is:

  • http://localhost:8080/UCMDBDataReceiver/services/UCMDBDataReceiver?wsdl

As implemented by the Data Receiver, the URL could look similar to the following:

  • http://testWSPAserver:4444/MyCo.IT.SvcMgt.ws.us:provider/UCMDBDataReceiver?wsdl

The URL of the web service is the same as the WSDL URL without the “?wsdl” at the end.

The source for the WSDL is included below:

<?xml version="1.0" encoding="UTF-8"?>
<wsdl:definitions targetNamespace="http://ucmdb.microfocus.com" xmlns:apachesoap="http://xml.apache.org/xml-soap" xmlns:impl="http://ucmdb.microfocus.com" xmlns:intf="http://ucmdb.microfocus.com" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" xmlns:wsdlsoap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<!--WSDL created by Apache Axis version: 1.4 Built on Apr 22, 2006 (06:55:48 PDT)-->
    <wsdl:types>
        <schema elementFormDefault="qualified" targetNamespace="http://ucmdb.microfocus.com" xmlns="http://www.w3.org/2001/XMLSchema">
            <element name="addData">
                <complexType>
                    <sequence>
                    <element name="xmlAdded" type="xsd:string"/>
                    </sequence>
                </complexType>
            </element>
            <element name="addDataResponse">
                <complexType/>
            </element>
            <element name="deleteData">
                <complexType>
                    <sequence>
                    <element name="xmlDeleted" type="xsd:string"/>
                    </sequence>
                </complexType>
            </element>
            <element name="deleteDataResponse">
                <complexType/>
            </element>
            <element name="updateData">
                <complexType>
                    <sequence>
                    <element name="xmlUpdate" type="xsd:string"/>
                    </sequence>
                </complexType>
            </element>
            <element name="updateDataResponse">
                <complexType/>
            </element>
        </schema>
    </wsdl:types>
 
    <wsdl:message name="addDataRequest">
        <wsdl:part element="impl:addData" name="parameters">
        </wsdl:part>
    </wsdl:message>
    <wsdl:message name="deleteDataResponse">
        <wsdl:part element="impl:deleteDataResponse" name="parameters">
        </wsdl:part>
    </wsdl:message>
    <wsdl:message name="updateDataResponse">
        <wsdl:part element="impl:updateDataResponse" name="parameters">
        </wsdl:part>
    </wsdl:message>
    <wsdl:message name="deleteDataRequest">
        <wsdl:part element="impl:deleteData" name="parameters">
        </wsdl:part>
    </wsdl:message>
    <wsdl:message name="addDataResponse">
        <wsdl:part element="impl:addDataResponse" name="parameters">
        </wsdl:part>
    </wsdl:message>
    <wsdl:message name="updateDataRequest">
        <wsdl:part element="impl:updateData" name="parameters">
        </wsdl:part>
    </wsdl:message>
    <wsdl:portType name="UCMDBDataReceiver">
        <wsdl:operation name="addData">
            <wsdlsoap:operation soapAction="addDataRequest"/>
            <wsdl:input message="impl:addDataRequest" name="addDataRequest">
            </wsdl:input>
            <wsdl:output message="impl:addDataResponse" name="addDataResponse">
            </wsdl:output>
        </wsdl:operation>
        <wsdl:operation name="deleteData">
            <wsdlsoap:operation soapAction="deleteDataRequest"/>
            <wsdl:input message="impl:deleteDataRequest" name="deleteDataRequest">
            </wsdl:input>
            <wsdl:output message="impl:deleteDataResponse" name="deleteDataResponse">
            </wsdl:output>
        </wsdl:operation>
        <wsdl:operation name="updateData">
            <wsdlsoap:operation soapAction="updateDataRequest"/>
            <wsdl:input message="impl:updateDataRequest" name="updateDataRequest">
            </wsdl:input>
            <wsdl:output message="impl:updateDataResponse" name="updateDataResponse">
            </wsdl:output>
        </wsdl:operation>
    </wsdl:portType>
    <wsdl:binding name="UCMDBDataReceiverSoapBinding" type="impl:UCMDBDataReceiver">
        <wsdlsoap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http" />
        <wsdl:operation name="addData">
            <wsdl:input name="addDataRequest">
                <wsdlsoap:body use="literal" />
            </wsdl:input>
            <wsdl:output name="addDataResponse">
                <wsdlsoap:body use="literal" />
            </wsdl:output>
        </wsdl:operation>
        <wsdl:operation name="deleteData">
            <wsdl:input name="deleteDataRequest">
                <wsdlsoap:body use="literal" />
            </wsdl:input>
            <wsdl:output name="deleteDataResponse">
                <wsdlsoap:body use="literal" />
            </wsdl:output>
        </wsdl:operation>
        <wsdl:operation name="updateData">
            <wsdl:input name="updateDataRequest">
                <wsdlsoap:body use="literal" />
            </wsdl:input>
            <wsdl:output name="updateDataResponse">
                <wsdlsoap:body use="literal" />
            </wsdl:output>
        </wsdl:operation>
    </wsdl:binding>
    <wsdl:service name="UCMDBDataReceiverService">
        <wsdl:port binding="impl:UCMDBDataReceiverSoapBinding" name="UCMDBDataReceiver">
            <wsdlsoap:address location="http://localhost:8080/UCMDBDataReceiver/services/UCMDBDataReceiver"/>
        </wsdl:port>
    </wsdl:service>
</wsdl:definitions>

Response Handling

The data receiver should return a string in the addDataResponse, deleteDataResponse, or updateDataResponse structures. The adapter passes the response data unprocessed to the probe’s probeMgr-adaptersDebug.log. The receiver can return any string data, and the responses are wrapped in SOAP-compliant XML. In the Jython script you can use the SOAPMessage and related Java classes to parse the response messages. The following is an example of a response message from the data receiver:

<2012-03-16 15:47:38,080> [INFO ] [Thread-110] - XMLtoWebService.py:addData received response:
<soapenv:Body xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
<intf:addDataResponse xmlns:intf="http://ucmdb.microfocus.com">
    <xml>&lt;result&gt;&lt;status&gt;error&lt;/status&gt;
    &lt;message&gt;Error publishing config item changes&lt;/message&gt;
    &lt;/result&gt;</xml>
</intf:addDataResponse>
</soapenv:Body>

The message shown is an error message <Error publishing config item changes>, but the content can be anything that the data receiver is designed to respond with. The response is an error message simply because that is the intent, because the designer says it is an error message and the push adapter expects the response to be some indication of success or failure. The content can be reconciliation IDs of all the successfully added CIs, or error messages for specific CIs. Customization of the GWSPA could include parsing the response message and taking actions such as resending certain CIs or performing other logging.

Testing the WSDL

The SOAPUI Eclipse plug-in is used to test web service layers during development. You can use SOAPUI to assist with customization of a web service. SOAPUI offers an integrated development environment (IDE) to test building, sending, and receiving of SOAP messages. In the SOAPUI perspective, the WSDL on pages 1-1 generated the following sample message:

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ucm="http://ucmdb.hp.com">
    <soapenv:Header/>
    <soapenv:Body>
        <ucm:addData>
            <ucm:xmlAdded>?</ucm:xmlAdded>
        </ucm:addData>
    </soapenv:Body>
</soapenv:Envelope>

The “?” in the xmlAdded element above is the location of the data, which is supplied by the web service push adapter integration.

Observing Results

When the push adapter is operating normally, in non-debug mode, the data is never written to a file until the final result is written (the intermediate TQL results and mapped data results are not normally visible in any log file). However, the results can be written to the probe’s debug file by un-commenting the logger.debug statements (remove the “#” character) in the DiscoveryMain section as shown here:

Ensure the logger statement begins on the same column as the other preceding and following lines. Jython is indent-sensitive and the script will fail if the indention of all lines is not correct.

The debug log file probeMgr-adaptersDebug.log on the probe here shows the contents of the output:

<2011-12-07 14:02:23,019> [INFO ] [Thread-273] - XMLtoWebService.py started
<2011-12-07 14:02:23,019> [DEBUG] [Thread-273] - ESB Push parameters:
<2011-12-07 14:02:23,019> [DEBUG] [Thread-273] - Wshost=harpy.trtc.com
<2011-12-07 14:02:23,019> [DEBUG] [Thread-273] - WShostport=5555
<2011-12-07 14:02:23,019> [DEBUG] [Thread-273] - WSuri=ws/DtITServiceManagement.esla.v1.ws.provider:UMDBDataReceiver
<2011-12-07 14:02:23,019> [INFO ] [Thread-273] - URL is http://harpy.trtc.com:5555/ws/DtITServiceManagement.esla.v1.ws.
provider:UMDBDataReceiver
<2011-12-07 14:02:23,035> [DEBUG] [Thread-273] - Connected to http://harpy.trtc.com:5555/ws/DtITServiceManagement.esla.v1.ws.
provider:UMDBDataReceiver
<2011-12-07 14:02:23,035> [ERROR] [Thread-273] - sending results
<2011-12-07 14:02:23,035> [DEBUG] [Thread-273] - <?xml version="1.0" encoding="UTF-8"?>
<root>
<data>
<objects>
<Object mode="" name="u_imp_ip_switch" operation="add" mamId="9e8c2f6bdfe4b7d0864c79e70833902c">
<field name="Correlation ID" key="true" datatype="char" length="">9e8c2f6bdfe4b7d0864c79e70833902c</field>
<field name="name" key="false" datatype="char" length="">nma_09sw</field>
<field name="location" key="false" datatype="char" length="" />
<field name="u_chassis_vendor_type" key="false" datatype="char" length="">ciscoCat2960-24TT</field>
<field name="serial_number" key="false" datatype="char" length="" />
<field name="ram" key="false" datatype="char" length="" />
<field name="os_version" key="false" datatype="char" length="" />
</Object>

Modifying the Jython Script

XMLtoWebService.py

The Jython script used by the Web Service push adapter is very similar to the XML push adapter. The script uses UCMDBDataReceiver.jar, included with the adapter. The script implements the SendDataToReceiver() method. SendDataToReceiver() uses three parameters:

  1. Action (add, update, or delete)
  2. The URL of the Data Receiver
  3. The data

For example, the add block looks like: SendDataToReceiver(“add”, URL, addResult)

All web service and SOAP layers are wrapped. The URL is the service endpoint address of the UCMDB data receiver. This is the same URL used to obtain the wsdl via the “?wsdl” suffix.

The source of the Jython script is shown below. The web service integration wrapper lines are highlighted in green.

####################################
# script: XMLtoWebService.py
####################################
# This jython script accepts TQL data results (adds, updates, and deletes) from the Integration adapter.
# and sends it to a web service. The web service is called UCMDBDataReceiver.
# A web service client of this name must be addressable at the URL provided by the parameters.
# The SendDataToReceiver.jar exposes the SendDataToReceiver function, as well as the service locator.
# examples of the service locator are in the testconnection section.
# regular expressions
import re
# logging
import logger
# web service interface
from com.hp.ucmdb import SendDataToReceiver
from com.hp.ucmdb.SendDataToReceiver import locateService
from com.hp.ucmdb.SendDataToReceiver import SendData
##############################################
########      VARIABLES             ##########
##############################################
SCRIPT_NAME = "XMLtoWebService.py"
logger.info(SCRIPT_NAME+" started")
def cleanUp(str):
 
# replace mode=""
str = re.sub("mode=\"\w+\"\s+", "", str)
 
# replace mamId with id
str = re.sub("\smamId=\"", " id=\"", str)
 
# replace empty attributes
str = re.sub("[\n|\s|\r]*<field name=\"\w+\" datatype=\"\w+\" />", "", str)
 
# replace targetRelationshipClass with name
str = re.sub("\stargetRelationshipClass=\"", " name=\"", str)
 
# replace Object with object with name
str = re.sub("<Object mode=\"", "<object mode=\"", str)
str = re.sub("<Object operation=\"", "<object operation=\"", str)
str = re.sub("<Object name=\"", "<object name=\"", str)
str = re.sub("</Object>", "</object>", str)
 
# replace field to attribute
str = re.sub("<field name=\"", "<attribute name=\"", str)
str = re.sub("</field>", "</attribute>", str)
 
#logger.debug("String = %s" % str)
#logger.debug("cleaned up")
 
return str
def isEmpty(xml, type = ""):
objectsEmpty = 0
linksEmpty = 0
 
m = re.findall("<objects />", xml)
if m:
#logger.warn("\t[%s] No objects found" % type)
objectsEmpty = 1
 
m = re.findall("<links />", xml)
if m:
#logger.warn("\t[%s] No links found" % type)
linksEmpty = 1
 
if objectsEmpty and linksEmpty:
return 1
return 0
##############################################
########      MAIN                  ##########
##############################################
def DiscoveryMain(Framework):
#fix this for web service export
errMsg = "UCMDBDataReceiver Service not found."
testConnection = Framework.getTriggerCIData("testConnection")
# Get Web Service Push variables
WShostName = Framework.getTriggerCIData("Host Name")
WShostport = Framework.getTriggerCIData("Protocol Port")
WSuri = Framework.getTriggerCIData("URI")
 
logger.info(SCRIPT_NAME+":ESB Push parameters:")
logger.info("Host Name="+WShostName)
logger.info("Protocol Port="+WShostport)
logger.info("URI="+WSuri)
URL = "http://"+WShostName+":"+WShostport+"/"+WSuri
logger.info("URL="+URL)
if testConnection == 'true':
# locate the service
test_receiver = SendDataToReceiver()
locator = test_receiver.locateService(URL)
#locator = locateService(URL)
if(locator):
logger.info(SCRIPT_NAME+":Test connection was successful")
return
else:
raise Exception, errMsg
return
# do same thing here if not just a test connection -
receiver = SendDataToReceiver()
locator = receiver.locateService(URL)
if(locator):
logger.info(SCRIPT_NAME+":Connected to "+URL)
else:
logger.error(SCRIPT_NAME+":no locator")
raise Exception, errMsg
return
 
# get add/update/delete result objects from the Framework
addResult = Framework.getTriggerCIData('addResult')
updateResult = Framework.getTriggerCIData('updateResult')
deleteResult = Framework.getTriggerCIData('deleteResult')
logger.debug(deleteResult)
 
# get referenced data - unused in this adapter implementation
#addRefResult = Framework.getTriggerCIData('referencedAddResult')
#updateRefResult = Framework.getTriggerCIData('referencedUpdateResult')
#deleteRefResult = Framework.getTriggerCIData('referencedDeleteResult')
# uncomment out the logger statements to see the data
empty = isEmpty(addResult, "addResult")
if not empty:
addResult = cleanUp(addResult)
# send to ESB web service
logger.info(SCRIPT_NAME+":sending addData Result")
rcvr = SendDataToReceiver()
resp = rcvr.SendData("add", URL, addResult)
logger.info(SCRIPT_NAME+":addData received response:"+resp)
#logger.debug(addResult)
empty = isEmpty(updateResult, "updateResult")
if not empty:
updateResult = cleanUp(updateResult)
# send to ESB web service
#logger.debug(updateResult)
logger.info(SCRIPT_NAME+":sending updateData Result")
rcvr = SendDataToReceiver()
resp = rcvr.SendData("update", URL, updateResult)
logger.info(SCRIPT_NAME+":received response:"+resp)
 
empty = isEmpty(deleteResult, "deleteResult")
if not empty:
deleteResult = cleanUp(deleteResult)
# send to ESB web service
#logger.debug(deleteResult)
logger.info(SCRIPT_NAME+":sending deleteData Result")
rcvr = SendDataToReceiver()
resp = rcvr.SendData("delete", URL, deleteResult)
logger.info(SCRIPT_NAME+":received response:"+resp)
logger.info(SCRIPT_NAME+" ended")

Customizing Response Message Processing

The data receiver should return a string containing any response or status desired. The web service push adapter passes by default the response to the probe’s info-level log. The response message is SOAP-formatted XML containing the returned response string(s) inside. Any data can be returned by the receiver such as grouped or individual error or success messages. If additional processing is desired, the response can be processed by the adapter’s Jython script. No Java programming is required.

An example of a return response message, sent using the following:

// stub example for building your own UCMDBDataData Receiver
public class UCMDBDataReceiver {
 
public String addData (String xmlAdd){
System.out.println(xmlAdd); // do something with the data
// send back a response message based on what you did
String tr = new String("a test response from addData!");
return tr;
}

is shown here:

<soapenv:Body xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
<addDataResponse xmlns="http://ucmdb.hp.com">
<addDataReturn>a test response from addData!</addDataReturn>
</addDataResponse>
</soapenv:Body>

Modifying the Data Receiver

A Java client can implement the classes contained in UCMDBDataReceiver.jar and call the web service in the same manner as Jython. In addition, the unwrapped methods may also be called. A Javadoc exists for the UCMDBDataReceiver.jar classes. The source code below shows how to use these essential methods to wrap the data in a SOAP message and send it to the receiver over HTTP.

The process is to create a UCMDBDataReceiverServiceLocator object, then assign the UCMDBDataReceiverEndPointAddress to the URL of the data receiver.

To send data, the locator’s getUCMDBDataReceiver method is called to create a UCMDBDataReceiver object. The UCMDBDataReceiver object implements the methods to actually send the add/change/delete data. There are three identical code blocks to process each type of request.

The source code for the SendDataToReceiver class is listed below. Highlighted objects and methods are the essential elements to use.

/**
* Test SendData for the UCMDB Data Receiver for the UCMDB Web Service Push Adapter
*/
package com.hp.ucmdb;
import com.hp.ucmdb.SendDataToReceiver;
/**
* TestSendData can be used to verify the SOAP classes are working.
* TestSendData creates a SendDataToReceiver class and invokes its SendData method.
* a response String is returned.
* The test URL is typically appended with "?wsdl" to get the WSDL of the service.
*/
public class TestSendData {
/**
* @param args - test SOAP message.
*  optional arguments [0] a test string [1] a service endpoint URL of a Data Receiver.
* the default URL is sent the incoming argument as a test message.
* the default URL is "http://localhost:8080/UCMDBDataReceiver/services/UCMDBDataReceiver".
* If any errors are encountered, TestClient will attempt to throw exceptions.
*/
public static void main(String[] args) {
// use test message if supplied, otherwise supply a default test string
String teststring = new String("Test SOAP message from UCMDBDataReceiver TestSendData.");
if(args.length > 0) {
teststring = args[0];
}
// use test URL if supplied, otherwise supply the default URL
String URL = new String("");
if(args.length > 1) {
URL = args[1];
}
// return response
String response = new String("");
// perform the tests
try {
if(URL.equals("")) {
UCMDBDataReceiverServiceLocator locator = new UCMDBDataReceiverServiceLocator();
UCMDBDataReceiver receiver = locator.getUCMDBDataReceiver();
URL = locator.getUCMDBDataReceiverAddress();
System.out.println("TestClient: tested URL="+locator.getUCMDBDataReceiverAddress());
System.out.println("TestClient: receiver="+receiver.toString());
}
SendDataToReceiver sdtr = new SendDataToReceiver();
// this sends a test push and gets a response message
response = sdtr.SendData("add", URL, args[0]);
System.out.println("Response received was:"+response);
} catch (Exception e) {
System.out.println("TestClient: Remote Error:");
e.printStackTrace();
}
}
}

Source code is also included in the UCMDBDataReceiver.jar file for the other classes:

  • TestClient.java
  • UCMDBDataReceiver.java
  • UCMDBDataReceiverProxy.java
  • UCMDBDataReceiverService.java
  • UCMDBDataReceiverServiceLocator.java
  • UCMDBDataReceiverSoapBindingStub.java

The source was generated in the Eclipse IDE, then modified. Exercise caution when modifying the UCMDB code, as much of it is auto-generated to match the SOAP specification and the UCMDB data receiver.

Javadoc

A fully-commented javadoc is provided with the generic web service push adapter. The javadoc is included in the docs folder javadoc. Start with index.html. The overview page provides access to the documentation for all classes and methods in the SDK.

All Classes

  • SendDataToReceiver: API for the web service wrapper
  • TestClient: test client to verify connectivity to an service endpoint
  • UCMDBDataReceiver: web service wrapper

The rest are automatically generated by the web service builder:

  • UCMDBDataReceiverProxy
  • UCMDBDataReceiverService
  • UCMDBDataReceiverServiceLocator
  • UCMDBDataReceiverSoapBindingStub

Overview

Basic usage of the SDK, including source code examples, is explained in the documentation in the package. This javadoc is for the UCMDB web service push adapter. The API may be called from Jython or Java.

The SDK provides two source samples, TestClient and SendDataToReceiver. TestClient provides a very limited test of the responding local client. SendDataToReceiver is the main class used to send data to a web service.

First, use this SDK (mainly the enclosed WSDL) to implement a UCMDB data receiver to communicate with this web service. Then use this SDK to create a push adapter in the UCMDB to push UCMDB TQL result data to the data receiver. Basic usage of this API is described below, with both Jython and Java implementations.

Implementing SendDataToReceiver()

SendDataToReceiver() wraps all functions with a single method:

  • Jython: SendDataToReceiver("add",yourURL,"Hello!")
  • Java: SendDataToReceiver("add",yourURL,"Hello!");

Or, create a SendDataToReceiver object (for example, to manipulate other settings) and then call the SendData method separately, as shown here:

  • Jython:

    rcvr = SendDataToReceiver()
    responseMsg = rcvr.SendData(“add”, yourURL, “Hello!”)
  • Java:

    SendDataToReceiver rcvr = new SendDataToReceiver();
    String responseMsg = rcvr.SendData(“add”, yourURL, “Hello!”);

Or, if you need to do it a step at a time, you can do the following:

  1. Create a new UCMDBDataReceiverServiceLocator() object x, then set the object's endpoint address later, shown here:

    • Jython:

      x = UCMDBDataReceiverServiceLocator()
      x.setUCMDBDataReceiverEndPointAddress(URL)
    • Java :

      UCMDBDataReceiverServiceLocator x = new UCMDBDataReceiverServiceLocator();
      x. setUCMDBDataReceiverEndPointAddress(URL);
  2. Then, create a UCMDBDataReceiver with

    • Jython: y = x.getUCMDBDataReceiver()

    • Java: UCMDBDataReceiver y = x.getUCMDBDataReceiver();

  3. Then, send the data via the SOAP web service like this:

    • Jython:

      • y.addData(yourData)

      • or y.updateData(yourData)

      • or y.deleteData(yourData)

    • Java:

      • y.addData(yourData);

      • or y.updateData(yourData);

      • or y.deleteData(yourData);

  4. It may be necessary to test connectivity, then if successful reuse the same locator object to return UCMDBDataReceiver to use for data transfer.

The classes contain no destructors and do not perform memory management.