Version 15

    Since 3.1.0 (CXF)

    Apache CXF comes with support for WS-RM 1.0. In this sample we will show how to create client and endpoint communicating each other using WS-RM 1.0. The sample uses WS-Policy specification to configure WS-RM.

    Creating the WS-RM based service and client is very simple. User needs to create regular JAX-WS service and client first. The last step is to configure WSRM.

    The service

    We will start with the following endpoint implementation (bottom-up approach):

    package org.jboss.test.ws.jaxws.samples.wsrm.service;
    
    import javax.jws.Oneway;
    import javax.jws.WebMethod;
    import javax.jws.WebService;
    
    @WebService
    (
       name = "SimpleService",
       serviceName = "SimpleService",
       targetNamespace = "http://www.jboss.org/jbossws/ws-extensions/wsrm"
    )
    public class SimpleServiceImpl
    {
       @Oneway
       @WebMethod
       public void ping()
       {
          System.out.println("ping()");
       }
    
       @WebMethod
       public String echo(String s)
       {
          System.out.println("echo(" + s + ")");
          return s;
       }
    }
    

    Let's say that compiled endpoint class is in directory /home/username/wsrm/cxf/classes. Our next step is to generate JAX-WS artifacts and WSDL.

     

    Generating WSDL and JAX-WS Endpoint Artifacts

    We will use wsprovide commandline tool to generate WSDL and JAX-WS artifacts. Here's the command:

    cd $JBOSS_HOME/bin
    
    ./wsprovide.sh --keep --wsdl \
       --classpath=/home/username/wsrm/cxf/classes \
       --output=/home/username/wsrm/cxf/wsprovide/generated/classes \
       --resource=/home/username/wsrm/cxf/wsprovide/generated/wsdl \
       --source=/home/username/wsrm/cxf/wsprovide/generated/src \
       org.jboss.test.ws.jaxws.samples.wsrm.service.SimpleServiceImpl
    

    The above command generates the following artifacts:

    # compiled classes
    ls /home/username/wsrm/cxf/wsprovide/generated/classes/org/jboss/test/ws/jaxws/samples/wsrm/service/jaxws/
    Echo.class  EchoResponse.class  Ping.class
    
    # java sources
    ls /home/username/wsrm/cxf/wsprovide/generated/src/org/jboss/test/ws/jaxws/samples/wsrm/service/jaxws/
    Echo.java  EchoResponse.java  Ping.java
    
    # contract artifacts
    ls /home/username/wsrm/cxf/wsprovide/generated/wsdl/
    SimpleService.wsdl
    

    All aforementioned generated artifacts will be part of endpoint archive. But before we will create the endpoint archive we need to reference generated WSDL from endpoint. To achieve that we will use wsdlLocation annotation attribute. Here's the updated endpoint implementation before packaging it to the war file:

    package org.jboss.test.ws.jaxws.samples.wsrm.service;
    
    import javax.jws.Oneway;
    import javax.jws.WebMethod;
    import javax.jws.WebService;
    
    @WebService
    (
       name = "SimpleService",
       serviceName = "SimpleService",
       wsdlLocation = "WEB-INF/wsdl/SimpleService.wsdl",
       targetNamespace = "http://www.jboss.org/jbossws/ws-extensions/wsrm"
    )
    public class SimpleServiceImpl
    {
       @Oneway
       @WebMethod
       public void ping()
       {
          System.out.println("ping()");
       }
    
       @WebMethod
       public String echo(String s)
       {
          System.out.println("echo(" + s + ")");
          return s;
       }
    }
    

    Created endpoint war archive consists of the following entries:

    jar -tvf jaxws-samples-wsrm.war 
         0 Wed Apr 16 14:39:22 CEST 2008 META-INF/
       106 Wed Apr 16 14:39:20 CEST 2008 META-INF/MANIFEST.MF
         0 Wed Apr 16 14:39:22 CEST 2008 WEB-INF/
       591 Wed Apr 16 14:39:20 CEST 2008 WEB-INF/web.xml
         0 Wed Apr 16 14:39:22 CEST 2008 WEB-INF/classes/
         0 Wed Apr 16 14:39:18 CEST 2008 WEB-INF/classes/org/
         0 Wed Apr 16 14:39:18 CEST 2008 WEB-INF/classes/org/jboss/
         0 Wed Apr 16 14:39:18 CEST 2008 WEB-INF/classes/org/jboss/test/
         0 Wed Apr 16 14:39:18 CEST 2008 WEB-INF/classes/org/jboss/test/ws/
         0 Wed Apr 16 14:39:20 CEST 2008 WEB-INF/classes/org/jboss/test/ws/jaxws/
         0 Wed Apr 16 14:39:20 CEST 2008 WEB-INF/classes/org/jboss/test/ws/jaxws/samples/
         0 Wed Apr 16 14:39:18 CEST 2008 WEB-INF/classes/org/jboss/test/ws/jaxws/samples/wsrm/
         0 Wed Apr 16 14:39:18 CEST 2008 WEB-INF/classes/org/jboss/test/ws/jaxws/samples/wsrm/service/
         0 Wed Apr 16 14:39:18 CEST 2008 WEB-INF/classes/org/jboss/test/ws/jaxws/samples/wsrm/service/jaxws/
      1235 Wed Apr 16 14:39:18 CEST 2008 WEB-INF/classes/org/jboss/test/ws/jaxws/samples/wsrm/service/SimpleServiceImpl.class
       997 Wed Apr 16 14:39:18 CEST 2008 WEB-INF/classes/org/jboss/test/ws/jaxws/samples/wsrm/service/jaxws/Echo.class
      1050 Wed Apr 16 14:39:18 CEST 2008 WEB-INF/classes/org/jboss/test/ws/jaxws/samples/wsrm/service/jaxws/EchoResponse.class
       679 Wed Apr 16 14:39:18 CEST 2008 WEB-INF/classes/org/jboss/test/ws/jaxws/samples/wsrm/service/jaxws/Ping.class
         0 Wed Apr 16 14:39:22 CEST 2008 WEB-INF/wsdl/
      2799 Wed Apr 16 14:39:20 CEST 2008 WEB-INF/wsdl/SimpleService.wsdl
    

    The content of web.xml file is:

    <?xml version="1.0" encoding="UTF-8"?>
    
    <web-app
       version="2.5" xmlns="http://java.sun.com/xml/ns/javaee" 
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
       xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
       <servlet>
          <servlet-name>SimpleService</servlet-name>
          <servlet-class>org.jboss.test.ws.jaxws.samples.wsrm.service.SimpleServiceImpl</servlet-class>
       </servlet>
       <servlet-mapping>
          <servlet-name>SimpleService</servlet-name>
          <url-pattern>/*</url-pattern>
       </servlet-mapping>
    </web-app>
    

    Generating JAX-WS Client Artifacts

    Before we will write regular JAX-WS client we need to generate client artifacts from WSDL. Here's the command to achieve that:

    cd $JBOSS_HOME/bin
    
    ./wsconsume.sh --keep \
       --package=org.jboss.test.ws.jaxws.samples.wsrm.generated \
       --output=/home/username/wsrm/cxf/wsconsume/generated/classes \
       --source=/home/username/wsrm/cxf/wsconsume/generated/src \
       /home/username/wsrm/cxf/wsprovide/generated/wsdl/SimpleService.wsdl
    

    The above command generates the following artifacts:

    # compiled classes
    ls /home/username/wsrm/cxf/wsconsume/generated/classes/org/jboss/test/ws/jaxws/samples/wsrm/generated/
    Echo.class          ObjectFactory.class  Ping.class           SimpleService_Service.class
    EchoResponse.class  package-info.class   SimpleService.class  SimpleService_SimpleServicePort_Client.class
    
    # java sources
    ls /home/username/wsrm/cxf/wsconsume/generated/src/org/jboss/test/ws/jaxws/samples/wsrm/generated/
    Echo.java          ObjectFactory.java  Ping.java           SimpleService_Service.java
    EchoResponse.java  package-info.java   SimpleService.java  SimpleService_SimpleServicePort_Client.java
    

    Now the last step is to write the regular JAX-WS client using generated artifacts.

     

    Writing Regular JAX-WS Client

    The following is the regular JAX-WS client using generated artifacts:

    package org.jboss.test.ws.jaxws.samples.wsrm.client;
    
    import java.net.URL;
    import javax.xml.namespace.QName;
    import javax.xml.ws.Service;
    import org.jboss.test.ws.jaxws.samples.wsrm.generated.SimpleService;
    
    public final class SimpleServiceTestCase
    {
    
       private static final String serviceURL = "http://localhost:8080/jaxws-samples-wsrm/SimpleService";
       
       public static void main(String[] args) throws Exception
       {
          // create service
          QName serviceName = new QName("http://www.jboss.org/jbossws/ws-extensions/wsrm", "SimpleService");
          URL wsdlURL = new URL(serviceURL + "?wsdl");
          Service service = Service.create(wsdlURL, serviceName);
          SimpleService proxy = (SimpleService)service.getPort(SimpleService.class);
          
          // invoke methods
          proxy.ping(); // one way call
          proxy.echo("Hello World!"); // request responce call
       }
       
    }
    

    Now we have both endpoint and client implementation but without WSRM in place. Our next goal is to turn on the WS-RM feature.

     

    Turning on WS-RM 1.0

    Extending WSDL Using WS-Policy

    To activate WSRM on server side we need to extend the WSDL with WSRM and addressing policies. Here is how it looks like:

    <?xml version="1.0" encoding="UTF-8"?>
    <wsdl:definitions name="SimpleService" targetNamespace="http://www.jboss.org/jbossws/ws-extensions/wsrm" xmlns:tns="http://www.jboss.org/jbossws/ws-extensions/wsrm" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" xmlns:wsp="http://www.w3.org/2006/07/ws-policy">
    <wsdl:types>
    <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:tns="http://www.jboss.org/jbossws/ws-extensions/wsrm" attributeFormDefault="unqualified" elementFormDefault="unqualified" targetNamespace="http://www.jboss.org/jbossws/ws-extensions/wsrm">
    <xsd:element name="ping" type="tns:ping"/>
    <xsd:complexType name="ping">
    <xsd:sequence/>
    </xsd:complexType>
    <xsd:element name="echo" type="tns:echo"/>
    <xsd:complexType name="echo">
    <xsd:sequence>
    <xsd:element minOccurs="0" name="arg0" type="xsd:string"/>
    </xsd:sequence>
    </xsd:complexType>
    <xsd:element name="echoResponse" type="tns:echoResponse"/>
    <xsd:complexType name="echoResponse">
    <xsd:sequence>
    <xsd:element minOccurs="0" name="return" type="xsd:string"/>
    </xsd:sequence>
    </xsd:complexType>
    </xsd:schema>
      </wsdl:types>
      <wsdl:message name="echoResponse">
        <wsdl:part name="parameters" element="tns:echoResponse">
        </wsdl:part>
      </wsdl:message>
      <wsdl:message name="echo">
        <wsdl:part name="parameters" element="tns:echo">
        </wsdl:part>
      </wsdl:message>
      <wsdl:message name="ping">
        <wsdl:part name="parameters" element="tns:ping">
        </wsdl:part>
      </wsdl:message>
      <wsdl:portType name="SimpleService">
        <wsdl:operation name="ping">
          <wsdl:input name="ping" message="tns:ping">
        </wsdl:input>
        </wsdl:operation>
        <wsdl:operation name="echo">
          <wsdl:input name="echo" message="tns:echo">
        </wsdl:input>
          <wsdl:output name="echoResponse" message="tns:echoResponse">
        </wsdl:output>
        </wsdl:operation>
      </wsdl:portType>
      <wsdl:binding name="SimpleServiceSoapBinding" type="tns:SimpleService">
    
        <!-- - - - - - - - - - - - - - - - - - - - - - - - - - -->
        <!-- Created WS-Policy with WSRM addressing assertions -->
        <!-- - - - - - - - - - - - - - - - - - - - - - - - - - -->
    
    
    <wsp:Policy>
          <wswa:UsingAddressing xmlns:wswa="http://www.w3.org/2006/05/addressing/wsdl"/>
          <wsrmp:RMAssertion xmlns:wsrmp="http://schemas.xmlsoap.org/ws/2005/02/rm/policy"/>
        </wsp:Policy>
    
        <soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"/>
        <wsdl:operation name="ping">
          <soap:operation soapAction="" style="document"/>
          <wsdl:input name="ping">
            <soap:body use="literal"/>
          </wsdl:input>
        </wsdl:operation>
        <wsdl:operation name="echo">
          <soap:operation soapAction="" style="document"/>
          <wsdl:input name="echo">
            <soap:body use="literal"/>
          </wsdl:input>
          <wsdl:output name="echoResponse">
            <soap:body use="literal"/>
          </wsdl:output>
        </wsdl:operation>
      </wsdl:binding>
      <wsdl:service name="SimpleService">
        <wsdl:port name="SimpleServicePort" binding="tns:SimpleServiceSoapBinding">
          <soap:address location="http://localhost:9090/hello"/>
        </wsdl:port>
      </wsdl:service>
    </wsdl:definitions>
    

     

     

    Basic WS-RM configuration

    Since 3.4.0

    Once the endpoint wsdl is properly updated with the policies elements, the JBossWS-CXF stack is automatically able to detect the need for the WS-Policy engine to be used, both on client and server side, for enabling WS-Reliable Messaging.

    The endpoint advertises RM capabilities through the published wsdl and the client is required to also enable WS-RM for successfully exchanging messages with the server.

    The regular jaxws client above is enough if the user does not need to tune any specific detail of the RM subsystem (acknowledgment / retransmission intervals, thresholds, ...)

     

    Advanced WS-RM configuration

    When users want to have full control over the way WS-RM communication is established, the current CXF Bus needs to be properly configured. This can be done through a CXF Spring configuration.

    Providing Client CXF Configuration

    Next step is to create the client CXF configuration file that will be used by client. The following file was copied/pasted from CXF 2.0.5 ws_rm sample. It simply activates the WSRM protocol for CXF client. We will name this file cxf.xml in our sample. Here's the content of this file:

    <beans
      xmlns="http://www.springframework.org/schema/beans"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xmlns:cxf="http://cxf.apache.org/core"
      xmlns:wsa="http://cxf.apache.org/ws/addressing"
      xmlns:http="http://cxf.apache.org/transports/http/configuration"
      xmlns:wsrm-policy="http://schemas.xmlsoap.org/ws/2005/02/rm/policy"
      xmlns:wsrm-mgr="http://cxf.apache.org/ws/rm/manager"
      xsi:schemaLocation="
        http://cxf.apache.org/core
        http://cxf.apache.org/schemas/core.xsd
        http://cxf.apache.org/transports/http/configuration
        http://cxf.apache.org/schemas/configuration/http-conf.xsd
        http://schemas.xmlsoap.org/ws/2005/02/rm/policy
        http://schemas.xmlsoap.org/ws/2005/02/rm/wsrm-policy.xsd
        http://cxf.apache.org/ws/rm/manager
        http://cxf.apache.org/schemas/configuration/wsrm-manager.xsd
        http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd">
     
      <cxf:bus>
        <cxf:features>
          <cxf:logging/>
          <wsa:addressing/>
          <wsrm-mgr:reliableMessaging>
            <wsrm-policy:RMAssertion>
              <wsrm-policy:BaseRetransmissionInterval Milliseconds="4000"/>           
              <wsrm-policy:AcknowledgementInterval Milliseconds="2000"/>          
            </wsrm-policy:RMAssertion>
            <wsrm-mgr:destinationPolicy>
              <wsrm-mgr:acksPolicy intraMessageThreshold="0" />
            </wsrm-mgr:destinationPolicy>
          </wsrm-mgr:reliableMessaging>
        </cxf:features>
      </cxf:bus>
        
    </beans>
    

    And that's almost all. The client configuration needs to picked up by the client classloader; in order to achieve that the cxf.xml file has to be put in the META-INF directory of client jar. That jar should then be provided when setting the class loader.

    Alternatively the bus configuration can also be read programmatically as follows:

     

    Updating Client Code to Read Bus Configuration File

    And here's the last piece the updated CXF client:

    package org.jboss.test.ws.jaxws.samples.wsrm.client;
    
    import java.net.URL;
    import java.io.File;
    import javax.xml.namespace.QName;
    import javax.xml.ws.Service;
    import org.apache.cxf.Bus;
    import org.apache.cxf.BusFactory;
    import org.apache.cxf.bus.spring.SpringBusFactory;
    import org.jboss.test.ws.jaxws.samples.wsrm.generated.SimpleService;
    
    public final class SimpleServiceTestCase
    {
    
       private static final String serviceURL = "http://localhost:8080/jaxws-samples-wsrm/SimpleService";
       
       public static void main(String[] args) throws Exception
       {
          // create bus
          SpringBusFactory busFactory = new SpringBusFactory();
          URL cxfConfig = new File("resources/jaxws/samples/wsrm/cxf.xml").toURL();
          Bus bus = busFactory.createBus(cxfConfig);
          busFactory.setDefaultBus(bus);
    
          // create service
          QName serviceName = new QName("http://www.jboss.org/jbossws/ws-extensions/wsrm", "SimpleService");
          URL wsdlURL = new URL(serviceURL + "?wsdl");
          Service service = Service.create(wsdlURL, serviceName);
          SimpleService proxy = (SimpleService)service.getPort(SimpleService.class);
          
          // invoke methods
          proxy.ping(); // one way call
          proxy.echo("Hello World!"); // request responce call
    
          // shutdown bus
          bus.shutdown(true);
       }
       
    }

    Sample Sources

    All sources from this tutorial are part of JBossWS-CXF distribution.