Axis2 Web Service Client Tutorial

Axis2 is a Java framework that provides comprehensive support for exposing and consuming web services. This short post will look at its SOAP client support and how it can be used to get a simple web service client up and running. For convenience I’ll be calling a web service that I recently built as part of another post.  If you don’t already have a web service to call you can grab the full source code for my sample service from github. Run a Maven build and deploy the WAR to your Servlet container.

What is a Web Service Client?

This post doesn’t attempt to explain the detailed inner workings of a web service client, but its still pretty useful to have an idea of what’s going on under the hood. Most web service clients provide the following

  • A client side proxy for the remote service we want to call, that allows our application to invoke a SOAP service using a simple method call. The proxy insulates our application from the intricacies of sending and receiving SOAP messages.
  • Marshalling of Java objects to XML so that application data can be converted to SOAP payloads for posting to the service endpoint.
  • Un-marshalling of XML back into Java objects so that SOAP payloads returned from the remote service can be interpreted by our application.
  • Establishing and pooling HTTP connections between client and and web service.

WSDL2Java

Axis2 provides WSDL2Java tooling that parses a WSDL to generate the client side proxies required to invoke a remote service. WSDL2Java can be run on the command line but I prefer to use a Maven plugin so that I have a fresh set of proxies generated as part of every build.

Maven Configuration

The Maven POM configuration below shows how the axis2-wsdl2code-maven-plugin can be configured to generate the required client side stubs.

<?xml version="1.0"?>  
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instan
                                    http://maven.apache.org/maven-v4_0_0.xsd">  
     <artifactId>axis2-webservice-client-sample</artifactId>  
     <modelVersion>4.0.0</modelVersion>  
     <inceptionYear>2011</inceptionYear>  
     <packaging>jar</packaging>  
     <groupId>com.blog.webservices.client</groupId>  
     <version>1.0</version>  
      <properties>  
           <axis2.version>1.6.2</axis2.version>  
           <log4j.version>1.2.16</log4j.version>  
      </properties>  
      <build>  
           <resources>  
                <resource>  
                     <directory>src/main/resources</directory>  
                     <filtering>true</filtering>  
                </resource>  
           </resources>  
           <plugins>  
                <plugin>  
                     <groupId>org.apache.axis2</groupId>  
                     <artifactId>axis2-wsdl2code-maven-plugin</artifactId>  
                     <version>1.6.2</version>  
                     <executions>  
                          <execution>  
                               <goals>  
                                    <goal>wsdl2code</goal>  
                               </goals>  
                               <configuration>  
                                    <wsdlFile>src/main/resources/wsdl/AccountDetailsService.wsdl</wsd
                                    <databindingName>adb</databindingName>  
                                    <packageName>com.blog.webservices.client</packageName>  
                                    <outputDirectory>src/main/java</outputDirectory>  
                                    <flattenFiles>true</flattenFiles>  
                               </configuration>  
                          </execution>  
                     </executions>  
                </plugin>  
                <plugin>  
                     <groupId>org.apache.maven.plugins</groupId>  
                     <artifactId>maven-compiler-plugin</artifactId>  
                </plugin>  
           </plugins>  
      </build>  
      <dependencies>  
           <dependency>  
                <groupId>org.apache.axis2</groupId>  
                <artifactId>axis2-kernel</artifactId>  
                <version>${axis2.version}</version>  
           </dependency>  
           <dependency>  
                <groupId>org.apache.axis2</groupId>  
                <artifactId>axis2-adb</artifactId>  
                <version>${axis2.version}</version>  
           </dependency>  
           <dependency>  
                <groupId>org.apache.axis2</groupId>  
                <artifactId>axis2-transport-http</artifactId>  
                <version>${axis2.version}</version>  
           </dependency>  
           <dependency>  
                <groupId>org.apache.axis2</groupId>  
                <artifactId>axis2-transport-local</artifactId>  
                <version>${axis2.version}</version>  
           </dependency>  
           <dependency>  
                <groupId>org.apache.axis2</groupId>  
                <artifactId>axis2-xmlbeans</artifactId>  
                <version>${axis2.version}</version>  
           </dependency>  
           <dependency>  
                <groupId>log4j</groupId>  
                <artifactId>log4j</artifactId>  
                <version>${log4j.version}</version>  
           </dependency>  
      </dependencies>  
 </project>

The POM configuration shown here is pretty simple. The interesting bit is the WSDL2Code configuration plugin between lines 22 and 40. The plugin is configured with the following information

  • Line 32– Location of WSDL we’re going to use for code generation.
  • Line 33 – Data binding framework we’re going to use for code generation. In this example I’ve used the standard ADB (Axis Data Binding) framework but we could easily plugin an equivalent framework like JAXB or XMLBeans.
  • Line 34 – Package name for the generated classes.
  • Line 35 – Output directory for generated classes.

Code Generation

Now that we have the POM configured the next step is to run a build and generate our classes. When we run ‘mvn clean install’ the WSDL2Code plugin will read the WSDL and invoke the Axis code generator to build a client side proxy for our service. When the build is complete our project structure should look similar to figure 1.0. You’ll notice that 2 classes were generated, both of which are explained below.

Figure 1.0 Project Structure

AccountDetailsServiceCallBackHandler

This is an abstract class that can be extend to implement a non blocking web service client. A non blocking client invokes the remote service on a separate thread and returns immediately, so as not to ‘block’ the client application while Axis waits on a response. The AccountDetailsServiceCallbackHandler abstract class should be extended to provide implementations for 2 callback methods, that are invoked by Axis once it has received a response from the service.
This non blocking approach is very useful in certain circumstances, for example, when a client application needs to call a number of different services at the same time. Rather than call each service sequentially, the client application can call multiple services simultaneously (on different threads) and handle the response from each service as it arrives, using appropriate callback implementations.

AccountDetailsServiceStub

This class acts as a client side proxy for the remote service and provides a means of building requests, invoking the service and processing responses.

Calling the Service Synchronously

The code sample below shows how we can use the generated classes to call our service synchronously. Note that the main thread will block while it waits on a response from the service.

package com.blog.samples.webservices.client;  
import java.rmi.RemoteException;  
import com.blog.webservices.client.AccountDetailsServicesStub;  
import com.blog.webservices.client.AccountDetailsServicesStub.AccountDetailsRequest;  
import com.blog.webservices.client.AccountDetailsServicesStub.AccountDetailsResponse;  

public class SynchronousWebServiceClientTest  
{  
     public static void main (String [] args) throws RemoteException  
     {  
           AccountDetailsServicesStub servicesStub = new AccountDetailsServicesStub(WebServiceCientUtils.SERVICE_ENDPOINT); 
           AccountDetailsRequest accountDetailsRequest = new AccountDetailsRequest();  
           accountDetailsRequest.setAccountNumber("12345");  
           AccountDetailsResponse accountDetailsResponse = servicesStub.accountDetails(accountDetailsRequest);  
           WebServiceCientUtils.logAccountDetails(accountDetailsResponse.getAccountDetails());  
      }  
 }

Calling the Service Asynchronously

The code sample below shows how we can call our service asynchronously. Invoking ‘startService’ on the service stub kicks off a web service request on a new thread and returns immediately so that execution of the client application is not blocked. We pass in a new instance of our callback handler to handle the web service response.

package com.blog.samples.webservices.client;  
import java.rmi.RemoteException;  
import com.blog.webservices.client.AccountDetailsServicesStub;  
import com.blog.webservices.client.AccountDetailsServicesStub.AccountDetailsRequest;  
public class AsynchronousWebServiceClientTest  
{  
     public static void main (String [] args) throws RemoteException, InterruptedException  
     {  
           AccountDetailsServicesStub servicesStub = new AccountDetailsServicesStub(WebServiceCientUtils.SERVICE_ENDPOINT);
           AccountDetailsRequest accountDetailsRequest = new AccountDetailsRequest();  
           accountDetailsRequest.setAccountNumber("12345");  
           WebServiceCientCallBackHandler callBackHandler = new WebServiceCientCallBackHandler();  
           servicesStub.startaccountDetails(accountDetailsRequest, callBackHandler);  
           Thread.sleep(5000);  
      }  
 } 

A sample callback handler implementation is shown below. Note that we implement the receiveResultAccountDetails method to handle successful responses and receiveErrorAccountDetails to handle errors. Axis2 will invoke the appropriate method depending on whether or not the web service call was successful.

package com.blog.samples.webservices.client;  
import org.apache.log4j.Logger;  
import com.blog.webservices.client.AccountDetailsServicesCallbackHandler;  
import com.blog.webservices.client.AccountDetailsServicesStub.AccountDetailsResponse;  
public class WebServiceCientCallBackHandler extends AccountDetailsServicesCallbackHandler  
{  
          private static final Logger logger_c = Logger.getLogger(WebServiceCientCallBackHandler.class);
          @Override  
          public Object getClientData()  
           {  
                return super.getClientData();  
           }  
           @Override  
           public void receiveResultaccountDetails(AccountDetailsResponse result_p)  
           {  
                super.receiveResultaccountDetails(result_p);  
                WebServiceCientUtils.logAccountDetails(result_p.getAccountDetails());  
           }  
           @Override  
           public void receiveErroraccountDetails(Exception ex_p)  
           {  
                super.receiveErroraccountDetails(ex_p);  
                logger_c.error("An error occurred calling AccountDetails Service", ex_p);  
           }  
 }  

Source Code

You can download the full source code for this tutorial here. I’m having an issue with my GitHub account at the minute but as soon as I get that sorted I’ll push the code up and add a link. If you liked the post or have questions about any of the material covered, feel free to leave a comment below.