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.
Comments