Writing Your Own services.xml for Axis2 Web Services

This tutorial by Deepal Jayasinghe mainly focuses on how to write a service configuration file or services.xml for Apache Axis2/Java from scratch. He covers all the applicable syntax in the services.xml file as well as their proper usage.

Introduction

In Axis1, when we deploy a service, we have a configuration file called service.wsdd to configure the service. In Axis2, we gave a different name for the service configuration file and called it the services.xml file. The syntax of both configuration files are completely different.

This tutorial covers all the applicable syntax in the services.xml file as well as their proper usage. In Apache Axis2/Java, we can either deploy a single service or multiple services in a single service archive file. Irrespective of the way we deploy our service, the service archive file must contain a services.xml file if it is to be a valid service. Depending on the way we deploy our service, the syntax of the services.xml will vary. There are two types of services.xml; one for a single service and one for a service group.

Writing services.xml for a Single Service

The root XML element of services.xml for a single service is service, so it looks like,

<service ..... >
...............
</service>

Service Name

When we deploy a single service in a service archive, the file name of the service will always be the name of the archive file, unless we have a name attributed to it. So if the name of the service archive file is foo.aar, then the name of the service will be foo.

If we want to add the name, we can do it as follows,

<service name="foo" >
...............
</service>

Service Description

A service description helps service authors to describe their service. When we try to list the services in the Axis2 Web administration console, we see only the services and their descriptions. If we don't add the service description into the services.xml, it will show the service name as its service description. Having a service description is very useful to the users who are going to access the service. Adding a service description is a just a matter of adding the following optional XML element. The value of the element could be either pure text or an HTML segment.

 

<Description>
This is my first Web service.
</Description>

Else it can be,

<Description>
<b>This is my first Web service.</b>
</Description>

Note : The description element is an option element in services.xml

Service Level Parameters

We can define parameters inside the services.xml as an immediate child element of the service element, and those can be accessed via message context (at the runtime) or via AxisService or AxisOperation. A parameter has one mandatory attribute and one optional attribute.The mandatory attribute is the name of the parameter while the optional attribute is the locked attribute.

The idea of a locked attribute is to express whether we allow the parameter value to be overridden by a child node in the hierarchy. For example, if we add a parameter in the axis2.xml file setting the locked attribute to True, then if a service tries to add a parameter with the same name, it will throw an exception

For example, let's say there is a parameter called 'Foo' in axis2.xml with a locked attribute value True, and a services.xml has the same parameter (a parameter with the name Foo), then there will be a deployment exception. We cannot override the parent parameters from child nodes if the locked attribute is set to True on them.

The value of the parameter can be anything, it can either be a simple text or it can be an XML element. We can add a service level parameter as follows,

<service>
.....
<parameter name=location>Colombo , Sri Lanka</parameter>
.....
</service>

Service Class

In Axis2, it is not mandatory to have a service implementation class for a given Web service. The Axis2 architecture allows us to write Web services without having a service implementation class. This is simply because the message receiver allows us to do so. With Axis2, once the request is handed over to the message receiver, Axis2 thinks (the AxisEngine) that it has finished its job and it's up to the message receiver to do the rest. Therefore, there is no special XML element for specifying the service implementation class in the services.xml. If we do have a service implementation class (most of the time we do), then we can add a special parameter called ServiceClass into the services.xml. Then the value of the parameter should be the full qualified name of the service class. So if the full qualified name of the service implementation is org.apache.Foo, then we could add the following parameter into the services.xml to specify the service implementation class.

<parameter name="ServiceClass" locked="false">org.apache.Foo</parameter>

Service Level Message Receivers

Message receiver is a special handler in Axis2, and it is the last handler in the In path (Request path). Each and every operation in a service has its own message receivers, and the type of message receiver may differ from one operation to another. A Message receiver is MEP (Message Exchange Pattern) dependent, so we must have different message receivers for different MEPs.

Take the scenario where we want to use the same message receiver for all the operations in a given service. We can easily do that by adding service level message receivers. In this case, we do not need to specify a message receiver at the operation level. What we need to do is to add the operation tag along with the name and the MEP to which it belongs.

We can specify the Message Receiver along with the MEP that is implemented in the services.xml. Then, at deployment, the correct Message receivers will be set for the operations in the services.

<messageReceivers>
<messageReceiver mep="http://www.w3.org/2004/08/wsdl/in-only"
class="org.apache.axis2.rpc.receivers.RPCInOnlyMessageReceiver"/>
<messageReceiver mep="http://www.w3.org/2004/08/wsdl/in-out"
class="org.apache.axis2.rpc.receivers.RPCMessageReceiver"/>
</messageReceivers>

Engaging Modules

There may be some instances where we cannot run our service without engaging the WS-Security module into the service. Engaging a module is just a matter of adding a module tag into the services.xml. If the module is available then the engaging will take place, else it will make our service a faulty service.

So if we want to engage a module called foo, then we can add the module element into the services.xml as follows,

<service>
<module ref=foo/>
</service>

Service Session Scope

There are four service session scopes available in Axis2 to deploy our service. If we do not specify the session scope, then the service will be deployed using the default session scope - the request session scope. To specify the scope, you must add an optional attribute to the service element, so the value of the attribute can be one of the four sessions listed below:

<service scope=application>
......
</service>

 

Service Target Namespace

The idea of a service target namespace is only applicable for WSDL generation. At runtime, if someone tries to view the WSDL for a given service using ?wsdl, then the target namespace of the generated WSDL will be the value specified by this. In the mean time, we drop a WSDL file into the META-INF directory, and we want to override its target namespace. So the solution is to add the target namespace value into the services.xml . The default value of the target namespace is http://ws.apache.org/axis2.

To specify a target namespace, you must add an optional attribute called targetNamespace. If we want to set a target namespace of the foo service to http://foo.org, then we can do that as follows,

 <service targetNamespace=http://foo.org>

</service>

Schema Target Namespace

If the WSDL file is not present in META-INF at the time of generating a WSDL file at runtime or generating a schema at deployment, you can use your own schema target namespace by adding a schema element into the services.xml.

At deployment, if we do not specify a schema target namespace, then the target namespace will be calculated using the package name of the service implementation class. For example, if the full qualified name of the service implementation class is org.apache.axis2.FooService then the schema namespace will be,

http://FooService.axis2.apache.org/xsd

If you want to provide your own value for that, you can do that by adding the following element into the services.xml file.

   <service>
<schema schemaNamespace=http://foo.org/xsd/>
</service>

elementFormDefault Value in WSDL

At the time of generating a WSDL using a Java class, the value of elementFormDefault in the schema is set to 'qualified' by default. Once we have qualified=true, then all the elements in the response will also be qualified. There are situations where we do not need this behavior and we want to set the value of elementFormDefault to Unqualified. To do that, we need to add the following entry into the services.xml file.

<schema elementFormDefaultQualified="false"/>

Note : We may or may not have the schemaNamespace attribute here.

Exposing a Service on a Selected Transport

Axis2 has the capability of exposing a service through multiple transports, and that is achieved using the concept called Listener Manager. For example, you can start the Axis2 system to expose its services via HTTP and TCP with the use of the Listener Manager.

When we have multiple transports, sometimes we want to expose the services on selected transports; a good example would be the system administration service. That particular service will be exposed only through SSL, and there should be a way to convey this information.

In the case of the server side with multiple transports, all the services will be exposed through those transports. If we want to expose our service only through a selected transport, then we can do that by adding the transport tag into the services.xml as shown below. If we do not add a transport element, then the service will be exposed through all the available transports.

<transports>
<transport>https</transport>
</transports>

Operations and Operation Related Elements

The previous section described how to specify service level data that are applicable for each and every operation in a given service. For example, if we engage a module at the service level, then that will affect all the operations in the service. However, there are also instances where we want to override or add new data to operations.

Note : If the service implementation class is Java, then all the public methods in the service class will be exposed whether we specify that in the services.xml or not. If we wrote our service using some other language, then we have to specify the operation that we want to publish.

Override Operation

As mentioned above, there may be instances where we want to override service data using operations. For example, if we have to override a service level parameter using an operation, we can do that by adding an operation tag.

<service>
<parameter name=location locked=false>Colombo , Sri Lanka </parameter>

<operation name=doSmt>
<parameter name=location locked=false>California ,USA</parameter>
</operation>
</service>

Operation Level Message Receivers

As mentioned above, we can specify service level message receivers. However, there are also instances where we want to use a different message receiver for a given operation. Then we can use a message receiver tag inside the operation.

 <operation name=doSmt>
<messageReceiver class="org.apache.axis2.MyMessageReceiver"/>
</operation>

Adding Action Mapping

Action mapping is adding an alias for an operation. This enables us to add any number of aliases for an operation. It helps us to filter requests or to perform different processing for different action mappings. In the case of a client request, he has to send the action mapping either as an SOAPAction or wsa:action. Then the Axis2 dispatchers will dispatch the incoming request to the correct operation.

  <operation name=doSmt>
<actionMapping>mapping1</actionMapping>
<actionMapping>http://foo.org/doSmt</actionMapping>
</operation>

Engaging a Module

Just as we engage a module to a service, we can also engage a module to an operation. For instance, if we want security only for selected operations, the best way is to engage a module to those operations. Engaging a module is just a matter of adding a module element as shown below,

  <operation name=doSmt>
<module ref="foo"/>
</operation>

Excluding Operations

Axis2 will expose all the public methods in the service implementation class (if it is implemented using Java). If we put the .wsdl file into META-INF and we do not want to publish all the operations in that, we can exclude the operations that we do not want to expose by adding the exclude operations tag as shown below,

<excludeOperations>
<operation>op1</operation>
</excludeOperations>

Writing services.xml for a Service Group

A service group is a convenient way of deploying multiple services together in one service archive file. Of course there will be a logical relationship between the services at runtime. The only difference in the services.xml for a service group and single service is its root element. For a service group, the root element is serviceGroup, and we can have multiple service elements inside the serviceGroup element.

<serviceGroup>
<service name=service1>
......
<service>
<service name=service2>
..........
</service>
</serviceGroup>

 

Note : In this case, the name of the service group is the name of the service archive file. The name attribute of the service element is mandatory and the name value should be unique across the whole server. There cannot be two services with the same name.

Once we know how to write a services.xml for a single service, then writing a services.xml for a service group is easy. As mentioned above, the service group is just a collection of service elements, so all the syntaxes mentioned above will be applicable.

References

Author

Deepal Jayasinghe, Senior Software Engineer, WSO2. deepal AT wso2 DOT com