此内容没有您所选择的语言版本。
Chapter 25. Developing a Consumer Without a WSDL Contract
Abstract
You do not need a WSDL contract to develop a service consumer. You can create a service consumer from an annotated SEI. Along with the SEI you need to know the address at which the endpoint exposing the service is published, the QName of the service element that defines the endpoint exposing the service, and the QName of the port element defining the endpoint on which your consumer makes requests. This information can be specified in the SEI’s annotations or provided separately.
25.1. Java-First Consumer Development
To create a consumer without a WSDL contract you must do the following:
-
Create a
Service
object for the service on which the consumer will invoke operations. -
Add a port to the
Service
object. -
Get a proxy for the service using the
Service
object’sgetPort()
method. - Implement the consumer’s business logic.
25.2. Creating a Service Object
Overview
The javax.xml.ws.Service
class represents the wsdl:service
element which contains the definition of all of the endpoints that expose a service. As such, it provides methods that allow you to get endpoints, defined by wsdl:port
elements, that are proxies for making remote invocations on a service.
The Service
class provides the abstractions that allow the client code to work with Java types as opposed to working with XML documents.
The create() methods
The Service
class has two static create()
methods that can be used to create a new Service
object. As shown in Example 25.1, “Service
create()
Methods”, both of the create()
methods take the QName of the wsdl:service
element the Service
object will represent, and one takes a URI specifying the location of the WSDL contract.
All services publish their WSDL contracts. For SOAP/HTTP services the URI is usually the URI for the service appended with ?wsdl
.
Example 25.1. Service
create()
Methods
public staticService
create
URL
wsdlLocation
QName
serviceName
WebServiceExceptionpublic staticService
create
QName
serviceName
WebServiceException
The value of the serviceName
parameter is a QName. The value of its namespace part is the target namespace of the service. The service’s target namespace is specified in the targetNamespace
property of the @WebService
annotation. The value of the QName’s local part is the value of wsdl:service
element’s name
attribute. You can determine this value in one of the following ways: . It is specified in the serviceName
property of the @WebService
annotation.
-
You append
Service
to the value of thename
property of the@WebService
annotation. -
You append
Service
to the name of the SEI.
Programmatically-created CXF consumers deployed in OSGi environments require special handling to avoid the likelihood of incurring ClassNotFoundException
s. For each bundle that contains programmatically-created CXF consumers, you need to create a singleton CXF default bus and ensure that all of the bundle’s CXF consumers use it. Without this safeguard, one bundle could be assigned the CXF default bus created in another bundle, which could cause the inheriting bundle to fail.
For example, suppose bundle A did not explicitly set a CXF default bus and was assigned the CXF default bus created in bundle B. If the CXF bus in bundle A needed to be configured with additional features (such as SSL or WS-Security) or needed to load certain classes or resources from the application in bundle A, it would fail. This is so because the CXF bus instance sets a thread context class loader (TCCL) as the bundle class loader of the bundle that created it (in this case bundle B). Furthermore, certain frameworks, such as wss4j (implements WS-Security in CXF) use the TCCL to load resources, such as calback handler classes or other property files, from inside the bundle. Because bundle A is assigned bundle B’s default CXF bus and it’s TCCL, the wss4j layer cannot load the required resources from bundle A, which results in ClassNotFoundException
errors.
To create the singleton CXF default bus, insert this code:
BusFactory.setThreadDefaultBus(BusFactory.newInstance().createBus());
at the beginning of the main
method that creates the service object, as shown in the section called “Example”.
Example
Example 25.2, “Creating a Service
Object” shows code for creating a Service
object for the SEI shown in Example 24.7, “Fully Annotated SEI”.
Example 25.2. Creating a Service
Object
package com.fusesource.demo; import javax.xml.namespace.QName; import javax.xml.ws.Service; public class Client { public static void main(String args[]) { BusFactory.setThreadDefaultBus(BusFactory.newInstance().createBus()); QName serviceName = new QName("http://demo.redhat.com", "stockQuoteReporter"); Service s = Service.create(serviceName); ... } }
The code in Example 25.2, “Creating a Service
Object” does the following:
Creates a singleton CXF default bus that is available to all CXF consumers of the service.
Builds the QName for the service using the targetNamespace
property and the name
property of the @WebService
annotation.
Calls the single parameter create()
method to create a new Service
object.
Using the single parameter create()
frees you from having any dependencies on accessing a WSDL contract.
25.3. Adding a Port to a Service
Overview
The endpoint information for a service is defined in a wsdl:port
element, and the Service
object creates a proxy instance for each of the endpoints defined in a WSDL contract, if one is specified. If you do not specify a WSDL contract when you create your Service
object, the Service
object has no information about the endpoints that implement your service, and therefore cannot create any proxy instances. In this case, you must provide the Service
object with the information needed to represent a wsdl:port
element using the addPort()
method.
The addPort() method
The Service
class defines an addPort()
method, shown in Example 25.3, “The addPort()
Method”, that is used in cases where there is no WSDL contract available to the consumer implementation. The addPort()
method allows you to give a Service
object the information, which is typically stored in a wsdl:port
element, necessary to create a proxy for a service implementation.
Example 25.3. The addPort()
Method
addPort
QName
portName
String
bindingId
String
endpointAddress
WebServiceException
The value of the portName
is a QName. The value of its namespace part is the target namespace of the service. The service’s target namespace is specified in the targetNamespace
property of the @WebService
annotation. The value of the QName’s local part is the value of wsdl:port
element’s name
attribute. You can determine this value in one of the following ways:
-
Specify it in the
portName
property of the@WebService
annotation. -
Append
Port
to the value of thename
property of the@WebService
annotation. -
Append
Port
to the name of the SEI.
The value of the bindingId
parameter is a string that uniquely identifies the type of binding used by the endpoint. For a SOAP binding you use the standard SOAP namespace: http://schemas.xmlsoap.org/soap/
. If the endpoint is not using a SOAP binding, the value of the bindingId
parameter is determined by the binding developer. The value of the endpointAddress
parameter is the address where the endpoint is published. For a SOAP/HTTP endpoint, the address is an HTTP address. Transports other than HTTP use different address schemes.
Example
Example 25.4, “Adding a Port to a Service
Object” shows code for adding a port to the Service
object created in Example 25.2, “Creating a Service
Object”.
Example 25.4. Adding a Port to a Service
Object
package com.fusesource.demo; import javax.xml.namespace.QName; import javax.xml.ws.Service; public class Client { public static void main(String args[]) { ... QName portName = new QName("http://demo.redhat.com", "stockQuoteReporterPort"); s.addPort(portName, "http://schemas.xmlsoap.org/soap/", "http://localhost:9000/StockQuote"); ... } }
The code in Example 25.4, “Adding a Port to a Service
Object” does the following:
Creates the QName for the portName
parameter.
Calls the addPort()
method.
Specifies that the endpoint uses a SOAP binding.
Specifies the address where the endpoint is published.
25.4. Getting a Proxy for an Endpoint
Overview
A service proxy is an object that provides all of the methods exposed by a remote service and handles all of the details required to make the remote invocations. The Service
object provides service proxies for all of the endpoints it is aware of through the getPort()
method. Once you have a service proxy, you can invoke its methods. The proxy forwards the invocation to the remote service endpoint using the connection details specified in the service’s contract.
The getPort() method
The getPort()
method, shown in Example 25.5, “The getPort()
Method”, returns a service proxy for the specified endpoint. The returned proxy is of the same class as the SEI.
Example 25.5. The getPort()
Method
public<T> T
getPort
QName
portName
Class<T>
serviceEndpointInterface
WebServiceException
The value of the portName
parameter is a QName that identifies the wsdl:port
element that defines the endpoint for which the proxy is created. The value of the serviceEndpointInterface
parameter is the fully qualified name of the SEI.
When you are working without a WSDL contract the value of the portName
parameter is typically the same as the value used for the portName
parameter when calling addPort()
.
Example
Example 25.6, “Getting a Service Proxy” shows code for getting a service proxy for the endpoint added in Example 25.4, “Adding a Port to a Service
Object”.
Example 25.6. Getting a Service Proxy
package com.fusesource.demo; import javax.xml.namespace.QName; import javax.xml.ws.Service; public class Client { public static void main(String args[]) { ... quoteReporter proxy = s.getPort(portName, quoteReporter.class); ... } }
25.5. Implementing the Consumer’s Business Logic
Overview
Once you instantiate a service proxy for a remote endpoint, you can invoke its methods as if it were a local object. The calls block until the remote method completes.
If a method is annotated with the @OneWay
annotation, the call returns immediately.
Example
Example 25.7, “Consumer Implemented without a WSDL Contract” shows a consumer for the service defined in Example 24.7, “Fully Annotated SEI”.
Example 25.7. Consumer Implemented without a WSDL Contract
package com.fusesource.demo; import java.io.File; import java.net.URL; import javax.xml.namespace.QName; import javax.xml.ws.Service; public class Client { public static void main(String args[]) { QName serviceName = new QName("http://demo.eric.org", "stockQuoteReporter"); Service s = Service.create(serviceName); QName portName = new QName("http://demo.eric.org", "stockQuoteReporterPort"); s.addPort(portName, "http://schemas.xmlsoap.org/soap/", "http://localhost:9000/EricStockQuote"); quoteReporter proxy = s.getPort(portName, quoteReporter.class); Quote quote = proxy.getQuote("ALPHA"); System.out.println("Stock "+quote.getID()+" is worth "+quote.getVal()+" as of "+quote.getTime()); } }
The code in Example 25.7, “Consumer Implemented without a WSDL Contract” does the following:
Creates a Service
object.
Adds an endpoint definition to the Service
object.
Gets a service proxy from the Service
object.
Invokes an operation on the service proxy.