Tuesday, January 4, 2011

Develop Web Service With Axis2 #7 - Add Custom Soap Header in Response Soap Message

Prev >>> Develop Web Service With Axis2 #6 - Workaround Solution for Custom "Header" on Skeleton/Stub side 

This article will talk about how to add soap headers in response soap message, not work around solution mentioned in previous article.

The approach is to use module/handler to inject the soap headers while responding to consumers.

follow this article Develop Web Service With Axis2 #4 - Axis2 Style Interceptor to create a new module.

------ module class

package mypackage;

import org.apache.axis2.AxisFault;
import org.apache.axis2.context.ConfigurationContext;
import org.apache.axis2.description.AxisDescription;
import org.apache.axis2.description.AxisModule;
import org.apache.axis2.description.AxisOperation;
import org.apache.axis2.description.AxisService;
import org.apache.axis2.modules.Module;
import org.apache.neethi.Assertion;
import org.apache.neethi.Policy;

public class SoapHeaderModule implements Module {
     public void init(ConfigurationContext configurationContext,
              AxisModule axisModule) throws AxisFault {
          System.out.println("Initializing the module");
     }

     public void engageNotify(AxisDescription axisDescription) throws AxisFault {

     }

      public boolean canSupportAssertion(Assertion assertion) {
           return false;
      }

       public void applyPolicy(Policy policy, AxisDescription axisDescription)
           throws AxisFault {
       }

        public void shutdown(ConfigurationContext configurationContext)
           throws AxisFault {
       }

}

-------- handler class

package mypackage;

import org.apache.axiom.om.OMAbstractFactory;
import org.apache.axiom.om.OMNamespace;
import org.apache.axiom.soap.SOAP11Constants;
import org.apache.axiom.soap.SOAP12Constants;
import org.apache.axiom.soap.SOAPEnvelope;
import org.apache.axiom.soap.SOAPFactory;
import org.apache.axiom.soap.SOAPHeaderBlock;
import org.apache.axis2.AxisFault;
import org.apache.axis2.context.MessageContext;
import org.apache.axis2.handlers.AbstractHandler;

public class HeaderOutHandler extends AbstractHandler {
    public InvocationResponse invoke(MessageContext messageContext)
            throws AxisFault {
        // add an Soap header for the outgoing soap message
        SOAPEnvelope soapEnvelope = messageContext.getEnvelope();

        if (soapEnvelope.getHeader() == null) {
            String soapNamespace = soapEnvelope.getNamespace()
                    .getNamespaceURI();
            // creating a soap factory according the the soap namespce uri
            SOAPFactory soapFactory = null;
            if (soapNamespace
                    .equals(SOAP11Constants.SOAP_ENVELOPE_NAMESPACE_URI)) {
                soapFactory = OMAbstractFactory.getSOAP11Factory();
            } else if (soapNamespace
                    .equals(SOAP12Constants.SOAP_ENVELOPE_NAMESPACE_URI)) {
                soapFactory = OMAbstractFactory.getSOAP12Factory();
            } else {
                System.out.println("Unknow soap message");
            }
            soapFactory.createSOAPHeader(soapEnvelope);
        }

        OMNamespace omNamespace = OMAbstractFactory.getOMFactory()
                .createOMNamespace("http://mycompany.com", "myNs");

        SOAPHeaderBlock soapHeaderBlock = soapEnvelope.getHeader()
                .addHeaderBlock("responseId", omNamespace);

        soapHeaderBlock.setText("11223344");

        soapHeaderBlock = soapEnvelope.getHeader().addHeaderBlock(
                "resonseTime", omNamespace);

        soapHeaderBlock.setText(new java.util.Date().toString());

        return InvocationResponse.CONTINUE;
    }
}

-------------------- module configuration file

<module name="CustHeaderModule" class="mypackage.SoapHeaderModule">
<OutFlow>
        <handler name="OutFlowHeaderOutHandler" class="mypackage.HeaderOutHandler">
             <order phase="CustHeaderPhase" />
        </handler>
</OutFlow>
</module>


----------------   modify axis2.xml

    <phaseOrder type="OutFlow">
        <!--      user can add his own phases to this area  -->
        <phase name="soapmonitorPhase"/>
        <phase name="OperationOutPhase"/>
        <phase name="CustHeaderPhase"/>
        <phase name="auditLogPhase"/>
        <!--system predefined phase-->
        <!--these phase will run irrespective of the service-->
        <phase name="RMPhase"/>
        <phase name="PolicyDetermination"/>
        <phase name="MessageOut"/>
        <phase name="Security"/>
    </phaseOrder>

Take note that I put CustHeaderPhase before auditLogPhase, so that inject soap headers first, auditLogPhase show response soap message including soap headers subsequently.

------------- add the module to services.xml

    <module ref="auditLogger"/>
   <module ref="CustHeaderModule"/>



-------- test it.

 <?xml version='1.0' encoding='utf-8'?>
<soapenv:Envelope xmlns:soapenv="http://www.w3.org/2003/05/soap-envelope">
<soapenv:Header>
    <myNs:responseId xmlns:myNs="http://mycompany.com">11223344</myNs:responseId>
    <myNs:resonseTime xmlns:myNs="http://mycompany.com">Tue Jan 04 17:13:16 CST 2011</myNs:resonseTime>
</soapenv:Header>
<soapenv:Body>
    <ns2:getUserProfileResponse xmlns:ns2="http://axis.test.com/ws">
        <ns2:userProfileResponse>
        <<ns1:age xmlns:ns1="http://bean.axis.test.com/xsd">32</ns1:age>
        <ns1:userName xmlns:ns1="http://bean.axis.test.com/xsd">dddddddddd</ns1:userName>
    </ns2:userProfileResponse>
</soapenv:Body>
</soapenv:Envelope>


============= Reminder ===============
to customize HTTP header is another useful function. refer to
http://www.keith-chapman.org/2008/10/axis2-setting-custom-http-headers-on.html


Example code:

MessageContext responseMessageContext =        MessageContext.getCurrentMessageContext().getOperationContext().getMessageContext(                 WSDLConstants.MESSAGE_LABEL_OUT_VALUE);   
List headers = new ArrayList();  headers.add(new Header(HTTPConstants.HEADER_CONTENT_ENCODING, "identity"));  responseMessageContext.setProperty(HTTPConstants.HTTP_HEADERS, headers);

Monday, January 3, 2011

Develop Web Service With Axis2 #6 - Workaround Solution for Custom "Header" on Skeleton/Stub side

Prev >>> Develop Web Service With Axis2 #5 - Custom Soap Header on Stub side 

It is not straightforward to add <soap:header> to response message on skeleton in axis2.
Someone suggests to write own message receiver, or add soap header by module, and someone suggests to convert header information to HTTP header,etc.


There is a workaround solution to put header information to body. Let's start with code-first approach.

---- define header information

package com.test.axis.bean;

import java.util.Date;

public class Header {

    private String requestId;

   // setter and getter methods
}

----- re-define the interface

package com.test.axis.service;

import com.test.axis.bean.AuthUserRequest;
import com.test.axis.bean.UserInfoRequest;
import com.test.axis.bean.UserInfoResponse;
import com.test.axis.bean.WebServiceFault;

public interface UserServices {
    UserInfoResponse getUserInfo(UserInfoRequest infoRequest) throws WebServiceFault;

    UserInfoResponse authUser(
            AuthUserRequest authRequest)
            throws WebServiceFault;
}

---- define userInfoRequest by this way

package com.test.axis.bean;

public class UserInfoRequest {

   private Header header;
    private String userId;
   
    // setter and getter methods
 }

------ re-define UserInfoResponse object, put header inside as well

package com.test.axis.bean;

import java.util.Date;

public class UserInfoResponse {

    private Header header;
   private String userName;
 
    // setter and getter methods
}

------ generate wsdl based on above java beans and interface

------ request message

<?xml version='1.0' encoding='utf-8'?>
<soapenv:Envelope xmlns:soapenv="http://www.w3.org/2003/05/soap-envelope">
    <soapenv:Body>
        <ns2:getUserInfo xmlns:ns2="http://axis.test.com/ws">
            <ns2:userInfoRequest>
                <header xmlns="http://bean.axis.test.com/xsd">
                    <requestId>552121</requestId>
                </header>
                <ns1:userId xmlns:ns1="http://bean.axis.test.com/xsd">user111</ns1:userId>
            </ns2:userInfoRequest>
        </ns2:getUserInfo>
    </soapenv:Body>
</soapenv:Envelope>

------ response soap message

<?xml version='1.0' encoding='utf-8'?>
<soapenv:Envelope xmlns:soapenv="http://www.w3.org/2003/05/soap-envelope">
<soapenv:Body>
<ns2:getUserInfoResponse xmlns:ns2="http://axis.test.com/ws">
<ns2:userInfoResponse>
<header xmlns="http://bean.axis.test.com/xsd">
<requestId>552121</requestId>
</header>
<ns1:userName xmlns:ns1="http://bean.axis.test.com/xsd">dddddddddd</ns1:userName>
</ns2:userInfoResponse>
</ns2:getUserInfoResponse>
</soapenv:Body>
</soapenv:Envelope>

Develop Web Service With Axis2 #5 - Custom Soap Header on Stub side

Prev >>> Develop Web Service With Axis2 #4 - Axis2 Style Interceptor 

1) if only add a simple string to header 


--- stub code

OMFactory omFactory =OMAbstractFactory.getOMFactory();
OMNamespace omNamespace = omFactory.createOMNamespace("http://mycompany.org", "myHeader");
OMElement header = omFactory.createOMElement("header", omNamespace);
header.setText("This is a custom soap header");
_stub._getServiceClient().addHeader(header);

-- soap message

<?xml version='1.0' encoding='utf-8'?>
<soapenv:Envelope xmlns:soapenv="http://www.w3.org/2003/05/soap-envelope">
  <soapenv:Header>
     <myHeader:header xmlns:myHeader="http://mycompany.org">
        This is a custom soap header
     </myHeader:header>
   </soapenv:Header>
   <soapenv:Body>
      <ns2:getUserInfo xmlns:ns2="http://axis.test.com/ws" />
    </soapenv:Body>
</soapenv:Envelope>

2) to use OMElement object to encapsulate soap header

--- stub code

OMElement header1 = AXIOMUtil.stringToOM("<header1><systemId>system-011</systemId></header1>");
OMElement header2 = AXIOMUtil.stringToOM("<header2><requestTime>"+new java.util.Date()+"</requestTime></header2>");
 _stub._getServiceClient().addHeader(header1);
 _stub._getServiceClient().addHeader(header2);

--- soap message

<?xml version='1.0' encoding='utf-8'?>
<soapenv:Envelope xmlns:soapenv="http://www.w3.org/2003/05/soap-envelope">
<soapenv:Header>
   <header1>
       <systemId>system-011</systemId>
   </header1>
   <header2>
       <requestTime>Mon Jan 03 14:29:00 CST 2011</requestTime>
   </header2>
</soapenv:Header>
<soapenv:Body>
<ns2:getUserInfo xmlns:ns2="http://axis.test.com/ws" />
</soapenv:Body>
</soapenv:Envelope>

3. SOAPHeaderBlock

--- stub code

OMNamespace omNamespace = OMAbstractFactory.getOMFactory()
                    .createOMNamespace("http://myCompany.com", "headerNs");
SOAPHeaderBlock header1 = OMAbstractFactory.getSOAP12Factory()
                    .createSOAPHeaderBlock("header1", omNamespace);
header1.addChild(AXIOMUtil.stringToOM("<header123>header content</header123>"));
_stub._getServiceClient().addHeader(header1);

---- soap message

<?xml version='1.0' encoding='utf-8'?>
<soapenv:Envelope xmlns:soapenv="http://www.w3.org/2003/05/soap-envelope">
<soapenv:Header>
    <headerNs:header1 xmlns:headerNs="http://myCompany.com">
         <header123>header content</header123>
    </headerNs:header1>
</soapenv:Header>
<soapenv:Body>
<ns2:getUserInfo xmlns:ns2="http://axis.test.com/ws" />
</soapenv:Body>
</soapenv:Envelope>

Reference
http://wso2.org/library/3156

Develop Web Service With Axis2 #4 - Axis2 Style Interceptor

Prev >>> Develop Web Service With Axis2 #3 - Custom Fault Message

If you are familiar with struts2, you would know there are a few of interceptors work together in strut2 framework. Similarly, Axis2 provides 4 flows or execution chains, InFlow, OutFlow, InFaultFlow and OutFaultFlow, which define a series of modules inside, or you can call them interceptors.

This article is strictly to follow the instruction provided by axis2 to implement a handler to print incoming/outgoing soap messages for each web service transactions.
http://axis.apache.org/axis2/java/core/docs/modules.html

1.  module class

package mypackage;

import org.apache.axis2.AxisFault;
import org.apache.axis2.context.ConfigurationContext;
import org.apache.axis2.description.AxisDescription;
import org.apache.axis2.description.AxisModule;
import org.apache.axis2.modules.Module;
import org.apache.neethi.Assertion;
import org.apache.neethi.Policy;

public class AuditLogModule implements Module {

    // initialize the module
    public void init(ConfigurationContext configContext, AxisModule module)
            throws AxisFault {
        System.out.println("init");
    }

    public void engageNotify(AxisDescription axisDescription) throws AxisFault {
    }

    // shutdown the module
    public void shutdown(ConfigurationContext configurationContext)
            throws AxisFault {
        System.out.println("shutdown");
    }

    public String[] getPolicyNamespaces() {
        return null;
    }

    public void applyPolicy(Policy policy, AxisDescription axisDescription)
            throws AxisFault {
    }

    public boolean canSupportAssertion(Assertion assertion) {
        return true;
    }
}

2.  handler class to print soap messages

------- class to print incoming soap message
package mypackage;

import org.apache.axis2.AxisFault;
import org.apache.axis2.context.MessageContext;
import org.apache.axis2.engine.Handler;
import org.apache.axis2.handlers.AbstractHandler;

public class AuditInHandler extends AbstractHandler implements Handler {

    public InvocationResponse invoke(MessageContext msgContext)
            throws AxisFault {
        System.out.println("Request Message :"
                + msgContext.getEnvelope().toString());
        return InvocationResponse.CONTINUE;
    }
}

------- class to print outgoing soap message

package mypackage;

import org.apache.axis2.AxisFault;
import org.apache.axis2.context.MessageContext;
import org.apache.axis2.engine.Handler;
import org.apache.axis2.handlers.AbstractHandler;

public class AuditOutHandler extends AbstractHandler implements Handler {

    @Override
    public InvocationResponse invoke(MessageContext msgContext)
            throws AxisFault {
        System.out.println("Response Message :"
                + msgContext.getEnvelope().toString());
        return InvocationResponse.CONTINUE;
    }

}

3. prepare a module configuration file module.xml

<module name="auditLogger" class="mypackage.AuditLogModule">
    <InFlow>
        <handler name="InFlowAuditLogHandler" class="mypackage.AuditInHandler">
            <order phase="auditLogPhase" />
        </handler>
    </InFlow>
    <OutFlow>
        <handler name="OutFlowAuditLogHandler" class="mypackage.AuditOutHandler">
            <order phase="auditLogPhase" />
        </handler>
    </OutFlow>

</module>

4. add the element in red to service.xml which the web service you are going to apply to with the interceptor.

<?xml version="1.0" encoding="UTF-8"?>
<!-- This file was auto-generated from WSDL -->
<!-- by the Apache Axis2 version: 1.5.3  Built on : Nov 12, 2010 (02:24:07 CET) -->
<serviceGroup>
    <service name="UserServices">
        <messageReceivers>
            <messageReceiver mep="http://www.w3.org/ns/wsdl/in-out" class="com.test.axis.ws.skeleton.UserServicesMessageReceiverInOut"/>
        </messageReceivers>
        <module ref="auditLogger"/>
        <parameter name="ServiceClass">com.test.axis.ws.skeleton.UserServicesSkeleton</parameter>
        <parameter name="useOriginalwsdl">true</parameter>
        <parameter name="modifyUserWSDLPortAddress">true</parameter>
        <operation name="authUser" mep="http://www.w3.org/ns/wsdl/in-out" namespace="http://axis.test.com/ws/service">
            <actionMapping>urn:authUser</actionMapping>
            <outputActionMapping>urn:authUserResp</outputActionMapping>
            <faultActionMapping faultName="WebServiceFault">urn:authUserWebServiceFault</faultActionMapping>
        </operation>
        <operation name="getUserInfo" mep="http://www.w3.org/ns/wsdl/in-out" namespace="http://axis.test.com/ws/service">
            <actionMapping>urn:getUserInfo</actionMapping>
            <outputActionMapping>urn:getUserInfoResp</outputActionMapping>
            <faultActionMapping faultName="WebServiceFault">urn:getUserInfoWebServiceFault</faultActionMapping>
        </operation>
    </service>
</serviceGroup>

5. deploy module to server.

copy the auditLog.mar file to %tomcat%\webapps\axis2\WEB-INF\modules
or create a folder %tomcat%\webapps\axis2\WEB-INF\modules\auditLog and put all necessary files inside.

<folder auditLog>
          |-  META-INF
                     MANIFEST.MF
                     module.xml
          |-  com\......\xx.class


6. modify axis2.xml

I am using axis2 v1.5.3. The configuration file axis2.xml of different axis2 version might be a bit different.

add the elements shown in red to axis2.xml

<!-- ================================================= -->
    <!-- Phases  -->
    <!-- ================================================= -->
    <phaseOrder type="InFlow">
        <!--  System predefined phases       -->
        <phase name="Transport">
            <handler name="RequestURIBasedDispatcher"
                     class="org.apache.axis2.dispatchers.RequestURIBasedDispatcher">
                <order phase="Transport"/>
            </handler>
            <handler name="SOAPActionBasedDispatcher"
                     class="org.apache.axis2.dispatchers.SOAPActionBasedDispatcher">
                <order phase="Transport"/>
            </handler>
        </phase>
        <phase name="Addressing">
            <handler name="AddressingBasedDispatcher"
                     class="org.apache.axis2.dispatchers.AddressingBasedDispatcher">
                <order phase="Addressing"/>
            </handler>
        </phase>
        <phase name="Security"/>
        <phase name="PreDispatch"/>
        <phase name="Dispatch" class="org.apache.axis2.engine.DispatchPhase">
            <handler name="RequestURIBasedDispatcher"
                     class="org.apache.axis2.dispatchers.RequestURIBasedDispatcher"/>
            <handler name="SOAPActionBasedDispatcher"
                     class="org.apache.axis2.dispatchers.SOAPActionBasedDispatcher"/>
            <handler name="RequestURIOperationDispatcher"
                     class="org.apache.axis2.dispatchers.RequestURIOperationDispatcher"/>
            <handler name="SOAPMessageBodyBasedDispatcher"
                     class="org.apache.axis2.dispatchers.SOAPMessageBodyBasedDispatcher"/>
            <handler name="HTTPLocationBasedDispatcher"
                     class="org.apache.axis2.dispatchers.HTTPLocationBasedDispatcher"/>
            <handler name="GenericProviderDispatcher"
                     class="org.apache.axis2.jaxws.dispatchers.GenericProviderDispatcher"/>
            <handler name="MustUnderstandValidationDispatcher"
                     class="org.apache.axis2.jaxws.dispatchers.MustUnderstandValidationDispatcher"/>
        </phase>
        <phase name="RMPhase"/>
        <!--  System predefined phases       -->
        <!--   After Postdispatch phase module author or service author can add any phase he want      -->
        <phase name="OperationInPhase">
            <handler name="MustUnderstandChecker"
                     class="org.apache.axis2.jaxws.dispatchers.MustUnderstandChecker">
                <order phase="OperationInPhase"/>
            </handler>
        </phase>
        <phase name="auditLogPhase"/>
        <phase name="soapmonitorPhase"/>
    </phaseOrder>
    <phaseOrder type="OutFlow">
        <!--      user can add his own phases to this area  -->
        <phase name="soapmonitorPhase"/>
        <phase name="OperationOutPhase"/>
        <phase name="auditLogPhase"/>
        <!--system predefined phase-->
        <!--these phase will run irrespective of the service-->
        <phase name="RMPhase"/>
        <phase name="PolicyDetermination"/>
        <phase name="MessageOut"/>
        <phase name="Security"/>
    </phaseOrder>
    <phaseOrder type="InFaultFlow">
        <phase name="Addressing">
            <handler name="AddressingBasedDispatcher"
                     class="org.apache.axis2.dispatchers.AddressingBasedDispatcher">
                <order phase="Addressing"/>
            </handler>
        </phase>
        <phase name="Security"/>
        <phase name="PreDispatch"/>
        <phase name="Dispatch" class="org.apache.axis2.engine.DispatchPhase">
            <handler name="RequestURIBasedDispatcher"
                     class="org.apache.axis2.dispatchers.RequestURIBasedDispatcher"/>
            <handler name="SOAPActionBasedDispatcher"
                     class="org.apache.axis2.dispatchers.SOAPActionBasedDispatcher"/>
            <handler name="RequestURIOperationDispatcher"
                     class="org.apache.axis2.dispatchers.RequestURIOperationDispatcher"/>
            <handler name="SOAPMessageBodyBasedDispatcher"
                     class="org.apache.axis2.dispatchers.SOAPMessageBodyBasedDispatcher"/>
            <handler name="HTTPLocationBasedDispatcher"
                     class="org.apache.axis2.dispatchers.HTTPLocationBasedDispatcher"/>
            <handler name="GenericProviderDispatcher"
                     class="org.apache.axis2.jaxws.dispatchers.GenericProviderDispatcher"/>
            <handler name="MustUnderstandValidationDispatcher"
                     class="org.apache.axis2.jaxws.dispatchers.MustUnderstandValidationDispatcher"/>
        </phase>
        <phase name="RMPhase"/>
        <!--      user can add his own phases to this area  -->
        <phase name="OperationInFaultPhase"/>
        <phase name="soapmonitorPhase"/>
    </phaseOrder>
    <phaseOrder type="OutFaultFlow">
        <!--      user can add his own phases to this area  -->
        <phase name="soapmonitorPhase"/>
        <phase name="OperationOutFaultPhase"/>
        <phase name="RMPhase"/>
        <phase name="PolicyDetermination"/>
        <phase name="MessageOut"/>
        <phase name="Security"/>
    </phaseOrder>

7.test it. try to call one web service method mentioned in previous article. we can see soap message printed on tomcat console.

Request Message :<?xml version='1.0' encoding='utf-8'?><soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:q0="http://axis.test.com/ws" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><soapenv:Body>
    <q0:getUserInfo>
      <q0:userId>aaaa</q0:userId>
    </q0:getUserInfo>
  </soapenv:Body></soapenv:Envelope>


Response Message :<?xml version='1.0' encoding='utf-8'?><soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"><soapenv:Body><ns2:getUserInfoResp xmlns:ns2="http://axis.test.com/ws"><ns2:return><ns1:age xmlns:ns1="http://bean.axis.test.com/xsd">22</ns1:age><ns1:userName xmlns:ns1="http://bean.axis.test.com/xsd">test name1</ns1:userName></ns2:return></ns2:getUserInfoResp></soapenv:Body></soapenv:Envelope>

Develop Web Service With Axis2 #3 - Custom Fault Message

Prev >>> Develop Web Service With Axis2 #2 - Work on Skeleton Code

public interface UserServices {
    UserInfoResp getUserInfo(String userId) throws WebServiceFault;

    UserInfoResp authUser(AuthUserReq authReq)  throws WebServiceFault;
}

Take note:

All the methods in the interface class,which will be exposed to web service, throw WebServiceFault.

// ==== WebServiceFault.java =====
package com.test.axis.bean;

public class WebServiceFault extends Exception {
    private String errCode;
    private String errMessage;

     // setter and getter methods
}

I want to respond an error code and an error description to web service consumer if something is wrong.

---- wsdl

<wsdl:operation name="getUserInfo">
            <soap:operation soapAction="urn:getUserInfo" style="document"/>
            <wsdl:input>
                <soap:body use="literal"/>
            </wsdl:input>
            <wsdl:output>
                <soap:body use="literal"/>
            </wsdl:output>
            <wsdl:fault name="WebServiceFault">
                <soap:fault use="literal" name="WebServiceFault"/>
            </wsdl:fault>
        </wsdl:operation>

---- skeleton code

    public com.test.axis.ws.bean.GetUserInfoResp getUserInfo(
            com.test.axis.ws.bean.GetUserInfo getUserInfo2)
            throws WebServiceFault {
        if (getUserInfo2== null || getUserInfo2.getUserId() == null) {
            WebServiceFault fault = new WebServiceFault();
            com.test.axis.ws.bean.WebServiceFaultE wsFaultE = new com.test.axis.ws.bean.WebServiceFaultE();
            com.test.axis.ws.bean.xsd.WebServiceFault param = new com.test.axis.ws.bean.xsd.WebServiceFault();
            param.setErrCode("Error Code 9999");
            param.setErrMessage("Error Desc: parameter is Null.");

            wsFaultE.setWebServiceFault(param);
            fault.setFaultMessage(wsFaultE);
            throw fault;
        }
        return null;
    }

---- request soap

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:q0="http://axis.test.com/ws" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<soapenv:Body>
  <q0:getUserInfo />
</soapenv:Body>
</soapenv:Envelope>

in this case, the request parameter is empty

--- response soap

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
  <soapenv:Body>
  <soapenv:Fault>
  <faultcode>soapenv:Server</faultcode>
  <faultstring>WebServiceFault</faultstring>
  <detail>
  <ns2:WebServiceFault xmlns:ns2="http://axis.test.com/ws">
  <ns2:WebServiceFault xmlns:ns1="http://bean.axis.test.com/xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="ns1:WebServiceFault">
  <ns1:errCode>Error Code 9999</ns1:errCode>
  <ns1:errMessage>Error Desc: parameter is Null.</ns1:errMessage>
  </ns2:WebServiceFault>
  </ns2:WebServiceFault>
  </detail>
  </soapenv:Fault>
  </soapenv:Body>
  </soapenv:Envelope>

from the elements in blue, we can see error code and message returned successfully.

----- stub code


try {
    _stub.getUserInfo(new GetUserInfo());
    } catch (RemoteException e) {
    e.printStackTrace();
    } catch (WebServiceFault e) {
    // print the custom error code and error message
    System.out.println(e.getFaultMessage().getWebServiceFault().getErrCode());
    System.out.println(e.getFaultMessage().getWebServiceFault().getErrMessage());
    }

------------------------------------------------------------

TODO:

a> consider to use the following elements to customize fault message

  <faultcode>ABC</faultcode>
  <faultstring>XYZ</faultstring>
  <detail>Detail of Fault</detail>

I have not tried it out.

probably, the following reference websites will be helpful:
http://www.cnblogs.com/huqingyu/archive/2008/04/09/1145868.html
http://apps.hi.baidu.com/share/detail/23219236
http://hi.baidu.com/hero%CD%F5%E6%DD/blog/item/ad3b47107dc024c1f6039eb7.html

Develop Web Service With Axis2 #2 - Work on Skeleton Code

Prev >>> Develop Web Service With Axis2 #1 - Start From Generating WSDL File

1. generate skeleton code based on WSDL file

WSDL2Java Reference

go to %testaxis1_folder% to run this command:

wsdl2java -uri resource\UserServices.wsdl -ss -sd -d adb -S .\src
-R .\resource\META-INF -ssi -p com.test.axis.ws.skeleton
-ns2p http://bean.axis.test.com/xsd=com.test.axis.ws.bean.xsd,http://axis.test.com/ws=com.test.axis.ws.bean

then, you will find
* all generated skeleton source code under this package com.test.axis.ws
* ant build file auto generated
* resource\META-INFO\services.xml and resource\META-INF\UserServices.wsdl

2. put all axis2 related jar files into your project build path.

3. complete your business logic in the skeleton class.

4. run task 'jar.server[default]' in the auto-generated build.xml,
then you can get UserServices.aar under folder %testaxis1_folder%\build\lib

5. deploy the aar file.

5.1 download axis2 war distribution.
5.2 deploy the war file to tomcat.
5.3 copy the aar file to %tomcat%\webapps\axis2\WEB-INF\services
or unzip the aar file and put the whole unzipped folder to %tomcat%\webapps\axis2\WEB-INF\services

6. write a client to call or use a tools to call the first method,you will see this response.

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
    <soapenv:Body>
          <soapenv:Fault>
                  <faultcode>soapenv:Server</faultcode>
                  <faultstring>Please implement  com.test.axis.ws.skeleton.UserServicesSkeleton#getUserInfo</faultstring>
                  <detail />
          </soapenv:Fault>
     </soapenv:Body>
</soapenv:Envelope>

=====================================
Consideration:

a> POJO style web service
http://axis.apache.org/axis2/java/core/docs/pojoguide.html

If your web service is very simple, not necessary to consider customized soap headers and fault, message level security,etc. POJO style web service is a nice choice.

b> JAX-WS style web serivce
http://axis.apache.org/axis2/java/core/docs/jaxws-guide.html

Both Axis2 and Metro support jax-ws API,which use annotation-based model to develop web service.
It seems current Axis2 is not ready to support WS-Security fully for JAX-WS, so I leave it aside first.

c> Data Binding
http://axis.apache.org/axis2/java/core/docs/adb/adb-howto.html

ADB is the data binding approach Axis2 suggests. The other ways are xmlbeans,jixb and jaxbri.

The skeleton and stub code will be different if you select different data binding as they adopt different approach to convert XML data to an object, or vice verse.

Develop Web Service With Axis2 #1 - Start From Generating WSDL File

- Contrast-First VS Code-First

Contract-first development style is to develop web services that start with the XML Schema/WSDL contract first followed by the Java code second.
Code-first development style is to generate web service contract,WSDL and XSD, based on the java code.

- WSDL Knowledge

learn it from
http://www.w3schools.com/wsdl/
http://www.w3school.com.cn/wsdl/index.asp
http://www.w3school.com.cn/schema/index.asp

- Code-First Style to generate WSDL file

1. download axis2 binary distribution.

2. configure environment variables.

If your OS is window vista, control panel - system - advanced system settings - tab 'advanced' - button 'Environment Variables...'
setup the following variables:
AXIS2_HOME= the folder of axis2
PATH = %AXIS2_HOME%\bin

3. create a java project in eclipse, let's say project name testaxis1.

4. define an interface class,which exposes all methods to web service.

package com.test.axis.service;

import com.test.axis.bean.AuthUserReq;
import com.test.axis.bean.UserInfoResp;
import com.test.axis.bean.WebServiceFault;

public interface UserServices {
    UserInfoResp getUserInfo(String userId) throws WebServiceFault;

    UserInfoResp authUser(AuthUserReq authReq) throws WebServiceFault;
}

5. complete its dependency classes as follows.

// ===== UserInfoResp.java ====
package com.test.axis.bean;

import java.util.Date;

public class UserInfoResp {

    private String userName;
    // setter and getter methods
}

// ===== AuthUserReq.java====
package com.test.axis.bean;

public class AuthUserReq{

    private String userName;
    private String password;

    // setter and getter methods
}

// ==== WebServiceFault.java =====
package com.test.axis.bean;

public class WebServiceFault extends Exception {
    private String errCode;
    private String errMessage;

     // setter and getter methods
}

6. generate wsdl file with tools java2wsdl.bat

Java2WSDL Reference
 
go to %testaxis1_folder%\bin and run this command:
java2wsdl -cn com.test.axis.service.UserServices -o ..\resource -of UserServices.wsdl -tn http://axis.test.com/ws/service -stn http://axis.test.com/ws -dlb doc/lit

you will find the WSDL file at %testaxis1_folder%\resource\UserServices.wsdl

Take note
* the xml elements in red.
Axis2 can not detect the parameter name defined in java interface, but gives a parameter 'args0' instead. It is better to modify the wsdl file and make it meaningful.
so change
<xs:element minOccurs="0" name="args0" nillable="true" type="xs:string"/>
to
<xs:element minOccurs="0" name="userId" nillable="true" type="xs:string"/>
and change
<xs:element minOccurs="0" name="args0" nillable="true" type="ax21:AuthUserReq"/>
to
<xs:element minOccurs="0" name="authUserReq" nillable="true" type="ax21:AuthUserReq"/>

It is very troublesome to manually change the name like 'args0', how to do it better?

someone suggests to use -g while compiling the java code, please refer to
http://wso2.org/blog/sumedha/3727


It is not straightforward on my view.

Actually, just remember DO NOT generate wsdl file based on an interface, based on an implement class instead, then axis2 can detect the real parameter name.



* the xml elements in blue. 
By default, axis2 set minOccurs="0" and nillable="true" for all fields. If based on your business logic, userName is mandatory and can not be Null, change the definition
  <xs:element minOccurs="0" name="userName" nillable="true" type="xs:string"/>
to
  <xs:element minOccurs="1" maxOccurs="1" name="userName" nillable="false" type="xs:string"/>

* -dlb doc/lit

This is a very good article to introduce the style of wsdl
http://www.ibm.com/developerworks/webservices/library/ws-whichwsdl/


--- Reference URLs
http://www.keith-chapman.org/2008/10/axis2-wsdl2java-generate-better-code.html