Cisco Support Community
cancel
Showing results for 
Search instead for 
Did you mean: 

UCCX 8.x - a Really Simple SOAP Client (no custom JAR)

Problem: it's a common task for a call center routing script to communicate with a remote procedure using the SOAP protocol, over HTTP. Usually, the script sends a piece of information to the SOAP server, parses the response, reads some useful data and then continues.

Analysis: Unfortunately, there is no built in step or method to create a simple SOAP client within a CRS script (and I don't blame the UCCX programmers for that). Some people use various frameworks (JAX-WS, Axis) to make SOAP calls easier, this usually requires an IDE like NetBeans or Eclipse, building a Java package, and uploading it - along with the framework itself - as custom JARs to the UCCX server. Personally, I don't really like that idea:

  • it makes debugging complicated;
  • if any change is needed, one must rebuild the whole solution, upload it to the UCCX server, restart the CCX engine - who likes this hassle;
  • I don't see the reason why we can't keep things simple and stupid - meaning, CRS scripts can do Java, and Java (at least Java 6 which comes with UCCX 8.x) can do everything you need to build a Really Simple SOAP Client.

UPDATE 1: if you are interested in a more OOP-style WS client (created with JAX-WS) read the following post:

https://supportforums.cisco.com/docs/DOC-26957.

UPDATE 2: I got a couple of comments and private messages about the CCX editor not accepting the second Set step due to one line:

return new String(baos.toByteArray());

This is a bug in some versions of UCCX. Unfortunately, for some reason it will sort of "hide" the Set step if it contains the above line. I don't know why it won't allow calling the toByteArray() method on a ByteArrayOutputStream object, this method is legal. Some versions of UCCX may accept it, some just won't. For instance, UCCX 8.0.2 happily accepts that method, UCCX 7.0SR5 does not.

If your UCCX is affected by the bug, then try replacing the above line with the following:

return baos.toString();

This has been tested and found working with UCCX Premium 7.0SR5 which does contain the bug, so the Set step disapperared with the original line but it did not with the replacement.

Solution: so, what is SOAP anyway?

In Plain English, it consists of:

  1. creating an XML document;
  2. SOAP Request: sending this XML document to the SOAP server. This is usually HTTP POSTing it to the web server;
  3. read the SOAP response (this is an XML document, too);
  4. parse the SOAP response and filter out the information we are looking for.

Looking at the tools the CCX editor offers for the above four tasks:

1. creating an XML document - easy. Two alternatives:

  • XML Template: you can upload an XML document on to the UCCX server, with some tokens; then, using the Keyword Transform Document step (where the already existing XML document will be the template), just replace the tokens with the desired values. That works just great if you don't want anything complicated and you are absolutely sure the result will also be a valid XML document (so there is no chance the Keyword Transform Document messes it up with some unexpected characters etc).
  • You can, of course, use Java to build the XML from scratch if you want to have more control. Personally, I like this option.

2. sending this XML document - now, one may say, there's the Create URL Document step, and it can do HTTP Post. Well, that's right, the step exists, it can sort of POST, but it's not the POST we are looking for. This step actually formats the value to be sent differently (application/x-www-form-urlencoded) which may or may not be accepted by the SOAP server. Java gives you more precise tools for that.

3. reading the SOAP response - this actually goes along with the previous step. But if we send something unacceptable to the SOAP server, we cannot expect a valid response either. Using Java gives you more granulated control.

4. parse the SOAP response and "fish out" the desired piece of information - easy. The Get XML Document Data step can operate with XPath expressions perfectly. So no Java code is needed.

**** DISCLAIMER: This is a proof of concept. I am not responsible for any damage, including the death of your favorite pet/rockstar. I cannot guarrantee this solution is the best, I cannot even guarrantee it would work in your system. Tested with IP IVR version 8.0(2) ****

I am going to explain the whole process using an example. Again, y u no build any packages, custom JARs. I don't even expect you to be fluent in Java.

In our not at all imaginary call center, there's a UCCX script that is supposed to do some routing decision based on a number which is calculated by some legacy systems, which speak SOAP only.

The expected SOAP request:

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">

   <soapenv:Header/>

   <soapenv:Body>

      <mis:GetCallAggregates xmlns:mis="http://mis.report.pointers.cz/">

         <startDate>2012-04-01T00:00:00</startDate>

         <endDate>2012-04-02T23:59:59</endDate>

      </mis:GetCallAggregates>

   </soapenv:Body>

</soapenv:Envelope>

If everything goes fine, then the SOAP response is:

<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">

   <soap:Body>

      <ns2:GetCallAggregatesResponse xmlns:ns2="http://mis.report.pointers.cz/">

         <GetCallAggregatesResult>

            <CallAggregate>

               <date>2012-04-01T00:00:00+02:00</date>

               <parameterName>AbandonIn30</parameterName>

               <value>12.0</value>

            </CallAggregate>

            <CallAggregate>

               <date>2012-04-02T00:00:00+02:00</date>

               <parameterName>AbandonIn30</parameterName>

               <value>125.0</value>

            </CallAggregate>

            <CallAggregate>

               <date>2012-04-01T00:00:00+02:00</date>

               <parameterName>BehindIVR</parameterName>

               <value>665.0</value>

            </CallAggregate>

            <CallAggregate>

               <date>2012-04-02T00:00:00+02:00</date>

               <parameterName>BehindIVR</parameterName>

               <value>5476.0</value>

            </CallAggregate>

            <!-- omitted for brevity -->

         </GetCallAggregatesResult>

      </ns2:GetCallAggregatesResponse>

   </soap:Body>

</soap:Envelope>

If we send something to the SOAP server and an error occurs, the SOAP response looks like this (I just inserted a bogus <boo> tag into the XML):

<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">

   <soap:Body>

      <soap:Fault>

         <faultcode>soap:Client</faultcode>

         <faultstring>Unmarshalling Error: unexpected element (uri:"", local:"boo"). Expected elements are (none)</faultstring>

      </soap:Fault>

   </soap:Body>

</soap:Envelope>

Now, take a look at the request again:

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">

   <soapenv:Header/>

   <soapenv:Body>

      <mis:GetCallAggregates xmlns:mis="http://mis.report.pointers.cz/">

         <startDate>2012-04-01T00:00:00</startDate>

         <endDate>2012-04-02T23:59:59</endDate>

      </mis:GetCallAggregates>

   </soapenv:Body>

</soapenv:Envelope>

As we can see, it's just a plain ordinary XML document (sans the XML declaration, for the sake of brevity). The so-called root element is soapenv:Envelope (its namespace in red, first row - expected by the SOAP server). The first child node is soapenv:Header; notice, it is empty - my SOAP server does not expect any headers. Then the soapenv:Body - this contains the next element, mis:GetCallAggregates (with the namespace URI in red, again), and this contains two elements, startDate and endDate (each containing a date and time string).

In other words, I am asking the SOAP server to run the GetCallAggregates method, with two parameters, startDate (its value is 2012-04-01T00:00:00) and endDate (its value is 2012-04-02T23:59:59). Actually, I am asking for some statistical information (CallAggregates) within the specified dates (1st April 2012 00:00:00, and 2nd April 2012 23:59:59).

This can be very easily constructed in Java, using only the tools Java 6 offers. And, as we know, Java 6 - more precisely, JDK 1.6 - is included in UCCX and if you have IP IVR or UCCX Enhanced or Premium, you can use it, most likely with Set steps.

Constructing XML in Java

First, I am going to create a new variable, type String, and name it soapRequest (initial value: null).

uccx_soap_vars_soapRequest.PNG

Then I am going to insert a Set step into my script, that would yield the value of the above soapRequest variable. The value field would be a closure (a block of Java code. Don't panic, I'll explain that, too!)

uccx_soap_XMLconstruct.PNG

The source code I am using at this step (please note: // starts a line of comment):

{

// first, create a new buffer:

java.io.StringWriter buffer = new java.io.StringWriter();

// define namespace URI's and prefixes

// (take a look at the SOAP request XML above, you'll see there are two namespace prefix - URI pairs

final String SOAPENV_NAMESPACE_URI = "http://schemas.xmlsoap.org/soap/envelope/";

final String SOAPENV_NAMESPACE_PREFIX = "soapenv";

final String MIS_NAMESPACE_URI = "http://mis.report.pointers.cz/";

final String MIS_NAMESPACE_PREFIX = "mis";

// some of the following methods may throw an exception (~ error), so we want to wrap them

// into a try..catch block. Remember, if an exception occurs, the resulting value,

// thus the soapRequest variable will be null

        try {

// create a document builder "factory" (that builds the document)

// make it namespace aware

// then crate a new document

        javax.xml.parsers.DocumentBuilderFactory factory = javax.xml.parsers.DocumentBuilderFactory.newInstance();

        factory.setNamespaceAware(true);

        javax.xml.parsers.DocumentBuilder documentBuilder = factory.newDocumentBuilder();

        org.w3c.dom.Document document = documentBuilder.newDocument();

// insert the "root" element (soapenv:Envelope), and introduce the required namespace:

        org.w3c.dom.Element root = document.createElementNS(SOAPENV_NAMESPACE_URI, SOAPENV_NAMESPACE_PREFIX + ":Envelope");

        root.setAttributeNS("http://www.w3.org/2000/xmlns/", "xmlns:" + SOAPENV_NAMESPACE_PREFIX, SOAPENV_NAMESPACE_URI);

// create the header element (precisely: soapenv:Header)

// we are going to append it to the root element right away (since it should not contain anything)

        org.w3c.dom.Element header = document.createElement("soapenv:Header");

        root.appendChild(header);

// create the body (precisely: soapenv:Body) element:

        org.w3c.dom.Element body = document.createElement("soapenv:Body");

// create another element, mis:GetCallAggregates, now with a different namespace, too:

        org.w3c.dom.Element gca = document.createElementNS(MIS_NAMESPACE_URI, MIS_NAMESPACE_PREFIX + ":GetCallAggregates");

        gca.setAttributeNS("http://www.w3.org/2000/xmlns/", "xmlns:" + MIS_NAMESPACE_PREFIX, MIS_NAMESPACE_URI);

// and, create two other elements, startDate and endDate

// each contains a date-time string:

        org.w3c.dom.Element startDate = document.createElement("startDate");

        startDate.setTextContent("2012-04-01T00:00:00");

        org.w3c.dom.Element endDate = document.createElement("endDate");

        endDate.setTextContent("2012-04-02T23:59:59");

// adding startDate and endDate to mis:GetCallAggregates

        gca.appendChild(startDate);

        gca.appendChild(endDate);

// adding mis:GetCallAggregates to the soapenv:Body element

        body.appendChild(gca);

// adding the soapenv:Body element to the root element (soapenv:Envelope)

        root.appendChild(body);

// and then adding the root element to the document

        document.appendChild(root);

// at this point, we've got the programmatic representation of XML

// but we need to transform it into a String so we can send it to the SOAP server

// this can be achieved, too: the source being the programmatic representation

// and the result is the buffer we specified on the first line

        javax.xml.transform.TransformerFactory transFactory = javax.xml.transform.TransformerFactory.newInstance();

        javax.xml.transform.Transformer transformer = transFactory.newTransformer();

        javax.xml.transform.Source source = new javax.xml.transform.dom.DOMSource(document);

        javax.xml.transform.stream.StreamResult result = new javax.xml.transform.stream.StreamResult(buffer);

        transformer.transform(source, result);

        } catch (Exception e) {

// again, if anything happens,the strack trace is sent to the standard output (stdout.log, most likely)

            e.printStackTrace();

// and then null is returned

            return null;

        } // try..catch block ends here

// but if there are no problems, the buffer is cast into a String:

return buffer.toString();

} //closure ends here

I know, it might be overwhelming, but once you understand the basic concepts of XML, this code will also be easier to read.

So at this point, we've got the SOAP request XML document - held by the soapRequest variable.

Issuing the SOAP request, using Java

Again, we need to connect to the web server, in other words, to the endpoint, and using HTTP POST, send the XML document created above.

First, I am going to introduce three new variables:

soapResponseString, type String, initial value: null

soapResponseDoc, type Document, initial value: DOC[]

soapServiceEndpoint, type String, initial value is "http://10.232.108.15:8080/MIS/services/MISReport"

uccx_soap_vars_again.PNG

Just some more details on them: soapResponseString is the raw "string", sent by the SOAP server as response. Since the Get XML Document Data step can only operate on documents of which type is Document, we'll have to use a different variable, with the expected type, soapResponseDoc. And, as you might have guessed, the soapServiceEndpoint contains the URL of the SOAP Server endpoint.

Then, the Set step. The variable is the soapResponseString, and the value field contains another nice block of code again:

uccx_soap_response.PNG

Source code (again, don't panic!):

{

// create a ByteArrayOutputStream - this is actually a buffer

// we are going to use to store the response coming from the SOAP server

java.io.ByteArrayOutputStream baos = new java.io.ByteArrayOutputStream();

// this is quite unnecessary: assigning the value of the soapRequest (still holding our XML document)

// to another variable, named content, type String; but anyway, it's just cleaner

String content = soapRequest;

// specify a timeout - 5000 milliseconds - if the web server does not start sending back

// anything within this timeframe, an exception will be raised

int readTimeout = 5000;

// now, the following methods are enclosed by a try..catch block

// to catch any exceptions - we just want to have control over them

// remember, an uncaught exception might stop your CRS script execution

// and you might not want that

try {

// a new URL, which is the soapServiceEndpoint variable value (http://ip:port/ etc):

     java.net.URL url = new java.net.URL(soapServiceEndpoint);

// creating a HTTP connection to the above URL:

     java.net.HttpURLConnection urlCon = (java.net.HttpURLConnection) url.openConnection();

// setting some important header parameters, first of all, content length, this is most likely expected

// by the SOAP server

     urlCon.setFixedLengthStreamingMode(content.length());

// setting the timeout:

     urlCon.setReadTimeout(readTimeout);

// we tell Java we will do input (in other words, we will read):

     urlCon.setDoInput (true);

// we tell Java we will do output (in other words, we will send):

     urlCon.setDoOutput (true);

// we tell Java not to cache:

     urlCon.setUseCaches (false);

// we are using HTTP POST

     urlCon.setRequestMethod("POST");

// finally, we set the Content-Type header,

// this way we are telling the SOAP server we are sending an XML, using the UTF-8 charset

     urlCon.setRequestProperty("Content-Type","text/xml;charset=UTF-8");

// opening an OutputStream (this is a one-way channel towards the SOAP server:

     java.io.DataOutputStream output = new java.io.DataOutputStream(urlCon.getOutputStream());

// we write the contents of the content variable (= soapRequest = XML document):

     output.writeBytes(content);

// telling Java to flush "speed up!" and then close the stream:

     output.flush();

     output.close();

// now getting the InputStream (getting a one way channel coming from the SOAP server):

     java.io.DataInputStream input = new java.io.DataInputStream(urlCon.getInputStream());

// buffered read from the InputStream, buffer size 4Kilobytes (= 4096 bytes):

// and the buffer is always written to the other buffer, named baos, that we specified

// on the first line of this block of code

     int bufSize = 4096; // buffer size, bytes

     byte[] bytesRead = new byte[bufSize];

     int bytesReadLength = 0;

     while(( bytesReadLength = input.read( bytesRead )) > 0 ) {

         baos.write(bytesRead,0,bytesReadLength);

     } //while block ends here

// closing the InputStream:

     input.close();

// closing the baos buffer

     baos.close();

} catch (Exception e) {

// again, if an error occurs, let's just send the stack trace to stdout (stdout.log)

// and then return null

    e.printStackTrace();

    return null;

} // try..catch block ends here

// construct a new String, and return that to the CRS script:

return new String(baos.toByteArray());

} // closure ends here

(N.B.: If your UCCX is affected by a strange bug - see UPDATE 2 - try replacing

return new String(baos.toByteArray());

with

return baos.toString();

)

There's still one more step, cast this soapResponseString to a Document. Notice the next step, the Get XML Document Data step expects the document be created by the Create XML Document step. But that's not a problem:

uccx_soap_casting.PNG

Voilá, we've got our SOAP response document. Again, it's something like this if everything goes perfectly:

<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">

   <soap:Body>

      <ns2:GetCallAggregatesResponse xmlns:ns2="http://mis.report.pointers.cz/">

         <GetCallAggregatesResult>

            <CallAggregate>

               <date>2012-04-01T00:00:00+02:00</date>

               <parameterName>AbandonIn30</parameterName>

               <value>12.0</value>

            </CallAggregate>

            <CallAggregate>

               <date>2012-04-02T00:00:00+02:00</date>

               <parameterName>AbandonIn30</parameterName>

               <value>125.0</value>

            </CallAggregate>

            <CallAggregate>

               <date>2012-04-01T00:00:00+02:00</date>

               <parameterName>BehindIVR</parameterName>

               <value>665.0</value>

            </CallAggregate>

            <CallAggregate>

               <date>2012-04-02T00:00:00+02:00</date>

               <parameterName>BehindIVR</parameterName>

               <value>5476.0</value>

            </CallAggregate>

            <!-- omitted for brevity -->

         </GetCallAggregatesResult>

      </ns2:GetCallAggregatesResponse>

   </soap:Body>

</soap:Envelope>

And like this if something goes bad:

<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">

   <soap:Body>

      <soap:Fault>

         <faultcode>soap:Client</faultcode>

          <faultstring>Unmarshalling Error: unexpected element (uri:"",  local:"boo"). Expected elements are (none)</faultstring>

      </soap:Fault>

   </soap:Body>

</soap:Envelope>

Getting the desired value from the SOAP response XML document

For now, let's pretend everything went fine, there were no errors. I am interested in the value of the "value" element of the first CallAggregate element (where the date is 2012-04-01 and the "parameterName" is AbandonIn30):

uccx_soap_response.PNG

We use the Get XML Document Data step for it (the result variable is named inString, type String, inital value: null):

uccx_soap_xpath.PNG

The actual XPath expression:

"//CallAggregate[1]/value"

Of course, you might check whether there are any soap:Fault nodes, you can do that using the XPath expression //faultcode (for instance).

Check Wikipedia or w3schools for more XPath examples.

Conclusion: this is a simplistic approach, yet it works very well in a real environment.

Happy coding!

Version history
Revision #:
1 of 1
Last update:
‎04-17-2012 05:49 AM
Updated by:
 
Labels (1)
Comments
New Member

Hi Gergely

This is great but one question if i may.

I had a situation where i had to create a bit of middleware to manipulate the responses sent back from the soap server so UCCX could understand them. You have not explained in this document how to handle the errors which i found the hardest part.

I have the solution working great now but i can see from what you have written here would have made my life a lot easier.

Mark

Hi, that's a good point. Can you please tell me what error conditions are you interested in and I will try to incorporate some more explanation on them.

Thanks.

G.

New Member

Congratulation Gergely!

This post is very helpful.

Valber

Congratulations Gergely for SPOT LIGHT Award ! Excellent document.

Regards

Lavanya

Technical Community Manager - Voice

New Member

Dear Gregly,

I  had created custome Java Class to use SOAP Requests using IVR. My code  is working fine when I call it from main function, but when I call the  function from IVR Script at UCCX 8.5.1 I get following error:

system.io.IOException: Server returned HTTP response code 500 for url <url>

I had successfully tested my code from below machines/OS:

Windows XP, Windows7, RedHat Linux, Solaris

Please tell me that what could be the problem.

Below is the code:

    public String[] BalanceInquiry( String sAccountNumber ) {

        String sReturnVal[] = new String[4];

        String soapResponse    =    "";

        String soapRequestURL    =     "http://10.0.18.24/PhoenixWebService/Service1.asmx";

        String soapRequestTXT    =     "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" +

                        "<soap:Envelope xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\">\n" +

                        "    <soap:Body>\n" +

                        "        <BalanceInquiry xmlns=\"http://tempuri.org/\">" +

                        "              <accountNo>" + sAccountNumber + "</accountNo>" +

                          "              </BalanceInquiry>" +

                        "    </soap:Body>\n" +

                        "</soap:Envelope>";

        try {

            URL url = new URL(soapRequestURL);

            URLConnection conn = url.openConnection();

            conn.setRequestProperty("Content-Type", "text/xml; charset=utf-8");

            conn.setRequestProperty("Content-Length", "" + soapRequestTXT.length());

            conn.setDoOutput(true);

            OutputStreamWriter wr = new OutputStreamWriter(conn.getOutputStream());

            wr.write(soapRequestTXT);

            wr.flush();

            BufferedReader rd = new BufferedReader(new InputStreamReader(conn.getInputStream()));

            String line = "";

            while ((line = rd.readLine()) != null) {

                soapResponse += line;

            }

            wr.close();

            rd.close();

            //if the everything went ok

            if(readStatusResponseXML(soapResponse) == true)

            {

                //set status value

                sReturnVal[0] = "true";

                //get balance

                sReturnVal[1] = readBalanceResponseXML(soapResponse);

                //get complete response

                sReturnVal[2] = soapResponse;

                //Get Responce Statuas

                sReturnVal[3] = readResponceValXML(soapResponse);//@Kashif

            }

            //else log the error here

            else

            {

                //set status value

                sReturnVal[0] = "false";

                sReturnVal[1] = "";

                sReturnVal[2] = soapResponse;

                //Get Responce Status

                sReturnVal[3] = readResponceValXML(soapResponse);//@Kashif

                //TODO

                //write logging code here

            }

            return sReturnVal;

        } catch (Exception exception) {

            sReturnVal[0] = exception.getMessage();

            return sReturnVal;

        }

    }

Hi,

I am afraid you are sending a malformed SOAP request, this is why the SOAP server throws an HTTP 500 (= Server error) message at you.

Try to take a look at the server logs of your SOAP server. HTTP 500 is something the server itself cannot deal with, but there's a chance it gives you some details why not.

G.

New Member

Hi,

thanks for this doc... and the pointer to it on the supportforums... I am trying to adapt the java for my use case but since i am new to this i need some help...

This is a sample of my SOAP request...

<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">

      <s:Header>

           <Action s:mustUnderstand="1" xmlns="http://schemas.microsoft.com/ws/2005/05/addressing/none">http://tempuri.org/IDeviceService/CreatePin</Action>

      </s:Header>

      <s:Body>

           <CreatePin xmlns="http://tempuri.org/">

                <UserID>1234567890123</UserID>

           </CreatePin>

      </s:Body>

</s:Envelope>

and the response:

<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">

      <s:Header/>

           <s:Body>

                <CreatePinResponse xmlns="http://tempuri.org/">

                     <CreatePinResult>195400</CreatePinResult>

                </CreatePinResponse>

           </s:Body>

</s:Envelope>

In your sample you have a "MIS" namespace... My request is a bit different, there is no "MIS" prefix, just the namespace for the CreatePin method...

I also have a header in the request... how much editing do i need to make this work? Should i consult someone with more expirience with Java since i have none?

The UserID is a dynamic param that is entered by the caller.

Thanx,

Kenan

Hi,

well, actually, implementing SOAP is not for beginners, in any programming language.

About that "mis" namespace in the body: instead of using the document.createElementNS method, you can simply create an element using document.createElement and then set its atribute using the setAttributeNS method.

Dynamic parameter: for instance, if that user input is stored in a string variable, you can simply create a new element (createElement), and then use the setTextContent method to set its value.

About the SOAP header: well, it depends on the server implementation, but I would try to send a SOAP request without it. If it's not acceptable, simply create a new element, in this case, Action, and add it to the Header element as its child node.

Anyway, try it out in an IDE first, like Eclipse or Netbeans before you start experimenting with the UCCX.

HTH

G.

New Member

Hi G,

thanks to your help the soap client is up and working flawlessly... i modified the soap request code a bit to make it more simple where instead of build the XML element by element i include the XML source and populate the dynamic tokens. The code is added bellow. If you have any recommendations I would be happy to hear them since I am new to this.

{

java.io.StringWriter buffer = new java.io.StringWriter();

try {

  // Create the XML Source Document and insert the dynamic tokens

String xmlSource = "<s:Envelope xmlns:s='http://schemas.xmlsoap.org/soap/envelope/'><s:Header><Action s:mustUnderstand='1' xmlns='http://schemas.microsoft.com/ws/2005/05/addressing/none'>http://tempuri.org/WebService/CreatePin</Action></s:Header><s:Body><CreatePin xmlns='http://tempuri.org/'><UserID>" + JMBG + "</UserID></CreatePin></s:Body></s:Envelope>";

      javax.xml.parsers.DocumentBuilderFactory factory = javax.xml.parsers.DocumentBuilderFactory.newInstance();

      javax.xml.parsers.DocumentBuilder documentBuilder = factory.newDocumentBuilder();

      org.w3c.dom.Document document = documentBuilder.newDocument();

      // Read in the XML source Document

      java.io.StringReader reader = new java.io.StringReader(xmlSource);

      org.xml.sax.InputSource is = new org.xml.sax.InputSource(reader);

      document = documentBuilder.parse(is);

      javax.xml.transform.TransformerFactory transFactory = javax.xml.transform.TransformerFactory.newInstance();

      javax.xml.transform.Transformer transformer = transFactory.newTransformer();

      javax.xml.transform.Source source = new javax.xml.transform.dom.DOMSource(document);

      javax.xml.transform.stream.StreamResult result = new javax.xml.transform.stream.StreamResult(buffer);

      transformer.transform(source, result);

      }

      catch (Exception e) {

                         e.printStackTrace();

        return e.getMessage();

        }

return buffer.toString();

}

Again thanks for this guide and the help.

Kenan

Hi,

well, I don't really see the point. The above code simply creates an XML and you return either some text if something goes wrong (the exception stack trace), or an empty StringWriter variable named buffer, since I don't see anything that writes its value. Are you sure you pasted the correct block of code? ;-)

G.

New Member

Hi G,

I think you might have missed something, probably to the lack of code comments on my side...

// Read in the XML source Document

java.io.StringReader reader = new java.io.StringReader(xmlSource);

org.xml.sax.InputSource is = new org.xml.sax.InputSource(reader);

// Parse the is into a document

document = documentBuilder.parse(is);

//transform the document to source

javax.xml.transform.Source source = new javax.xml.transform.dom.DOMSource(document);

//transform the source to buffer

javax.xml.transform.stream.StreamResult result = new javax.xml.transform.stream.StreamResult(buffer);

transformer.transform(source, result);

Like i said i am new to this so tragic mistakes on my part would not surprise me... but this is tested and working so i must have got something right.

Hi. Well, I now I see it. Sorry, I might need to get new glasses.

Anyway, to be absolutely honest with you, crafting an XML from a String (xmlSource) is not considered elegant. It works, as we see, but it's not elegant. But actually, Strings may give you better readability, if it's a simple XML (and in this case, it really is), and they might be a bit faster (and StringBuffers or StringBuilders may be even faster). So it's really arguable in this case whether or not to go the elegant way or the simple way.

About exception handling: remember, e.printStackTrace() writes to standard output, which is in our case, catalina.out (UCCX runs within the Apache Tomcat container). So if you want to read the exception generated stack trace, you might need to go and browse through that file.

A second note about exception handling: I don't see your script as a whole, but since you return the exception message (return e.getMessage()) in case an Exception is thrown, then you should have a mechanism that checks whether the returned string is an XML or just an error message. But I assume you already have that :-)

Did you already try this in UCCX, as a whole, including sending/receiving SOAP messages?

G.

New Member

Thanks again for the reply. I just got a chance to test this today on UCCX. The web server responds with an Error 400  - Bad request.

the return e.GetMessage is temp for testing.

I am looking into this right now. You wouldnt happen to know where the catalina log is located would you?

Hi,

can you just grab SoapUI and simulate a SOAP request and see the response?

I am afraid you are constructing an invalid XML and this is why the SOAP server sends that error code.

Try reactive debugging of the UCCX script, you'll be able to see the XML (as the value of the SoapRequest variable).

G.

New Member

Hi G,

Sorry for the delay with reply... just got the chance to test this again today.

Grabed soapUI, imported the wsdl and the requests the soapUI generated differed from the ones i was given by the ws developers. I substituted the above xmlSource with the request from soapUI and all works fine... almost all... i am trying to extract the PIN value from the XML Document with Get XML Doc Data but am having some problems with it... I'll figure it out.

Thanks again for all your help.

Hi,

glad to hear that.

Actually, XPath (Get XML Document Data) can be really tricky sometimes, if you get stuck, just post the XML returned by the WS server and we can look at it together.

G.

New Member

Hi,

Still haven't been able to grap the PIN value out...

This is the XML response after the CreateXMLDocument step...

TEXT[<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n<s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\"><s:Header><ActivityId CorrelationId=\"6d77e586-3847-4573-9595-30e7e94eed03\" xmlns=\"http://schemas.microsoft.com/2004/09/ServiceModel/Diagnostics\">69958924-f8a0-4195-8399-6a843c113edd</ActivityId></s:Header><s:Body><CreatePinResponse xmlns=\"http://tempuri.org/\"><CreatePinResult>075869</CreatePinResult></CreatePinResponse></s:Body></s:Envelope>]

After this i run the Get XML Doc Data step with an XPATH expression like

PIN = Get XML DocumentData (soapCreatePinDocument, "XPATH") with:

"/Envelope/Body/CreatePinResponse/CreatePinResult/" OR

"//CreatePinResponse/CreatePinResult/"

but neither of these worked... I tried using the descendant/child params but still the same...

Will try to brush up on XPATH on w3c today and try again with this tommorow.

Kenan.

Hi,

yes, XPath chokes on the namespace (in this case, s).

Can you try this:

//*[local-name()='CreatePinResult']/text()

G.

New Member

Hi.

Thanks again G. I just removed "text()" at the end and it works like a charm.

Where can i FedEx you a beer?

I'm guesing somewhere in Romania by your surname...

Miss. It's Prague, Czech Republic (at this moment).

I really appreciate your offer but since I am in Prague, sending beer here would be like importing cheese to France

Anyway, I am glad it works. Actually, the method text() is appended automatically within a UCCX script, at least I guess, based on this behaviour, but "real", "canonical" XPath expects it. You cannot just write //something because it would return an XML element, not a String.

Happy coding.

G.

New Member

Greg, this is really great manual I really need to satisfy requests at Customers'.

Thanks.

New Member

Hi Gergely,

i tried your code. but i am getting an error.

"unable to parse expression;invalid identifier:java".

I put e set node and tried to set the value through expression editor. but i got the above error.

whether i need to import anything or need to do any other additional configuration to use the above code.

Please help

Hello and thanks for this great document!

I used this and created code to send AXL SOAP requests to Cisco CUCM. Unfortunately CUCM only accepts HTTPS for AXL connections. At the moment the request are not send because of the unknown certificate of the CUCM.

Adding the certificats in the OS Administration doesn't seem to make any difference.

Is there a way to add certificates to the java certificate store on UCCX?

Thanks,

Bernhard

New Member

Hello Bernhard.

I had a similar problem with uccx 8.0, and the SSL Handshake Exception was thrown.

The issue was fixed when I opened a TAC and an engineer imported all of the Web Service's certificates of my customer.

When I used the uccx 8.5 this same issue was not detected. The SSL Handshake between the Customer's WS and UCCX happened automatically.

I hope to have helped you.

Valber Carvalho.

Turned out I hit bug CSCue13884.

Had to raise a tac case to import our root certificate to the java keystore.

It's working now!

New Member

Hi Gergely,

I am following your java code example, but I have some problem, first part (create xml document) adds strange symbols to it, could do you help me ?

CCX version: 8.5.1.11004-25

 

 

Hi, sure. May I ask you to post your question under the Discussions section for better visibility. Thanks. G.

New Member

I know this post has been around for a while, but hoping that someone who has written this into UCCX Script Editor would be willing to post a sample script here for others to review.

New Member

Hi jsteinberg,

 

I had shared with the community an example three years ago.

Go to: https://supportforums.cisco.com/discussion/11279331/how-using-custom-java-class-uccx-5x-soap-example

 

Following this Gergely's steps you will have sucess in your implementation, but you need to be prepared to understand Java Code and Soap 1.x.

It's not easy.

 

Good Luck!

 

Valber Carvalho

 

New Member

Hello!
I do not fully understand the process you've described, if I put the skriptet here you can look at?

New Member

Thanks to everyone for this great post. Thanks to Gergely the original creator of this post as well.

Just want to know if anyone got this working with https and CUCM AXL API

I could not and any help would be greatly appreciated

Firstly I think , I need to use javax.net.ssl.HttpsURLConnection instead of java.net.HttpURLConnection

But I am not sure how to send the username and password

I was able to form the soapRequest string successfully but the soapResponseString is always null

I cannot even see the request hitting the CUCM from AXL logs in CUCM

U"<?xml version=\"1.0\" encoding=\"UTF-8\"?><soapenv:Envelope xmlns:soapenv=\"http://schemas.xmlsoap.org/soap/envelope/\"><soapenv:Header/><soapenv:Body><ns:getLine xmlns:ns=\"http://www.cisco.com/AXL/API/10.5\"><pattern>123456</pattern><routePartitionName>IP-Phones</routePartitionName></ns:getLine></soapenv:Body></soapenv:Envelope>"

{
java.io.ByteArrayOutputStream baos = new java.io.ByteArrayOutputStream();
String content = soapRequest;
int readTimeout = 5000;
try {
java.net.URL url = new java.net.URL(soapServiceEndpoint);
java.net.HttpURLConnection urlCon = (java.net.HttpURLConnection) url.openConnection();
urlCon.setFixedLengthStreamingMode(content.length());
urlCon.setReadTimeout(readTimeout);
urlCon.setDoInput (true);
urlCon.setDoOutput (true);
urlCon.setUseCaches (false);
urlCon.setRequestMethod("POST");
urlCon.setRequestProperty("Content-Type","text/xml;charset=UTF-8");
java.io.DataOutputStream output = new java.io.DataOutputStream(urlCon.getOutputStream());
output.writeBytes(content);
output.flush();
output.close();
java.io.DataInputStream input = new java.io.DataInputStream(urlCon.getInputStream());
int bufSize = 4096;
byte[] bytesRead = new byte[bufSize];
int bytesReadLength = 0;
while(( bytesReadLength = input.read( bytesRead )) > 0 ) {
baos.write(bytesRead,0,bytesReadLength);
}
input.close();
baos.close();
} catch (Exception e) {
e.printStackTrace();
return null;
}
return new String(baos.toByteArray());
}

New Member

Hi sreeraman,

Please, access Cisco DevNet for more information. Login is required.

http://developer.cisco.com/site/axl/learn/how-to/axl-java-sample-application.gsp

Good lucky,

Valber Carvalho

New Member

Hello Gergely,

I founded the post very useful for my activity. Many thanks for it. I would require some advice some, guidance when the SOAP is used with https. In that case there are 2 steps:

- first: upload soap webservice certificate in the UCCX tomcat-trust

- second: is it enough to simply change the url from http to https - I guess I saw your answer in this direction somewhere but I cant find that post right now - or it is necessary to use Https.ssl.URLConnection method?

I was not able to reach second step because I am still working on the certificate side. The UCCX even if it is on 10.5 version does not seem to recognize the uploaded certificate and so the TLS fails with Fatal Alert message - Internal Error - code 80 from IVR side.

I am stuck on this also and any ideea would be very much appreciated.

Kind regards,

Marius

New Member

Hi Marius,

Some years ago I had the same problem than you.
I needed to import some digital certificates to the UCCX v8.5.
To solve it I had to open a TAC and the support engineer imported all certificates using a tool called: CetTool.exe.
It is only possible because TAC has the root user from your Linux/UCCX and they are able to import any files to there.

I am not sure if it is other way to do it.

Best Regards,

Valber Carvalho.

New Member

Hello Valber,

Thank you very much for your advice. I saw that you experienced same issue - on your posts -  but I thought that Cisco solved that bug. You hit that problem in 8.X version and now I am on 10.5.

I use this occasion to ask an advice from your side:

- in order to launch https actions is enough to change the URL from the soap endpoint from http in https or I need to Https.ssl.URLConnection method ?

Kind regards,

Marius

New Member

Hi Marius.

Normally if you change the URL is enough.
Some cases you need to set some properties to the URLConnection object (see setRequestProperty() method below). I think that you can check with Web Services owner what is really necessary to establish your connection.
Doing it you are maniputing the SOAP header, see the sample of code:

{
 String soapResponse= "";
 String soapRequestURL= URLWebServer;
 String soapRequestTXT = docRequestSoap;
 int readTimeout = TimeoutWebServer ;
 try {
 java.net.URL url = new java.net.URL(soapRequestURL);
 java.net.URLConnection conn = url.openConnection();
 conn.setRequestProperty("Content-Type", "txt/xml; charset=utf-8");
 // Set Key and  Value to HTTP header authentication
 conn.setRequestProperty("Authorization","Basic dXJhd3M6a2g1a3ptZXkzcXk2ZXg1czB0MjM=");
 conn.setRequestProperty("SOAPAction", strMetodoSoapAction );
 conn.setRequestProperty("Content-Length", "" + soapRequestTXT.length());
 conn.setDoOutput(true);
 conn.setReadTimeout(readTimeout);
 java.io.OutputStreamWriter wr = new java.io.OutputStreamWriter(conn.getOutputStream());
 wr.write(soapRequestTXT);
 wr.flush();
 java.io.BufferedReader rd = new java.io.BufferedReader(new java.io.InputStreamReader(conn.getInputStream()));
 String line = "";
 while ((line = rd.readLine()) != null) {
 soapResponse += line;
 }
 wr.close();
 rd.close();
 return soapResponse;
 } catch (Exception exception) {
 return "Erro: " + exception;
 //return "ErroWebService";
}
}

Use the Soap UI software (it's free) you can test the connection and see the Soap Requests and Responses are working properly.

New Member

Hello Valber,

I tried in my lab to access cucm axl web service and seems that for uccx 11.x version the https connection worked. The SOAP action did not worked - but I am not sure that I formed the SOAP ok. But I have a point to start. I will dig tomorrow again.

Kind regards for your advice.

Marius