Code Generate Custom SOAP Faults For Axis2/C

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

This entry was posted in 2 minutes guide, adb, axis2/c, codegen, SOA, tool, Tutorial/Guide, web services, WSDL, wsdl2c, xml schema and tagged , , , , , , , , . Bookmark the permalink.

10 Responses to Code Generate Custom SOAP Faults For Axis2/C

  1. robert says:

    Hi!

    You wrote:

    “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.”

    I checked out axis2 version 1.5, but i cannot create such code you explained above. Is it possible to write down step-by-step how did you generate the code and which version of WSDL2C was used?

    Thanks your help, br,

    robert.

  2. dimuthu says:

    Hi,
    You can check a test cases to the wsdl and generated files from this jira, https://issues.apache.org/jira/browse/AXIS2C-1344. Make sure your generated code is same as that.
    Or probably there may be old jars in the classpath.

    Thanks
    Dimuthu

  3. robgarv says:

    Hi Dimuthu,

    Great article. Been having to learn Axis2/C in a hurry and have found your writings on it quite useful. Just thought I would take a minute during rebuilds to tell ya that.

    Rob G.

  4. domv says:

    Hi Dimuthu,

    Many thx for your articles.
    Very helpful when dealing with axis2c.
    Is the fault exception handling available in axis2/Java 1.5.1 codegen or do I have to get it from the sources ?

    Dom V.

  5. dimuthu says:

    Hi,
    As I remember people was working on Axis2/Java 1.5 release in a different branch. So most probably this will not be in that release. 🙁 That mean you have to get it from source.
    Thanks
    Dimuthu

  6. balaji says:

    hi dimuthu
    I need to pass the axis2c fault from C side to java side (tomcat).
    Currently what i do is:
    adb_test response =NULL;
    AXIS2_HANDLE_ERROR(env,
    AXIS2_ERROR_REPOS_BAD_SEARCH_TEXT,
    AXIS2_FAILURE);

    return response;

    what i see at the end is : org.apache.axis2.AxisFault: Data element of the OM Node is NULL.

    What i want is my error that i have set (AXIS2_ERROR_REPOS_BAD_SEARCH_TEXT) at the C level?

  7. balaji says:

    just to add to it,
    I understand that to create a axis fault using axis2c i need to set the fault element using AXIS2_HANDLE_ERROR and just return NULL from business logic. I assume now, axis2c would create SOAP fault using the error message that the AXIS2_HANDLE_ERROR had set .

    So it should pass the fault message to my java layer and Java layer has to recoginse it has axis fault and output the error message accordingly.
    But what i see in tomcat logs are the following (which tells the axis2fault created by axis2c is lost and just NULL is returned.So how to I pass the fault information to Java side.

    at org.apache.axis2.description.OutInAxisOperationClient.handleResponse(Ou tInAxisOperation.java:370)
    at org.apache.axis2.description.OutInAxisOperationClient.send(OutInAxisOpe ration.java:416)
    at org.apache.axis2.description.OutInAxisOperationClient.executeImpl(OutIn AxisOperation.java:228)
    at org.apache.axis2.client.OperationClient.execute(OperationClient.java:16 3)
    at WSAmplify_v2_1ServiceStub.test(WSAmplify_v2_1 ServiceStub.java:2144)

  8. dimuthu says:

    Hi,
    Can you trace and check the details element present in your soap message?
    Dimuthu

  9. dimuthu says:

    Hi,
    You have to set AXIS2_ERROR_SET method with the error code and fault->your_excpetion_name with your exception object. In the tutorial it is shown in like this,
    /* 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;

    Got it?
    Dimuthu

  10. Jaime Casero says:

    good to know the gen tool is supporting this scenario now.

    I’m using Axis2Java 1.5.4 to compile my WSDL, the code generated doesn’t include the third parameter about the fault, some option for the compiler needed?

    So,I had to use a ThreadLocal technique to simulate this feature. In the business logic section i added the custom error to the current thread, using the pthread lib, then i returned NULL (pretty the same as the example, replacing the AXIS2_ERROR_SET call with my own threadlocal utility).

    Then, going back into the skel interface you have to override the on_fault callback like this:
    axiom_node_t* AXIS2_CALL
    axis2_svc_skel_SystemServicesService_on_fault(axis2_svc_skeleton_t *svc_skeleton,
    const axutil_env_t *env, axiom_node_t *node) {

    axiom_node_t * retNode = NULL;
    retNode = CSTAErrorManager::getReference().getError();
    //retNode = adb_ErrorValue_serialize(eValue, env, NULL, NULL, AXIS2_TRUE, NULL, NULL);
    axiom_node_add_child (node, env, retNode);
    return retNode;
    }

    hope this helps someone.

Leave a Reply

Your email address will not be published. Required fields are marked *