cancel
Showing results for 
Search instead for 
Did you mean: 
cancel
Announcements

UCCX OOP SOAP client with 1 custom JAR (using JAX-WS)

7437
Views
10
Helpful
3
Comments

UPDATE - 2013-11-29: For some reason, the screenshots are not visible, even though when I switch to edit mode, they're there. I cannot help it, it must be something wrong with the website engine.

Due to popular demand, I am attaching the PDF version of this document, where all the screenshots are visible.

Problem: one of the most requested features for a Cisco Unified Contact Center Express (UCCX) script is to have an easy Web Services (WS) client (also known as SOAP client) implementation.

Some use various frameworks, like Apache Axis to achieve this; even though from the administration perspective this might not be a good solution, since both the framework libraries and the client code must be uploaded on to the UCCX platform. One should carefully watch the framework version and dependencies, making UCCX administration more complex.

Analysis: a rather extreme approach is not to use any custom libraries and classes, but to insert Java code directly into the script that crafts XML and communicates with the Web Services Server. This is not an elegant solution at all, since it requires detailed information about the internals (like the XML structure) of the web service itself, which was supposed to be hidden by the Web Services Object Oriented Programming (OOP) style.

Solution: the Java Development Kit (JDK) version 1.6 already contains the necessary libraries and tools to set up a WS client. In other words, all UCCX versions that are built on JDK 1.6 are perfectly capable of creating a lightweight OOP WS client, using the JAX-WS (aka "Metro") project libraries, which are built into the JDK itself.

This step by step guide has been created to show an example of a WS client implementation in UCCX 8.5.

Please note:

  • This is a "contract first" approach, assuming the WSDL is available (both while project compilation and runtime);
  • JDK 1.6 is installed (avoid JDK 1.7, which is the latest JDK version in order to eliminate some compatiblity issues with UCCX);
  • the Eclipse Integrated development environment (IDE) is used in this guide, however, any other Java-capable IDE should work.

This example uses a Web Service that is available at xmethods.net, namely RestFulServices's Currency Convertor.

1. First, get the WSDL URL from the web page mentioned above:

image2012-9-14 10:48:17.png

As we can see, no detailed Description or Usage Notes. No problem, we will figure it out.

2. Using the command line, generate the necessary files using the JDK wsimport tool.

Notice this guide has been written using a computer using Ubuntu Linux, however, the JDK on Windows or Mac does have the same libraries and tools, so don't worry.

But first, let's just create a new directory for the files: in this example, currency_converter.

Then issue the wsimport command with the following switches: -verbose (to see detailed messages), -keep (to keep Java source files, just in case we wanted to dig deeper) and finally, the WSDL URL itself.

image2012-9-14 10:53:52.png

After hitting Enter, wsimport prints out status messages.

image2012-9-14 10:55:13.png

If it ended with "Compiling code..." and without any error messages, then all the necessary artefacts have been created.

Now let's just stop for a moment to see, what we have done so far.

Actually, these "artefacts" are simple Java class files, representing the objects necessary to build a WS client that conforms to the specification in the WSDL which serves as a contract.

Please note, we are about to write a program in Java, even though the web server is built in using a .NET language. This is one of the strengths of the Web Services approach: object oriented programming, using a transparent communication method (XML, HTTP) where the client and the server may be built on a completely different platform.

Actually, this is all. The only thing you have to do is to upload these classes to the UCCX, do the necessary reloads and you can use these artefacts right away in your scripts.

In order to achieve better understanding, it's strongly recommended to test the WS functionality in a simple Java program.

Why we need an IDE: for its content assist technology. Remember, at this point we don't have any clues what methods are available and what methods should be use.

3. Start up Eclipse and create a new Java project, for instance, CurrencyConverter4UCCX:

image2012-9-14 16:1:41.png

Then add the artefacts we created with the wsimport tool to the project so our little Java program can use them. Right click the project name, choose Properties; then Java Build Path, Libraries tab. Click the Add External Class Folder... button.

image2012-9-14 16:5:0.png

We just add the currency_converter directory we created a while ago; Eclipse is intelligent enough to include all the subdirectories and files there.

In the Package Explorer tab, if you expand the Referenced Libraries, you should see this hierarchy:

image2012-9-14 16:7:6.png

Don't worry about their contents yet, we will get to that later.

4. Create a new Java class, in a new package. In this example, I create a new Java class called Main, in the sk.concentra package. This means the qualified name of the class is sk.concentra.Main. Think of the term "qualified name" as the "fully qualified domain name" (FQDN), or the full name of a person. You might have noticed if you insert custom Java code in a UCCX script, you always use qualified names for types.

How to create a new class: In Package Explorer, right click CurrencyConverter4UCCX. New > Class. Fill  in the  Package (for instance: sk.concentra) and Name Fields (for  instance: Main). Check the Which method stubs would you like to create?  "public static void main(String[] args)", like this:

image2012-9-14 16:11:40.png

And, as we can see, a new Java class skeleton has been created for us:

image2012-9-14 16:13:3.png

5. Now expand all the packages in Referenced Libraries (in the Package Explorer tab) and look for a pair of classes that are named I..Service and ...Service where ... is... well, something. In this case, we can clearly see one pair: CurrencyService and ICurrencyService:

image2012-9-14 16:23:53.png

Now, take a deep breath and write their qualified names into Main.java's main method. Actually, let's start right away with this:

net.restfulwebservices.servicecontracts._2008._01.ICurrencyService iCurrencyService

        =  (new net.restfulwebservices.servicecontracts._2008._01.CurrencyService()).getBasicHttpBindingICurrencyService();

image2012-9-14 16:37:10.png

You just created a new variable, named iCurrencyService, of type net.restfulwebservices.servicecontracts._2008._01.ICurrencyService.

Its value is based on a new instance of the class net.restfulwebservices.servicecontracts._2008._01.CurrencyService; and you called getBasicHttpBindingICurrencyService() method on this new instance, which actually returns a variable, of type ICurrencyService.

Now, I owe you some explanation again.

CurrencyService is the implementation ("It works like this").

ICurrencyService is an interface ("It should work like this").

CurrencyService actually implements ICurrencyService, with all the nuts and bolts. ICurrencyService is just a contract that contains how the web service should be implemented.

So, when we create a new instance of the implementation, we already have a working implementation of that web service. But it's not what we want, a full implementation that we can't control. So we use its method (in this case, getBasicHttpBindingICurrencyService()), to get the contract, so we can implement our own WS client.

It's kind of against the logic, I know, but it's the way we go. A rule of a thumb: use the content assist feature of Eclipse (or your favorite IDE) to see, what methods are available on the implementation that return the type of the interface. Chances are that the method name would contain the expressions "get", "http" or even "port".

6. So we have the iCurrencyService, which is again, the "contract". It contains the methods available on the WS server.

At this moment, we will use the Content Assist feature of Eclipse. Write down the word iCurrencyService, then dot (.) and wait for the Content Assist window to appear. If it doesn't, make it appear (in Eclipse, Ctrl-Space).

As we can see, there's only one method, getConversionRate (your WS, of course, may contain more methods). By pressing Enter, Content Assist will insert this method, with the required variables.

At this point, our Java program looks like this:

image2012-9-14 16:41:37.png

That's right, it's underlined red because of an error. It wants those parameters badly. And of course, they should exist before we actually  call this method. Make some room before this row. Now  just double click that fromCurrency so it gets selected and hit Ctrl-1  (Quick fix). Let's see what quick fix is available.

Create a local variable: that's what we are looking for.

Let's do this both for fromCurrency and toCurrency.

You should end up with something like this:

image2012-9-14 16:46:35.png

Also, don't forget the fact that all Java commands should end with a semicolon ( ; ).

Hmm, it's underlined again. What is it this time? Let's just double click the whole thing and again, press Ctrl-1 for quick Fix.

This time, let's use that little lightbulb with a red and white cross to see what's wrong and what should be fixed:

It'll tell you an exception should be caught. So let's do that: surround with try and catch. Now it looks like this:

image2012-9-14 16:50:32.png

And it still complains about what? fromCurrency and toCurrency are not initialized.

Let's just do this for now: assign null to them.

CurrencyCode fromCurrency = null;

CurrencyCode toCurrency = null;

This clears the error, but does not satisfy our needs. We don't want to send nulls. We want to send something useful.

Let's explore a few things.

Both fromCurrency and toCurrency are of type CurrencyCode. Can this CurrencyCode be instantiated?

Let's see:

no-instantiate.png

No.

Well, are there any fields we can use? Let's see:

enum-fields.png

Yes, it does. (Actually, CurrencyCode is an enum):

Let's do the  same thing both for fromCurrency and toCurrency. For instance, the  fromCurrency be EUR (Euros) and the toCurrency USD (US Dollars):

        CurrencyCode fromCurrency = CurrencyCode.EUR;

        CurrencyCode toCurrency = CurrencyCode.USD;

One more thing: the getConversionRate method is called, but not returning anything. Let's fix that.

Let's just doubleclick and select getConversionRate method, hit Ctrl-1 for Quick Fix, and use "Assign to new local variable").

image2012-9-14 17:7:2.png

Alright, now just make it write out that conversionRate, right?

Make  some room after that Currency conversionRate etc. row, and write down the variable conversionRate. Again, wait for the content assist. If it  doesn't appear, make it appear using Ctrl-Space.

That getRate() method looks promising. It even tells you its type:  Double. Let's just make it complete by assigning the method result to a  Double type variable then:

image2012-9-14 17:11:38.png

Let's just use a simple System.out.println to see what we got:

System.out.println("And the EUR->USD rate is: " + rate);

Let's clean things up a bit, so we have a nicely formatted code.

image2012-9-14 17:20:33.png

Of course, don't forget to save it.

Now, time for some serious work for our little program.

Run it: click that Main.java file and then click that Play button.

Now wait... and watch the console tab.

image2012-9-14 17:23:3.png

If you can see this, grab a beer or a bottle of wine or a glass of milk or <your preferred drink> to celebrate with.

There are still two things remaining if we want to have the same functionality in UCCX:

  • package the classes we need into one jar,
  • types should be fully qualified so we can use them in UCCX.

Let's start with the more difficult stuff:

Select each type in the main method, that has been imported. Click "Copy Qualified name". And then just press Ctrl-V to replace it.

You should end up with something like this.

image2012-9-14 17:29:36.png

Imports are unused, so let's just delete them. Finally, this is what you should see:

package sk.concentra;

public class Main {

    public static void main(String[] args) {

        net.restfulwebservices.servicecontracts._2008._01.ICurrencyService iCurrencyService

        =  (new net.restfulwebservices.servicecontracts._2008._01.CurrencyService()).getBasicHttpBindingICurrencyService();

         net.restfulwebservices.datacontracts._2008._01.CurrencyCode  fromCurrency =  net.restfulwebservices.datacontracts._2008._01.CurrencyCode.EUR;

         net.restfulwebservices.datacontracts._2008._01.CurrencyCode  toCurrency =  net.restfulwebservices.datacontracts._2008._01.CurrencyCode.USD;

        try {

             net.restfulwebservices.datacontracts._2008._01.Currency  conversionRate = iCurrencyService.getConversionRate(fromCurrency,  toCurrency);

            Double rate = conversionRate.getRate();

            System.out.println("And the EUR->USD rate is: " + rate);

         } catch  (net.restfulwebservices.servicecontracts._2008._01.ICurrencyServiceGetConversionRateDefaultFaultContractFaultFaultMessage  e) {

            // TODO Auto-generated catch block

            e.printStackTrace();

        }   

    }

}

Or, if you prefer a screenshot:

image2012-9-14 17:31:23.png

You will actually need this code in your UCCX script.

7. Package the artefacts into one JAR file. I prefer to do this using the command line:

image2012-9-16 22:0:21.png

This means, in the directory we created earlier, that holds the necessary artefacts, we create a JAR file named CurrencyConverter4UCCX.jar:

jar cvf CurrencyConverter4UCCX.jar .

(That last dot represents the working directory.)

Voila, we've got our JAR file, named CurrencyConverter4UCCX.jar  holding all the required helper classes to run the same program we tried  a while ago in Eclipse.

8. So let's upload this JAR in UCCX. Using the Application Administration web, go to System > Custom File Configuration and then click Upload Custom Jar Files, then Upload Documents (Or, upload Documents, default language, classpath folder). Upload the CurrencyConverter4UCCX.jar file we created.

Then go back to the Custom Classes configuration page and make sure the JAR file is "Selected Classpath Entry":

image2012-9-16 20:44:42.png

You must restart the CCX Engine. The quickest way is to ssh into the UCCX and issue the utils service <servicename> command, like this:

image2012-9-16 20:54:22.png

Alternatively, log into the Unified CCX Serviceability page and restart the engine from there:

image2012-9-16 20:48:20.png

9. Let's create a script to test the WS functionality. One variable, of type double, will be needed. Name it conversionRate.

Then insert a Set step, where the return value would be the conversionRate variable, and the value is the following code block:

{

net.restfulwebservices.servicecontracts._2008._01.ICurrencyService iCurrencyService

        =  (new net.restfulwebservices.servicecontracts._2008._01.CurrencyService()).getBasicHttpBindingICurrencyService();

         net.restfulwebservices.datacontracts._2008._01.CurrencyCode  fromCurrency =  net.restfulwebservices.datacontracts._2008._01.CurrencyCode.EUR;

         net.restfulwebservices.datacontracts._2008._01.CurrencyCode  toCurrency =  net.restfulwebservices.datacontracts._2008._01.CurrencyCode.USD;

        try {

             net.restfulwebservices.datacontracts._2008._01.Currency  conversionRate = iCurrencyService.getConversionRate(fromCurrency,  toCurrency);

            Double rate = conversionRate.getRate();

            //System.out.println("And the EUR->USD rate is: " + rate);

            return rate;

         } catch  (net.restfulwebservices.servicecontracts._2008._01.ICurrencyServiceGetConversionRateDefaultFaultContractFaultFaultMessage  e) {

            // TODO Auto-generated catch block

            // e.printStackTrace();

            return -1.0D;

        }

}

Remember, this was the program we tried before in Eclipse, with some modifications. For instance, if an exception is throw, we return -1 (or, more precisely, -1.0D) so the rest of the script knows there was an exception.

Take a deep breath and try to do a "Step Over" type debugging. If you see a non-negative value assigned to the conversionRate variable, congratulations, go out and celebrate.

image2012-9-16 22:10:36.png

If you get stuck or run into a problem:

  • if it does not work in Eclipse, it won't work in UCCX, either;
  • it's really necessary to reload the CCX engine;
  • it takes a while to contact the WS server, after all, there's a lot going on behind the scenes;
  • if you see an error message complaining about the class file version: remember, JDK 1.6 is supported (you probably have a different version).

Happy coding!

G.

Comments
Beginner

Hi Gergely,

Thank you for this post.  I'm trying to follow along, but the images are missing.  Would it be possible to rectify the issue?

Thanks!

Advocate

Hi Michael,

must be something wrong with this forum engine. When I switch to edit mode, I can see the images.

Anyway, I will try to re-upload them to here again tomorrow.

G.

Advocate

Michael,

I am afraid I can't fix it by reposting the images - I can't find them anywhere.

However, I am ready to help you with this, can you please contact me privately and give me some details about your project. Thanks.

G.

CreatePlease to create content
Content for Community-Ad
August's Community Spotlight Awards