Axis2/C入门教程之二(服务端实现详细分析)

本文承接Axis2/C入门教程之一,详细分析服务端代码hello_svc.c的实现。

hello_svc.c代码如下:

#include <axis2_svc_skeleton.h>
#include <axutil_log_default.h>
#include <axutil_error_default.h>
#include <axutil_array_list.h>
#include <axiom_text.h>
#include <axiom_node.h>
#include <axiom_element.h>
#include <stdio.h>

axiom_node_t *axis2_hello_greet(const axutil_env_t *env, axiom_node_t *node);

int AXIS2_CALL hello_free(axis2_svc_skeleton_t *svc_skeleton, const axutil_env_t *env);

axiom_node_t* AXIS2_CALL hello_invoke(axis2_svc_skeleton_t *svc_skeleton, const axutil_env_t *env, axiom_node_t *node, axis2_msg_ctx_t *msg_ctx);

int AXIS2_CALL hello_init(axis2_svc_skeleton_t *svc_skeleton, const axutil_env_t *env);

axiom_node_t* AXIS2_CALL hello_on_fault(axis2_svc_skeleton_t *svc_skeli, const axutil_env_t *env, axiom_node_t *node);

axiom_node_t * build_greeting_response(const axutil_env_t *env, axis2_char_t *greeting);

axiom_node_t * axis2_hello_greet(const axutil_env_t *env, axiom_node_t *node)
{
    axiom_node_t *client_greeting_node = NULL;
    axiom_node_t *return_node = NULL;

    AXIS2_ENV_CHECK(env, NULL);

    if (node)

    {
        client_greeting_node = axiom_node_get_first_child(node, env);
        if (client_greeting_node &&
                axiom_node_get_node_type(client_greeting_node, env) == AXIOM_TEXT)
        {
            axiom_text_t *greeting = (axiom_text_t *)axiom_node_get_data_element(client_greeting_node, env);
            if (greeting && axiom_text_get_value(greeting , env))
            {
                const axis2_char_t *greeting_str = axiom_text_get_value(greeting, env);
                printf("Client greeted saying \"%s\" \n", greeting_str);
                return_node = build_greeting_response(env, "Hello Client!");
            }
        }
    }
    else
    {
        AXIS2_ERROR_SET(env->error, AXIS2_ERROR_SVC_SKEL_INVALID_XML_FORMAT_IN_REQUEST, AXIS2_FAILURE);
        printf("ERROR: invalid XML in request\n");
        return_node = build_greeting_response(env, "Client! Who are you?");
    }

    return return_node;
}

axiom_node_t * build_greeting_response(const axutil_env_t *env, axis2_char_t *greeting)
{
    axiom_node_t* greeting_om_node = NULL;
    axiom_element_t * greeting_om_ele = NULL;

    greeting_om_ele = axiom_element_create(env, NULL, "greetResponse", NULL, &greeting_om_node);

    axiom_element_set_text(greeting_om_ele, env, greeting, greeting_om_node);

    return greeting_om_node;
}

static const axis2_svc_skeleton_ops_t hello_svc_skeleton_ops_var = 
{
    hello_init,
    hello_invoke,
    hello_on_fault,
    hello_free
};

axis2_svc_skeleton_t * axis2_hello_create(const axutil_env_t *env)
{
    axis2_svc_skeleton_t *svc_skeleton = NULL;
    svc_skeleton = AXIS2_MALLOC(env->allocator,
            sizeof(axis2_svc_skeleton_t));

    svc_skeleton->ops = &hello_svc_skeleton_ops_var;
    svc_skeleton->func_array = NULL;

    return svc_skeleton;
}

int AXIS2_CALL hello_init(axis2_svc_skeleton_t *svc_skeleton, const axutil_env_t *env)
{
    svc_skeleton->func_array = axutil_array_list_create(env, 0);
    axutil_array_list_add(svc_skeleton->func_array, env, "helloString");
    return AXIS2_SUCCESS;
}

axiom_node_t* AXIS2_CALL hello_invoke(axis2_svc_skeleton_t *svc_skeleton, const axutil_env_t *env, axiom_node_t *node, axis2_msg_ctx_t *msg_ctx)
{
    return axis2_hello_greet(env, node);
}

axiom_node_t* AXIS2_CALL hello_on_fault(axis2_svc_skeleton_t *svc_skeli, const axutil_env_t *env, axiom_node_t *node)
{
    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;
}

int AXIS2_CALL hello_free(axis2_svc_skeleton_t *svc_skeleton, const axutil_env_t *env)
{
    if (svc_skeleton->func_array)
    {
        axutil_array_list_free(svc_skeleton->func_array, env);
        svc_skeleton->func_array = NULL;
    }

    if (svc_skeleton)
    {
        AXIS2_FREE(env->allocator, svc_skeleton);
        svc_skeleton = NULL;
    }

    return AXIS2_SUCCESS;
}

AXIS2_EXPORT int axis2_get_instance(axis2_svc_skeleton_t **inst, const axutil_env_t *env)
{
    *inst = axis2_hello_create(env);
    if (!(*inst))
    {
        return AXIS2_FAILURE;
    }

    return AXIS2_SUCCESS;
}

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;
}

使用Axis/C实现服务是需要遵循的步骤包括:

  1. 实现和服务操作对应的函数。在示例中,实现了一个实现“greet”的操作,该函数名为axis2_hello_greet。
  2. 实现由axis2_svc_skeleton接口定义的一组函数,包括init、invoke、on_fault、free。在示例中,分别实现为hello_init,hello_invoke,hello_on_fault和hello_free
  3. 实现create函数(axis2_hello_create),该函数用于创建服务框架的实例。create函数将创建一个axis2_svc_skeleton并分配相应的函数指针,将axis2_svc_skeleton interface接口映射到上步中我们实现的接口中。
  4. 实现axis2_get_instance和axis2_remove_instance函数,这些函数用于通过引擎创建和销毁服务实例,每个服务必须定义这些函数。
  5. 为服务编写services.xml文件。services.xml可视为服务的发布描述文件,我们至少应该在其中配置服务器名称(name)、操作(operations)以及包含在服务实现中的共享库文件名称(shared library file name)。

操作(operation)实现:
axis2_hello_greet函数实现了greet操作的业务逻辑,hello_invoke函数直接调用该函数。该函数以axiom_node的形式接收有效载荷(payload),处理它以理解请求逻辑,以axiom_node的形式准备响应并将其返回。
函数代码:

axiom_node_t * axis2_hello_greet(const axutil_env_t *env, axiom_node_t *node)
{
    axiom_node_t *client_greeting_node = NULL;
    axiom_node_t *return_node = NULL;

    AXIS2_ENV_CHECK(env, NULL);

    if (node)

    {
        client_greeting_node = axiom_node_get_first_child(node, env);
        if (client_greeting_node &&
                axiom_node_get_node_type(client_greeting_node, env) == AXIOM_TEXT)
        {
            axiom_text_t *greeting = (axiom_text_t *)axiom_node_get_data_element(client_greeting_node, env);
            if (greeting && axiom_text_get_value(greeting , env))
            {
                const axis2_char_t *greeting_str = axiom_text_get_value(greeting, env);
                printf("Client greeted saying \"%s\" \n", greeting_str);
                return_node = build_greeting_response(env, "Hello Client!");
            }
        }
    }
    else
    {
        AXIS2_ERROR_SET(env->error, AXIS2_ERROR_SVC_SKEL_INVALID_XML_FORMAT_IN_REQUEST, AXIS2_FAILURE);
        printf("ERROR: invalid XML in request\n");
        return_node = build_greeting_response(env, "Client! Who are you?");
    }

    return return_node;
}

注:有效载荷(payload):In computing and telecommunications, the payload is the part of transmitted data that is the actual intended message. The payload excludes any headers or metadata sent solely to facilitate payload delivery.
个人理解有效载荷即包裹在传输数据对象中实际有意义的信息。

框架Create方法:axis2_hello_create创建并返回一个axis2_svc_skeleton实例。该函数最重要的部分是函数指针赋值。他们用于将接口操作映射为对应的实现函数。这部分工作通过将服务框架的ops成员(hello_svc_skeleton_ops_var)分配给ops结构变量的地址(axis2_svc_skeleton::ops)来完成。
函数代码:

static const axis2_svc_skeleton_ops_t hello_svc_skeleton_ops_var = 
{
    hello_init,
    hello_invoke,
    hello_on_fault,
    hello_free
};

axis2_svc_skeleton_t * axis2_hello_create(const axutil_env_t *env)
{
    axis2_svc_skeleton_t *svc_skeleton = NULL;
    svc_skeleton = AXIS2_MALLOC(env->allocator,
            sizeof(axis2_svc_skeleton_t));

    svc_skeleton->ops = &hello_svc_skeleton_ops_var;
    svc_skeleton->func_array = NULL;

    return svc_skeleton;
}
调用操作实现:
服务框架的hello_invoke函数是调用操作的入口点。示例中只有一个greet操作,所以代码很简单。如果有多个操作,则需要从消息上下文中查找信息以确实要执行的具体操作。示例代码:
axiom_node_t* AXIS2_CALL hello_invoke(axis2_svc_skeleton_t *svc_skeleton, const axutil_env_t *env, axiom_node_t *node, axis2_msg_ctx_t *msg_ctx)
{
    return axis2_hello_greet(env, node);
}
Axis2/C调用的invoke函数参数包括一个服务框架指针,一个环境指针,一个包含请求有效载荷的axiom_node的实例和一个包含上下文信息的axis2_msg_ctx的实例。可以从上下文信息中提取与传入消息相关的必要信息。

Axis2/C期望invoke函数返回一个表示响应有效载荷的axiom_node指针。

服务描述(services.xml)
services.xml文件包含服务器启动时由Axis2/C发布引擎读取的服务详细信息。services.xml示例如下:

<service name="hello">
	<parameter name="ServiceClass" locked="xsd:false">hello</parameter>
	<description>Quick start guide hello service sample.</description>
	<operation name="greet"/>
</service>
上述配置指定服务名称为hello。
ServiceClass参数值将由发布引擎被映射到具体服务实现上,如本例中ServiceClass值为hello,则服务将映射为libhello.so(Linux)或hello.dll(MS Windows)。
description元素包含服务简要描述。(用于通过浏览器访问时显示,地址:http://localhost:9090/axis2/services)

一个service元素中可包含一个或多个operation元素。示例中仅有一个名为greet的操作。

编译服务:
Linux:

gcc -shared -olibhello.so -I$AXIS2C_HOME/include/axis2-1.6.0/ -L$AXIS2C_HOME/lib -laxutil -laxis2_axiom -laxis2_parser -laxis2_engine -lpthread -laxis2_http_sender -laxis2_http_receiver hello_svc.c
MS Windows:
编译:
cl.exe /D "WIN32" /D "_WINDOWS" /D "_MBCS" /D "AXIS2_DECLARE_EXPORT" /D "AXIS2_SVR_MULTI_THREADED" /w /nologo /I %AXIS2C_HOME%\include /c hello_svc.c
链接:
link.exe /nologo /LIBPATH:%AXIS2C_HOME%\lib axutil.lib axiom.lib axis2_parser.lib axis2_engine.lib /DLL /OUT:hello.dll *.obj
发布服务:
在AXIS2C_HOME/services下建立服务同名文件夹(本例为hello),并将services.xml和库文件(libhello.so(Linux)或hello.dll(MS Windows))复制到该文件夹中。
启动Axis服务器AXIS2C_HOME/services/axis2_http_server.exe,通过浏览器访问http://localhost:9090/axis2/services浏览已发布的服务。

为服务提供WSDL
Axis2/C不支持WSDL动态生成,但是可以通过将用于生成服务框架的合约(contract,这里应该指的是静态WSDL文件)和相应的服务建立连接来实现该功能。可以通过两种方式实现上述目标:
  • 将WSDL文件添加到服务DLL所在的文件夹中,WSDL文件名称需与服务名称一致
  • 在services.xml中提供WSDL文件路径,例如:
<parameter name="wsdl_path">PATH</parameter>
静态WSDL文件可以通过在service结尾增加?wsdl访问,例如对与Calculator服务,可通过浏览器访问http://localhost:9090/axis2/services/Calculator?wsdl。

猜你喜欢

转载自blog.csdn.net/angel_lys/article/details/80275204