Chapter 28. Developing a Consumer From a WSDL Contract
Abstract
One way of creating a consumer is to start from a WSDL contract. The contract defines the operations, messages, and transport details of the service on which a consumer makes requests. The starting point code for the consumer is generated from the WSDL contract. The functionality required by the consumer is added to the generated code.
28.1. Generating the Stub Code
Overview
The cxf-codegen-plugin
Maven plug-in generates the stub code from the WSDL contract. The stub code provides the supporting code that is required to invoke operations on the remote service.
For consumers, the cxf-codegen-plugin
Maven plug-in generates the following types of code:
- Stub code — Supporting files for implementing a consumer.
- Starting point code — Sample code that connects to the remote service and invokes every operation on the remote service.
Generating the consumer code
To generate consumer code use the cxf-codegen-plugin
Maven plug-in. Example 28.1, “Consumer Code Generation” shows how to use the code generator to generate consumer code.
Example 28.1. Consumer Code Generation
<plugin> <groupId>org.apache.cxf</groupId> <artifactId>cxf-codegen-plugin</artifactId> <version>${cxf.version}</version> <executions> <execution> <id>generate-sources</id> <phase>generate-sources</phase> <configuration> <sourceRoot>outputDir</sourceRoot> <wsdlOptions> <wsdlOption> <wsdl>wsdl</wsdl> <extraargs> <extraarg>-client</extraarg> </extraargs> </wsdlOption> </wsdlOptions> </configuration> <goals> <goal>wsdl2java</goal> </goals> </execution> </executions> </plugin>
Where outputDir is the location of a directory where the generated files are placed and wsdl specifies the WSDL contract’s location. The -client
option generates starting point code for the consumer’s main()
method.
For a complete list of the arguments available for the cxf-codegen-plugin
Maven plug-in see Section 44.2, “cxf-codegen-plugin”.
Generated code
The code generation plug-in generates the following Java packages for the contract shown in Example 26.1, “HelloWorld WSDL Contract”:
-
org.apache.hello_world_soap_http — This package is generated from the
http://apache.org/hello_world_soap_http
target namespace. All of the WSDL entities defined in this namespace (for example, the Greeter port type and the SOAPService service) map to Java classes this Java package. -
org.apache.hello_world_soap_http.types — This package is generated from the
http://apache.org/hello_world_soap_http/types
target namespace. All of the XML types defined in this namespace (that is, everything defined in thewsdl:types
element of the HelloWorld contract) map to Java classes in this Java package.
The stub files generated by the cxf-codegen-plugin
Maven plug-in fall into the following categories:
Classes representing WSDL entities in the org.apache.hello_world_soap_http package. The following classes are generated to represent WSDL entities:
-
Greeter — A Java interface that represents the Greeter
wsdl:portType
element. In JAX-WS terminology, this Java interface is the service endpoint interface (SEI). -
SOAPService
— A Java service class (extendingjavax.xml.ws.Service
) that represents the SOAPServicewsdl:service
element. -
PingMeFault — A Java exception class (extending
java.lang.Exception
) that represents the pingMeFaultwsdl:fault
element.
-
Greeter — A Java interface that represents the Greeter
- Classes representing XML types in the org.objectweb.hello_world_soap_http.types package. In the HelloWorld example, the only generated types are the various wrappers for the request and reply messages. Some of these data types are useful for the asynchronous invocation model.
28.2. Implementing a Consumer
Overview
To implement a consumer when starting from a WSDL contract, you must use the following stubs:
- Service class
- SEI
Using these stubs, the consumer code instantiates a service proxy to make requests on the remote service. It also implements the consumer’s business logic.
Generated service class
Example 28.2, “Outline of a Generated Service Class” shows the typical outline of a generated service class, ServiceName_Service
[2], which extends the javax.xml.ws.Service
base class.
Example 28.2. Outline of a Generated Service Class
@WebServiceClient(name="..." targetNamespace="..." wsdlLocation="...") public class ServiceName extends javax.xml.ws.Service { ... public ServiceName(URL wsdlLocation, QName serviceName) { } public ServiceName() { } // Available only if you specify '-fe cxf' option in wsdl2java public ServiceName(Bus bus) { } @WebEndpoint(name="...") public SEI getPortName() { } . . . }
The ServiceName
class in Example 28.2, “Outline of a Generated Service Class” defines the following methods:
-
ServiceName(URL wsdlLocation, QName serviceName)
— Constructs a service object based on the data in thewsdl:service
element with the QName ServiceName service in the WSDL contract that is obtainable from wsdlLocation. -
ServiceName()
— The default constructor. It constructs a service object based on the service name and the WSDL contract that were provided at the time the stub code was generated (for example, when running thewsdl2java
tool). Using this constructor presupposes that the WSDL contract remains available at a specified location. -
ServiceName(Bus bus)
— (CXF specific) An additional constructor that enables you to specify the Bus instance used to configure the Service. This can be useful in the context of a multi-threaded application, where multiple Bus instances can be associated with different threads. This constructor provides a simple way of ensuring that the Bus that you specify is the one that is used with this Service. Only available if you specify the-fe cxf
option when invoking thewsdl2java
tool. -
getPortName()
— Returns a proxy for the endpoint defined by thewsdl:port
element with thename
attribute equal to PortName. A getter method is generated for everywsdl:port
element defined by the ServiceName service. Awsdl:service
element that contains multiple endpoint definitions results in a generated service class with multiplegetPortName()
methods.
Service endpoint interface
For every interface defined in the original WSDL contract, you can generate a corresponding SEI. A service endpoint interface is the Java mapping of a wsdl:portType
element. Each operation defined in the original wsdl:portType
element maps to a corresponding method in the SEI. The operation’s parameters are mapped as follows: . The input parameters are mapped to method arguments.
- The first output parameter is mapped to a return value.
-
If there is more than one output parameter, the second and subsequent output parameters map to method arguments (moreover, the values of these arguments must be passed using
Holder
types).
For example, Example 28.3, “The Greeter Service Endpoint Interface” shows the Greeter SEI, which is generated from the wsdl:portType
element defined in Example 26.1, “HelloWorld WSDL Contract”. For simplicity, Example 28.3, “The Greeter Service Endpoint Interface” omits the standard JAXB and JAX-WS annotations.
Example 28.3. The Greeter Service Endpoint Interface
package org.apache.hello_world_soap_http; ... public interface Greeter { public String sayHi(); public String greetMe(String requestType); public void greetMeOneWay(String requestType); public void pingMe() throws PingMeFault; }
Consumer main function
Example 28.4, “Consumer Implementation Code” shows the code that implements the HelloWorld consumer. The consumer connects to the SoapPort port on the SOAPService service and then proceeds to invoke each of the operations supported by the Greeter port type.
Example 28.4. Consumer Implementation Code
package demo.hw.client; import java.io.File; import java.net.URL; import javax.xml.namespace.QName; import org.apache.hello_world_soap_http.Greeter; import org.apache.hello_world_soap_http.PingMeFault; import org.apache.hello_world_soap_http.SOAPService; public final class Client { private static final QName SERVICE_NAME = new QName("http://apache.org/hello_world_soap_http", "SOAPService"); private Client() { } public static void main(String args[]) throws Exception { if (args.length == 0) { System.out.println("please specify wsdl"); System.exit(1); } URL wsdlURL; File wsdlFile = new File(args[0]); if (wsdlFile.exists()) { wsdlURL = wsdlFile.toURL(); } else { wsdlURL = new URL(args[0]); } System.out.println(wsdlURL); SOAPService ss = new SOAPService(wsdlURL,SERVICE_NAME); Greeter port = ss.getSoapPort(); String resp; System.out.println("Invoking sayHi..."); resp = port.sayHi(); System.out.println("Server responded with: " + resp); System.out.println(); System.out.println("Invoking greetMe..."); resp = port.greetMe(System.getProperty("user.name")); System.out.println("Server responded with: " + resp); System.out.println(); System.out.println("Invoking greetMeOneWay..."); port.greetMeOneWay(System.getProperty("user.name")); System.out.println("No response from server as method is OneWay"); System.out.println(); try { System.out.println("Invoking pingMe, expecting exception..."); port.pingMe(); } catch (PingMeFault ex) { System.out.println("Expected exception: PingMeFault has occurred."); System.out.println(ex.toString()); } System.exit(0); } }
The Client.main()
method from Example 28.4, “Consumer Implementation Code” proceeds as follows:
Provided that the Apache CXF runtime classes are on your classpath, the runtime is implicitly initialized. There is no need to call a special function to initialize Apache CXF.
The consumer expects a single string argument that gives the location of the WSDL contract for HelloWorld. The WSDL contract’s location is stored in wsdlURL
.
You create a service object using the constructor that requires the WSDL contract’s location and service name. Call the appropriate getPortName()
method to obtain an instance of the required port. In this case, the SOAPService service supports only the SoapPort port, which implements the Greeter
service endpoint interface.
The consumer invokes each of the methods supported by the Greeter service endpoint interface.
In the case of the pingMe()
method, the example code shows how to catch the PingMeFault fault exception.
Client proxy generated with -fe cxf option
If you generate your client proxy by specifying the -fe cxf
option in wsdl2java (thereby selecting the cxf
frontend), the generated client proxy code is better integrated with Java 7. In this case, when you call a getServiceNamePort()
method, you get back a type that is a sub-interface of the SEI and implements the following additional interfaces:
-
java.lang.AutoCloseable
-
javax.xml.ws.BindingProvider
(JAX-WS 2.0) -
org.apache.cxf.endpoint.Client
To see how this simplifies working with a client proxy, consider the following Java code sample, written using a standard JAX-WS proxy object:
// Programming with standard JAX-WS proxy object // (ServiceNamePortType port = service.getServiceNamePort(); ((BindingProvider)port).getRequestContext() .put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY, address); port.serviceMethod(...); ((Closeable)port).close();
And compare the preceding code with the following equivalent code sample, written using code generated by the cxf
frontend:
// Programming with proxy generated using '-fe cxf' option // try (ServiceNamePortTypeProxy port = service.getServiceNamePort()) { port.getRequestContext().put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY, address); port.serviceMethod(...); }