Understanding Apache Axis2/C Services

If you have ever written an Apache Axis2/C service, there is a 99.9% chance that you have used the codegen tool to generate the code for the service. If so you only need to write your business logic inside functions generated by the the tool.

But there may be situations that you can’t use the codegen tool to get your work done. May be you don’t have a WSDL. Or codegen tool fails generating code for you WSDL (very few chance ;).

In these situations you have to go to writing the complete service manually, I.e. You will write the code for the business logic + Axis2 Skeleton (code to plug the service logic to axis2/c).

Axis2/C service consists on:

  • service.xml – Simple way to describe the service and operation. (Far simpler than a wsdl)
  • Skeleton – c pseudo class inherited from axis2_svc_skeleton
  • Service logic – You service logic per service operation

services.xml

This contain the name and some description of the service, the name of the .dll (in windows) or .so (in linux) to load the service and list of operations with their parameters.

E.g.

<service name="echo">
  <!-- dll or so name of the service -->
  <parameter name="ServiceClass" locked="xsd:false">echo</parameter>
  <description>This is a testing service, to test whether the system is working or not</description> 

  <!-- list of operations -->
  <operation name="echoString">
    <!-- addressing information for the operation-->
    <parameter name="wsamapping">http://ws.dimuthu.org/axis2/echoString</parameter>
  </operation>
</service>

In addition to shown ones, there are a bunch of parameters available to customize your service. Visit Axis2/C manual for more.
Skeleton
This is the code that form the dll for your service. At least you should implement the following set of functions there.

  • Required function to create any axis2/c DLL
    • axis2_get_instance – Call at the creation of the DLL
    • axis2_remove_instance – Call at the removal of the DLL
  • Functions to be implement the axis2_svc_skeleton interface (sorry I’m using the java jargons)
    • init- Initialize the service (execute per start of the server)
    • invoke – invocation of the service (execute per each client request)
    • on_fault – invocation of the fault (execute if invoke returns NULL with error set)
    • free – Freeing the service (execute per stop of the server)
  • In addition to these mandatory functions I will be using axis2_echo_create to create the service skeleton for the echo service.

I will start with showing the structure of the service skeleton functions first.

/**
 * echo free method
 */
int AXIS2_CALL echo_free(
    axis2_svc_skeleton_t * svc_skeleton,
    const axutil_env_t * env);

/**
 * echo invoke method
 */
axiom_node_t *AXIS2_CALL echo_invoke(
    axis2_svc_skeleton_t * svc_skeleton,
    const axutil_env_t * env,
    axiom_node_t * node,
    axis2_msg_ctx_t * msg_ctx);

/**
 * echo init method
 */
int AXIS2_CALL echo_init(
    axis2_svc_skeleton_t * svc_skeleton,
    const axutil_env_t * env);

/**
 * echo on_fault method
 */
axiom_node_t *AXIS2_CALL echo_on_fault(
    axis2_svc_skeleton_t * svc_skeli,
    const axutil_env_t * env,
    axiom_node_t * node);

Note that you have the freedom to give any name to each of these function. In fact you should give a name with your service name as the prefix to avoid name conflicts. Then you can specify service skeleton to take these functions to represent to init, invoke, on_fault, free methods using the following code.

/**
 * you should specify your functions set exactly in the order
 * init, invoke, on_fault, free
 */
static const axis2_svc_skeleton_ops_t echo_svc_skeleton_ops_var = {
    echo_init,
    echo_invoke,
    echo_on_fault,
    echo_free
};

/**
 * Create function
 */
axis2_svc_skeleton_t *
axis2_echo_create(
    const axutil_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));

    /* you give the function pointers array to the ops of the skeleton */
    svc_skeleton->ops = &echo_svc_skeleton_ops_var;

    svc_skeleton->func_array = NULL;

    return svc_skeleton;
}

So you create the service skeleton for your echo service inside the axis2_echo_create function. In fact you need to call this function from the DLL creation function (i.e. ‘axis2_get_instance’ funciton). And in the ‘axis2_remove_instance’ function you call the macro AXIS2_SVC_SKELETON_FREE which in fact call the echo_free function to free the resource at the removal of DLL. Here are the implementation for the DLL creation and removal functions in this particular service.

/**
 * Calls at the creation of the dll.
 */
AXIS2_EXPORT int
axis2_get_instance(
    axis2_svc_skeleton_t ** inst,
    const axutil_env_t * env)
{
    *inst = axis2_echo_create(env);
    if (!(*inst))
    {
        return AXIS2_FAILURE;
    }

    return AXIS2_SUCCESS;
}

/**
 * Calls at the removal of the dll.
 */
AXIS2_EXPORT int
axis2_remove_instance(
    axis2_svc_skeleton_t * inst,
    const axutil_env_t * env)
{
    axis2_status_t status = AXIS2_FAILURE;
    if (inst)
    {
        status = AXIS2_SVC_SKELETON_FREE(inst, env);
    }
    return status;
}

Up to this, it was all about preparing the service skeleton, So where you put your business logic?.

Service logic

Just create a function with the following format.

axiom_node_t *axis2_echo_echo(
    const axutil_env_t * env,
    axiom_node_t * input_node) {

    /* Write your business logic Right Here */
}

Here you extract out the input parameters from the the input_node and build the return node based on your output parameters. (Note that even though this is an echo example you can’t return the same input node as the output node, instead you have to replicate the input node and return it to avoid the double freeing)
Since this particular example you only have one operation you can call your business logic right inside of your echo_invoke function. But it doens’t work for cases where multiple operations present. Here is the right way to do it.

/*
 * This method invokes the right service logic
 */
axiom_node_t *AXIS2_CALL
echo_invoke(
    axis2_svc_skeleton_t * svc_skeleton,
    const axutil_env_t * env,
    axiom_node_t * node,
    axis2_msg_ctx_t * msg_ctx)
{
	axis2_op_ctx_t *operation_ctx = NULL;
	axis2_op_t *operation = NULL;
	axutil_qname_t *op_qname = NULL;
	axis2_char_t *op_name = NULL;

	/* logic to get the op name from message context */
	operation_ctx = axis2_msg_ctx_get_op_ctx(msg_ctx, env);
	operation = axis2_op_ctx_get_op(operation_ctx, env);
	op_qname = (axutil_qname_t *)axis2_op_get_qname(operation, env);
	op_name = axutil_qname_get_localpart(op_qname, env);

	/* compare it with our operation name, this is useful when
	   there are multiple operations exist */
	if (op_name)
	{
		if (!axutil_strcmp(op_name, "echoString"))
		{
			return axis2_echo_echo(env, node);
		}
	}

	/* set error in the failure path */
	AXIS2_ERROR_SET(env->error, AXIS2_ERROR_OP_NAME_MISSING, AXIS2_FAILURE);

	/* log the error */
	AXIS2_LOG_ERROR( env->log, AXIS2_LOG_SI, "op name %s is not known", op_name);

	return NULL;
}

So that’s the part you write the code. Compile the code linking necessary libraries in the lib directory of your axis2/c pack and don’t forget to include the necessary axis2/c headers.

  • Necessary libraries for linux (Note: Some libraries are not directly called from your code, but it s better link all as some systems may complain about missing symbols in run time)
    • libaxutil.so
    • libaxis2_axiom.so
    • libaxis2_engine.so
    • libaxis2_parser.so
    • libpthread.so
    • libaxis2_http_sender.so
    • libaxis2_http_receiver.so
    • libguththila.so
  • Necessary libraries for Windows
    • axiom.lib
    • axis2_engine.lib
    • axis2_parser.lib
    • axutil.lib
  • Necessary header files (for both windows and linux)
    • axis2_svc_skeleton.h – To create instance of svc_skeleton
    • axis2_msg_ctx.h – Required in retrieving the dispatched operation name

If you follow this post up to now, this is the time you deploy the service with Axis2/C. Create a directory called ‘echo’ inside the services directory of axis2 repository (the root directory of you put axis2/c binary) and copy the services.xml and the dll in to that. (The DLL should be in the name echo.dll or libecho.so, if you change the dll name you should update the services.xml “ServiceClass” parameter).

Start the axis2 simple server (which is located in the <axis2_c_repo>/bin) and you are done. Your service will be deployed in “http://localhost/axis2/services/echo”. Use this endpoint to access your brand new service.

This entry was posted in axis2/c, Tutorial/Guide, web services and tagged , , , , . Bookmark the permalink.

2 Responses to Understanding Apache Axis2/C Services

  1. Jim Snook says:

    Question:

    Once loaded does an Apache2/C service stay loaded for faster performance on subsequent calls?

  2. dimuthu says:

    Hi Jim,
    Yes. Axis2/C services (the dlls) are loaded only at the startup. If your question is to check whether the service caches it’s output to better performance, then the answer is no. So In theory first and all the subsequent requests take same amount of time to process the requests. But since most of the loadings are done in the server start, you can still expect good performance for all requests.

    Thanks,
    Dimuthu

Leave a Reply

Your email address will not be published. Required fields are marked *