2006/09/13

Working With Apache Axis2

  • By Deepal Jayasingha
  • |
  • 13 Sep, 2006

This article by Deepal Jayasinghe takes you through the new features and enhancements of Apache Axis2 explaining how exactly this SOAP engine qualifies to be the platform for the next generation of Web services and Service Oriented Architectures (SOA).

Introduction

Apache Axis2 is the next generation of Web services stack which is built on a new architecture. Axis2 was designed to be more flexible, efficient and configurable Axis 1.X. Even though the architecture is new, some of the well established concepts from Axis 1.x, for example, handlers are preserved in Axis2. Axis2 comes with lots of new features, enhancements and new industry specification implementations. New features and enhancements include AXIOM, Asynchronous Web services, MTOM, MEP support and archive based deployment architecture.

Understanding AXIOM

As mentioned above Axis2 is built on a fresh architecture compared to Axis 1.x. One of the main goals of introducing Axis2 was to provide a better XML processing mechanism. As you know Axis 1.x uses DOM as its XML representation, but the drawback is that you need to keep the complete objects hierarchy (corresponding to incoming message) in memory. For a message of small size this is not a problem, but with large messages, this becomes an issue. To overcome this problem Axis2 introduced a new XML representation in its core named AXIOM (AXIS2 Object Model)

All messages in Axis2 are represented as an object model using AXIOM. The advantage of AXIOM over other traditional XML infoset representations is that AXIOM is based on PULL parser technique, while most others are based on PUSH parser technique. The main difference of PULL over PUSH is that in the case of PULL technique, invoker has the full control over the parser and he can ask for the next event when he wants. But in the case of PUSH, when we ask the parser to proceed it will fire the events until it reaches the end of the document.

As AXIOM is based on PULL parser technique it has so called ‘on-demand-build/deferred-build’ capability, where an object model is built only if it is asked to do so. In addition, one can directly access underline PULL parser from AXIOM and use that rather than building OM (Object Model).

If you don’t have a good understanding on AXIOM, developing Web services with Axis2 is not an easy task. Of course, you can develop simple services without knowing anything about AXIOM, but if you are thinking of implementing WS-* specification or perform certain advanced tasks with Axis2, it’s wiser to learn AXIOM first.

What is an Axis2 Repository?

In simple terms, Axis2 is a SOAP processing engine and its main function is to deliver incoming SOAP messages into target applications, and in this context application is a Web service. Meanwhile just delivering an incoming message to an application is not enough, we need to have QoS such as security, reliability etc. To provide these QoS or to extend Axis2 main functions, Axis2 has introduced the concept of extension modules. Axis2 repository keeps these two kinds of resources-Web services and modules, in a simple repository, a directory in the file system which has the specific structure as shown in Figure 1.

Figure 1: Axis2 Repository

As you can see there are three sub directories inside the repository. Their purpose can be described as:

  • lib : If both services and modules want to share some third party libraries then those should be placed in lib directory
  • modules : All the extension modules, or rather, module archive files should be in the modules sub directory
  • services : All the Web services or rather service archive files should be added to the services sub directory

Single Service vs. Service Group

A service archive file can contain one or more services, but one service archive file can only have one services.xml file (services description file).

Option 1: The simplest scenario is having one service inside an archive file; in that case services.xml can be written as one of the following format,

<service> 
<parameter name=”serviceClass”>org.apache.axis2.MyService<parameter>
......
......
<service>

It should noted here that in the above case, name of the service will be the name of the archive file as the service name is not set.

Option 2 : In the case of multiple services in one service archive, better known as a service group, the services.xml file should look like the following and it should contain <service name=”xxx”> </service> for each of the services in the archive file.

<serviceGroup>
<service name=”MyService”>
<parameter name=”serviceClass”>org.apache.axis2.MyService<parameter>
........
........
<service>
<serviceGroup>

Here name attribute of the service element is a mandatory attribute, and value of the name attribute becomes the name of the service, and name of the service group will be the name of the archive file.

Each of the services will be named using the value of the name attribute corresponding to its service element. For example, if the service archive file contains two services then services.xml should look like below:

<serviceGroup>
<service name=”ABC”>
<parameter name=”serviceClass”>org.apache.axis2.ABCService<parameter>
<service>
<service name=”XYZ”>
<parameter name=”serviceClass”>org.apache.axis2.XYZService<parameter>
<service>
<serviceGroup>

WSDL Auto Generation

In the case of contract first approach, you first generate server side code (service skeleton) for a given WSDL and then fill the skeleton as you wish. Next you create service archive file and deploy the service archive file. When Axis2 finds a service, first it checks whether there are any WSDL files in META-INF directory. If there are any, then WSDL files are read first after which corresponding AxisServices is created. If there are no WSDL files present, then it goes through services.xml file and loads the service implementation class using which generates WSDL file (this process is called Java2WSDL) and corresponding AxisService. So when someone asks for ?wsdl of a given service it will serialize AxisService as a WSDL.

In the case of Java2WSDL, what it does is first investigate service implementation class using both Java reflection and JAM (annogen) and then generate XML schema for the given class, after which rest of the WSDL document is generated. Please beware that the generated wsdl file *will* differ from the original wsdl file which was used to generate service skeleton.

Relating WSDLs and Services in a Service Archive File

As discussed above the problem of getting completely different WSDL files at runtime from the WSDL file which was used to generate service skeleton can be avoid by placing the original WSDL file in archive file. So if user is interested in seeing the same WSDL at run time, then he has to put his original WSDL file into META-INF directory in the service archive file. There are few rules that user has to follow if he is trying to put the original WSDL into service archive.

Rule 1: In the case of single service archive containing only one service, then as discussed in Option1, name of the service must always be the name of the archive file. Then the name of the service archive file should be the name of the service element in the WSDL (local name), as shown in the example below:

<wsdl:definitions xmlns:wsdl=https://schemas.xmlsoap.org/wsdl/ …..>
<wsdl:types>
<xs:schema targetNamespace="https://org.apache.axis2/xsd" … >
</xs:schema>
</wsdl:types>
<wsdl:portType name="MyPort">
...
</wsdl:portType>
<wsdl:binding name="MyBiding" type="tns: MyPort">
...
</wsdl:binding>

<wsdl:service name="MyService">
<wsdl:port name=" MyPort " binding="tns: MyBiding ">
<soap:address location="https://127.0.0.1:3502/axis2/services/MyService" />
</wsdl:port>
</wsdl:service>

In this case, name of the service archive should be MyService.aar, then only the correct mapping will take place and user can get the same wsdl that he used to generate the service.

Rule 2: In the case of service group, user can add more than one (depending on his requirement) WSDL files and he can relate them using the same logic discussed in Rule 1. For example, if user has two WSDL files, and the service elements names being “foo” and “bar” respectively, then his services.xml file should look like as follows:

<serviceGroup>
<service name=”foo”>
....
</service>
<service name=”bar”>
....
</service>
</ serviceGroup>

Parameters and Properties

Axis2 has two hierarchies called description hierarchy(static data) and context hierarchy (dynamic data). Static data is the data which is coming from different descriptors like axis2.xml, services.xml and module.xml, while dynamic data is populated and used at run time. Parameters are designed to be used as static data, and they can be defined anywhere in the above description files. An example use case of a parameter could be, say a service wants to write data to a file when someone invokes the service, in which case the name of the file can be taken from a parameter. The way of defining a parameter in any of the descriptors is as follows:

 <parameter name=”myPara” locked=”false”>My Parameter</parameter>

Note: Parameters are designed to store primitive data types (String , int double etc…) data and OM elements, NOT any type of objects, but it is not invalid to store any types of objects inside a parameter. As best practice always store serializable objects inside a parameter.

Properties are designed to share data across the run time environment, and value of the property can be any type. Therefore, main difference between parameters and properties is that parameters are stored in description hierarchy while properties are stored in context hierarchy. The simplest usage of a property would be to share data between two handlers in the execution chain. One handler can set some property in the message context and some other handler can perform a validation depending on that property.

Most of the time users get confused between properties and parameters. They try to access them in the wrong way, for an example, when you define parameters in a description file and try to access them as properties at the runtime, then you won't get the required result. Most of the time you will end up getting null point exceptions. At the runtime what you have is a message context, so message context has two ways to access them separately:

  • Object obj = msgCtx.getProperty(key);
  • Parameter parameter = msgCtx.getParameter(key);

Service and Module Isolation

In Axis2 both the services and modules are isolated, meaning, each of them has their own classloaders. Service isolation (module isolation as well) is very important feature in real time environment where two different services wants to use two different versions of a third parry library. Then how can we achieve this goal? The only way is to use separate class loading mechanisms.What happens if they put the two versions in the same location (abc-1.0.jar ,abc-1.1.jar )? Since full qualified name (including package name) of the two classes are the same in the both the versions, no one can initialize two versions of the same class in one JVM. Therefore, Axis2 introduced service isolation to tackle this problem; if you want to use two versions of the same third party library then put them into a service archive file, which will allow two services to load two versions without any problem at all.

Note that if you put some resources inside a service archive file (say foo), then you can not directly access those resource from some other location. You have to somehow get foo’s classloader and the load the resources using it.

There can be few ways of accessing resources in a service archive:

Option 1 : If you are trying to access the resources outside the service implementation class, say for example you are trying to initialize service implementation class at the message receiver level, then the correct way is as follows:

  1. First get the corresponding AxisService since class loader reference is there in it
  2. AxisService service = msgCtx.getAxisService();
  3. Next, get its class loader
  4. ClassLoader loader = service.getClassLoader (); 
  5. If you want to load or initialize some class in the archive file, then
  6. Class servicClass =Class.forName("MyServiceClass" , loader,true);
  7. If you want to read a resource file in it, then
  8. InputSream in=loader.getResourceAsStream(“MyResources”);

Option 2 : If you are trying to access the resources inside service implementation class or any classes loaded by the service class loader, then the correct way of accessing the class loader is:

ClassLoader loader = getClass().getClassLoader();

Using this loader you can load resources, load classes etc...

How Service Class Gets Access to MessageContext

In Axis2 the last handler of the incoming execution chain is message receiver, and one of the main jobs of Axis2 is to deliver messages to MessageReceiver. Then its up to the MessageReceiver to hand over the message to application or do the processing by itself. Axis2 comes with a set of default MessageReceivers so that if some service author wants, they can use them for their services. In the meantime, service author can write his own message receivers for his services.

If the service author is trying to write a MessageReceiver for his service, then he can write it the way he wants. The advantage in this case is that the service author knows about the service implementation class and he can program message receiver to pump message context into service implementation class.

Most of the time service authors are not going to write his own MessageReceiver (it is not required to do so in all the cases) and they try to use default message receivers. In that case, service author does not have any control on the message receivers, and it will be getting either OMElement or Java object(s). But there can be many instances where service author requires to access Message Context inside his service implementation class (say for a instance to access HTTP headers). To solve this problem Axis2 has a mechanism call “dependency injection”, so if you want to access incoming message context inside your service implementation class, add the following method to service implementation class. Then using operation context you can not only get either incoming or outgoing message context, but also access to service context as well.

 public void setOperationContext(OperationContext operationContext) {

}

Session Management and Service Scope

Axis2 supports four levels of service scopes:

  1. Application scope
  2. SOAP session scope
  3. Transport session scope
  4. Request scope

The basic rule is if you deploy a service in one of the above scopes, then you will be able to get a session corresponding to that scope. For example, if service author deploys his service in an application scope, then that service gets an application session.

Service author can specify his service scope in his services.xml. If service author does not specify scope, then the default scope - Request scope will come in to action. The correct way of specifying the scope is as follows:

<service name=”MyService”scope=”application”> 

</service>

Application sessions takes the longest lifetime which is equivalent to the lifetime of the system. If some service is deployed in application scope, then to manage session client, you need not to send any session data. The most important thing with application scope is that there will be only one instance of the service implementation class throughout the life time of the server instance.

SOAP session has lesser lifetime than application session. In Axis2 the definition of SOAP session is to access a set of services in a service group or to manage session across all the services belonging to a service group. To get into the correct SOAP session, user has to send a specific SOAP header called servicegroupid. SOAP session corresponding to a service invocation will be removed from the system if that does not get touched for a period of 30 seconds.

Note that if service is deployed in SOAP session scope, then each session will have its own service class instances which imply that if some service is deployed in SOAP session scope there can be multiple instances of the service implementation class.

In the case of Transport session, session management or finding the correct session object is achieved by using some transport specific data, for example in the case of HTTP it is HTTP cookies. If a service is deployed in a transport session, and then there will be service instances for each of the transport sessions. What's interesting with this approach is that user can have multiple SOAP sessions in one transport session. To get into the correct SOAP session (which is stored in Transport session), user has to send service group id SOAP header in addition to cookies.

There is no session management for request session scope. Request session is only for one request so it is not necessary to have any sessions around it.

Summary

As a third generation SOAP engine, Axis2 provides a better SOAP processing model with all the latest standards along with considerable performance increase in both speed and memory footprint. New features enhance the flexibility and usability of Axis2 especially in advanced scenarios.

Author

Deepal Jayasinghe, Senior Software Engineer, WSO2 Inc. deepal at wso2 dot com

 

About Author

  • Deepal Jayasingha
  • WSO2 Inc.