This tutorial guides you through the process of developing a Web services client using WSO2 WSF/C Web Services Framework on Windows platform and using Microsoft Visual Studio Development environment. Upon completion of this tutorial, you will be able to set up your own development environment, compile WSF/C source in debug mode and know the basics on implementing a client using WSF/C API, compiling & debugging your program using Visual Studio and how to automate the build process using nmake build script.
Table of Contents
WSO2 Web Services Framework/C is a standards compliant, open source, C library for providing and consuming Web services in C. WSF/C is a complete solution for building and deploying Web services with a wide range of WS-* specification implementations including MTOM, WS-Addressing, WS-Policy, WS-Security, WS-SecurityPolicy, WS-Reliable Messaging and WS-Eventing.
It is assumed that you have Microsoft Visual Studio installed on your machine. First you need to download WSF/C and its dependency runtime libraries. Download the source distribution of WSO2 WSF/C, to enable you compile it in debug mode that allows you to step into WSF/C functions at the point you are debugging your applications with WSF/C.
WSF/C uses NMAKE based build system for windows. Using it is quite simple. Unzip the WSF/C source to a preferred folder. Throughout this tutorial I refer to this folder as “E:\wsf_c”. In this directory, you will find a file named configure.in, which is used to specify build options. Set the following options where you have placed the dependency libraries. Also make sure to set DEBUG=1. Sample directory locations are given in the configure.in file to make life easier for you.
DEBUG = 1
LIBXML2_BIN_DIR = E:\libxml2-2.6.30.win32
ICONV_BIN_DIR = E:\iconv-1.9.2.win32
APACHE_BIN_DIR = "E:\Apache22"
OPENSSL_BIN_DIR = E:\OpenSSL
Following options are for enabling modules in WSF/C.
ENABLE_SANDESHA2C =>WS-Reliable messaging module
ENABLE_SAVANC =>WS-Eventing module
ENABLE_RAMPARTC => WS-Security module
Since we are focusing on the basics, set all these to 0 so that these modules will not be compiled.
Now, from the start menu open visual studio command prompt and go to wsf_c directory. Then run the build.bat file in wsfc directory which will build WSF/C. Once the build is completed you will get the binary dist of WSF/C in wso2-wsf-c-bin-1.2.1-win32 directory.
Next, create an environment variable named WSFC_HOME to point to wso2-wsf-c-bin-1.2.1-win32 and add %WSFC_HOME%\lib to path environment variable. Now, Go to wso2-wsf-c-bin-1.2.1-win32\bin directory and run axis2_http_server.exe. If the server starts, Congradiulation! and you have followed all instructions correctly.
I will use the echo sample located in wsf_c\axis2c\samples\client\echo, to describe the implementation of the client.
Open visual studio and go to File->New-> Project and select win32-> Console Application option and give a name for the client. I have given EchoClient as the project name.
Next, select console application and empty project options.
Next, right click on the solution explorer and select add-> existing item.
Then, browse to directory wsf_c\axis2c\samples\client\echo and select echo.c file.
Click on the project and open the properties tab from the menu. Set the include path as the following:
In the linker options set following options.
Now, you are ready to compile the echo client code. Press F7 key to build the project, or, you can use the build icon in the build toolbar shown below.
Writing a client using WSFC includes the following steps:
1. Create the environment to be used by the client.
All functions in WSF/C take the pointer to axutil_env_t struct as an argument. Environment encapsulates the memory allocation, logging, error and thread related functionality.
2. Create a service client instance
Service client (axis2_svc_client.h) is the basic client API that should be used to implement clients. Service client takes the repository path ( WSFC_HOME -> containing the axis2.xml and library folders) as a parameter.
3. Create an options instance and set to service client
Options (axis2_options.h) is used for specifying client configuration options to the client. For example, the service endpoint address, WS-Addressing options like action, reply-to are some of the options set to axis2_options.
4. Construct payload
WSF/C has its own xml informed model called axiom. This model is used to construct xml messages exchanges between a client and a service. Therefore, the next step will be to construct an appropriate xml payload using axiom.
5. Send and receive responses
Service client provides four methods for invoking services. The most widely used method is the axis2_svc_client_send_receive method. It takes the axiom payload as an argument and returns the received axiom payload.
5. Handle the response received
Once the response is received, is need to be processed using axiom.
Let’s go through the echo client code.
1. First, we create the environment. echo.log is the log file name created for our client.
env = axutil_env_create_all("echo.log", AXIS2_LOG_LEVEL_TRACE);
2. Next, we create the axis2 options struct
options = axis2_options_create(env);
3. Next, we create and endpoint ref struct and set it to the options.
address = "http://localhost:9090/axis2/services/echo";
endpoint_ref = axis2_endpoint_ref_create(env, address);
axis2_options_set_to(options, env, endpoint_ref);
4. Next step is to create a svc_client instance.
client_home = AXIS2_GETENV("WSFC_HOME");
svc_client = axis2_svc_client_create(env, client_home);
5. Constucting the payload is done using the build_om_payload_for_echo_svc function.
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);
6. Next, axis2_svc_client_send_seceive method is used to send and receive the payload
ret_node = axis2_svc_client_send_receive(svc_client, env, payload_node);
By reading through the code, you will be able to understand how the client works.
You can obtain the debug tool bar shown below using the view->toolbar option. Alternatively, press F5 key to run the client.
Debugging code using Visual Studio is quite simple. Go to the line where you want to set a break point and left click on the left hand side pane on the editor (Refer the illustration below). You can use Ctrl+B to set a break point for a function using a dialog box.
Use F10 and F11 keys to step over and step into code. You can use the debug toolbar for this as well. Another important concern is to know how to set commadline arguments to the application during debugging. You can do this by setting command line arguments options as given below.
Go to Project -> properties-> Configuration Properties->Debugging. Set command arguments in the Command Arguments option. For example, I have set it to -p8080, in order to make the client use port 8080 instead of the default port 9090. If you are debugging a dll project instead of an exe project (which will be the case when you are developing a Web service using WSF/C), you can select the executable that will use the dll in the command option.
Following image shows how to step through the code. Since we built WSF/C with debug flag on, you should be able to step in to WSF/C functions as well as your client code.
You can write a small makefile what will enable you build your code using nmake tool on the command line.
In a makefile, you can define a build target and define what should happen under that build target. For compiling and linking, Microsoft C/C++ compiler cl.exe and linker link.exe will be used. You can see the options available with these tools by pressing /? and turning on the help menu. The following image shows what you get when you use .cl /?.
Let's write a simple makefile with a build target echo_client to build above code.
CFLAGS=/nologo /D "WIN32" /D "_MBCS" /D "_WINDOWS" /I$(WSFC_HOME)\include
LDFLAGS= /nologo /LIBPATH:$(WSFC_HOME)\lib
LIBS = axiom.lib axutil.lib axis2_engine.lib axis2_parser.lib
$(CC) $(CFLAGS) echo.c /c
$(LD) $(LDFLAGS) echo.obj $(LIBS) /OUT:echo.exe
You can use macros like WSFC_HOME within a makefile. Here I have defined WSFC_HOME to point to the wsfc repository, CC represents the compiler and CFLAGS for the compiler arguments. Note how include path is specified using /I option. Similarly LD is for the linker and LDFLAGS is for the linker arguments. I have defined another macro LIBS seperately which contains all the dependency libraries that our client program uses.
These macros can be refered using $(). So, In the target echo_client I use $(CC) to refer to the compilar. echo.c file is the c file to be compiled. If you have large number of files and it is hard to specify the files one by one, you can use *.c wildcard to compile all the c files contained in the directory. Here /c flag is used to tell the compiler to only compile the files.Then we tell the linker to link the genarated objects with the dependency libraries and create the executable. Since we compiled only the echo.c file, there is only echo.obj file to be linked. /OUT flag is used to tell the linker to create the output file with the specified name. Here it will be an executable named echo.exe. Copy and paste above lines on notepad and save it to a file named echo_client.mk. You can use this file to build the code on the commad line using the command nmake -f echo_client.mk. When you have large numbers of programs to build, it is best to use makefiles to automate builds.
This tutorial discussed Steps involved in developing a client using WSF/C and how to use Visual Studio in developing a client with WSF/C.
Nandika Jayawardana, Software Engineer at wso2, committer Apache Software Foundation, nandika at wso2 dot com