Introducing Apache Axis2/C

Archived Content
This article is provided for historical perspective only, and may not reflect current conditions. Please refer to relevant product page for more up-to-date product information and resources.
  • By Samisa Abeysinghe
  • 4 Sep, 2006

Background

Apache Axis2/C is a C based implementation of the popular Axis2 SOAP engine Architecture. Axis2 was first implemented in Java. The effort on Axis2/C was initiated in September 2005, by which time, Axis2 architecture was gradually stabilizing with the experience gained and lessons learned through the Java implementation.

The Choice of C

One of the key decisions made when initiating the C implementation of Axis2 was the choice of the programming language. Prior to Axis2/C, Axis architecture was implemented in C++. By the time Axis2/C was initiated, Axis C++ was in its 1.5 release. Hence many had the question of why develop Axis2 in C and not in C++. The primary reason was that when it comes to integrating the SOAP engine on various platforms with various softwares, we cannot afford to assume that all platforms would have the full set of C++ libraries. There were many instances where the use of STL template classes in Axis C++ was a concern when using it on some pure C based platforms. Also, on the other hand, it is quite natural to extend or wrap a C library to be integrated with a C++ system. However, it is not natural to integrate a C++ system into a C system. So in case of Axis C++, those wishing to use a C API, had to use a library with C++ classes in its core. With Axis2/C the community wanted to address these problems and decided to implement the Axis2 architecture in C. It is expected that Axis2/C would better cater the needs of both C as well as C++ users.

Pseudo Object Oriented Design

When implementing Axis2 in C, there were few key challenges to be met. One of the main challenges was that Axis2 was originally designed with object oriented mindset, with Java being the first choice of implementation technology. With C programming language being a procedural language, a pseudo object oriented approach was adopted to minimize the possibilities of slipping off the original Axis2 architecture. Pseudo object oriented design was realized by placing operations in the publicly exposed header file and hiding the data by placing them in the source file. To help users to deal with the complex syntax resulting by this pseudo object oriented approach, macros were used.

Following code fragments show how this was realized:

Header file axis2_engine.h contains:

    /* the ops struct*/
struct axis2_engine_ops
{
axis2_status_t (AXIS2_CALL *
send)(
struct axis2_engine *engine,
const axis2_env_t *env,
axis2_msg_ctx_t *msg_ctx);
};


/* the struct itself */
struct axis2_engine
{
axis2_engine_ops_t *ops;

};

/* the easy to use macro */

#define AXIS2_ENGINE_SEND(engine, env, msg_ctx) \
((engine)->ops->send(engine, env, msg_ctx))

Source file engine.c contains:

/* the impl struct with data */

typedef struct axis2_engine_impl
{
axis2_engine_t engine;
axis2_conf_ctx_t *conf_ctx;
} axis2_engine_impl_t;

Platform Independence and Portability

Influenced by portable run-time platforms such as Apache Portable Run-time (APR), the Axis2/C developers wanted to make Axis2/C implementation portable and platform independent as much as possible. One of the obvious and easy to use approaches would have been to use APR as the base and implement the Axis2/C engine on top of it. However, there were few concerns. One of which is, what if one wanted to use a portable run-time other than APR? In that case, the approach would have been to implement an abstraction layer to allow room to use any portable run-time of choice. However, it was concluded that in order to write such an abstraction layer it would seem almost like writing yet another portable run-time, hence, the Axis2/C community decided against it. Another concern was that it was felt that Axis2/C would not require the full set of features supported by APR as a whole. The containers such as hash table and array list seems very useful, hence, those were borrowed into the util module of Axis2/C.

To deal with the greatest portability challenges of memory management, error handling, log handling and threading, the community came up with the idea of an environment. The environment struct would encapsulate allocator, error, log and thread pool concepts. The idea is to allow anyone to plug in the memory management, error handling, logging and threading mechanisms of their choice to help achieve the portability and integration objectives. The environment struct is passed around to any function to ensure the availability of the cross cutting functionality encapsulated by environment.

Ability to port across platforms was paramount to achieve one of the key original objectives for initiating the effort on a C based implementation for Axis2. This objective is to provide Web services extensions to popular open source scripting languages and Web browsers based on Axis2/C. There has been couple of efforts on this front already. A PHP extension is being developed and is already functional. A Firefox extension is also available that extends AJAX capabilities to support consuming Web services.

Design Characteristics

Axis2 is designed with XML in/out model in mind. The Axis2 engine takes XML as input, processes it and gives out the result in XML format. To support this XML in/out processing model, Axis2 has its own lightweight and fast XML object model called AXIOM. AXIOM is optimized for representing SOAP, and supports the complete XML infoset.

Apache Axis2/C implements AXIOM as a separate independent sub project with a separate build system within Axis2/C source tree. Like in the case of Axis2/Java AXIOM implementation, it is expected that this sub project would move out of Axis2/C source tree to be an independent project. But for the time being, it lives within the Axis2/C project. Axis2/C AXIOM implementation consists of the base XML object model implementation as well as the SOAP object model implementation based on the XML object model implementation. It also consists of the attachment related structs, to support MTOM/XOP.

AXIOM is designed to use StAX API. In order to leverage the performance yielded by the use of an XML pull parser API, the C implementation too wanted to use a pull model. However, the developers could not find any complete and open source implementation of an XML pull parser. The only one around was the Guththila parser, which lacked many features and was far from complete. The only usable solution was to use the libXml2 implementation which supports some form of a pull API. Though not completely pull, it saved the day. Guththila, which is a pure pull parser and is custom built for Axis2/C is being currently worked on. Though Guththila source too is within the Axis2/C source tree, it is expected to be an independent project on its own in the future.

Consuming Services

Axis2/C provides an easy to use “service client” API that can be used to consume services. One needs to set “options” on top of service client if they need to set the various options to be used by the service client in the invocation. Then the service invocation could be triggered by using one of the methods of invocation that takes in the request pay load and returns response payload.

At a very abstract level, here are the steps to send a request and receive response using the client API:

1. Create an endpoint struct instance with service address

2. Create options struct instance

3. Set the to address on options using endpoint created in step 1

4. Create service client struct instance with client repository location as a parameter (client repository is the folder where the axis2.xml configuration file is located)

5. Set options on top of service client

6. Send request with AXIOM payload and receive response in AXIOM format

Here is a code snippet that implements above steps (please take a look at the echo sample in Axis2/C for complete code):

endpoint_ref = axis2_endpoint_ref_create(env, address);
options = axis2_options_create(env);
AXIS2_OPTIONS_SET_TO(options, env, endpoint_ref);
svc_client = axis2_svc_client_create(env, client_home);
AXIS2_SVC_CLIENT_SET_OPTIONS(svc_client, env, options);
ret_node = AXIS2_SVC_CLIENT_SEND_RECEIVE(svc_client, env, payload);

Providing Services

For providing a service, one needs to implement the service skeleton interface (axis2_svc_skeleton struct). This interface defines four functions that a service needs to implement. They are:

1.init() - initializing

2.free() - destruction

3.invoke() - actual business logic implementation

4.on_fault() - what to be done in case of a fault

The most important method is invoke(), when the service is invoked, it is this method that the message receiver would be calling.

Here is an example implementation of the invoke method from the math sample. In this sample, based on the operation name, the invoke function calls the correct method serving the operation

    
axiom_node_t* AXIS2_CALL
math_invoke(axis2_svc_skeleton_t *svc_skeleton,
const axis2_env_t *env,
axiom_node_t *node,
axis2_msg_ctx_t *msg_ctx)
{
if (node)
{
if (AXIOM_NODE_GET_NODE_TYPE(node, env) == AXIOM_ELEMENT)
{
axiom_element_t *element = NULL;
element = (axiom_element_t *)AXIOM_NODE_GET_DATA_ELEMENT(node, env);
if (element)
{
axis2_char_t *op_name = AXIOM_ELEMENT_GET_LOCALNAME(element, env);
if (op_name)
{
if ( AXIS2_STRCMP(op_name, "add") == 0 )
return axis2_math_add(env, node);
if ( AXIS2_STRCMP(op_name, "sub") == 0 )
return axis2_math_sub(env, node);
if ( AXIS2_STRCMP(op_name, "mul") == 0 )
return axis2_math_mul(env, node);
if ( AXIS2_STRCMP(op_name, "div") == 0 )
return axis2_math_div(env, node);

}
}
}

}
return fault_node;
}

WS-* Support

In some point of the Apache Axis C++ project, it was felt that the architecture was too rigid to alter to make it support the emerging Web services specifications. This further rationalized the Apache Axis2/C effort, because the Axis2 architecture is designed to be extended with the concept of modules, enabling it to support WS-* stack more effectively.

The first Web service specification to be supported with Axis2/C was WS-Addressing. WS-Addressing is built into Axis2/C engine. All one has to do is to engage the addressing module, and WS-Addressing would be operational. The implementation has been tested for interoperability with the Java implementation and it passed the WS-Addressing test suite.

MTOM/XOP is also implemented in Axis2/C and is built into AXIOM and core engine. As in the case of addressing, one could enable MTOM and send binary data with SOAP messages using Axis2/C implementation. This again has been tested with the Java implementation for interoperability and the results are positive.

WS-Security and WS-Reliable Messaging are the other two from the WS-*specifications that would be supported by Axis2/C in the near future. WS-Security implementation, called Rampart/C, is being worked on currently. UsernameToken is already supported. Message encryption and signing are on the cards. At the moment, the support libraries, such as AXIOM Sec, that are required for signing and encryption are being worked on.

WS-Reliable messaging is being supported by the Sandesha2/C implementation under Sandesha2 project. The implementation is currently in progress.

It is expected that Axis2/C would also have WS-Policy, WS-Coordination, WS-Atomic Transactions and WS-Eventing implementations. These implementations are being planned.

REST with Axis2/C

Apache Axis2/C, like its Java counterpart, also supports REST, in addition to SOAP. The rationale for supporting REST is to let users use the same service and expose it as both SOAP as well as REST. It is important to notice that Axis2/C is a SOAP engine, so it is optimized for SOAP processing. Still, it is expected that exposing the same service to both SOAP and REST worlds using the same engine would be a luxury to user.

One can enable REST with Axis2/C by enabling the REST setting in axis2.xml configuration file. Axis2/C is capable of supporting REST both with HTTP POST as well as HTTP GET requests.

Code Generation

One of the popular models of developing and consuming Web services is to use a given WSDL and generate the code against it, using a tool, often provided alongside a SOAP engine. For Apache Axis2/C one could generate the C code for a given WSDL file using the WSDL code generation tool that comes with the Axis2 Java implementation. The fact that the Java tool is designed to generate any given code using the language mappings given in XSLT templates made this task possible.

Basic XML in/out code as well as code with Axis Data Binding (ADB) could be generated with the Axis2/Javas WSDL2Code code generation tool.

There has been an effort to develop a WSDL2C code generation tool in C. The initial code is available and it follows a similar model to that of the Java tool.

Dynamic Invocation

The most popular way of exposing a Web service interface is to provide a WSDL file. Hence, dynamic invocation model, where a WSDL file is provided and the client is dynamically generated in line with the WSDL and invoke the Web service on the fly, is a very easy to use programming model to consume services. In this model, the client program does not assume any prior knowledge about the service. Axis2/C provides dynamic invocation support to consume a Web service. Dynamic invocation is implemented based on the XML Schema and WSDL object model implementations in C, which are also part of Apache Axis2/C.

Conclusion

Apache Axis2/C has gradually become a feature packed and mature SOAP engine, over a short period of nearly one year. With its level of support for WS-* stack, easy to use client and service programming models, ability to embed in other platforms and speed of execution nurtured by the use of C, Axis2/C is poised to be one of the major players in the Web services arena.

Author

Samisa Abeysinghe, Software Architect, WSO2 Inc. samisa at wso2 dot com

About Author

  • Samisa Abeysinghe
  • VP, Training
  • WSO2 Inc.