Web services use SOAP faults to report fault cases back to clients. The faults can be generated from the SOAP framework in a case of invalid SOAP messages, invalid security tokens or they can be generated from the service business logic itself. The fault messages may contain simply a string indicating the error, or it may contain lot of details which could be useful to the clients find the problem. In fact the format of the SOAP fault is a standard. But services can send custom details within the details element in a SOAP fault.

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
   <soapenv:Body>
      <soapenv:Fault>
         <faultcode>soapenv:Sender</faultcode>
         <faultstring>..</faultstring>
         <detail> You Custom Message </detail>
      </soapenv:Fault>
   </soapenv:Body>
</soapenv:Envelope>

And you can define the schema of your custom fault message in a WSDL, so the clients can prepare to handle fault scenarios. In facts tools like Apache Axis2 WSDL2C, WSDL2Java tool will help you to work with custom faults when they are defined in the WSDL.  Here is an example section of a WSDL with an operation which can throw two faults “MyFirstException”, “MySecondException”.

<!-- fault schemas -->
<types>
 <schema ..>
  <element name="MyFirstException">
   <complexType>
    <sequence>
     <element name="text" type="xsd:string"/>
    </sequence>
   </complexType>
  </element>
  <!-- fault element -->
  <element name="MySecondException">
   <complexType>
    <sequence>
     <element name="number" type="xsd:int"/>
    </sequence>
   </complexType>
  </element>
 </schema>
</types>

<!-- the fault messages -->
<message name="MySecondExceptionFault">
 <part name="fault" element="ns1:MySecondException"/>
</message>

<message name="MyFirstExceptionFault">
 <part name="fault" element="ns1:MyFirstException"/>
</message>

<!-- operation to throw fault -->
<portType name="MyType">
 <operation name="myOperation">
  <input message="tns:myOperationRequest"/>
  <output message="tns:myOperationResponse"/>
  <fault name="MySecondException" message="tns:MySecondExceptionFault"/>
  <fault name="MyFirstException" message="tns:MyFirstExceptionFault"/>
 </operation>
</portType>

Note that the operation “myOperation” is throwing faults “MyFirstException”, “MySecondExcpetion”. If you generate the Java code for this operation, it would be simple as this,

public MyOperationRequest
MyOperation(MyOperationRequest) throws
          MyFirstExcpetion,
          MySecondExcpetion {
  // here is your business logic
}

Anyway we are going to write codes in ‘C’ language, which doesn’t have similar exception mechanism. Let see how we can do it, starting with writing the service and then writing a client.

Writing Services With Custom SOAP Faults

Once you generate the ‘C’ codes for the WSDL using WSDL2C tool, you should first have a look at the skeleton header file.

    /**
     * the generated fault union for operation "myOperation|urn:myuri:1.0",
     * in a case you want to return a fault, put the appropriate adb object for
     * the union variable pointer comes as the last parameter of the method
     */
    typedef union
    {
        adb_MyFirstException_t* MyFirstException;
        adb_MySecondException_t* MySecondException;

    } axis2_skel_MyService_myOperation_fault;

    /**
     * auto generated function declaration
     * for "myOperation|urn:myuri:1.0" operation.
     * @param env environment ( mandatory)
     * @param _myOperation of the adb_myOperation_t*
     *
     * @return adb_myOperationResponse_t*
     */
    adb_myOperationResponse_t* axis2_skel_MyService_myOperation(const axutil_env_t *env,
                                      adb_myOperation_t* _myOperation,
                                      axis2_skel_MyService_myOperation_fault *fault);

And at the very end of the header file, you will see an enumeration of constants corresponding to each fault is generated.

    typedef enum
    {
        AXIS2_SKEL_MYSERVICE_ERROR_NONE = AXIS2_SKEL_MYSERVICE_ERROR_CODES_START,

        AXIS2_SKEL_MYSERVICE_MYOPERATION_FAULT_MYFIRSTEXCEPTION,
        AXIS2_SKEL_MYSERVICE_MYOPERATION_FAULT_MYSECONDEXCEPTION,

        AXIS2_SKEL_MYSERVICE_ERROR_LAST

    } axis2_skel_MyService_error_codes;

That’s all you need to aware of. The plan is whenever you need to report the fault, you have to do three things inside the business logic.

  1. Create the adb object for the fault, in this case either “adb_MyFirstException_t” or “adb_MySecondException_t” and set it to the fault pointer variable.
  2. Set the constant corresponding to the fault using “AXIS2_ERROR_SET” function
  3. return NULL

Here is an example how you do it inside the actual business logic code.

    adb_myOperationResponse_t* axis2_skel_MyService_myOperation(const axutil_env_t *env,
                                      adb_myOperation_t* myOperation,
                                      axis2_skel_MyService_myOperation_fault *fault )
    {
        /* the buisness logic */

        ....

        if(/* checking some condition to throw the "MyFirstException" fault */)
        {
          /* 1. Creating the adb object and set it to the fault pointer variable */
          adb_MyFirstException_t *exp = NULL;
          exp = adb_MyFirstException_create(env);
          adb_MyFirstException_set_text(exp, env, "this is the exception 1"); /* custom value */

          fault->MyFirstException = exp;

          /* 2. Setting the error constant corrosponding to the fault */
          AXIS2_ERROR_SET(env->error,
                      AXIS2_SKEL_MYSERVICE_MYOPERATION_FAULT_MYFIRSTEXCEPTION,
                      AXIS2_FAILURE);

          /* 3. Returning NULL */
          return NULL;
        }

        else if(/* checking some condition to throw the "MySecondException" fault */)
        {
          /* 1. Creating the adb object and set it to the fault pointer variable */
          adb_MySecondException_t *exp = NULL;
          exp = adb_MySecondException_create(env);
          adb_MySecondException_set_number(exp, env, 2);/* custom value */

          fault->MySecondException = exp;

          /* 2. Setting the error constant corrosponding to the fault */
          AXIS2_ERROR_SET(env->error,
                      AXIS2_SKEL_MYSERVICE_MYOPERATION_FAULT_MYSECONDEXCEPTION,
                      AXIS2_FAILURE);
          /* 3. Returning NULL */
          return NULL;
        }

        /* return the response in no fault scenario */
        return response;
    }

That’s all you have to do, Axis2/C will make sure to build the fault and put your custom message inside the details element.

Writing Clients to Handle custom SOAP Faults

After generating the code for clients using WSDL2C tool, this time you should look at the generated stub header file first. It is just similar to the skeleton header files may be except all the “skel” prefixes are renamed to “stub” and additional parameter “stub” for the operation.

    /**
     * the generated fault union for operation "myOperation|urn:myuri:1.0",
     * in a case the server return a fault, the corresponding adb object will be loaded for
     * the union variable pointer comes as the last parameter of the method
     */
    typedef union
    {
        adb_MyFirstException_t* MyFirstException;
        adb_MySecondException_t* MySecondException;

    } axis2_stub_MyService_myOperation_fault;

    /**
     * auto generated function declaration
     * for "myOperation|urn:myuri:1.0" operation.
     * @param env environment ( mandatory)
     * @param _myOperation of the adb_myOperation_t*
     *
     * @return adb_myOperationResponse_t*
     */
    adb_myOperationResponse_t* axis2_stub_MyService_myOperation(axis2_stub_t* stub, const axutil_env_t *env,
                                      adb_myOperation_t* _myOperation,
                                      axis2_stub_MyService_myOperation_fault *fault);

    typedef enum
    {
        AXIS2_STUB_MYSERVICE_ERROR_NONE = AXIS2_STUB_MYSERVICE_ERROR_CODES_START,

        AXIS2_STUB_MYSERVICE_MYOPERATION_FAULT_MYFIRSTEXCEPTION,
        AXIS2_STUB_MYSERVICE_MYOPERATION_FAULT_MYSECONDEXCEPTION,

        AXIS2_STUB_MYSERVICE_ERROR_LAST

    } axis2_stub_MyService_error_codes;

Looking at this, you may have got the idea how to differentiate what fault is being thrown by the server and how to extract the parameters of the custom fault. Here is an example client code correctly handling exceptions.

    /* the structure to keep the fault */
    axis2_stub_MyService_myOperation_fault fault;

    ..... /* the part preparing the request is ignored here */

    /* invoking the "myOperation" operation */
    response = axis2_stub_op_MyService_myOperation(stub, env, op, &fault);

    /* checking the response == NULL implies fault is sent  */
    if(response == NULL)
    {
        /* getting the error number to distinguish the fault */
        error_code = env->error->error_number;

        /* compare error code with constants of each faults */
        if(error_code == AXIS2_STUB_MYSERVICE_MYOPERATION_FAULT_MYFIRSTEXCEPTION) {

            /* extracting out the adb objects */
            axis2_char_t *text = adb_MyFirstException_get_text(fault.MyFirstException, env);

            /* do a printf of the message */
            printf("My First Exception called: with param %s\\n", text);

        }
        else if(error_code == AXIS2_STUB_MYSERVICE_MYOPERATION_FAULT_MYSECONDEXCEPTION) {
            /* extracting out the adb objects */
            int number = adb_MySecondException_get_number(fault.MySecondException, env);

            /* do a printf of the message */
            printf("My Second Exception called: with param %d\\n", number);

        }

    }

Note that this feature is available only in the very latest WSDL2C tool. Try to get latest build from Axis2/Java to use this up to date tool.

You can download the WSDL and codes used in this example from here, https://issues.apache.org/jira/secure/attachment/12399724/case45.zip

Axis2/C ADB is a C language binding to the XML schema. ADB object model represents an XML specific to a given schema in a WSDL. You can use the Axis2 codegen tool to generate ADB codes for your WSDL and use that to build and parse your XMLs. The idea is, if you use ADB to build and parse your xmls, it will be really easy to do that and you don’t need to know or understand anything about the schema or the wsdl.

Apache Axis2/C to can be used to send and receive binaries as MTOM, SWA or base64 encoded. But ADB generated code still support to send and receive base64 encoded binaries only. So if you use contract first approach  with Axis2/C (i.e start with the WSDL, then write the service based on that), you have to use base64-encoded (non-optimized) as the binary transferring method. Note that you can use the other methods like MTOM or SWA, if you write the code to build and parse the xmls from AXIOM which is a general model for XML like DOM.

Say you have an element with base64Binary type in your request XML. So the schema for that element would be,

<xs:complexType name="Person">
    <xs:sequence>
        <xs:element name="image" type="xs:base64Binary"/>
        ... <!-- some more elements -->
    </xs:sequence>
</xs:complexType>

After you code generated, you will get the adb_person.h and adb_person.c files with the following function prototypes and the implementations,

        /**
         * Constructor for creating adb_Person_t
         * @param env pointer to environment struct
         * @return newly created adb_Person_t object
         */
        adb_Person_t* AXIS2_CALL
        adb_Person_create(
            const axutil_env_t *env );

        /**
         * Free adb_Person_t object
         * @param  _Person adb_Person_t object to free
         * @param env pointer to environment struct
         * @return AXIS2_SUCCESS on success, else AXIS2_FAILURE
         */
        axis2_status_t AXIS2_CALL
        adb_Person_free (
            adb_Person_t* _Person,
            const axutil_env_t *env);

        /**
         * Getter for image.
         * @param  _Person adb_Person_t object
         * @param env pointer to environment struct
         * @return axutil_base64_binary_t*
         */
        axutil_base64_binary_t* AXIS2_CALL
        adb_Person_get_image(
            adb_Person_t* _Person,
            const axutil_env_t *env);

        /**
         * Setter for image.
         * @param  _Person adb_Person_t object
         * @param env pointer to environment struct
         * @param arg_image axutil_base64_binary_t*
         * @return AXIS2_SUCCESS on success, else AXIS2_FAILURE
         */
        axis2_status_t AXIS2_CALL
        adb_Person_set_image(
            adb_Person_t* _Person,
            const axutil_env_t *env,
            axutil_base64_binary_t*  arg_image);

So you can manipulate the person element in c language using the create, get, set and free function.

    /* here the env is the axutil_env_t* instance - the axis2/c environment */
    FILE *f = fopen("./images/person.png", "r+");
    int binary_count;
    /* binary read a function you may write to read the binary data to the
     variable binary and the count to the variable binary_count */
    unsigned char *binary = binary_read(f, env, &binary_count);
    axutil_base64_binary_t *base64 = axutil_base64_binary_create_with_plain_binary(
                                        env, binary, binary_count);

    adb_Person_t *person = adb_Person_create(env);
    adb_Person_set_image(person, env, base64);

You can set this adb person directly to your request or to a setter of another adb instance to complete the ADB tree. So this way you can send binaries (as base64 encoded) using the Axis2/C ADB generated code.

WSDL2Java and WSDL2C tools shipped with the Axis2/Java project are used to generate Stubs(client side) and Skeletons(Server Side) respectively for Axis2/Java and Axis2/C codes. This make the development of web services and consumers more quicker and easier.

The tools come with many options allowing the user to customize the code generation. One of the such option is ‘-uw’ which used to turn on the ‘unwrapping’ mode. WSDL2Java project had this option for a long time, But it is just Today I was able to enable that option in the WSDL2C tool. So lets see how it make the code easier.

Say you have the ’simpleAdd’ operation with the following schema,

            <xs:element name="simpleAdd">
                <xs:complexType>
                    <xs:sequence>
                        <xs:element minOccurs="0" name="param0" type="xs:int"/>
                        <xs:element minOccurs="0" name="param1" type="xs:int"/>
                    </xs:sequence>
                </xs:complexType>
            </xs:element>
            <xs:element name="simpleAddResponse">
                <xs:complexType>
                    <xs:sequence>
                        <xs:element minOccurs="0" name="return" type="xs:int"/>
                    </xs:sequence>
                </xs:complexType>
            </xs:element>

When you generate the code for server side with Java using the following command, (note the option -uw which stands for ‘unwrapping’)

wsdl2java.sh -uri xxx.wsdl -u -ss -sd -uw

it gives you the following service function.

public int simpleAdd(int param0, int param1)
{
    //TODO : fill this with the necessary business logic
}

Note the input and output parameters of the function are simple types. If you don’t give the ‘-uw’ option, You will instead get a function with input and output variables as class names wrapping these simple types which is not this much straight forward.

So now the same is available with C. You can use WSDl2C tool with the same option set to generate a very simple service API.

wsdl2c.sh -uri xxx.wsdl -u -ss -sd -uw

And here is the service logic you may write, Can you think of an API simpler than this,

        /**
         * auto generated function definition signature
         * for "simpleAdd|http://ws.apache.org/axis2" operation.
         * @param env environment ( mandatory)
         * @param _param0 of the int
         * @param _param1 of the int
         *
         * @return int
         */
        int axis2_skel_UnwrappedAdder_simpleAdd(const axutil_env_t *env,
                                              int _param0,
                                              int _param1 )

        {
          return _param0 + _param1;
        }

You can find the code for complete case (wsdl, skel and stub) in the Axis2/C Jira Attachment space for the ‘unwrapping’ issue. Note that this update is only available in the nightly build and not in the release. You can download the nightly build from wso2’s axis2/java nightly build page.


© 2007 Dimuthu’s Blog | iKon Wordpress Theme by Windows Vista Administration | Powered by Wordpress