Feeds:
Posts
Comments

Posts Tagged ‘Axis2/C’

NTLM auth support for Axis2/C

I have added NTLM authentication support for Axis2/C recently.

I have added this support by writing a dynamically loadable library called axis2_ntlm which wrap an existing NTLM library. Currently it wrap a NTLM library called Heimdal [1].

However one can write his own wrapper for the external NTLM library of his choice.

When using Heimdal, if I send same messages to a server requiring NTLM authentication using different connections, I noticed that some authentication requests fail with server responding that the provided credentials are not valid, even when the provided credentials are perfectly valid. If I repeat the same request again it is authorized. This intermittent failure come from Heimdal, because when linked with a wrapper for a different external library like libntlm[2] it works fine. It seems that Heimdal no longer actively support it’s NTLM library, so I encourage ppl to use libntlm instead. I have attached the code for libntlm wrapper for Axis2/C NTLM below[3] as a text file. Also you can download this libntlm wrapper for Axis2/C at [4]. One use this code to compile a wrapper for Axis2/C by studing how it is done for Heimdal. Note the additions to configure.ac, samples/configure.ac when it is done for Heimdal.

[1] http://www.h5l.org/

[2] http://josefsson.org/libntlm/

[3] https://damithakumarage.files.wordpress.com/2011/06/libntlm_wrapper-c.doc

[4] https://github.com/damitha23/libntlm-axis2c.git

Read Full Post »

Dinesh Premalal in his blog post on Axis2/C with cbthread explain that using cbthreads he ran about 100,000 concurrent requests using Axis2/C client. The reason for this achievement is because of the non-preemptive and continuation-based threads implemented with cbthreads. However in his next blog Axis2/C with pthreads he goes on explaining how he used similer Axis2/C client with pthreads and could only run about 300 concurrent requests. This is predictable because each thread in pthreads consume considerable amount of memory in it’s stack-based nature implementation. In adition Dinesh’s program for pthreads does not use a thread pool which cause the the program to create threads until system resources exhausted.

In cbthreads home page it is mentioned

“A downside of having a non-preemptive thread system is that you cannot assign threads to blocking system calls as you can with pthreads, and have threads run while the system call blocks. If you do, the entire process blocks.”

This reason highly discourage using cbthreads for Axis2/C. Besides pthreads is provenly portable among numerous platforms. In my opinion what Axis2/C needs is a good thread pool implementation using pthreads.

In search of a good thread pool using pthreads I came across this one which seems to be a good candidate. I wrote a similer client as Dinesh wrote for testing cbthreads using this pthreads thread pool.

Axis2/C client With pthread threadpool for 10,000 request
real 1m9.974s
user 0m34.890s
sys 0m9.297s

Axis2/C client with cbthreads for 10,000 requests
real 3m29.835s
user 0m13.621s
sys 0m8.857s

This shows that using pthreads with a good thread pool is the right way to use pthreads for Axis2/C with it’s all proven features.

To try this with Axis2/C echo.cpp client program do the following steps.

1. Download the pthread pool from here and build the source by executing make in the root source folder.

2. Install Axis2/C. I assume you installed it into /axis2c/deploy.(You need Axis2/C 1.6 or build it from svn source)

3. Copy the following echo.cpp source file and build.sh build script into the same source folder.

4. Execute build.sh and run the exectuable program.

One thing to notice about the echo.cpp code is creating service client for each thread. Main overhead of creating service client is associated with creating the Axis2/C main cofiguration context. This is reduced by creating each service client passing the same configuratin context as following.

conf_ctx = axis2_svc_client_get_conf_ctx(svc_client, env);
svc = axis2_svc_client_get_svc(svc_client, env);
svc_client = axis2_svc_client_create_with_conf_ctx_and_svc(env, client_home, conf_ctx, svc);

echo.cpp

#include <TThreadPool.hh>
#include <stdio.h>
#include <stdio.h>
#include <axiom.h>
#include <axis2_util.h>
#include <axiom_soap.h>
#include <axis2_client.h>
#include <axis2_conf_ctx.h>
#include <axis2_svc.h>

#define NTHREADS 10000
#define POOLSIZE 100

axiom_node_t *
build_om_payload_for_echo_svc(const axutil_env_t * env);

typedef struct _svcinfo {
    const axutil_env_t *env;
    axis2_svc_client_t *svc_client;
    axis2_char_t *address;
}svc_info_t;

static void svc_client_send_request (void *v);

class echo : public TThreadPool::TJob
{
public:
   echo ( int id )
       : TThreadPool::TJob( id )
   {
   }

public:
  void run ( void * arg )
  {
      svc_client_send_request(arg);
  }
};

int
main(int argc, char **argv)
{
    const axutil_env_t *env = NULL;
    const axis2_char_t *address = NULL;
    axis2_endpoint_ref_t *endpoint_ref = NULL;
    axis2_options_t *options = NULL;
    const axis2_char_t *client_home = NULL;
    axis2_svc_client_t *svc_client = NULL;
    int i = 0;
    TThreadPool * pool = NULL;
    axis2_conf_ctx_t *conf_ctx = NULL;
    axis2_svc_t *svc = NULL;

    /* Set up the environment */
    env = axutil_env_create_all("echo.log", AXIS2_LOG_LEVEL_TRACE);

    /* Set end point reference of echo service */
    address = "http://localhost:9091/axis2/services/echo";
    if (argc > 1)
    {
        if (axutil_strcmp(argv[1], "-h") == 0)
        {
            printf("Usage : %s [endpoint_url]\n",
                   argv[0]);
            printf("use -h for help\n");
            return 0;
        }
        else
        {
            address = argv[1];
        }
    }
    printf("Using endpoint : %s\n", address);

    /* Set up deploy folder. It is from the deploy folder, the configuration is picked up
     * using the axis2.xml file.
     * In this sample client_home points to the Axis2/C default deploy folder. The client_home can
     * be different from this folder on your system. For example, you may have a different folder
     * (say, my_client_folder) with its own axis2.xml file. my_client_folder/modules will have the
     * modules that the client uses
     */
    client_home = AXIS2_GETENV("AXIS2C_HOME");
    if (!client_home || !strcmp(client_home, ""))
        client_home = "../..";

    /* Create service client */
    svc_client = axis2_svc_client_create(env, client_home);
    if (!svc_client)
    {
        printf
            ("Error creating service client, Please check AXIS2C_HOME again\n");
        AXIS2_LOG_ERROR(env->log, AXIS2_LOG_SI,
                        "Stub invoke FAILED: Error code:" " %d :: %s",
                        env->error->error_number,
                        AXIS2_ERROR_GET_MESSAGE(env->error));
        return -1;
    }

    /* Set service client options */
    axis2_svc_client_set_options(svc_client, env, options);

    /* Engage addressing module */
    axis2_svc_client_engage_module(svc_client, env, AXIS2_MODULE_ADDRESSING);

    conf_ctx = axis2_svc_client_get_conf_ctx(svc_client, env);
    svc = axis2_svc_client_get_svc(svc_client, env);
    pool = new TThreadPool( POOLSIZE );

    for (i = 0; i < NTHREADS; i++)
    {
        echo *job = new echo(i);
        axis2_svc_client_t *t_client = NULL;
        svc_info_t *svcinfo = NULL;

        svcinfo = (svc_info_t *) malloc (sizeof (svc_info_t));
        if (!svcinfo)
        {
            fprintf (stderr, "no enough memory\n");
            exit (1);
        }
        svcinfo->env = env;
        t_client = axis2_svc_client_create_with_conf_ctx_and_svc(env, client_home, conf_ctx, svc);
        svcinfo->svc_client = t_client;
        svcinfo->address = (axis2_char_t *) address;
        pool->run( job, (void *) svcinfo, true );
    }

    pool->sync_all();
    axis2_svc_client_free(svc_client, env);
    printf("success\n");
    return 0;
}

/* build SOAP request message content using OM */
axiom_node_t *
build_om_payload_for_echo_svc(
    const axutil_env_t * env)
{
    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;
    axis2_char_t *om_str = NULL;

    ns1 =
        axiom_namespace_create(env, "http://ws.apache.org/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, "Hello World!", text_om_node);
    om_str = axiom_node_to_string(echo_om_node, env);

    if (om_str)
    {
        AXIS2_FREE(env->allocator, om_str);
        om_str = NULL;
    }
    return echo_om_node;
}

static void
svc_client_send_request (void *v)
{
    svc_info_t *svcinfo;
    const axutil_env_t *env;
    axis2_svc_client_t *svc_client;
    axiom_node_t *payload = NULL;
    axiom_node_t *ret_node;
    axis2_options_t *options = NULL;
    axis2_endpoint_ref_t *endpoint_ref = NULL;
    axis2_char_t *address = NULL;

    svcinfo = (svc_info_t *)v;
    env = svcinfo->env;
    svc_client = svcinfo->svc_client;
    address = svcinfo->address;

    options = (axis2_options_t *) axis2_svc_client_get_options(svc_client, env);
    if(!options)
    {
        options = axis2_options_create(env);
        axis2_svc_client_set_options(svc_client, env, options);
    }

    endpoint_ref = axis2_endpoint_ref_create(env, address);

    axis2_options_set_to(options, env, endpoint_ref);
    axis2_options_set_action(options, env,
                             "http://ws.apache.org/axis2/c/samples/echoString");

    /* Build the SOAP request message payload using OM API. */
    payload = build_om_payload_for_echo_svc(env);

    /* Send request */
    ret_node = axis2_svc_client_send_receive(svc_client, env, payload);

    if (ret_node)
    {
        axiom_node_free_tree(payload, env);
        axiom_node_free_tree(ret_node, env);
        ret_node = NULL;
        printf("Invoke SUCCESSFUL\n");
    }
    else
    {
        AXIS2_LOG_ERROR(env->log, AXIS2_LOG_SI,
                        "Stub invoke FAILED: Error code:" " %d :: %s",
                        env->error->error_number,
                        AXIS2_ERROR_GET_MESSAGE(env->error));
        printf("echo client invoke FAILED\n");
    }

    if(svcinfo)
    {
        AXIS2_FREE(env->allocator, svcinfo);
        svcinfo = NULL;
    }
    axis2_svc_client_free(svc_client, env);
}

build.sh

gcc *.cpp -oecho -I./src libthrpool.a -lstdc++ -lpthread -L/axis2c/deploy/lib -lm -laxis2_engine -laxutil -laxis2_axiom -I. -I/axis2c/deploy/include/axis2-1.6.0/

Read Full Post »

The draft named Robust design techniques for C Programs by David Turner introduce a modern languages like approach for exception handling in C. It speaks about four approaches for error handling in C.

1. Being extremely paranoid.

2. Implementing transactions which in concept similer to commit/rollback approach in databases.

3. Structured Exception Handling(SEH)

4. Cleanup stack exception handling(CSEH)

Of the four he recommends 4 th approach as the best. He expect that this paper will stir interest in this technique and motivate enough people to introduce such features in the libraries and programs they write..

I recently wrote an article for WSO2 Oxygentank called “Using WSF/C extended error handling mechanism in your own modules and applications.” which basically explain how WSF/C stack handle errors. From the above four approaches what WSF/C has taken is paranoid approach like many popular libraries programmed in C. However I belive that WSF/C could also greatly benefit from CSEH approach if carefully adopted. Infact James Clerk hinted about this approach in the early days of Axis2/C design discussions.

Read Full Post »