Clarity PPM

Expand all | Collapse all

Calling external SOAP from Clarity GEL

  • 1.  Calling external SOAP from Clarity GEL

    Posted 12-24-2014 01:42 PM

    Has anyone called external SOAP service from Clarity GEL script? If yes, please send an example.

    We need to interface with other applications which support SOAP interface, e.g. Service Now or Rally



  • 2.  Re: Calling external SOAP from Clarity GEL

    Posted 12-24-2014 06:54 PM

    The current invoke tag via the com.niku.union.gel.SOAPTagLibrary doesn’t allow one to modify the request headers and many web service require authentication such as ServiceNow.

     

    So you have a couple of options:

     

    Use the Jelly soap invokeRaw tag which allows you to define the HTTP headers via the soapAction attribute. For ServiceNow, you would need to include the Authorization header.  This tag lib doesn’t come out of the box, so you will need to find the jar and drop it into the lib.

     

    Write a simple wrapper class that handles getting information from external web services and just use the invokeStatic tag in the jelly:core tag lib.

     

    Using brute force to make the soap call by using the java.net.URL class. Being in the On-Demand environment (but not for long!), I often find myself resorting to a brute force method.

     

    The script below calls a simple ServiceNow getRecords on the sys_user table.

     

    <gel:script
        xmlns:core="jelly:core"
        xmlns:file="jelly:com.niku.union.gel.FileTagLibrary"
        xmlns:gel="jelly:com.niku.union.gel.GELTagLibrary"
        xmlns:nikuq="http://www.niku.com/xog/Query"
        xmlns:soap="jelly:com.niku.union.gel.SOAPTagLibrary"
        xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
        xmlns:sql="jelly:sql"
        xmlns:util="jelly:util"
        xmlns:x="jelly:org.apache.commons.jelly.tags.xml.XMLTagLibrary"
        xmlns:xog="http://www.niku.com/xog"
        xmlns:xsd="http://www.w3.org/2001/XMLSchema"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
    
        <gel:log>Start this Script</gel:log>
        
        <!-- Set the endpoint to ServiceNow and set our Authorization for Basic -->
        <core:set var="soapEndPoint" value="https://demo.service-now.com/sys_user.do?SOAP" />
        <core:invokeStatic var="base64" className="com.niku.union.utility.Base64" method="encode">
            <core:arg type="java.lang.String" value="admin:password" />
        </core:invokeStatic>
        <core:set var="basicAuth" value="Basic ${base64}" />
        <gel:log>basicAuth = ${basicAuth}</gel:log>
    
        <!-- Open a connection to ServiceNow and set our request headers -->
        <core:new var="soapUrl" className="java.net.URL">
            <core:arg type="java.lang.String" value="${soapEndPoint}" />
        </core:new>
        <core:invoke var="connection" on="${soapUrl}" method="openConnection"/>
        <core:expr value="${connection.setDoOutput(true)}" />
        <core:expr value='${connection.setRequestMethod("POST")}'/>
        <core:expr value='${connection.setRequestProperty("Content-type", "text/xml; charset=utf-8")}'/>
        <core:expr value='${connection.setRequestProperty("SOAPAction", soapEndPoint)}'/>
        <core:expr value='${connection.setRequestProperty("Authorization", basicAuth)}'/>
        <core:set var="requestXml">
            <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
                <soapenv:Header>
                    <Action soapenv:mustUnderstand="1" xmlns="http://schemas.microsoft.com/ws/2005/05/addressing/none">http://www.service-now.com/sys_user/getRecords</Action>
                </soapenv:Header>
                <soapenv:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
                    <getRecords xmlns="http://www.service-now.com/sys_user">
                        <last_name xmlns="">greiff</last_name>
                </getRecords>
                </soapenv:Body>
            </soapenv:Envelope>
        </core:set>
        
        <!-- Write out our getRecords request to ServiceNow --> 
        <core:invoke var="outputStream" on="${connection}" method="getOutputStream" />
        <core:new var="outputStreamWriter" className="java.io.OutputStreamWriter">
            <core:arg type="java.io.OutputStream" value="${outputStream}" />
        </core:new>    
        <core:expr value="${outputStreamWriter.write(requestXml)}" />
        <core:expr value="${outputStreamWriter.close()}" />
        
        <!-- Read in the response from ServiceNow into a string -->
        <core:invoke var="inputStream" on="${connection}" method="getInputStream" />
        <core:new var="inputStreamReader" className="java.io.InputStreamReader">
                <core:arg type="java.io.InputStream" value="${inputStream}" />
        </core:new>
        <core:new var="stringBuilder" className="java.lang.StringBuilder" />
        <core:set var="data" value="${inputStreamReader.read()}" />
        <core:while test="${data != -1}">
        <core:invokeStatic var="char" className="java.lang.Character" method="toChars" >
            <core:arg type="int" value="${data}" />
        </core:invokeStatic>
        <core:invokeStatic var="charString" className="java.lang.String" method="valueOf" >
            <core:arg value="${char}" />
        </core:invokeStatic>
            <core:set var="char" value="${java.lang.String.valueOf(java.lang.Character.toChars(data))}"/>
                <core:invoke method="append" on="${stringBuilder}">
                    <core:arg value="${charString}" />
                </core:invoke>
                <core:set var="data" value="${inputStreamReader.read()}" />
        </core:while>
    
        <!-- Here is a string of the xml reponse payload -->
        <gel:log>${stringBuilder.toString()}</gel:log>
    
        <gel:log>End this Script</gel:log>
    </gel:script>
    

     

     

     

    V/r,

    Gene



  • 3.  Re: Calling external SOAP from Clarity GEL

    Posted 01-23-2015 03:07 PM

    I just want to suggest one difference if you only need to provide the SOAPAction header (and not something else like basic-auth as some systems/services like ServiceNow are wanting).

     

    In order to set and call the correct web service method, then the regular <soap:invoke> tag has a soapAction attribute for setting that header already, and another tag/library doesn't have to be used to get it.



  • 4.  Re: Calling external SOAP from Clarity GEL

    Posted 12-16-2015 01:50 PM

    Only a question, how did you transform the String (stringBuilder) into a XML Document, for example, I tried

     

    <gel:parse var="wellFormed" escapeText="false">

         ${stringBuilder.toString()}

    </gel:parse>

     

    but didn't work

     

    Any ideas?



  • 5.  Re: Calling external SOAP from Clarity GEL

    Posted 12-16-2015 04:16 PM

    If you want to get your response into a document via parse, just feed it the inputStream.

     

    <!-- Read in the response from ServiceNow into a org.w3c.dom.Document -->
    <core:invoke var="inputStream" on="${connection}" method="getInputStream" />
    <gel:parse var="wellFormed" file="${inputStream}" />
    <gel:serialize fileName="wellFormed.xml" var="${wellFormed}"/>
    

     

     

    V/r,

    Gene



  • 6.  Re: Calling external SOAP from Clarity GEL

    Posted 12-22-2017 12:35 AM

    I am now working on a ServiceNow integration using Basic Authentication and I tried to use your sample code.  However when executing the webservice call (either insert or getRecords actions) I get a connection timed out exception  I have tested calling the webservice from the CA PPM 14.3 Test server command line (linux) using the instructions I found on 

    https://www.codeproject.com/Tips/1015753/Calling-SOAP-WebService-And-Parsing-The-Result-Fro

    and it worked fine... I also tested using SOAPUI with no issues.

     

    I really do not know what could be going wrong... Maybe the secure https soapEndPoint causes a problem?

    or maybe the fact that when calling a webservice from a process it uses a special linux user which then in turn gets blocked by the firewall (between PPM and ServiceNow servers)??

     

    Here is the code I am executing:

     

    <core:set var="soapEndPoint" value="https://xyzdev.service-now.com/u_clarity.do?SOAP" />
    <core:invokeStatic var="base64" className="com.niku.union.utility.Base64" method="encode">
    <core:arg type="java.lang.String" value="clarity:xyz" />
    </core:invokeStatic>
    <core:set var="basicAuth" value="Basic ${base64}" />
    <gel:log>basicAuth = ${basicAuth}</gel:log>

    <!-- Open a connection to ServiceNow and set our request headers -->
    <core:new var="soapUrl" className="java.net.URL">
    <core:arg type="java.lang.String" value="${soapEndPoint}" />
    </core:new>
    <core:invoke var="connection" on="${soapUrl}" method="openConnection"/>
    <core:expr value="${connection.setDoOutput(true)}" />
    <core:expr value='${connection.setRequestMethod("POST")}'/>
    <core:expr value='${connection.setRequestProperty("Content-type", "text/xml; charset=utf-8")}'/>
    <core:expr value='${connection.setRequestProperty("SOAPAction", soapEndPoint)}'/>
    <core:expr value='${connection.setRequestProperty("Authorization", basicAuth)}'/>
    <core:set var="requestXml">
    <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
    <soapenv:Header>
    <Action soapenv:mustUnderstand="1" xmlns="http://schemas.microsoft.com/ws/2005/05/addressing/none">http://www.service-now.com/u_clarity/getRecords</Action>
    </soapenv:Header>
    <soapenv:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:u="http://www.service-now.com/u_clarity" >
    <u:getRecords>
    <u_budget_code>871</u_budget_code>
    </u:getRecords>
    </soapenv:Body>
    </soapenv:Envelope>
    </core:set>



  • 7.  Re: Calling external SOAP from Clarity GEL

    Posted 12-22-2017 10:20 AM

    My guess is the your namespace for the request payload.

     

    Try either this:

     

    or removing the u namespace:

    V/r,

    Gene



  • 8.  Re: Calling external SOAP from Clarity GEL

    Posted 12-22-2017 10:42 AM

    Unfortunately it didn't affect the results  I am still getting the Connection Timed Out error message

     

    Maybe it has something to do with the https from the endpoint url? Do I need to import the servicenow certificate onto PPM server or something similar?

     

    thanks,

    Olivier



  • 9.  Re: Calling external SOAP from Clarity GEL

    Posted 12-22-2017 11:18 AM

    I just tested this script on a Jakarta and Kingston instance and it worked.

     

    <?xml version="1.0" encoding="utf-8"?>
    <gel:script
         xmlns:core="jelly:core"
         xmlns:file="jelly:com.niku.union.gel.FileTagLibrary"
         xmlns:gel="jelly:com.niku.union.gel.GELTagLibrary"
         xmlns:nikuq="http://www.niku.com/xog/Query"
         xmlns:soap="jelly:com.niku.union.gel.SOAPTagLibrary"
         xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
         xmlns:sql="jelly:sql"
         xmlns:util="jelly:util"
         xmlns:x="jelly:org.apache.commons.jelly.tags.xml.XMLTagLibrary"
         xmlns:xog="http://www.niku.com/xog"
         xmlns:xsd="http://www.w3.org/2001/XMLSchema"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">


         <gel:log>Start this Script</gel:log>
         
         <!-- Set the endpoint to ServiceNow and set our Authorization for Basic -->
         <core:set var="soapEndPoint" value="https://xyzdev.service-now.com/sys_user_list.do?SOAP" />
         <core:invokeStatic var="base64" className="com.niku.union.utility.Base64" method="encode">
              <core:arg type="java.lang.String" value="clarity:xyz" />
         </core:invokeStatic>
         <core:set var="basicAuth" value="Basic ${base64}" />
         <gel:log>basicAuth = ${basicAuth}</gel:log>

         <!-- Open a connection to ServiceNow and set our request headers -->
         <core:new var="soapUrl" className="java.net.URL">
              <core:arg type="java.lang.String" value="${soapEndPoint}" />
         </core:new>
         <core:invoke var="connection" on="${soapUrl}" method="openConnection"/>
         <core:expr value="${connection.setDoOutput(true)}" />
         <core:expr value='${connection.setRequestMethod("POST")}'/>
         <core:expr value='${connection.setRequestProperty("Content-type", "text/xml; charset=utf-8")}'/>
         <core:expr value='${connection.setRequestProperty("SOAPAction", soapEndPoint)}'/>
         <core:expr value='${connection.setRequestProperty("Authorization", basicAuth)}'/>
         <core:set var="requestXml">
              <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
                   <soapenv:Header>
                        <Action soapenv:mustUnderstand="1" xmlns="http://schemas.microsoft.com/ws/2005/05/addressing/none">http://www.service-now.com/sys_user/getRecords</Action>
                   </soapenv:Header>
                   <soapenv:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
                        <getRecords xmlns="http://www.service-now.com/sys_user">
                             <last_name xmlns="">someUserToSearchFor</last_name>
                   </getRecords>
                   </soapenv:Body>
              </soapenv:Envelope>
         </core:set>
         
         <!-- Write out our getRecords request to ServiceNow -->
              
         <core:invoke var="outputStream" on="${connection}" method="getOutputStream" />
         <core:new var="outputStreamWriter" className="java.io.OutputStreamWriter">
              <core:arg type="java.io.OutputStream" value="${outputStream}" />
         </core:new>     
         <core:expr value="${outputStreamWriter.write(requestXml)}" />
         <core:expr value="${outputStreamWriter.close()}" />
         
         <!-- Read in the response from ServiceNow into a string -->
         <core:invoke var="inputStream" on="${connection}" method="getInputStream" />
         <core:new var="inputStreamReader" className="java.io.InputStreamReader">
                   <core:arg type="java.io.InputStream" value="${inputStream}" />
         </core:new>

         <core:new var="stringBuilder" className="java.lang.StringBuilder" />
         <core:set var="data" value="${inputStreamReader.read()}" />
         <core:while test="${data != -1}">
         <core:invokeStatic var="char" className="java.lang.Character" method="toChars" >
              <core:arg type="int" value="${data}" />
         </core:invokeStatic>
         <core:invokeStatic var="charString" className="java.lang.String" method="valueOf" >
              <core:arg value="${char}" />
         </core:invokeStatic>
              <core:set var="char" value="${java.lang.String.valueOf(java.lang.Character.toChars(data))}"/>
                   <core:invoke method="append" on="${stringBuilder}">
                        <core:arg value="${charString}" />
                   </core:invoke>
                   <core:set var="data" value="${inputStreamReader.read()}" />
         </core:while>

         <core:set var='soapEnvIndex' value='${stringBuilder.toString().indexOf("SOAP-ENV")}' />
         <gel:log>This is the index for soapEnvIndex = ${soapEnvIndex}</gel:log>
         <gel:log>${stringBuilder.toString().substring(soapEnvIndex-1)}</gel:log>

         <gel:log>End this Script</gel:log>
         
    </gel:script>

     

    V/r,

    Gene



  • 10.  Re: Calling external SOAP from Clarity GEL

    Posted 12-22-2017 11:44 AM

    which endpoint url and credentials did you use? because mine just isn't working  and the ones in the above script aren't real endpoint url / credentials...



  • 11.  Re: Calling external SOAP from Clarity GEL

    Posted 12-22-2017 01:47 PM

    You might be running into a web services setting issue on ServiceNow.

     

    Try setting the the elementFormDefault attribute of the embedded XML schema to the value of unqualified.

     

    You will find this setting under System Properties -> Web Services.

    V/r,

    Gene