login button

Building Axis2/C Web Services with the help of Axis2/Java Tools

Story :

Level : Project : Realm :

In this introductory tutorial, Dimuthu Gamage explains how to use Axis2/Java tools to ease the development of Axis2/C Web Services.

 

Applies To

Axis2/C 1.0.0 and higher
Environment Windows or Linux

 

Introduction

Apache Axis2/C and Axis2/Java are two implementations of the same architecture of a Web service framework, implemented in different two languages. From the two, Axis2/Java contains many popular tools that provide great help for the development of Web services that is not limited to Axis2/Java developers but also for Axis2/C developers. In this tutorial, I will illustrate the use of two such Java tools, namely, WSDL2C and Java2WSDL tools to write Axis2/C Web services that includes both a client and a service.

When you begin to write a Web service you can choose from one of the two approaches available to developing a Web service. I.e. either the code-first approach or the contract-first approach. In code first approach, initially, you will code the interfaces you want to publish as the Web service. Then you generate the WSDL which then become the contract (at least the major part of the contract). On the other hand, with the contract-first approach, you first start with the WSDL and then generate code to provide and consume the service. From these two approaches you need to carefuly select the best, based on your problem environment. You can find some discussions on this topin in a few the articles published on Oxygen Tank[1], as well.

WSDL2C is a tool that generates a 'C' code for a given WSDL. This is the tool you choose if you are following the contract-first approach. This will generate most of the code for you in terms of consuming or providing the service. Therefore, you will only need to worry about writing your business logic thereafter. WSDL2C tool acts as a 'C' extension to the WSDL2Code tool and both are shipped with the Axis2/Java project. WSDL2C also supports ADB Databinding[2] that maps schema types in your WSDL to native C types and structures. This hides the complexity in handling XML and allows you to play around with just simple 'C' types.

Java2WSDL is a tool to generate the WSDL from Java code. It is the tool to be selected when you use the code-first approach. We still don't have a similar tool for C, like may be C2WSDL and I don't believe that there will be any in the near future. This kind of tool is hard to implement, mainly due to the lack of support for 'Reflection' in the 'C' language. Hence, code first approach is not an option for 'C' developers. So they have to start by writing the WSDL first. But because WSDLs are really complex, no one will try to write it by hand but instead, will use help from some kind of a tool. This is where Java2WSDL tool comes handy.

In this tutorial, I'm illustrating you a kind of hybrid approach but in fact, more or less the contract-first approach to develop an Axis2/C Web service. That is, first we write a simple Java interface to expose as the Web serivce and then we generate the WSDL from it using the Java2WSDL tool. Finally, we generate the 'C' code using WSDL2C tool. There are users[5] in the Axis2/C list who have successfully used this approach in developing Axis2/C Web services.

In this tutorial, I will take you through the following steps:

 

Table of Contents

  1. Write a Java interface - Using simple types for the operation arguments
  2. Generate the WSDL.
  3. Generate the 'C' code of the service.
  4. Writing your business logic for the service
  5. Generate the 'C' code of the client.
  6. Writing your 'C' Client.

 

Step1 - Writing a Service Interface in Java

Think you are an organizer of a sporting event like a soccer tournament or a base ball tournament. You want to publish your score board to the public media like the television, news papers and other sport Web sites. So you decided to write a Web service, first with a very simple operation that just publishes the total score of a given team. As I mentioned earlier, you will start by writing a simple Java program to define your service interface.


// The namespace your service belong to.. -1
package org.organizers.sports;

// The name of the service.. -2
interface ScoreBoard
{
    // The service operation, only simple types used for arguments and return types.. -3
    public int getTotalScore(int matchNumber, String team);
}



The following is an explanation of the numbered comments:



1. This is the namespace of the classes declared in this file. For C programmers who have not done a lot of Java programming, they may feel a little strange here. But don't worry, you will never need to remember this namespace when you are writing the 'C' code. The code generated will take care of this. But for now, remember to put your Java file in the same directory hierarchy as the namespace, i.e. in this case inside org/organizers/sports.



2. This is the interface you are going to expose as the Web service. I choose the name 'ScoreBoard'. Remember to save your file in the same name as the interface name (that is the Java rule!). In this case, it is 'ScoreBoard.java'. (It will be in org/organizers/sports/ScoreBoard.java')



3. These are the operations you want to have in your service. For step1, we only have simple types for arguments and return types.



After you finish writing the interface, try to compile it. For that, you remain in the directory where you put 'ScoreBoard.java' and then type:

javac ScoreBoard.java



If you have done well, you will have ScoreBoard.class in the directory.  This is all you need to do with Java in order to develop the service, unless, you find that you need to do some changes to the Web service interface later.

 

Step2 - Generate the WSDL



Now it is the time to generate the WSDL from the Java class. You will need Apache Axis2/Java in your computer to use the tool.  It is recommended that you download Axis2/Java nightly build[6], rather than the releases, because this will surely have updated support for all the constructs in WSDL and XML Schema.



Next, make sure you have setup Axis2/Java correctly.  Simply set the <path to axis2 java home>/bin directory to your PATH environment variable.



Then Go to the top of the directory hierarchy of your Java project, (in this case you should be in one directory level above the org directory in org/organizers/sports hierarchy).



Then type the following command[7].

Java2WSDL.sh -cn org.organizers.sports.ScoreBoard



If you have setup things correctly, you should have ScoreBoard.wsdl file generated in your current directory. Just open it and check whether it seems like your expected WSDL.

 

Step3 - Generate the C Service

Now its time to generate the 'C' code which will help you develop your service from WSDL2C tool[7]. Unfortunately, there are no shell or batch scripts shifted with Axis2/Java to run WSDL2C. You can use the script found in tools/codegen/javatool/WSDL2C.sh (or WSDL2C.bat) of the Axis2/C distribution to run the tool. Before you use that script, you need to set the environment variable AXIS2_HOME to the root directory of the Axis2/Java distribution. For more detail on this regards, read the README in tools/codegen/javatool directory of the Axis2/C distribution.



To generate the C skeleton, use following command:

WSDL2C.sh -uri ScoreBoard.wsdl -u -ss -sd

or

WSDL2C.bat -uri ScoreBoard.wsdl -u -ss -sd



Here '-u' is to say that we want each structure in separate files. Currently, this is the only mode WSDL2C support. Option '-ss' says that we want to generate the server side code. (We will look in to generating client side code later.) Option '-sd' is to generate the Service Descriptor(services.xml), which contains information about the service name, operations and other meta data which are required to deploy the service in Axis2/C. In addition to these, we are making use of a default values "adb" to the option '-d' - that stands for 'Databinding'  and "src" to the option '-o' that stands for the output directory. ADB is the only databinding schema supported by the WSDL2C tool.



If you followed me up right thus far, you should have by now generated the following set of files..

 -    src 

         |

         +---------- adb_getTotalScore.c

         |

         +---------- adb_getTotalScore.h

         |

         +---------- adb_getTotalScoreResponse.c

         |

         +---------- adb_getTotalScoreResponse.h

         |

         +---------- axis2_skel_ScoreBoard.c

         |

         +---------- axis2_skel_ScoreBoard.h

         |

         +---------- axis2_svc_skel_ScoreBoard.c

         |

         +---------- build.sh

 -    resources

         |

         +---------- services.xml


Step4 - Writing the Service Logic

After having all of this files generated it is worth to have a look at what each of this file do..

adb_getTotalScore.h -

    As the name suggests, this is the interface that allows you to create the request XML for the getTotalScore operation. If you can open the WSDL, you will find that the XML Schema component of the request XML will be something like this:


            <xs:element name="getTotalScore">
                <xs:complextype>
                    <xs:sequence>
                        <xs:element minoccurs="0" name="param0" nillable="true" type="xs:int"></xs:element>
                        <xs:element minoccurs="0" name="param1" nillable="true" type="xs:string"></xs:element>
                    </xs:sequence>
                </xs:complextype>
            </xs:element>

In order to prepare this XML, what you would need to do is to build the adb object adb_getTotalScore and fill your desired value for param0 and param1. This can be done using the following logic..


    /* env is the environment, which should be passed to all axis2/c functions */
    adb_getTotalScore_t* my_get_total_score = adb_getTotalScore_create(env);
    
    /* Set the Match Number */
    adb_getTotalScore_set_param0(my_get_total_score, env, 5);

    /* Set the Team to get the score */
    adb_getTotalScore_set_param1(my_get_total_score, env, "Sri Lanka");
 

    /* Some application of the class */

    /* After using this object in your code, you can free the content of the object using */
    adb_getTotalScore_free(my_get_total_score, env);

From the server side, what you need to do is to handle this request adb object, rather than building it. I.e. the code generated will build the request adb object, for you to consume.

There you will find the following two functions that can be used to extract the information from the adb object:


    int match_number;
    axis2_char_t* team_name;

    match_number = adb_getTotalScore_get_param0(my_get_total_score, env);
    team_name = adb_getTotalScore_get_param1(my_get_total_score, env);

adb_getTotalScoreResponse.h -

This is the adb header corresponding to the response. Here is how you use this to set the response data.

You already have the values of the match number and the team name from the request, So you query the databases for the scores of the team for a particular match and build the response adb object using that.


/* say you have the score in the variable 'score' */

/* create the response */
 adb_getTotalScoreResponse_t * response;
response = adb_getTotalScoreResponse_create(env);

/* set the score to the response */
adb_getTotalScoreResponse_set_return(response, env, score);

/* return from the function */

return response;

axis2_skel_ScoreBoard.c -

This is the place you have to write your service logic.

Here is the completed code for your service operation:

        /**
         * auto generated function definition signature
         * for "getTotalScore|http://sports.organizers.org" operation.
         
         * @param getTotalScore
         */

        adb_getTotalScoreResponse_t* axis2_skel_ScoreBoard_getTotalScore (const axutil_env_t *env,
                                                          adb_getTotalScore_t *_gettotalscore1)
        {


            adb_getTotalScoreResponse_t *response;

            int match_number;
            axis2_char_t *team_name;

            int score;

            /* Extract the request */

            match_number = adb_getTotalScore_get_param0(_gettotalscore1, env);
            team_name = adb_getTotalScore_get_param1(_gettotalscore1, env);

            /* Here you need to query the database to retrieve the score from the match_number
             * and the team_name information, say we have the score = 5 */
            score = 5;

            /* Build the response adb */

            response = adb_getTotalScoreResponse_create(env);
            if(response)
            {
                adb_getTotalScoreResponse_set_return(response, env, score);
            }

            return response;
        }

After writing the service logic, it is then time to build the code. If you are on Linux, then you can use the "build.sh" script generated by WSDL2C for this purpose. To use the script you need to set the AXIS2C_HOME variable to the directory where Axis2/C is installed. If you are on Windows, you have to generate a 'dll' from the source files - either using a custom written Makefile or with the help from an IDE.

Now, it is the time to deploy the service. Create a directory call 'ScoreBoard' inside the 'services' directory in your AXIS2C_HOME and copy the libScoreBoard.so(or ScoreBoard.dll) and services.xml to that directory.



 -    services

         |

         +---------- libScoreBoard.so/ScoreBoard.dll 

         |

         +---------- services.xml

Start the Axis2 server (you can find the executable in $AXIS2C_HOME/bin/axis2_http_server folder) and go to "http://localhost:9090/axis2/services" on the browser and make sure your service is in the list.

 

Step 5. Generate the 'C' Client Code

You can use the WSDL2C.sh/WSDL2C.bat to generate the client code from the wsdl with the following command:

WSDL2C.sh -uri ScoreBoard.wsdl -u

or in windows

WSDL2C.bat -uri ScoreBoard.wsdl -u



You will have the following files generated in the "src" directory:

 -    src 

         |

         +---------- adb_getTotalScore.c

         |

         +---------- adb_getTotalScore.h

         |

         +---------- adb_getTotalScoreResponse.c

         |

         +---------- adb_getTotalScoreResponse.h

         |

         +---------- axis2_stub_ScoreBoard.c

         |

         +---------- axis2_stub_ScoreBoard.h

 

Step 6. Writing the 'C' Client Logic

On client side, you cannot handle adb objects straight away like you did on service side. Instead, you need to prepare your client by creating an Axis2 stub object.



You can use the following function declared in the axis2_stub_ScoreBoard.h to create your stub:

        /**
         * axis2_stub_create_ScoreBoard
         * Create and return the stub with services populated
         * @param env Environment ( mandatory)
         * @param client_home Axis2/C home ( mandatory )
         * @param endpoint_uri Service endpoint uri( optional ) - if NULL default picked from WSDL used
         * @return Newly created stub object
         */
        axis2_stub_t*
        axis2_stub_create_ScoreBoard(const axutil_env_t *env,
                                        axis2_char_t *client_home,
                                        axis2_char_t *endpoint_uri);

As you can see you need the environment (env), the client home (this is Axis2/C repository which you already set in the AXIS2C_HOME environment variable). and the optional endpoint uri. Here are the codes to create the stub using the above function.

    endpoint_uri = "http://localhost:9090/axis2/services/ScoreBoard";
    env = axutil_env_create_all("alltest.log", AXIS2_LOG_LEVEL_TRACE);
    client_home = AXIS2_GETENV("AXIS2C_HOME");

    stub = axis2_stub_create_ScoreBoard(env, client_home, endpoint_uri);

Once you create the stub you can use the following two functions in the axis2_stub_ScoreBoard.h to invoke the service.



            adb_getTotalScoreResponse_t*
            axis2_stub_op_ScoreBoard_getTotalScore( axis2_stub_t *stub, const axutil_env_t *env,
                                                        adb_getTotalScore_t* _getTotalScore);


Or

            void axis2_stub_start_op_ScoreBoard_getTotalScore( axis2_stub_t *stub, const axutil_env_t *env,
                                                    adb_getTotalScore_t* _getTotalScore,
                                                  void *user_data,
                                                  axis2_status_t ( AXIS2_CALL *on_complete ) (const axutil_env_t *, adb_getTotalScoreResponse_t* _getTotalScoreResponse, void *data) ,
                                                  axis2_status_t ( AXIS2_CALL *on_error ) (const axutil_env_t *, int exception, void *data) );



Here, use the first function if you want to do a blocking call. That is to say that the next line executes only after the service returns with the results for the first line of code. And you can use the second function, if you want to do a non-blocking Web service call, where you can set a call back function to execute when the service operation is returns with a response.



The input and output parameters for this function are adb objects. You already know how to handle adb objects. So I will skip the details. Just check the getters and setters in the header file corresponding to the adb object.



#include "axis2_stub_ScoreBoard.h"

#include <stdio.h>
#include <axiom.h>
#include <axis2_util.h>
#include <axiom_soap.h>
#include <axis2_client.h>


int getTotalScore_demo(const axutil_env_t *env, axis2_stub_t *stub);

int main(int argc, char **argv)
{
    /** the core variables */
    axutil_env_t *env = NULL;
    axis2_char_t *client_home = NULL;
    axis2_char_t *endpoint_uri = NULL;
    axis2_stub_t *stub = NULL;

    /* steps to create the stub */
    endpoint_uri = "http://localhost:9090/axis2/services/ScoreBoard";
    env = axutil_env_create_all("alltest.log", AXIS2_LOG_LEVEL_TRACE);
    client_home = AXIS2_GETENV("AXIS2C_HOME");

    stub = axis2_stub_create_ScoreBoard(env, client_home, endpoint_uri);

    if(stub)
    {
        /* call the method that actually invoke the service written below */
        return getTotalScore_demo(env, stub);
    }
    return -1;
}

int getTotalScore_demo(const axutil_env_t *env, axis2_stub_t *stub)
{

    adb_getTotalScore_t *get_score;
    int match_number;
    axis2_char_t *team_name;

    adb_getTotalScoreResponse_t *response;
    int score;

    /* Build the request adb */

    get_score = adb_getTotalScore_create(env);
    if(get_score == NULL)
    {
        return -1;
    }
    match_number = 11;
    adb_getTotalScore_set_param0(get_score, env, match_number);
    team_name = "Sri Lanka";
    adb_getTotalScore_set_param1(get_score, env, team_name);

    /* Finished building the request, time to call the service */
    response = axis2_stub_op_ScoreBoard_getTotalScore(stub, env, get_score);

    /* Extract the response */
    if(response)
    {
        score = adb_getTotalScoreResponse_get_return(response, env);
        printf("Returned Score: %d\n", score);

        return 0;
    }
    return -1;
}

Now, build the generated files with your client program to create an executable.

In Linux, with gcc you can use the following command to compile. (note that I assume you have set AXIS2C_HOME)

gcc -o ScoreBoard -I $AXIS2C_HOME/include/axis2-1.3.1/  -L$AXIS2C_HOME/lib \

    -laxutil \

    -laxis2_axiom \

    -laxis2_engine \

    -laxis2_parser \

    -lpthread \

    -laxis2_http_sender \

    -laxis2_http_receiver \

    -laxis2_libxml2 \

    *.c

Start the service and run the "ScoreBoard" executable. Ensure you receive the score returned from the service.

 

Conclusion

In this tutorial you studied how to write Axis2/C services and clients with the help of Axis2/Java tools. We used Java2WSDL tool to generate a WSDL with a simple Java program. We then used the WSDL to generate code for both the client and service using the WSDL2C tool. We also discussed how to handle adb structures and let the generated code deal with tedious XML handling.

 

Resources 

 

Author

Dimuthu Gamage is a Senior Software Engineer at WSO2. He is a commiter of the Apache Web Service project and a developer for WSF/PHP and WSF/Ruby projects.

AttachmentSize
article_attachments.zip30.6 KB
0
No votes yet
Tags :