Web services: Create a client with wsimport
6 min read

Web services: Create a client with wsimport

Web services: Create a client with wsimport

Following is a quick tutorial on how to create/use a web service (WS) client with wsimport.

Recently, I have been playing with creating a client to a WS using wsimport. Why wsimport? Because I've hass the WS server (using AXIS) and a bunch of source code generated using AXIS too. For my purpose, this was too complex. Way too complex...

The point of the execrcise is to create a fairly thin client (a wizard to be more exact) using the web services. Therefore, I'm trying to minimise the dependencies. Voila the main reason for not using the provided source code...

Step 1: WSDL

The normal way to get the WSDL content is to connect to the server (of course). Unfortunately, while this works fine in a browser, my setup messes up with the proxy settings and cannot connect from the outside. So, I had to use Firefox and "save the file".

Once I had the file saved locally, I have used wsimport like this:

wsimport.exe -verbose -p ec.estat.edit -target 2.1 \
  -Xnocompile editWS.wsdl

If you don't specify -Xnocompile, then the generated .java files are compiled and the source removed (or at least that's what happened to me). You can worh with the .class files too (it's very nice if you put them in a separate jar, once you know what to do with them).

Step2: Inspection

This step is a bit boring if this is not your first attempt to working with wsimport-based WS, but I'd think it's worth a few minutes of your time even if it's not.

The main point of using wsimport was (and is) that the provided source had (IMO) too many outside dependencies. Therefore, I wanted to make sure that the code generated with wsimport is not as complex. In fairness, it's totally logical that it's not, since wsimport is part of JDK, which means that whatever code is generated will work only with the JDK libraries. Nice.

The number of generated .java files is roughly 2 x number_of_WS + number_of_complex_types. Of course, there are several more "management" classes like the ObjectFactory or the WS container (in my case EditWebServices). Looking through any class, we find only dependencies to normal java libraries and a couple of javax libraries (probably the most interesting is the javax.xml.ws):

package ec.estat.edit.ws;

import java.net.MalformedURLException;
import java.net.URL;
import java.util.logging.Logger;
import javax.xml.namespace.QName;
import javax.xml.ws.Service;
import javax.xml.ws.WebEndpoint;
import javax.xml.ws.WebServiceClient;
import javax.xml.ws.WebServiceFeature;

/**
  * This class was generated by the JAX-WS RI.
  * JAX-WS RI 2.1.6 in JDK 6
  * Generated source version: 2.1
  *
  */
@WebServiceClient(name = "EditWebServices",
    targetNamespace = "http://core.ws.edit.vld",
    wsdlLocation = "http://.../axis2/services/EditWebServices?wsdl")
public class EditWebServices
    extends Service
{

    private final static URL EDITWEBSERVICES_WSDL_LOCATION;
    private final static Logger logger =
        Logger.getLogger(ec.estat.edit.ws.EditWebServices.class.getName());
...

Woo-hoo! We don't have 3rd party dependencies! Nice!

Step3: Hello world

Now that we have the web services .java files, we integrate them in our project. It is obvious that thereare more ways to do that. For example, you can:

  • create aproject for the ws source, with the purpose of producing a jar file and (let's assume we use eclipse here) link that project as a dependency to your "hello world" project.
  • Create a hello world project and dump the source code inthere. The -p ec.estat.edit flag in wsimport placed all generated sources in a specific package, so all you have to do is drag-and-drop the main directory into your source code and refresh the project contents.

Once you have the WS code in place, try to compile. It should go smoothly (at least the generated code...). Now, you can start playing with your web service.

My WS has a simple authentication mechanism (a credentials object passed around). It also has a method to return a list of "programs" available on the server. So, I went on to create the request to get the list and display it...

Some problems

My first attempt of getting the WS example working was an almost failure. I've probably made a mistake to look at the credentials object and build one manually. Ouch! All those JAXBElement items to create, cast and assign... there must be a simpler way! It took me a while (about 5 mins) to figure out why I couldn't find any practical examples of hard JAXBElement assignments... Because it's silly to do something like that! That's why!

Initialise objects

Once you realise you don't have to do manual creation of JAXB objects, everything becomes rather easy. Now you can get the credentials from an object like:

<xs:complexType name="CredentialsType">
  <xs:sequence>
    <xs:element minOccurs="0" name="domain" nillable="true" type="xs:string"/>
    <xs:element minOccurs="0" name="password" nillable="true" type="xs:string"/>
    <xs:element minOccurs="0" name="username" nillable="true" type="xs:string"/>
  </xs:sequence>
</xs:complexType>

and initialise them like:

CredentialsType getCredentials(String user, String pass, String domain)
{
  ObjectFactory of = new ObjectFactory();
  CredentialsType c = of.createCredentialsType();

  c.setUsername(of.createCredentialsTypeUsername(user));
  c.setPassword(of.createCredentialsTypePassword(pass));
  c.setDomain(of.createCredentialsTypeDomain(domain));

  return c;
}

Of course, this doesn't help too much, as you still haven't "talked" to the server yet. But it becomes easier to see where this is going.

Talking to the server

As you've seen so far, this example is based on my WS descriptor. While yours is likely to be different, the principles remain the same.

What I'm trying to do, is to get a list of entities (programs in my case) from the server. One program is described by an object ProgramVOType declared like so:

<xs:complexType name="ProgramVOType">
  <xs:sequence>
    <xs:element minOccurs="0" name="name" nillable="true" type="xs:string"/>
  </xs:sequence>
</xs:complexType>

As you can see, the structure is pretty simple; it only contains the program name. The method (conveniently called retrieveProgramsList() on the server) will return a list of ProgramVOType objects. In order to get them, we need to follow some steps:

  1. Build the request]
  2. Call the method
  3. Process the results

Build the request

In my WS, all methods have "Request" and "Response" objects one has to create or access respectively. My RetrieveProgramsListRequest is basically the object representing the list of parameters for my method. This case is really simple, since the object only contains the user credentials.

<xs:element name="retrieveProgramsListRequest">
  <xs:complexType>
    <xs:sequence>
      <xs:element minOccurs="0" name="credentials" nillable="true"
        type="ax22:CredentialsType"/>
    </xs:sequence>
  </xs:complexType>
...

Therefore, my java code looks like this:

// Object factory (can be put in a singleton)
//
ObjectFactory of = new ObjectFactory();

// Build the "Request" object for the retrieveProgramsList
//
RetrieveProgramsListRequest r = new RetrieveProgramsListRequest();

// set the credentials
//
r.setCredentials(of.createCreateJobRequestCredentials(
  getCredentials("user", "pass", "dom")));

Call the method

Since the web service is SOAP over http, we get its "port":

EditWebServices e = new EditWebServices();
EditWebServicesPortType port = e
  .getEditWebServicesHttpSoap11Endpoint();

Now, we basically have a connection to the server (actually, we have the mechanism which allows us to "talk" to the server). port is our object representing the WS. It contains all the methods from the WSDL, including... retrieveProgramsList. Now, we call the method:

// call the method to get the programs list
//
RetrieveProgramsListResponse reply = port.retrieveProgramsList(r);

Process the results

Now, the reply object contains the reply. The RetrieveProgramsListResponse object looks like:

<xs:element name="retrieveProgramsListResponse">
  <xs:complexType>
    <xs:sequence>
      <xs:element maxOccurs="unbounded" minOccurs="0"
        name="programsList" nillable="true" type="ax22:ProgramVOType"/>
      <xs:element minOccurs="0" name="errorObject" nillable="true"
        type="ax22:ErrorObject"/>
    </xs:sequence>
  </xs:complexType>
</xs:element>

As you can see, it contains both my list, but also an ErrorObject, which expresses an exception/error raised on the server. We can process that object:

if (reply.getErrorObject() != null)
  throw new Exception("Get Program List: " +
    reply.getErrorObject().getValue().getErrorMessage());

If you don't catch this object, it might be difficult to see why the "real" result is null. I've experienced that behaviour when my credentials were incorrect...

Getting the response objects is really easy too:

// Get the programs list
//
List<ProgramVOType> programs = reply.getProgramsList();

If you take into account that the list's elements are ProgramVOType objects, you'll have to do something like:

for (ProgramVOType program : programs) {
  System.out.println(program.getName().getValue());
}

to get the values (just remember that if you want to populate a table or something else with the list).

But you did get the values!

Conclusion

This approach is not too simple and it really depends on your context; if you have a wealth of libraries you're familiar with, like the apache ones, then it might be quicker to push a WS client into production using them. If you have any constraints (e.g. dependencies, resources...), then this approach might be better.

wsimport is a nice tool which is capable of generating the basic framework for WS communication. It's manual work and you might find out that advanced IDEs like eclipse would do something similar for you at a touch of a button. Not to mention that C# Express does the web service thing visually and the resulted source is already part of your project.

HTH,