Friday, January 21, 2011

Develop Web Service With Axis2 #9 - HTTP Basic Authentication in Weblogic

My Environment:
JDK v1.6.x
Axis2 V1.5.4
Weblogic v1.03

1) add 'user'/'user group' in weblogic

login weblogic server administration console

select 'Security Realms' under domain structure on the left of admin console page

click 'myrealm' which is the default realm name in weblogic,and open configuration 'Settings for myrealm'

click tab 'Users and Groups' to add user and user group

click sub-tab 'Groups' first to add one new group

click sub-tab 'Users', and click 'New' button to add new user and key in password here as well.

click the user name which was created above hyperlink, and select 'Groups' tab to assign the user to the user group created above

2) add configuration shown below in web.xml

<security-constraint>
   <web-resource-collection>
      <web-resource-name>mywsapi</web-resource-name>
      <url-pattern>/services/{your service name}</url-pattern>
   </web-resource-collection>
   <auth-constraint>
      <role-name>{user name configured in Weblogic}</role-name>
   </auth-constraint>
</security-constraint>
<login-config>
   <auth-method>BASIC</auth-method>
   <realm-name>myrealm</realm-name>   <-- this is the default realm name in weblogic
</login-config>
<security-role>
   <role-name>{user name configured in Weblogic}</role-name>
</security-role>

3) add configuration shown below in weblogic.xml

<security-role-assignment>
   <role-name>{user name configured in Weblogic}</role-name>
   <principal-name>{user group configured in Weblogic}</principal-name>
</security-role-assignment>

4) on stub side, you need to do more to pass user name and password as follows.

Options opt = _stub._getServiceClient().getOptions();
HttpTransportProperties.Authenticator mbAuth = new HttpTransportProperties.Authenticator();
mbAuth.setUsername("{user name configured in weblogic}");
mbAuth.setPassword("{password configured in weblogic}");
mbAuth.setPreemptiveAuthentication(true);
opt.setProperty(HTTPConstants.AUTHENTICATE, mbAuth);
_stub._getServiceClient().setOptions(opt);

Tuesday, January 11, 2011

Develop Web Service With Axis2 #8 - Troubleshooting while working with Axis2

My Environment:
JDK  v1.6.x
Axis2  v1.5.4
Ant  v1.7.1
Hibernate  v3.3.1
Tomcat  v6.0.26
Weblogic v1.03

1) tomcat can not start up properly if deploy axis2 project to tomcat

I got the errors as follows

SEVERE: Error in dependencyCheck
java.util.zip.ZipException: invalid bit length repeat
    at java.util.zip.InflaterInputStream.read(InflaterInputStream.java:147)
    at java.util.zip.ZipInputStream.read(ZipInputStream.java:146)

or

Jan 9, 2011 7:35:42 PM org.apache.catalina.core.StandardContext start
SEVERE: Error in dependencyCheck
java.util.zip.ZipException: too many length or distance symbols
    at java.util.zip.InflaterInputStream.read(InflaterInputStream.java:147)
    at java.util.zip.ZipInputStream.read(ZipInputStream.java:146)

or

org.apache.axis2.deployment.DeploymentException: A ClassNotFoundException error occurred in loading the message receiver

Solution:
You must follow up the build.xml provided by axis2 strictly if you write your own build.xml

For example,

<copy todir="${build.dist.dir}/WEB-INF/lib" filtering="true" overwrite="yes">

if filtering="true", the jar file will be not unzipped properly if pack in war file.

2) can not load hibernate

My .aar structure is as follows

.aar
      /com       --- classes files
      /lib          --- hibernate jar files and other the 3rd jar files
      /META-INF
      hibernate.cfg.xml
      log4j.properties


in services.xml, add the following configuration

<parameter name="ServiceTCCL">composite</parameter>

if using this configuration, axis2 will load jar files under lib folder inside aar


3) log4j

Finally, I found if put log4j.properties in aar, it will not work, the correct location is %WAR%\WEB-INF\classes

4)  jar conflict if deploying to weblogic

These are well-known exceptions if deloying axis2 project into weblogic.

org.hibernate.QueryException: ClassNotFoundException: org.hibernate.hql.ast.HqlToken


Solution:
*change the war file struecture

WAR
   |- axis2-web
   |- WEB-INF
          |- classes
                  |- com/xxx    Here is your DAO, business classes, utils,etc
                  log4j.properties
                  hibernate.cfg.xml
                  your own other properties 
          |- conf
                   axis2.xml
          |- lib
                   all axis2-related jar files
                   log4j-related jar files
                   hibernate-related jar files
                   other 3rd party jar files
           |- modules
           |- services
                    yourservice.aar     Only inlcude skeleton classes here, almost generated by axis2
                            com/...../xxxMessageReceiverInOut.class
                            com/...../xxxSkeleton.class
                            META-INF\services.xml
                            META-INF\wsdl.list
                            META-INF\xxxx.wsdl
           web.xml
           weblogic.xml


* in weblogic.xml, should configure

    <container-descriptor>
        <prefer-web-inf-classes>true</prefer-web-inf-classes>
    </container-descriptor>

* in hibernate.cfg.xml, it is not necessary to configure 'hibernate.query.factory_class'

there is a jar ANTLR conflict between hibernate v3.x and weblogic, so someone suggests to configure explicitly 'hibernate.query.factory_class'

for hibernate v3.x query translator,
hibernate.query.factory_class= org.hibernate.hql.ast.ASTQueryTranslatorFactory
for hibernate v2.x query translator
hibernate.query.factory_class= org.hibernate.hql.classic.ClassicQueryTranslatorFactory

It is NOT necessary to do so.

<prefer-web-inf-classes> will ensure weblogic load %WAR%\WEB-INF\lib , let hibernate auto-choose which query translator to use.


5) can not find aar/mar file if deploying to weblogic

need to add aar file name, or mar file name in \services\services.list and \mrodules\modules.list

6) cannot show custom WSDL file, always show auto-generated WSDL file

in your own .aar\META-INF folder to add a file named wsdl.list , and put your wsdl file name in it.

in services.xml ,  <parameter name="useOriginalwsdl">true</parameter>

7) xerces jar file conflict if deploying to weblogic

java.lang.LinkageError: loader constraint violation in interface itable initialization: when resolving method "org.apache.xerces.dom.ElementImpl.getSchemaTypeInfo()Lorg/w3c/dom/TypeInfo;" the class loader (instance of weblogic/utils/classloaders/ChangeAwareClassLoader) of the current class, org/apache/xerces/dom/ElementImpl, and the class loader (instance of <bootloader>) for interface org/w3c/dom
/Element have different Class objects for the type org/w3c/dom/TypeInfo used in the signature

Solution: remove xercesImpl-2.8.1.jar from axis2\WEB-INF\lib

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