Apache Axis2/C User's Guide

Introduction

Welcome to Axis2/C, the Apache Axis2 implementation in C. This User's Guide will help you understand what Axis2/C has to offer and how to get started with it.

What is Axis2/C?

Axis2/C is an effort to implement the Axis2 architecture in C programming language. For more information on the architecture C Specific Architectural Notes are also available.

After months of continued discussion and coding in this direction, Axis2/C now delivers the following key features:

  • Speed - Axis2/C uses its own XML object model and StAX (Streaming API for XML) parsing to achieve significant speed. In addition to that, Axis2/C is inherently benefited by the speed of its implementation language, namely C, compared to Java implementation.

  • Low memory foot print- Axis2 architecture was designed ground-up keeping in mind the low memory foot print. Axis2/C strives to achieve the same with a well designed memory management strategy.

  • AXIOM/C- Axis2/C comes with its own light-weight object model for XML, AXIOM/C which is the C implementation of AXIOM.

  • MEP Support - Supports Message Exchange Patterns (MEPs)

  • Flexibility - Axis2/C architecture gives the developer complete freedom to insert extensions into the engine (using modules and handlers) for custom SOAP header processing.

  • Transport Framework - We have a clean and simple abstraction for integrating and using transports, and the core of the engine is completely transport-independent.

  • Composition and Extensibility - Modules and phases improve support for composability and extensibility. Modules support composability and are able to add support for new WS-* specifications in a simple and clean manner. They are however, not hot deployable as they change the overall behavior of the system.

Axis2/C team is working hard to continuously improve the implementation. Please note that this is an open-source effort. If you feel you have some time to spare, please get involved and lend us a hand! The Axis2/C developer community welcomes your participation and contributions.

Let us know what you think! Please send your feedback on Axis2/C to "axis-c-user@ws.apache.org" and please remember to prefix the subject of the mail with [Axis2].

The following sections will guide on how to write Web service clients and services.

Web Services Using Axis2/C

Before starting, please make sure that you have installed Axis2/C correctly and whether you can run the axis2_http_server located in AXIS2C_HOME/bin (See Installation Guide for details).

Writing Web Services Using Axis2/C

Creating Web Service (Echo service)

First let's see how we can write a simple Web Service (echo service) using Axis2/C's primary interfaces and how to deploy it. For this purpose we will create a Web Service with one operation as follows.

axiom_node_t* axis2_echo_echo(axiom_node_t *echo_node){}

You can have a peek at the complete source code for this example echo service located in the "AXIS2C_HOME/samples/server/echo" directory.

How to write the Web Service?

Writing a new Web Service with Axis2/C involves four steps. Let's take echo service as our example.

  1. Write a echo_skeleton.c file which implements the API given in axis2_svc_skeleton.h header file.

  2. Write the service implementation source file (in this case echo.c service) which implements the actual business logic.
  3. Write a services.xml file to explain the Web service.
  4. Create a folder with the service name under AXIS2C_HOME/services and put the compiled service ( .dll or .so file) for the Web service and services.xml file into that folder.

Step1 :Write the echo_skeleton.c file implementing the axis2_svc_skeleton.h

axis2_svc_skeleton.h header file has the axis2_svc_skeleton_ops_t operations struct which defines four function pointers to be implemented and assigned by a service skeleton.

They are:

int (AXIS2_CALL * init) (axis2_svc_skeleton_t *svc_skeleton,
 const axis2_env_t *env);

axiom_node_t * (AXIS2_CALL* invoke)(axis2_svc_skeleton_t *svc_skeli, 
 const axis2_env_t *env, axiom_node_t *node);

axiom_node_t *(AXIS2_CALL* on_fault)(axis2_svc_skeleton_t *svc_skeli,
 const axis2_env_t *env, axiom_node_t *node);

int (AXIS2_CALL *free)(axis2_svc_skeleton_t *svc_skeli, 
 const axis2_env_t *env);

Let's implement the above functions for echo service.

/* Initialize the service */

int AXIS2_CALL

echo_init(axis2_svc_skeleton_t *svc_skeleton,

                        const axis2_env_t *env)

{

    svc_skeleton->func_array = axis2_array_list_create(env, 0);

    /* Add the implemented operation names of the service to  

     * the array list of functions 

     */


    AXIS2_ARRAY_LIST_ADD(svc_skeleton->func_array, env, "echoString");

    /* Any initialization stuff of echo service should go here */

    return AXIS2_SUCCESS;

}



/*

 * This method invokes the right service method 

 */


axiom_node_t* AXIS2_CALL

echo_invoke(axis2_svc_skeleton_t *svc_skeleton,

            const axis2_env_t *env,

            axiom_node_t *node)

{

    /* Invoke the business logic.

     * Depending on the function name invoke the correct impl method.

     * We have only echo in this sample, hence invoke echo method.

     * To see how to deal with multiple impl methods, have a look at the

     * math sample.

     */


    return axis2_echo_echo(env, node);

}



/* On fault, handle the fault */

axiom_node_t* AXIS2_CALL

echo_on_fault(axis2_svc_skeleton_t *svc_skeli, 

              const axis2_env_t *env, axiom_node_t *node)

{

   /* Here we are just setting a simple error message inside an element 

    * called 'EchoServiceError' 

    */


    axiom_node_t *error_node = NULL;

    axiom_node_t* text_node = NULL;

    axiom_element_t *error_ele = NULL;

    error_ele = axiom_element_create(env, node, "EchoServiceError", NULL, 

        &error_node);

    AXIOM_ELEMENT_SET_TEXT(error_ele, env, "Echo service failed ", 

        text_node);

    return error_node;

}



/* Free the resources used */

int AXIS2_CALL

echo_free(axis2_svc_skeleton_t *svc_skeleton,

            const axis2_env_t *env)

{

    /* Free the function array */

    if(svc_skeleton->func_array)

    {

        AXIS2_ARRAY_LIST_FREE(svc_skeleton->func_array, env);

        svc_skeleton->func_array = NULL;

    }

    

    /* Free the function array */

    if(svc_skeleton->ops)

    {

        AXIS2_FREE(env->allocator, svc_skeleton->ops);

        svc_skeleton->ops = NULL;

    }

    

    /* Free the service skeleton */

    if(svc_skeleton)

    {

        AXIS2_FREE(env->allocator, svc_skeleton);

        svc_skeleton = NULL;

    }



    return AXIS2_SUCCESS; 

}

Now we can write the create function of the echo_service_skeleton as follows:

/*Create function */

axis2_svc_skeleton_t *

axis2_echo_create(const axis2_env_t *env)

{

    axis2_svc_skeleton_t *svc_skeleton = NULL;

    /* Allocate memory for the structs */

    svc_skeleton = AXIS2_MALLOC(env->allocator, 

        sizeof(axis2_svc_skeleton_t));



    svc_skeleton->ops = AXIS2_MALLOC(

        env->allocator, sizeof(axis2_svc_skeleton_ops_t));



    /* Assign function pointers */

    svc_skeleton->ops->free = echo_free;

    svc_skeleton->ops->init = echo_init;

    svc_skeleton->ops->invoke = echo_invoke;

    svc_skeleton->ops->on_fault = echo_on_fault;



    return svc_skeleton;

}

In addition to the above functions, every service must have the following two functions with exactly the same function signature as in xxx_skeleton.c file.

AXIS2_EXPORT int 

axis2_get_instance(axis2_svc_skeleton_t **inst,

                   const axis2_env_t *env)

{

    *inst = axis2_echo_create(env);

    if(!(*inst))

    {

        return AXIS2_FAILURE;

    }

    return AXIS2_SUCCESS;

}



AXIS2_EXPORT int 

axis2_remove_instance(axis2_svc_skeleton_t *inst,

                      const axis2_env_t *env)

{

    axis2_status_t status = AXIS2_FAILURE;

    if (inst)

    {

        status = AXIS2_SVC_SKELETON_FREE(inst, env);

    }

    return status;

}

Axis2/C engine can load the service dll. However, it needs to know which method to call. Since C does not have reflection, we need to have some dll exposing functions known to Axis2/C engine. axis2_get_instance and axis2_remove_instance are the two functions that need to be exposed from a service dll (or any other dll of Axis2/C engine). Axis2/C engine calls axis2_get_instance method, which creates a new service instance, and casts the return pointer to axis2_svc_skeleton interface. Then, the interface methods can be called by Axis2/C engine.

Step2 : Now we can write the echo service in a file echo.c

axiom_node_t *

axis2_echo_echo (const axis2_env_t *env, axiom_node_t *node)

{

    axiom_node_t *text_parent_node = NULL;

    axiom_node_t *text_node = NULL;

    axiom_node_t *ret_node = NULL;



    AXIS2_ENV_CHECK(env, NULL);

   

    /* Expected request format is :-

     * <ns1:echoString xmlns:ns1="http://localhost:9090/axis2/services/echo">

     *      <text>echo5</text>

     * </ns1:echoString>

     */


    if (!node) /* 'echoString' node */

    {

        AXIS2_ERROR_SET(env->error, AXIS2_ERROR_SVC_SKEL_INPUT_OM_NODE_NULL, AXIS2_FAILURE);

        printf("Echo client ERROR: input parameter NULL\n");

        return NULL;

    }



    text_parent_node = AXIOM_NODE_GET_FIRST_CHILD(node, env);

    if (!text_parent_node) /* 'text' node */

    {

        AXIS2_ERROR_SET(env->error, AXIS2_ERROR_SVC_SKEL_INVALID_XML_FORMAT_IN_REQUEST, AXIS2_FAILURE);

        printf("Echo client ERROR: invalid XML in request\n");

        return NULL;

    }

    

    text_node = AXIOM_NODE_GET_FIRST_CHILD(text_parent_node, env);

    if (!text_node) /* actual text to echo */

    {

        AXIS2_ERROR_SET(env->error, AXIS2_ERROR_SVC_SKEL_INVALID_XML_FORMAT_IN_REQUEST, AXIS2_FAILURE);

        printf("Echo client ERROR: invalid XML in request\n");

        return NULL;

    }

    

    if (AXIOM_NODE_GET_NODE_TYPE(text_node, env) == AXIOM_TEXT)

    {

        axiom_text_t *text = (axiom_text_t *)AXIOM_NODE_GET_DATA_ELEMENT(text_node, env);

        if( text && AXIOM_TEXT_GET_VALUE(text , env))

        {

            axis2_char_t *text_str = AXIOM_TEXT_GET_VALUE(text, env);

            printf("Echoing text value  %s \n", text_str);

            ret_node = build_om_programatically(env, text_str);

        }

    }

    else

    {

        AXIS2_ERROR_SET(env->error, AXIS2_ERROR_SVC_SKEL_INVALID_XML_FORMAT_IN_REQUEST, AXIS2_FAILURE);

        printf("Echo client ERROR: invalid XML in request\n");

        return NULL;

    }

        

    return ret_node;

}



/* Builds the response content */

axiom_node_t *

build_om_programatically(const axis2_env_t *env, axis2_char_t *text)

{

    axiom_node_t *echo_om_node = NULL;

    axiom_element_t* echo_om_ele = NULL;

    axiom_node_t* text_om_node = NULL;

    axiom_element_t * text_om_ele = NULL;

    axiom_namespace_t *ns1 = NULL;

    

    ns1 = axiom_namespace_create (env, "http://localhost:9090/axis2/services/echo", "ns1");



    echo_om_ele = axiom_element_create(env, NULL, "echoString", ns1, &echo_om_node);

    

    text_om_ele = axiom_element_create(env, echo_om_node, "text", NULL, &text_om_node);



    AXIOM_ELEMENT_SET_TEXT(text_om_ele, env, text, text_om_node);

    

    return echo_om_node;

}

Step3 :Write the services.xml file

Axis2/C uses "services.xml" file to keep configurations of a Web service. Each Web service deployed in Axis2/C needs a "services.xml" file containing the configurations. Note that services.xml has the same semantics as Axis2 Java's services.xml file. Only difference is that instead of giving package qualified class name, we use the dll name for class attributes.

"services.xml" for echo will be as follows:

<service name="echo">

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

<description>

This is a echo service

</description>



<operation name="echoString">

<parameter name="wsamapping">

http://localhost:9090/axis2/services/echo/echoString

</parameter>

</operation>

</service>

Name of the service will be the name of the folder with the shared library and services.xml. In this example we will have a folder named "echo" in which we have the echo.dll (or libecho.so on Linux platform) and services.xml file.

You can write a services.xml file to include a group of services instead of a single service. This makes management and deployment of a set of related services very easy. At runtime you can share information between these services within a single interaction using the axis2_svc_grp_ctx (Service Group Context ). If you hope to use this functionality, the services.xml file should have following format:

<serviceGroup>

<service name="Service1">

<!-- details for Service1 -->

</service>

<service name="Service2">

<!-- details for Service2 -->

</service>

<module ref="ModuleName" />

<parameter name="serviceGroupParam1" locked="false">value 1</parameter>

</serviceGroup>

Note : Name of the service is a compulsory attribute

Step4 :Create the Web service folder in services folder of the repository.

In Axis2/C , it is required to create a folder with the corresponding service/service group name which will contain the shared library (compiled service) and the services.xml file which describes the Web service. So for this example, we will have to create a folder named "echo", which contains the services.xml file and echo dll.

Step5 :Archive Based Deployment Model

Axis2 uses ".aar" (Axis Archive) file as the deployment package for the Web services. Therefore, for echo service we will use "echo.aar". Note that the name of the service will be the name of the archive file. To create "echo.aar" user can create a zip file containing echo.so and services.xml and rename the zip extension to aar. Then Axis2 understands it as a service archive.

Writing Web Services Skeleton Using Code Generator

WSDL2C tool

Axis2/Java WSDL2C tool supports generation of Axis2/C stub and skeleton. Axis2/Java SVN revision 414253 and later versions provide this facility. A basic guide for the tool can be found here.

We will run the tool with the following parameters and generate the skeleton and other required files to support ADB (Axis Data Binding). In order to run the tool, set all the .jar library files in the Axis2/Java to the classpath. To generate code with no data binding support, just replace -d adb -u with -d none

java org.apache.axis2.wsdl.WSDL2C -uri interoptestdoclitparameters.wsdl -ss -sd -d adb -u 


If you need an XML in/out programming model, you can just ignore the data binding support by setting the following parameters. Give "-l c" option to enable C language code generation.

java org.apache.axis2.wsdl.WSDL2C -uri interoptestdoclitparameters.wsdl -ss -sd -d none

The WSDL interoptestdoclitparameters.wsdl can be found in <axis2_src_dir>/test/resources directory. This is used to generate stub and skeleton code throughout this User's Guide.

Implement the Business Logic

Locate the skeleton source file from the generated files: "axis2_WSDLInteropTestDocLitService.c". You can go through the rest of the guide to add the business logic to the following operations in the WSDL.

  • echoString - Operation that echoes a String value.
  • echoStringArray - Operation that accepts a string array as the input and echoes them back.
  • echoStruct - Operation that accepts a Struct as the input and echoes them back.

Complete skeleton source file for the above operations can be found under <axis2_src_dir>/samples/codegen/server/interop_doc2 directory with the name "axis2_WSDLInteropTestDocLitService.c".

echoString

If you generate the code with data binding support, you will find the following code segment in the "axis2_WSDLInteropTestDocLitService.c". Fill the business logic inside this function as shown below:

axis2_echoStringResponse_t*
axis2_WSDLInteropTestDocLitService_echoString
 (const axis2_env_t* env ,axis2_echoString_t* param6 )
{
 /* Todo fill this with the necessary business logic *}


Once the business logic is filled, it will be as follows. The code is simple and the inline comments provide explanation.

axis2_echoStringResponse_t*
axis2_WSDLInteropTestDocLitService_echoString
 (const axis2_env_t* env ,axis2_echoString_t* param6 )
{
 axis2_echoString_t* echo_in = param6;
 axis2_echoStringResponse_t* echo_out = NULL;

 char* echo_string = NULL;
 
 /* retrieve the string input */
 echo_string = AXIS2_ECHOSTRING_GET_PARAM0 ( echo_in, env );

 /* create the response and set the output string */
 echo_out = axis2_echoStringResponse_create ( env );
 AXIS2_ECHOSTRUCTRESPONSE_SET_RETURN ( echo_out, env, echo_string );

 return echo_out;
}