Monday, November 21, 2011

WS-RM enabling a Web Service Client on Jboss

This post is about configuring a web application that acts as a web service client deployed on Jboss 5.1.0 GA .

The reason for preparing this post is that altough parts of this information exist in various forums and in the CXF documentation, we were unable to find a single place to summarize all necessary steps so far.

The actual web service is another application running on a Weblogic 10.3 cluster. The main WS-RM related characteristics are directly encoded in the WSDL:

 1. A policy has been defined to specify the related WS-RM characteristics:

       <wsp:Policy wsu:Id="wsrm10policy">

              <wsp:ExactlyOne>

                     <wsp:All>

                           <wsam:Addressing>

                                  <wsp:Policy />

                           </wsam:Addressing>

                           <wsrmp:RMAssertion xmlns:wsrmp="http://schemas.xmlsoap.org/ws/2005/02/rm/policy">

                                  <wsrmp:BaseRetransmissionInterval

                                         Milliseconds="4000" />

                                  <wsrmp:DeliveryAssurance>

                                         <wsp:Policy>

                                                <wsp:All>

                                                       <wsrmp:ExactlyOnce />

                                                       <wsrmp:InOrder />

                                                </wsp:All>

                                         </wsp:Policy>

                                  </wsrmp:DeliveryAssurance>

                           </wsrmp:RMAssertion>

                     </wsp:All>

              </wsp:ExactlyOne>

       </wsp:Policy>

2. The policy is referenced in the binding of our Web Service:

<wsdl:binding name="AsyncDeliveryRMServiceSoapBinding" type="tns:AsyncDeliveryRMServicePortType">

              <wsp:PolicyReference URI='#wsrm10policy' />

              . . . . . .

       </wsdl:binding>

The focus of this post is on the client side (Jboss). The following steps outline the WS-RM specific configuration:

  • Use the server WSDL to generate the required artifacts (Web Service interface, etc.) using wsdl2java.
  • Configure a  jaxws:client and a decoupled endpoint to receive WS-RM responses:
  • <jaxws:client id="asyncRMClient"

                xmlns:h="http://namespace.my/"

                serviceClass="my.namespace. AsyncDeliveryRMServicePortType"

                serviceName="h:AsyncDeliveryRMService"

                endpointName="h:AsyncDeliveryRMServicePort"

                wsdlLocation="http://192.168.56.120/wsserver/AsyncDeliveryRMService?wsdl"

                >

           . . . . .

    </jaxws:client>

     

    <http:conduit name="{http://namespace.my/}AsyncDeliveryRMServicePort.http-conduit">

                  <http:client DecoupledEndpoint="http://192.168.56.1:9990/decoupled_endpoint"/>

    </http:conduit>


  • Add the following dependencies in the pom.xml of the Maven project (notice the inclusion of cxf-rt-transports-http-jetty as it is necessary for the decoupled endpoint to be created) :

        <dependency>

            <groupId>org.apache.cxf</groupId>

            <artifactId>cxf-rt-frontend-jaxws</artifactId>

            <version>${cxf.version}</version>

        </dependency>

 

        <dependency>

            <groupId>org.apache.cxf</groupId>

            <artifactId>cxf-rt-transports-http</artifactId>

            <version>${cxf.version}</version>

        </dependency>

 

        <dependency>

            <groupId>org.apache.cxf</groupId>

            <artifactId>cxf-rt-ws-rm</artifactId>

            <version>${cxf.version}</version>

        </dependency>

 

        <dependency>

            <groupId>org.apache.cxf</groupId>

            <artifactId>cxf-rt-ws-addr</artifactId>

            <version>${cxf.version}</version>

        </dependency>

        <dependency>

            <groupId>org.apache.cxf</groupId>

            <artifactId>cxf-rt-ws-policy</artifactId>

            <version>${cxf.version}</version>

        </dependency>

        <dependency>

            <groupId>org.apache.cxf</groupId>

            <artifactId>cxf-common-utilities</artifactId>

            <version>${cxf.version}</version>

        </dependency>

 

        <dependency>

            <groupId>org.apache.cxf</groupId>

            <artifactId>cxf-rt-transports-http-jetty</artifactId>

            <version>${cxf.version}</version>

            <exclusions>

                <exclusion>

                    <artifactId>geronimo-servlet_2.5_spec</artifactId>

                    <groupId>org.apache.geronimo.specs</groupId>

                </exclusion>

            </exclusions>

        </dependency>

  • Create a class (RMRequestPoster in our case) to invoke the client configured above and wire this dependency in the Spring configuration:

    <bean

        class="my.sender.RMRequestPoster"

        id="rmRequestPoster">

 

        <property name="rmPort">

            <ref bean="asyncRMClient"/>

        </property>

 

   </bean>

  • Add code to invoke the service:

public class RMRequestPoster {

. . . .

//This is the jaxws:client configured in step 3

private AsyncDeliveryRMServicePortType rmPort;

. . . .

public void postRequest() {

      

//Perform any application specific actions

. . . . .

 

//Invoke a one-way operation on the port

rmPort.postMessage(<required parameters>);

       

. . . . . .

}

       

    }

  • IMPORTANT: Copy jetty.jar and jetty-util.jar under <%JBOSS_HOME%>/server/<your server>/deployers/ jbossws.deployer
  • If the decoupled endpoint must operate behind a proxy, it may be necessary to change the replyTo address in the WS-Addressing headers. An interceptor can be used to achieve this: 

public class ReplyToInterceptor extends AbstractPhaseInterceptor<Message>

{

    public ReplyToInterceptor() {

        super(Phase.PRE_LOGICAL);

        addAfter(MAPAggregator.class.getName());

    }

    @Override

    public void handleMessage(Message message) {

        AddressingProperties maps = ContextUtils.retrieveMAPs(message, false,

           true);

        EndpointReferenceType replyTo = maps.getReplyTo();

        replyTo.getAddress().setValue(

           "http://192.168.56.1:9999/decoupled_endpoint");

    }

}