Setting up a simple Axis2 Web Service within IDEA 7

Damodar Chetty

June 27, 2008

 

Overview

The first thing to note about Axis2 v1.4 is its confusing name structure. Is this Axis version 2? Or is it Axis2 version 1.4?

I really dislike this trend (Struts2 is another culprit) where an open source project tries to ride the coat tails of a predecessor's success. If you were attracted to Axis2 thinking it was the next release of the original Axis project (hereinafter called Axis1) - you've just been had.

In reality, Axis2 is a whole new reimagining of web services - its a rearchitected, redesigned version of a third generation web service engine. It may be safe to say (not that I'm saying it) that the gulf between Apache SOAP and Axis1 is about the same as that between Axis1 and Axis2.

However, my scope for this post is not to compare Axis2 to either Axis1, XFire, Glassfish Metro or any other of the options out there.

We are currently in the midst of using Axis2 on one of our projects, and I was curious to see what the mechanics would be to get it working within an IDE. As a result, this post will focus on setting up an Axis2 project, using a trivial Hello World-style web service that sports a single operation that warmly greets its users.

The software environment for this article includes IDEA 7, Axis2 v1.4, and Tomcat 6.

I'll assume that you've already got Tomcat and IDEA installed. If not, go do that - I'll still be here when you get back :)

A dummies guide to Axis2

Motivation

The primary motivation behind Axis2 seems to have been a need for:

(a) an optimized XML representation (using AXIOM - the AXIs Object Model);

(b) a variety of deployment choices (hot deploy, hot update, using archives/directories/POJOs, etc.);

(c) a simpler way to add optional functionality (ie the WS-* specs) using modules;

(d) a more robust way of making contextual and configuration information available to your service (via two parallel hierarchies); and

(e) simplified APIs to access services from a client.

Execution Chain

When a web service request is sent to a Axis2 service, that incoming message is received by a Transport Receiver, usually AxisServlet, at the web service's endpoint. AxisServlet is set to intercept all requests in the context path /axis2/services/.

The processing that a message undergoes while flowing through the system is termed an "execution chain". Axis2 models this chain using a hierarchical scheme of Flows/Pipes, Phases, and Handlers, where a Flow/Pipe is composed of 1+ phases, and each phase is composed of 1+ handlers.

Flows, phases, and handlers are defined in axis2.xml - the global configuration file for the Axis2 engine. While you will rarely touch the predefined flows and phases, you are much more likely to add/remove handlers or change their order within a given phase.

Flows/Pipes

Axis2 Flows/Pipes are named based on the direction of the message flow, and the type of message they carry. E.g., InFlow and OutFlow for standard messages; and InFaultFlow and OutFaultFlow for SOAP faults.

You combine these basic flows to engineer any desired Message Exchange Pattern. E.g., an in-only MEP has just one flow, whereas for an in-out MEP, the message goes through the InFlow and then back out the OutFlow.

Phases

For a given Flow/Pipe, there is a standard sequence of phases that is invoked. E.g., the default InFlow comprises these phases: Transport (handles processing specific to the transport being used), Security (handles WS-Security), PreDispatch (handles WS-Addressing), Dispatch (determines the target service/operation to which the message should be sent), RMPhase (handles WS-ReliableMessaging), OperationInPhase, and soapmonitorPhase.

The first 5, up to RMPhase, are Global phases, i.e., they are defined by Axis2, and will be called for every incoming message.

Handlers

A phase is composed of 1+ handlers, which are the actual worker bees. Each handler is associated with its implementing class, and has an associated phase rule that defines the phase to which this handler belongs, the order of execution within that phase, etc.

Handlers are stateless - they retrieve any required state/runtime environment information from a MessageContext - a bundle that holds the SOAP message, parameters, etc.

A handler processes a message, and based on the circumstances can choose to either let the message continue onward, suspend processing until some condition is met, or abort processing.

E.g., the Dispatcher handler's role is to determine the appropriate service and operation that should be invoked for an incoming message. This occurs bang in the middle of the InFlow as part of the Dispatch phase. Axis2 supports dispatching based on the transport URI, on the SOAP Action request header, using WS-Addressing headers, etc.

As a message is processed by the InFlow, it moves through each phase, and each handler within that phase, in sequence. The final destination for an incoming message is a Message Receiver which ends up calling the actual service implementation method.

OutFlow

For an MEP that involves an outgoing response, the response is handed off to be processed by the OutFlow, which invokes its phases and handlers in sequence. The final processing step for an outgoing message is a Transport Sender, which transmits the SOAP message over the transport. There are senders in place for standard transports such as HTTP/HTTPS, JMS, SMTP, etc.

 

Deployment schemes

The basic schemes use a zipped file (a .aar or .mar) that packages the implementation classes for the service/module with a deployment descriptor.

Service Archive (.aar)

To deploy a service, you simply drop the .aar file into the WEB-INF\services directory. Axis2's hot deployment mechanism will automatically detect the new service and load it in.

The services.xml file is the deployment descriptor for the new service, and is found in WEB-INF\services\<your-service-name>\META-INF. It specifies the fully qualified class name for your service's implementation class. By default, all public methods in this class are exposed as operations. You can specify message receivers either at the service-level, or at the individual operation-level.

The META-INF folder can also contain .xsd and .wsdl files for your service.

Any third party JARs that your service needs can be added to a lib folder within WEB-INF\services\<your-service-name>.

 

Module Archive (.mar)

A module is typically an implementation of a WS-* spec (such as WS-ReliableMessaging), but could also be some custom application- or domain-specific extension.

A module archive (.mar) is structured in a similar fashion to a service archive, except that a module.xml is the deployment descriptor for the module which describes the module's handlers, and their phase rules. Implementation classes include the module implementation class - which runs at system initialization time and so can be used to perform initialization (such as opening database connections) and handler implementations (which are invoked to process a request).

 

Download and Install Axis2

Lets start by downloading the Axis2 distribution, which is available on the project page at http://ws.apache.org/axis2/download.cgi. At the time of this writing, the current version was Axis2 1.4.

Axis2 is available in two binary forms:

         as a standard binary distribution, which lets you run a standalone web server (you'll find differing opinions whether it is enterprise-grade or not);or

         as a Web Archive distribution (axis2.war), which is more interesting from our perspective. You deploy this axis2.war file to any standard servlet container.

This is very different from Axis1, which we deployed as a JAR, into a custom web application. Instead, now, you package your service classes, resources, and descriptors using a service archive (.aar) and deploy it to a folder within the axis2 web application.

The installation guide on the Axis2 web site is actually very well written (http://ws.apache.org/axis2/1_4/installationguide.html).

 

Copy axis2.war to your %CATALINA_HOME%\webapps folder. Start up your Tomcat container. As Tomcat starts up you should see these comforting lines that indicate that Tomcat recognized your new WAR file, and that it was initialized successfully:

INFO: Deploying web application archive axis2.war

Jun 26, 2008 2:10:44 PM org.apache.catalina.loader.WebappClassLoader validateJarFile

INFO: validateJarFile(C:\Java\apache-tomcat-6.0.14\webapps\axis2\WEB-INF\lib\servlet-api-2.3.jar) - jar not loaded. See Servlet Spec 2.3, section 9.7.2. Offending class: javax/servlet/Servlet.class

[INFO] Deploying module: addressing-1.4 - file:/C:/Java/apache-tomcat-6.0.14/webapps/axis2/WEB-INF/modules/addressing-1.4.mar

[INFO] Deploying module: script-1.4 - file:/C:/Java/apache-tomcat-6.0.14/webapps/axis2/WEB-INF/modules/axis2-scripting-1.4.mar

[INFO] Deploying module: metadataExchange-1.4 - file:/C:/Java/apache-tomcat-6.0.14/webapps/axis2/WEB-INF/modules/mex-1.4.mar

[INFO] Deploying module: ping-1.4 - file:/C:/Java/apache-tomcat-6.0.14/webapps/axis2/WEB-INF/modules/ping-1.4.mar

[INFO] Deploying module: soapmonitor-1.4 - file:/C:/Java/apache-tomcat-6.0.14/webapps/axis2/WEB-INF/modules/soapmonitor-1.4.mar

[INFO] Deploying module: metadataExchange - file:/C:/Java/apache-tomcat-6.0.14/webapps/axis2/WEB-INF/lib/mex-1.4-impl.jar

[INFO] Deploying Web service: version-1.4.aar - file:/C:/Java/apache-tomcat-6.0.14/webapps/axis2/WEB-INF/services/version-1.4.aar

Jun 26, 2008 2:10:52 PM org.apache.coyote.http11.Http11Protocol start

INFO: Starting Coyote HTTP/1.1 on http-80

Jun 26, 2008 2:10:52 PM org.apache.jk.common.ChannelSocket init

INFO: JK: ajp13 listening on /0.0.0.0:8009

Jun 26, 2008 2:10:52 PM org.apache.jk.server.JkMain start

INFO: Jk running ID=0 time=0/31  config=null

Jun 26, 2008 2:10:52 PM org.apache.catalina.startup.Catalina start

INFO: Server startup in 8890 ms

 

Note the presence of a multitude of [INFO] lines that start with "Deploying module", and the single line that starts with "Deploying Web service". These are the default modules and services that are packaged with axis2.war.

If you mosey along to your %CATALINA_HOME%/webapps folder you should see the exploded directory structures for this WAR.

Now we're ready to test our Axis2 installation:

1.      Point your browser to the following URL: http://localhost/axis2/.
You might have to use http://localhost:8080/axis2/, if you haven't configured Tomcat to use the default port 80.
If Axis2 was installed into Tomcat correctly, you should see the following page.

2.      Click on the Validate link to check how happy Axis2 is, in its new home.
The presence of the green lines indicate that Axis2 is pretty content living in its container.

 

3.      Click on the Back Home link (just below the Axis2 logo) to return to the main page, and choose the Services link instead. Now, you should see the Version service that was successfully installed during startup.

4.      The two elements of interest are the End Point Reference (EPR) for this web service and the list of Available Operations (there's just one supported).
Point your browser at http://localhost/axis2/services/Version/getVersion.
You should see the following XML fragment as your response:

That's it - you've just invoked your very first Axis2 web service.

 

A Custom Web Service

Now, lets get a bit more adventurous - and deploy a sample Hello World web service.  At this point, we're going to shift focus to IDEA 7.

Since I like to see the nuts and bolts of how things work behind the scenes, I'm going to roll my own for now. An option might be to download and install WebServicesPlugin for IDEA7.

As we saw earlier, Axis2 runs as its own web application, axis2.war, that you deploy to your container. The services you develop must be copied to axis2/WEB-INF/services either as an exploded directory with a standard format, or as a service archive file.

What this means is that there's a lot of stuff that needs to happen to make this work right - and most of this is fairly manual. So lets roll up our sleeves.

1)     Set up an IDEA Project (see one of my previous articles for this step). Make it a simple Java project with no addons.

2)     As shown, this project has a single module, with a single source folder.

3)     I'm making the assumption here that this project (or rather the Axis2Test module) represents a single Web Service, represented as mytestservice. As a result, all of this project's .class files will be copied out to webapps\axis2\WEB-INF\services\mytestservice.

4)     Setup dependencies - so as to include all the Axis2 JARs.

5)     Setup a Run/Debug Configuration so that you can start up Tomcat right from within IDEA. You can ignore the warning displayed - since we don't have a Web Facet to deploy.

6)     Create a new class as follows:
package com.swengsol;
import javax.jws.WebService;
@WebService
public class HelloWorld {
    public String sayHello(String name) {
        return "Hello " + name;
    }
}

7)     In %CATALINA_HOME%\webapps\axis2\WEB-INF\services create a new folder mytestservice which is the service that we're trying to publish.

8)      Within here, create a META-INF folder which will hold our service.xml. Create a service.xml file as follows:
<service name="mytestservice">
    <description>
        This is a dummy hello world service
    </description>
    <parameter name="ServiceClass">com.swengsol.HelloWorld</parameter>
    <operation name="sayHello">
       <messageReceiver  class="org.apache.axis2.rpc.receivers.RPCMessageReceiver" />
    </operation>
</service>

9)     Build your project so that the HelloWorld.class file is generated and copied to your service folder.

10) Verify that your final structure looks something like this:

11) Now start up your Run/Debug Configuration in Debug mode.

12) In addition to the previous lines, you'll see the following, indicating that your new service was picked up just fine.
[INFO] Deploying Web service: mytestservice - file:/C:/Java/apache-tomcat-6.0.14/webapps/axis2/WEB-INF/services/mytestservice/

13) Now, browse on over to http://localhost/axis2/services/HelloWorldService/sayHello?name=Damodar to see your service in action.

14) Finally, place a debug breakpoint at an appropriate location within the service method, and refresh your browser page. Execution should stop at your break point allowing you to step through your code.

 

Points to note

1)     This is obviously not the only way to structure an Axis2 project, so if you come up with a better way to do so, I'd appreciate your letting me know. For now, this is sufficient to allow me to proceed - and I'm sure I'll fine tune this as I go along.

2)     I'd have liked to be able to edit the services.xml deployment descriptor right from within IDEA - and I haven't quite figured out the best way to do that yet.

My workaround?

Use File > Open to browse to the services.xml. Then right click the editor tab, and choose Add To Favorites. Now pick the Favorites tab in your View As drop down to find this file quickly. I really wish there were a way to symbolically link to files, as Eclipse would have allowed.

3)     Any changes you make to services.xml are not automatically picked up by the Axis2 engine ... until you tell it to, by setting the hotupdate parameter to true in %CATALINA_HOME%\axis2\WEB-INF\conf\axis2.xml:
<parameter name="hotupdate">true</parameter>

4)     The correct way to deploy your code is by packaging your service as a service archive file (.aar). An easy option is to use Ant to build this for you.