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'm going to be calling a web service that I recently built as part of another blog 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. Simply 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.
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.
1:  <?xml version="1.0"?>  
2:  <project xmlns="" xmlns:xsi="" xsi:schemaLocation="  
3:                            ">  
4:       <artifactId>axis2-webservice-client-sample</artifactId>  
5:       <modelVersion>4.0.0</modelVersion>  
6:       <inceptionYear>2011</inceptionYear>  
7:       <packaging>jar</packaging>  
8:       <groupId></groupId>  
9:       <version>1.0</version>  
10:       <properties>  
11:            <axis2.version>1.6.2</axis2.version>  
12:            <log4j.version>1.2.16</log4j.version>  
13:       </properties>  
14:       <build>  
15:            <resources>  
16:                 <resource>  
17:                      <directory>src/main/resources</directory>  
18:                      <filtering>true</filtering>  
19:                 </resource>  
20:            </resources>  
21:            <plugins>  
22:                 <plugin>  
23:                      <groupId>org.apache.axis2</groupId>  
24:                      <artifactId>axis2-wsdl2code-maven-plugin</artifactId>  
25:                      <version>1.6.2</version>  
26:                      <executions>  
27:                           <execution>  
28:                                <goals>  
29:                                     <goal>wsdl2code</goal>  
30:                                </goals>  
31:                                <configuration>  
32:                                     <wsdlFile>src/main/resources/wsdl/AccountDetailsService.wsdl</wsdlFile>  
33:                                     <databindingName>adb</databindingName>  
34:                                     <packageName></packageName>  
35:                                     <outputDirectory>src/main/java</outputDirectory>  
36:                                     <flattenFiles>true</flattenFiles>  
37:                                </configuration>  
38:                           </execution>  
39:                      </executions>  
40:                 </plugin>  
41:                 <plugin>  
42:                      <groupId>org.apache.maven.plugins</groupId>  
43:                      <artifactId>maven-compiler-plugin</artifactId>  
44:                 </plugin>  
45:            </plugins>  
46:       </build>  
47:       <dependencies>  
48:            <dependency>  
49:                 <groupId>org.apache.axis2</groupId>  
50:                 <artifactId>axis2-kernel</artifactId>  
51:                 <version>${axis2.version}</version>  
52:            </dependency>  
53:            <dependency>  
54:                 <groupId>org.apache.axis2</groupId>  
55:                 <artifactId>axis2-adb</artifactId>  
56:                 <version>${axis2.version}</version>  
57:            </dependency>  
58:            <dependency>  
59:                 <groupId>org.apache.axis2</groupId>  
60:                 <artifactId>axis2-transport-http</artifactId>  
61:                 <version>${axis2.version}</version>  
62:            </dependency>  
63:            <dependency>  
64:                 <groupId>org.apache.axis2</groupId>  
65:                 <artifactId>axis2-transport-local</artifactId>  
66:                 <version>${axis2.version}</version>  
67:            </dependency>  
68:            <dependency>  
69:                 <groupId>org.apache.axis2</groupId>  
70:                 <artifactId>axis2-xmlbeans</artifactId>  
71:                 <version>${axis2.version}</version>  
72:            </dependency>  
73:            <dependency>  
74:                 <groupId>log4j</groupId>  
75:                 <artifactId>log4j</artifactId>  
76:                 <version>${log4j.version}</version>  
77:            </dependency>  
78:       </dependencies>  
79:  </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

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.

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.
1:  package;  
2:  import java.rmi.RemoteException;  
3:  import;  
4:  import;  
5:  import;  
6:  public class SynchronousWebServiceClientTest  
7:  {  
8:       public static void main (String [] args) throws RemoteException  
9:       {  
10:            AccountDetailsServicesStub servicesStub = new AccountDetailsServicesStub(WebServiceCientUtils.SERVICE_ENDPOINT);  
11:            AccountDetailsRequest accountDetailsRequest = new AccountDetailsRequest();  
12:            accountDetailsRequest.setAccountNumber("12345");  
13:            AccountDetailsResponse accountDetailsResponse = servicesStub.accountDetails(accountDetailsRequest);  
14:            WebServiceCientUtils.logAccountDetails(accountDetailsResponse.getAccountDetails());  
15:       }  
16:  }  
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.
1:  package;  
2:  import java.rmi.RemoteException;  
3:  import;  
4:  import;  
5:  public class AsynchronousWebServiceClientTest  
6:  {  
7:       public static void main (String [] args) throws RemoteException, InterruptedException  
8:       {  
9:             AccountDetailsServicesStub servicesStub = new AccountDetailsServicesStub(WebServiceCientUtils.SERVICE_ENDPOINT);  
10:            AccountDetailsRequest accountDetailsRequest = new AccountDetailsRequest();  
11:            accountDetailsRequest.setAccountNumber("12345");  
12:            WebServiceCientCallBackHandler callBackHandler = new WebServiceCientCallBackHandler();  
13:            servicesStub.startaccountDetails(accountDetailsRequest, callBackHandler);  
14:            Thread.sleep(5000);  
15:       }  
16:  }  
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.
1:  package;  
2:  import org.apache.log4j.Logger;  
3:  import;  
4:  import;  
5:  public class WebServiceCientCallBackHandler extends AccountDetailsServicesCallbackHandler  
6:  {  
7:            private static final Logger logger_c = Logger.getLogger(WebServiceCientCallBackHandler.class);  
8:            @Override  
9:            public Object getClientData()  
10:            {  
11:                 return super.getClientData();  
12:            }  
13:            @Override  
14:            public void receiveResultaccountDetails(AccountDetailsResponse result_p)  
15:            {  
16:                 super.receiveResultaccountDetails(result_p);  
17:                 WebServiceCientUtils.logAccountDetails(result_p.getAccountDetails());  
18:            }  
19:            @Override  
20:            public void receiveErroraccountDetails(Exception ex_p)  
21:            {  
22:                 super.receiveErroraccountDetails(ex_p);  
23:                 logger_c.error("An error occurred calling AccountDetails Service", ex_p);  
24:            }  
25:  }  
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.


  1. Thank you! Your tutorials are very helpful :)

    1. Great blogs is very useful for learners.

  2. This comment has been removed by the author.

  3. Hello again,

    I tried your little tutorial and I am getting the following exception

    org.apache.axis2.AxisFault: org.apache.axis2.databinding.ADBException: Unexpected subelement ....
    My ws are deployed on tomcat and are working fine because they were tested using SOAPUI.

    Any ideas, please?


    1. I also tested with TCPMON and the results are ok.

  4. Good stuff. Surprising difficult to get a simple example out of the Axis2 documentation - more about generation and tools than about how to use the actual generated output. Thanks for your write up - right on the money

  5. Good toturial..
    Can you please also provide the contain of AccountDetailsServiceStub,so it can be more clear

  6. Brian ... Stuff is very good. Need to know your source code does not have class which is being reference in Asynchronous and synchronous. One query do i need to add these classes in client project or webservice project.

    1. This comment has been removed by the author.

    2. Yes, I can't find that file, along with the AccountDetailsServicesCallbackHandler (even it is customized, I need the skeleton please)

  7. Well said, the post is really the freshest on this valuable topic. I fit in with your conclusions and will thirstily look forward to your next updates
    host a website | hosting

  8. Hi Brian, many thanks for this extremely useful article, this is really neat!
    Have you sorted your GitHub problem, and if so, can you please give us the URL for your source code?
    Many thanks!

  9. I dont see any response object being returned form the Asynch do we get response pls?

  10. How to use WSDL URL Instead of the WSDL File ?

  11. Have any tried to use the WSDL URL instead of file?

    1. You could navigate to the WSDL URL in a browser and then save the WSDL to a file.
      If you're creating a web service client it might be worth taking a look at a post I published a few days ago The second half of the post describes how you can create a web service client using Apache CXF.

  12. Thanks @Mohan Babu and @Brian I tried using the service URL in xyz?wsdltag and it worked.


Post a Comment

Popular posts from this blog

Spring Web Services Tutorial

REST Endpoint Testing With MockMvc

An Introduction to Wiremock

Spring Batch Tutorial

Externalising Spring Configuration

Docker & Spring Boot

jQuery Tables

Health Checks, Metrics & More with Spring Boot Actuator

Java Concurrency - Multi Threading with ExecutorService