I have been working in Linux for a long time and recently moved to windows for a little change. (Hopefully for a little time :). I find it is really easy to create a project for an Axis2/C client or service with Visual Studio. (I’m using Visual Studio 2005). Anyway I thought it is better to write down it somewhere so someone may find it useful.

Compiling Axis2/C Client

I’m using the math sample for this guide. You can find the source code inside “samples\client\math” directory of any  axis2/c binary or source.

  1. Create a new Visual Studio C++ Project, Select the project type to ‘Win32 Console Application’. Give a name to the project (say ‘math_client’) and in the wizard choose ‘Empty Project’. Then you will have an empty C/C++ project.
  2. Add the source files of the math samples to the project. You can do this by right clicking the project name in the Solution Explorer and clicking Add->Existing Item. After this step you will have axis2_math_stub.h in the Header Files section and the axis2_math_stub.c and math_client.c in the Source files section.
  3. Add Axis2/C include file directory. For this, go to ‘Tools->Options’ from the menu and select ‘Projects and Solutions->VC++ Directories’ from the Tree menu. Select the ‘Include files’ from ‘Show Directories for:’ drop down menu and add the ‘Axis2/C installed dir/includes’ directory. Note that if you follow this to add include directories you only need to do this only once for all the projects. If you want to do this only specifically to this project Go to ‘Project->Properties’ from the menu and Go to ‘Configuration Properties->C/C++->General and add the directories under ‘Additional Include Directories’)
  4. Add Axis2/C library directory. Here is how you do this. Go to the VC++ Directories form mentioned in the step 3 and select ‘Library files’ from the ‘Show Directories for:’ drop down menu. Then add the ‘Axis2/C installed dir/lib’ directory. If you add this here all the following project will use this directory to search for libraries. If you want this only to this project just skip this step and do what is specially mentioned in the step 5.
  5. Declare what libraries to link. For this, go to ‘Project->Properties’ from the menu and select “Configuration Properties->Linker->Input’ from the tree menu. In the ‘Additional Dependencies’ Text box add the following Axis2/C libraries.
    • axiom.lib
    • axutil.lib
    • axis2_engine.lib
    • axis2_parser.lib

    If you skipped the step 4, you have to type the complete path for the libraries.

  6. So that’s all you have to do.  You can compile and run the client using F5 or Ctrl+F5.

Debugging Axis2/C Client

  1. Since by default new project is created with the debug configurations, you can debug the program straight away. To test set a breakpoint at the start of the main function (in the math_client.c) and Run with Debugging (F5)

Compiling Axis2/C Service

We will use the corrosponding math service to create a Visual Studio project. It is same as how you created the math client project. But it has very little differences. (It requires only one additional step)

  1. Create a project in Visual Studio. This time select “Win32 Project” as the project type. Give a name to the project (”math_service”) and continue to the wizard. In there select the “Application Type” to be “DLL” and tick the “Empty Project” so we will have an empty project to build a DLL.
  2. Add the math service sample files to the project. You can find the source for the sample inside “samples\client\math” directory of the Axis2/c distribution.
  3. Add the Axis2/C include directory. If you have done it earlier using “Tools->Options” dialog box you don’t need to do that again since that settings persist over every project. But if you did it using “Project->Properties” dialog box you have to redo the step3 described in compiling client section for this project too.
  4. Add the Axis2/C library directory. Same as the step 4 in compiling client section.
  5. Declare the libraries to link. Same as the step 5 in the comping client section.
  6. Declare the “AXIS2_DECLARE_EXPORT” preprocessor constant. For this go to the “Project->Properties” and click the “Configuration Properties->C/C++->Preprocessor” and add the “AXIS2_DECLARE_EXPORT” at the end separated with a semi column.
  7. That is all needed to compile the service. Now you can build the project using “Build->BuildSolution or just type F7.

Debugging Axis2/C Service

  1. After you compile the source, you cand find the resulted DLL inside the debug directory of your project directory. Just copy it to the <axis2 installed dir>\services\math directory replacing math.dll file.
  2. Then Run the simple Axis2 Http Server in the <axis2 installed dir>\bin.
  3. Start the project you just created for the math service and go to “Debug->Attach To Process”. There you can see the list of running processes. Just select the axis2_http_server process.
  4. Now as a test create a breakpoint at the start of axis2_math_add function in the math.c file. (You will get a warning that no symbol is loaded for the file, but you can ignore that for the moment)
  5. Now send a request using the math client that you compiled earlier. You will see the breakpoint is hit in the service project. So you can debug the service as you like.

SOAP Headers are used in many WS-* standards (for an example WS-Security) to transmit information related to the QOS aspects of the service. So only the SOAP engines which process these QOS parameters are supposed to see these headers. Web Service Developer or Consumer need to care only what is in the SOAP payload (which is what inside the SOAP body element) and not SOAP headers.

But that is theory, In practice we see many places where people uses SOAP headers to transmit what they should have easily sent through SOAP Body. And many SOAP stacks also provide APIs to allow people to send/receive custom headers and standards like WSDL too provide ways to describe services which accept custom headers. So I thought it might worth to discuss how you describe custom headers in a service from a WSDL and how you can consume and serve in real world with SOAP stacks like Axis2/C and WSF/PHP.

In a WSDL 1.1 you first declare your headers in the binding section itself which is the section that is used to provide the messaging and transport protocol information.

  <binding name="RetHeaderBinding" type="tns:RetHeaderPortType">
    <soap:binding transport="http://schemas.xmlsoap.org/soap/http" style="document"/>
    <operation name="echoString">
      <soap:operation soapAction="http://soapinterop.org/" style="document"/>
      <input>
        <soap:body use="literal"/>
        <soap:header message="tns:Header1" part="Header1" use="literal"/>
        <soap:header message="tns:Header2" part="Header2" use="literal"/>
      </input>
      <output>
        <soap:body use="literal"/>
        <soap:header message="tns:Header1" part="Header1" use="literal"/>
        <soap:header message="tns:Header2" part="Header2" use="literal"/>
      </output>
    </operation>
  </binding>

There you can see we have Header1 and Header2 in both in input and output of the operation. It refers to the message section with the specific part name that represent the header. Here is how message section will look like.

  <message name="Header1">
    <part name="Header1" element="types:Header1"/>
  </message>
  <message name="Header2">
    <part name="Header2" element="types:Header2"/>
  </message>

In this section it refers to the schema element that actually describe the element structure/schema. Here I put a string and an int in each header.

  <s:element name="Header1">
      <s:complexType>
        <s:sequence>
          <s:element name="string" type="s:string"/>
          <s:element name="int" type="s:int"/>
        </s:sequence>
      </s:complexType>
  </s:element>
  <s:element name="Header2">
      <s:complexType>
        <s:sequence>
          <s:element name="int" type="s:int"/>
          <s:element name="string" type="s:string"/>
        </s:sequence>
      </s:complexType>
  </s:element>

Here is a valid SOAP message for the WSDL description.

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
   <soapenv:Header>
      <ns1:Header1 xmlns:ns1="http://soapinterop.org/xsd">
         <ns1:string>test1</ns1:string>
         <ns1:int>5</ns1:int>
      </ns1:Header1>
      <ns1:Header2 xmlns:ns1="http://soapinterop.org/xsd">
         <ns1:int>8</ns1:int>
         <ns1:string>test2</ns1:string>
      </ns1:Header2>
   </soapenv:Header>
   <soapenv:Body>
      My payload
   </soapenv:Body>
</soapenv:Envelope>

If you are using Apache Axis2/C WSDL2C Codegen tool (which is shipped with Axis2/Java) you will be able to directly use the API provided with the generated code to deal with custom headers. Here is how the client API is generated for the above WSDL.

         /**
          * auto generated method signature
          * for "echoString|http://ws.dimuthu.org/blog/headers" operation.
          *
          * @param _echoStringParam
          * @param _header1
          * @param _header2
          *
          * @param dp_header10 - output header
          * @param dp_header21 - output header
          * @return adb_echoStringReturn_t*
          */
         adb_echoStringReturn_t*
         axis2_stub_op_RetHeaderService_echoString( axis2_stub_t *stub, const axutil_env_t *env,
                                              adb_echoStringParam_t* _echoStringParam,
                                              adb_Header1E0_t* _header1,
                                              adb_Header2E1_t* _header2,
                                              adb_Header1E0_t** dp_header10 /* output header double ptr*/,
                                              adb_Header2E1_t** dp_header21 /* output header double ptr*/)

Note that the first parameter is the environment variable passed in every function in Axis2/c. The second parameters is the payload. Input headers (input headers for server side) are in 3rd and 4th parameters, you can build them similar to how you build payload object. 5th and 6th are for the output headers which you only need to provide some pointers and the generated code will build you the objects from the response data.

You service will also have a similar API. There you have to create adb objects for output headers (like you build output payload) and assign the pointer to these output header double pointers.

             adb_Header2_t* arg_Header1 = adb_Header1_create(env);
             adb_Header1_set_string(arg_Header1, env, "I m response Header 1");
             adb_Header1_set_int(arg_Header1, env, 1);
             adb_Header10_t* _header1 = adb_Header10_create(env);
             adb_Header10_set_Header1(_header1, env, arg_Header1);
             *dp_header10 = _header1;

Now how about PHP, can you provide the same API form the PHP as well. Yes, PHP has references similar to C pointers. WSF/PHP already have the custom headers support in the SVN and to be released with the 2.0.
Here is the generated API for the service business logic for the above WSDL.

/**
 * Service function echoString
 * @param string $input
 * @param object of Header1 $header_in0 input header
 * @param object of Header2 $header_in1 input header
 * @param reference object of Header1 $header_out0 output header
 * @param reference object of Header2 $header_out1 output header
 * @return string
 */
function echoString($input, $header_in0, $header_in1, &$header_out0, &$header_out1) {

...
}

These tool provides APIs to consume and provide data through custom headers in same easiness you send the data in the payload. Anyway it is really recommended to not to use custom SOAP headers in your Service and try to avoid them as much as possible. Whenever you think there is valid reason to put a custom header (it is relevant to QOS aspects), just try to integrate the header processing logic to the SOAP engine. In Axis2 C or Java you can write Axis2 modules for that. That will really speed up the processing of your service and give consumers a well-designed API.