Develop Web services with Axis2, Part 1: Deploy and consume simple Web services using the Axis2 runtime

Get an introduction to the new architecture of Axis2 and learn how to deploy and consume Web services using Axis2. This is the first installment of a two-part series about developing Web services using the Axis2 runtime. Axis2 is the next generation of Apache Axis Simple Object Access Protocol (SOAP) runtime.

Gopalakrishnan U (ugopalak@in.ibm.com), Software Engineer, IBM India Software Labs

Gopalakrishnan U is currently working with the Research Group of IBM India Software Labs. He has been working with various research and development projects in Web Services technologies for last 3 years and had made contributions to IBM Emerging Technologies Tool Kit (ETTK). He has co-authored an article on Web services security in developerWorks before. You can contact Gopalakrishnan at ugopalak@in.ibm.com.



Shreevidya Rao (shreerao@in.ibm.com), Software Engineer, IBM 

Shreevidya Rao is a Software Engineer with the Research Group of IBM Software Labs. She has been with IBM for the past year and has five years of Java and J2EE experience . She is currently working on developing a distributed manageable logger. You can contact her at shreerao@in.ibm.com.



04 November 2005

Introduction

Axis2 is the next generation of Apache Axis. Axis2 is inspired by the Axis 1.x handler model, but it's based on a much more flexible and extensible new architecture. Axis2 is completely rewritten based on the new architecture and has no common code with Axis 1.x. The motivation behind Axis2 is the search for a more modular, flexible, and efficient architecture that can easily plug in implementation of other associated Web services standards and protocols like WS-Security, WS-ReliableMessaging, and so forth.

Features of Axis2 include:

  • A new core XML processing model called AXIOM (AXIs Object Model)
  • Support for In-Only and In-Out message exchange patterns (MEP)
  • A blocking and non-blocking client API (application programming interface)
  • Built-in WS-Addressing support
  • XMLBeans data binding support
  • A new deployment model
  • Support for HTTP (Hypertext Transfer Protocol), SMTP (Simple Mail Transfer Protocol), and TCP (Transmission Control Protocol) transports

This series is based on the Axis2 0.92 release. You can get the latest version of Axis2 from the Apache Web site.


Axis architecture overview

Figure 1. Axis2 architecture
Axis2 architecture

The Axis2 architecture separates logic and state; this allows the logic to execute in parallel threads. The static and dynamic state of the service and invocation are stored in Description and Context classes, respectively. The Axis2 architecture is implemented using seven independent modules:

  1. Information model: This module manages the state of the SOAP engine. This model defines a collection of classes for holding the state, and the engine manages the life cycle of these information objects. The Information model has two types of classes for holding the state. The Description classes hold data that is static in nature and exists throughout the life of the Axis engine instance, such as configuration of transports, services, and operations. Context classes hold dynamic information of the service and operation that are valid on the context of an invocation, such as the current request and response SOAP message, From address, To address, and other elements.
  2. XML processing model: Axis2 introduces a new model called AXIOM for processing SOAP messages. AXIOM uses StAX (Streaming API for XML) to parse the XML. StAX is a standard streaming pull parser Java™ API. AXIOM is very lightweight and does deferred building of the XML infoset -- in other words, an object is created only when it is absolutely necessary. As a whole, AXIOM and Axis2 have a small memory footprint compared to Axis 1.
  3. SOAP processing model: Axis2 architecture defines two pipes (or Flows) called InPipe (InFlow) and OutPipe (OutFlow) for handling request messages and response messages on the server side. On the client side, the pipes are inverted -- in other words, the SOAP request message flows through the OutPipe and the response message flows through the InPipe. A pipe or flow contains a series of handlers grouped into phases. The phases are executed in a predefined order, as shown in the Figure 1 above. In addition to the set of predefined phases and handlers, users can configure user phases and associated handlers at the operation, service, or global level. Handlers act as interceptors of the SOAP message and can process the header or body of the SOAP message. The InPipe is configured with the following phases:
    • TransportIn
    • PreDispatch
    • Dispatch
    • PostDispatch
    • PolicyDetermination
    • User phases
    • Message validation
    We will discuss the details of each of these phases in Part 2 of this series. Passing through all the phases configured in the Inpipe, the request message reaches the MessageReceiver, which then invokes the actual service implementation. The OutPipe of the server has the following phases:
    • Message initialization
    • Policy determination
    • User phases
    • MessageOut
    The user-configured phase comes in the User phases section of both these pipes. When a fault occurs during the execution of these pipes, the faults goes through InFaultPipe or OutFaultPipe pipes. InFaultPipe is invoked on the client side when the Fault message is received; the OutFaultPipe is invoked on the server side when an invocation causes a fault to be sent to the client. The user can add handlers to the predefined phases and configure in what order the handlers should be run.
  4. Deployment module: This module configures the Axis engine and deploys the services and modules. axis2.xml (found in webapps/axis2/WEB-INF) contains the global configuration of the Axis2 engine, including:
    • Global modules
    • Global receivers
    • Transports
    • User phase definitions
    Configuration of each service is contained in a services.xml file in the service archive. We will discuss this file in more detail later in the article.
  5. WSDL and code generation: This module takes care of generating client stub and server skeleton code from the WSDL file. The Axis2 code generator emits XML files that are applied with the correct XML style sheets to generate code in the needed language.
  6. Client API: The Axis2 client API invokes operations following In-Only and In-Out message patterns defined by WSDL 2.0. The client API supports both blocking and non-blocking invocation of In-Out operations.
  7. Transports: This module contains handlers that interact with the transport layer. There are two types of transport handlers, TransportListener and TransportSender. The TransportListener receives the SOAP message from the transport layer and passes it to the InPipe for processing. A TransportSender sends the SOAP message received from the OutPipe over the specified transport. Axis2 provides handlers for HTTP, SMTP, and TCP. For HTTP transport, the AxisServlet on the server side and a simple standalone HTTP server (provided by Axis2) on the client side work as the TransportReceiver.

Deploying Axis2

Deploying Axis2 is as simple as Axis 1. First, find the Axis2 Web application, axis2.war, in the webapps dir of Axis2 binary distribution. Deploy this war file in a servlet container. In Tomcat when unpackWARs is set to true in the server configuration, just copying the axis2.war to $TOMCAT_HOME/webapps dir is enough to deploy Axis2. Now start Tomcat and access http://localhost:<port>/axis2. This will show the Axis2 welcome page, click on the 'Validate' Link on this page. You should get 'Axis2 Happiness page' without any errors.


Developing a StockQuoteService

Here you will see how to develop a StockQuoteService with two operations, an In-Only subscribe() operation and an In-Out getQuote() operation. The subscribe() operation will subscribe for hourly quotes for the given symbol and getQuote() will get the current quote for the given symbol.

Listing 1 below is a sample implementation of the StockQuoteService:

Listing 1. StockQuoteService implementation
package stock;

import org.apache.axis2.om.OMAbstractFactory;

import org.apache.axis2.om.OMElement;

import org.apache.axis2.om.OMFactory;

import org.apache.axis2.om.OMNamespace;

		

public class StockQuoteService {

		 

  public void subscribe(OMElement in){

    String symbol = in.getText();

    System.out.println("Subscription request for symbol ="+symbol);

    // put the actual subscribe code here...    

  }

		  

		  

  public OMElement getQuote(OMElement in){

		  

    // Get the symbol from request message

    String symbol = in.getText();

		    

    int quote = 0;

    if(symbol.equals("IBM")){

      quote = 100;

    }

    // Put more quotes here ...

		    

    // Create response

    OMFactory fac = OMAbstractFactory.getOMFactory();

    OMNamespace omNs = fac.createOMNamespace(

      "http://www.developerworks.com/example", "example");

    OMElement resp = fac.createOMElement("getQuoteResponse", omNs);

    resp.setText(String.valueOf(quote));

    return resp;      

  }

}

Wondering about the method signatures? We will talk about the OMElement in the methods indicated above in just a moment.


Deploying the service

Deployment descriptor

In Axis2, service deployment information is contained in the services.xml file (in versions older than 0.92, the file is named service.xml). For the above StockQuoteService, the service deployment descriptor will look like Listing 2 below.

Listing 2. Services.xml
<service name="StockQuoteService">

  <parameter name="ServiceClass" locked="xsd:false">

    stock.StockQuoteService

  </parameter>

		

  <operation name="getQuote">

    <messageReceiver 

      class="org.apache.axis2.receivers.RawXMLINOutMessageReceiver"/>

  </operation>

		

  <operation name="subscribe">

    <messageReceiver 

      class="org.apache.axis2.receivers.RawXMLINOnlyMessageReceiver"/>

  </operation>

</service>

The name attribute of the service defines the name of the service. Axis2 uses the name of the service to create the endpoint address of the service as http://localhost:<port>/axis2/services/<nameofservice>. So for the StockQuoteService, the service endpoint will be http://localhost:<port>/axis2/services/StockQuoteService. The ServiceClass parameter specifies the service implementation class.

Each <operation> element defines the configuration of an operation in the service. The name attribute of the <operation> should be set to the name of the method in the service implementation class. The messageReceiver element defines the message receiver to be used for handling this operation. Axis2 provides two built-in MessageReceivers for In-Only and In-Out operations without data binding; org.apache.axis2.receivers.RawXMLINOnlyMessageReceiver for the In-Only operation and org.apache.axis2.receivers.RawXMLINOutMessageReceiver for the In-Out operation. When no messageReceiver is specified, Axis2 will try to use org.apache.axis2.receivers.RawXMLINOutMessageReceiver as the default. The RAWXML message receivers mentioned above pass the content of the <Body> of the incoming SOAP message to the service implementation as an OMElement (OMElement is the AXIOM abstraction of an XML element). The operation should return the XML content that goes in the <Body> element of the SOAP response as an OMElement. This explains why subscribe() and getQuote() operations take and return OMElement.

The services.xml can also contain multiple services grouped as a servicegroup.

Packaging

Axis 2 services are packaged as Axis Archive (.aar). This is a JAR file (created using jar or zip utilities) with the services.xml file packaged in META-INF dir of the archive. The StockQuoteService when packaged as StockQuoteService.aar will have the following structures:

./stock/StockQuoteService.class
./META-INF/services.xml

A prepackaged StockQuoteService archive can be found in the Download section of this article.

Deploying

Deployment of the service in Axis2 is quite simple; just copy the .aar file to the axis2/WEB-INF/services directory in the axis2 Web application in your servlet container. In case of Tomcat, it will be $TOMCAT_HOME/webapps/axis2/WEB-INF/services.

Another cool way of deploying the service is using the 'Upload Service' tool in the Axis2 administration console. Go to the http://localhost:<port>/axis2 and select the 'Administration' link. Enter admin/axis2 as user name and password and log in. (You can configure the username/password in axis2.xml.) Select the 'Upload Service' link in the tool section, select the .aar file, and click Upload. That's it! You will get a green success message when uploaded successfully. The service is deployed and ready to be invoked. This feature is very handy when you want to deploy the service in a remote Axis2 server.


Consuming Web Services using Axis2

The nature of Web service invocation is decided by the MEP, the transport protocol, and the synchronous and / or asynchronous behavior of the client API. Axis2 currently supports In-Only and In-Out MEPs defined by WSDL 2.0. The Axis2 client API supports synchronous and asynchronous invocation of services. Asynchronous behavior is provided at the API level and transport level when invoking In-Out operations. API level asynchrony is achieved by callback and uses a single transport connection for transferring both request and response (for example, request and response transferred over a single HTTP connection). In transport level asynchrony, separate transport connections are used for sending the request and receiving the response -- for example, when you use SMTP for transport.

Given below are the details of invoking the In-Only and In-Out operations using the Axis2 client API.

Invoking In-Only operation

The org.apache.axis2.clientapi.MessageSender class is used to invoke an In-Only operation, as shown in Listing 3 below, that invokes the subscribe() operation of the StockQuoteService.

Listing 3. Invoking In-Only operation
try{

  EndpointReference targetEPR = new EndpointReference(

      "http://localhost:8080/axis2/services/StockQuoteService");

          

  // Make the request message

  OMFactory fac = OMAbstractFactory.getOMFactory();

  OMNamespace omNs = fac.createOMNamespace(

      "http://www.developerworks.com/example", "example");

  OMElement payload = fac.createOMElement("subscribe", omNs);

  payload.setText("IBM"); 

          

  // Send the request

  MessageSender msgSender = new MessageSender();

  msgSender.setTo(targetEPR);

  msgSender.setSenderTransport(Constants.TRANSPORT_HTTP);

  msgSender.send("subscribe", payload);         

  }catch (AxisFault axisFault) {

      axisFault.printStackTrace();

  }

MessageSender.send() sends the request message and returns it immediately. The transport to be used is specified by MessageSender.setSenderTransport(). This example sends the message over HTTP.

Invoking an In-Out operation

The org.apache.axis2.clientapi.Call class is a convenient way for invoking an In-Out operation. This Call class supports the following four patterns for invoking In-Out operations:

  1. Blocking Single Transport pattern: This is the simplest way of invoking an In-Out Web service operation. The service invocation is blocked until the operation completes and the response or fault is received. It uses a single transport connection for sending and receiving the response, as shown in Listing 4 below.

    Listing 4. Blocking Single Transport pattern
    try {
    
                     
    
      EndpointReference targetEPR = new EndpointReference(
    
          "http://localhost:8080/axis2/services/StockQuoteService");
    
                   
    
      // Create request message
    
      OMFactory fac = OMAbstractFactory.getOMFactory();
    
      OMNamespace omNs = fac.createOMNamespace(
    
          "http://www.developerworks.com/example", "example");
    
        OMElement payload = fac.createOMElement("getQuote",omNs);
    
      payload.setText("IBM");
    
    
    
      // Create the call
    
      Call call = new Call();
    
      call.setTo(targetEPR);
    
    			
    
      call.setTransportInfo(Constants.TRANSPORT_HTTP,
    
        Constants.TRANSPORT_HTTP, false);
    
      // Invoke blocking
    
      OMElement result = call.invokeBlocking("getQuote", payload);
    
                   
    
      System.out.println("Quote ="+result.getText());
    
    }catch (AxisFault axisFault) {
    
        axisFault.printStackTrace();
    
    }

    The first part of the code creates the request message using AXIOM. Call.setTransportInfo() sets the transports to be used for sending the request and getting the response. The Boolean argument of the Call.setTransportInfo() operation tells if a separate transport connection is to be used for sending the request and receiving the response. In this case, ask for a single HTTP connection for sending and receiving.

  2. Non-Blocking Single Transport pattern: In this invocation pattern, you achieve a non-blocking call using only a single transport connection underneath. This kind of behavior is needed when there are many Web service invocations to be done in a single client application, and you don't want the client to be blocked for every invocation. Here the invocation returns immediately and the client gets a callback when the response is available, as shown in Listing 5 below.

    Listing 5. Non-Blocking Single Transport pattern
    try {
    
      EndpointReference targetEPR = new EndpointReference(
    
          "http://localhost:8080/axis2/services/StockQuoteService");
    
                
    
      //Create the request
    
      OMFactory fac = OMAbstractFactory.getOMFactory();
    
      OMNamespace omNs = fac.createOMNamespace(
    
          "http://www.developerworks.com/example", "example");
    
      OMElement payload = fac.createOMElement("getQuote", omNs);
    
      payload.setText("IBM");
    
          
    
                
    
        // Create the call
    
        Call call = new Call();
    
        call.setTo(targetEPR);
    
                
    
        // Set the transport info.
    
        call.setTransportInfo(org.apache.axis2.Constants.TRANSPORT_HTTP,
    
          org.apache.axis2.Constants.TRANSPORT_HTTP, false);
    
                
    
      // Callback to handle the response
    
                
    
        Callback callback = new Callback() {
    
          
    
          public void onComplete(AsyncResult result) {
    
            System.out.println("Quote = "
    
              + result.getResponseEnvelope().getBody().getFirstElement()
    
                .getText());
    
          }
    
          
    
          public void reportError(Exception e) {
    
            e.printStackTrace();
    
          }
    
        };
    
                
    
                
    
      // Invoke non blocking
    
                
    
        call.invokeNonBlocking("getQuote", payload, callback);            
    
                
    
      //Wait till the callback receives the response.
    
                
    
        while (!callback.isComplete()) {
    
          Thread.sleep(1000);
    
        }
    
                
    
                
    
      call.close();
    
    } catch (AxisFault axisFault) {
    
      axisFault.printStackTrace();
    
    } catch (Exception ex) {
    
      ex.printStackTrace();
    
    }

    The Call.invokeNonBlocking() method returns immediately without blocking. Call.invokeNonBlocking() takes an object of org.apache.axis2.clientapi.CallBack, which will be triggered when the response comes from the service. The CallBack has two abstract methods, onComplete(AsynchResult) and reportError(Exception), that need to be implemented by the concrete CallBack class. The Axis2 engine calls the onComplete() method when the service invocation completes normally. On a fault message from the server, the reportError() method of the Callback is invoked. Callback.isComplete() tells you whether the operation invocation is completed or not.

    Since both of the above approaches use a single transport connection to send and receive messages, they are not suitable for long-running transactions. This is because the transport connection might timeout before the response is available. To address this problem, two separate transport connections can be used for request and response. But since a different transport connection is used to retrieve the response, the request and response need to be correlated. Axis2 supports WS-Addressing, which provides a solution to this problem by using <wsa:MessageID> and <wsa:RelatesTo> headers. So addressing the module needs to be enabled when using dual transport, as shown in the two patterns below.

  3. Blocking Dual Transport pattern: This pattern is useful when the service operation is In-Out in nature, but the transport used is One-Way (for example, SMTP) or when the service execution takes a long time and HTTP connection times out. See Listing 6 below.

    Listing 6. Blocking Dual Transport pattern
    try{
    
      EndpointReference targetEPR = new EndpointReference(
    
          "http://localhost:8080/axis2/services/StockQuoteService");
    
                    
    
      OMFactory fac = OMAbstractFactory.getOMFactory();
    
      OMNamespace omNs = fac.createOMNamespace(
    
          "http://www.developerworks.com/example", "example");
    
      OMElement payload = fac.createOMElement("getQuote",omNs);
    
      payload.setText("IBM");
    
            
    
      Call call = new Call();
    
        call.setTo(targetEPR);
    
    
    
        call.setTransportInfo(
    
          Constants.TRANSPORT_HTTP, Constants.TRANSPORT_HTTP, true);
    
    
    
        //Blocking Invocation
    
        OMElement result = call.invokeBlocking("getQuote", payload);
    
      System.out.println("Quote = "+result.getText());
    
    	
    
    }catch (AxisFault axisFault) {
    
        axisFault.printStackTrace();
    
    }catch (Exception ex) {
    
        ex.printStackTrace();
    
    }
  4. Non-Blocking Dual Transport pattern: This pattern provides maximum flexibility, both in terms of not blocking at the API level and at the transport level, as shown in Listing 7 below.

    Listing 7. Non-Blocking Dual Transport pattern
    try {
    
           
    
      EndpointReference targetEPR = new EndpointReference(
    
          "http://localhost:8080/axis2/services/StockQuoteService");
    
      OMFactory fac = OMAbstractFactory.getOMFactory();
    
      OMNamespace omNs = fac.createOMNamespace(
    
          "http://www.developerworks.com/example", "example");
    
      OMElement payload = fac.createOMElement("getQuote",omNs);
    
      payload.setText("IBM");
    
            
    
               
    
        Call call = new Call();
    
        call.setTo(targetEPR);
    
    
    
        call.setTransportInfo(
    
          Constants.TRANSPORT_HTTP, Constants.TRANSPORT_HTTP, true);
    
      // Callback to handle the response
    
               
    
        Callback callback = new Callback() {
    
        public void onComplete(AsyncResult result) {
    
          System.out.println("Quote = "
    
              + result.getResponseEnvelope().getBody().getFirstElement()
    
            .getText());
    
          }
    
    
    
        public void reportError(Exception e) {
    
            e.printStackTrace();
    
          }
    
        };
    
                
    
    
    
      // Non-Blocking Invocation
    
              
    
        call.invokeNonBlocking("getQuote", payload, callback);
    
      // Wait till the callback receives the response.
    
              
    
        while (!callback.isComplete()) {
    
          Thread.sleep(1000);
    
        }
    
        call.close();          
    
    }catch (AxisFault axisFault) {
    
          axisFault.printStackTrace();
    
    }catch (Exception ex) {
    
          ex.printStackTrace();
    
    }

Summary

In Part 1 of this two-part series, you learned about the Axis2 architecture and saw how to deploy and consume a simple Web service using different invocation patterns. In the Part 2 of this series, you will review the architecture in more detail and see how to customize Axis2 by adding handlers and Axis2 message receivers. You will also see how use the XMLBeans data binding support provided by Axis2.


Download

DescriptionNameSize
StockQuoteService Axis2 Archive fileStockQuoteService.aar3 KB

Resources

Learn

Get products and technologies

  • Get your hands on application development tools and middleware products from DB2®, Lotus®, Rational®, Tivoli®, and WebSphere®. You can download evaluation versions of the products at no charge, or select the Linux® or Windows® version of developerWorks' Software Evaluation Kit.

Discuss

Comments

developerWorks: Sign in

Required fields are indicated with an asterisk (*).


Need an IBM ID?
Forgot your IBM ID?


Forgot your password?
Change your password

By clicking Submit, you agree to the developerWorks terms of use.

 


The first time you sign into developerWorks, a profile is created for you. Select information in your profile (name, country/region, and company) is displayed to the public and will accompany any content you post. You may update your IBM account at any time.

All information submitted is secure.

Choose your display name



The first time you sign in to developerWorks, a profile is created for you, so you need to choose a display name. Your display name accompanies the content you post on developerWorks.

Please choose a display name between 3-31 characters. Your display name must be unique in the developerWorks community and should not be your email address for privacy reasons.

Required fields are indicated with an asterisk (*).

(Must be between 3 – 31 characters.)

By clicking Submit, you agree to the developerWorks terms of use.

 


All information submitted is secure.

Dig deeper into SOA and web services on developerWorks


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=1
Zone=SOA and web services
ArticleID=98178
ArticleTitle=Develop Web services with Axis2, Part 1: Deploy and consume simple Web services using the Axis2 runtime
publish-date=11042005