Chapter 29. Finding WSDL at Runtime
Abstract
Hard coding the location of WSDL documents into an application is not scalable. In real deployment environments, you will want to allow the WSDL document’s location be resolved at runtime. Apache CXF provides a number of tools to make this possible.
29.1. Mechanisms for Locating the WSDL Document
When developing consumers using the JAX-WS APIs you are must provide a hard coded path to the WSDL document that defines your service. While this is OK in a small environment, using hard coded paths does not work well in enterprise deployments.
To address this issue, Apache CXF provides three mechanisms for removing the requirement of using hard coded paths:
Injecting the proxy into your implementation code is generally the best option because it is the easiest to implement. It requires only a client endpoint and a configuration file for injecting and instantiating the service proxy.
29.2. Instantiating a Proxy by Injection
Overview
Apache CXF’s use of the Spring Framework allows you to avoid the hassle of using the JAX-WS APIs to create service proxies. It allows you to define a client endpoint in a configuration file and then inject a proxy directly into the implementation code. When the runtime instantiates the implementation object, it will also instantiate a proxy for the external service based on the configuration. The implementation is handed by reference to the instantiated proxy.
Because the proxy is instantiated using information in the configuration file, the WSDL location does not need to be hard coded. It can be changed at deployment time. You can also specify that the runtime should search the application’s classpath for the WSDL.
Procedure
To inject a proxy for an external service into a service provider’s implementation do the following:
Deploy the required WSDL documents in a well known location that all parts of the application can access.
NoteIf you are deploying the application as a WAR file, it is recommended that you place all of the WSDL documents and XML Schema documents in the
WEB-INF/wsdl
folder of the WAR.NoteIf you are deploying the application as a JAR file, it is recommended that you place all of the WSDL documents and XML Schema documents in the
META-INF/wsdl
folder of the JAR.- Configure a JAX-WS client endpoint for the proxy that is being injected.
-
Inject the proxy into your service provide using the
@Resource
annotation.
Configuring the proxy
You configure a JAX-WS client endpoint using the jaxws:client
element in you application’s configuration file. This tells the runtime to instantiate a org.apache.cxf.jaxws.JaxWsClientProxy
object with the specified properties. This object is the proxy that will be injected into the service provider.
At a minimum you need to provide values for the following attributes:
-
id
—Specifies the ID used to identify the client to be injected. -
serviceClass
—Specifies the SEI of the service on which the proxy makes requests.
Example 29.1, “Configuration for a Proxy to be Injected into a Service Implementation” shows the configuration for a JAX-WS client endpoint.
Example 29.1. Configuration for a Proxy to be Injected into a Service Implementation
<beans ... xmlns:jaxws="http://cxf.apache.org/jaxws" ... schemaLocation="... http://cxf.apache.org/jaxws http://cxf.apache.org/schemas/jaxws.xsd ..."> <jaxws:client id="bookClient" serviceClass="org.apache.cxf.demo.BookService" wsdlLocation="classpath:books.wsdl"/> ... </beans>
In Example 29.1, “Configuration for a Proxy to be Injected into a Service Implementation” the wsdlLocation
attribute instructs the runtime to load the WSDL from the classpath. If books.wsdl
is on the classpath, the runtime will be able to find it.
For more information on configuring a JAX-WS client see Section 17.2, “Configuring Consumer Endpoints”.
Coding the provider implementation
You inject the configured proxy into a service implementation as a resource using the @Resource
as shown in Example 29.2, “Injecting a Proxy into a Service Implementation”.
Example 29.2. Injecting a Proxy into a Service Implementation
package demo.hw.server;
import org.apache.hello_world_soap_http.Greeter;
@javax.jws.WebService(portName = "SoapPort", serviceName = "SOAPService",
targetNamespace = "http://apache.org/hello_world_soap_http",
endpointInterface = "org.apache.hello_world_soap_http.Greeter")
public class StoreImpl implements Store {
@Resource(name="bookClient") private BookService proxy;
}
The annotation’s name
property corresponds to the value of the JAX-WS client’s id
attribute. The configured proxy is injected into the BookService
object declared immediately after the annotation. You can use this object to make invocations on the proxy’s external service.
29.3. Using a JAX-WS Catalog
Overview
The JAX-WS specification mandates that all implementations support:
a standard catalog facility to be used when resolving any Web service document that is part of the description of a Web service, specifically WSDL and XML Schema documents.
This catalog facility uses the XML catalog facility specified by OASIS. All of the JAX-WS APIs and annotation that take a WSDL URI use the catalog to resolve the WSDL document’s location.
This means that you can provide an XML catalog file that rewrites the locations of your WSDL documents to suite specific deployment environments.
Writing the catalog
JAX-WS catalogs are standard XML catalogs as defined by the OASIS XML Catalogs 1.1 specification. They allow you to specify mapping:
- a document’s public identifier and/or a system identifier to a URI.
- the URI of a resource to another URI.
Table 29.1, “Common JAX-WS Catalog Elements” lists some common elements used for WSDL location resolution.
Element | Description |
---|---|
| Maps a URI to an alternate URI. |
| Rewrites the beginning of a URI. For example, this element allows you to map all URIs that start with http://cxf.apache.org to URIs that start with classpath:. |
| Maps a URI to an alternate URI based on the suffix of the original URI. For example you could map all URIs that end in foo.xsd to classpath:foo.xsd. |
Packaging the catalog
The JAX-WS specification mandates that the catalog used to resolve WSDL and XML Schema documents is assembled using all available resources named META-INF/jax-ws-catalog.xml
. If your application is packaged into a single JAR, or WAR, you can place the catalog into a single file.
If your application is packaged as multiple JARs, you can split the catalog into a number of files. Each catalog file could be modularized to only deal with WSDLs accessed by the code in the specific JARs.
29.4. Using a contract resolver
Overview
The most involved mechanism for resolving WSDL document locations at runtime is to implement your own custom contract resolver. This requires that you provide an implementation of the Apache CXF specific ServiceContractResolver interface. You also need to register your custom resolver with the bus.
Once properly registered, the custom contract resolver will be used to resolve the location of any required WSDL and schema documents.
Implementing the contract resolver
A contract resolver is an implementation of the org.apache.cxf.endpoint.ServiceContractResolver interface. As shown in Example 29.3, “ServiceContractResolver Interface”, this interface has a single method, getContractLocation()
, that needs to be implemented. getContractLocation()
takes the QName of a service and returns the URI for the service’s WSDL contract.
Example 29.3. ServiceContractResolver Interface
public interface ServiceContractResolver { URI getContractLocation(QName qname); }
The logic used to resolve the WSDL contract’s location is application specific. You can add logic that resolves contract locations from a UDDI registry, a database, a custom location on a file system, or any other mechanism you choose.
Registering the contract resolver programmatically
Before the Apache CXF runtime will use your contract resolver, you must register it with a contract resolver registry. Contract resolver registries implement the org.apache.cxf.endpoint.ServiceContractResolverRegistry interface. However, you do not need to implement your own registry. Apache CXF provides a default implementation in the org.apache.cxf.endpoint.ServiceContractResolverRegistryImpl
class.
To register a contract resolver with the default registry you do the following:
- Get a reference to the default bus object.
-
Get the service contract registry from the bus using the bus'
getExtension()
method. - Create an instance of your contract resolver.
-
Register your contract resolver with the registry using the registry’s
register()
method.
Example 29.4, “Registering a Contract Resolver” shows the code for registering a contract resolver with the default registry.
Example 29.4. Registering a Contract Resolver
BusFactory bf=BusFactory.newInstance(); Bus bus=bf.createBus(); ServiceContractResolverRegistry registry = bus.getExtension(ServiceContractResolverRegistry); JarServiceContractResolver resolver = new JarServiceContractResolver(); registry.register(resolver);
The code in Example 29.4, “Registering a Contract Resolver” does the following:
Gets a bus instance.
Gets the bus' contract resolver registry.
Creates an instance of a contract resolver.
Registers the contract resolver with the registry.
Registering a contract resolver using configuration
You can also implement a contract resolver so that it can be added to a client through configuration. The contract resolver is implemented in such a way that when the runtime reads the configuration and instantiates the resolver, the resolver registers itself. Because the runtime handles the initialization, you can decide at runtime if a client needs to use the contract resolver.
To implement a contract resolver so that it can be added to a client through configuration do the following:
-
Add an
init()
method to your contract resolver implementation. -
Add logic to your
init()
method that registers the contract resolver with the contract resolver registry as shown in Example 29.4, “Registering a Contract Resolver”. -
Decorate the
init()
method with the@PostConstruct
annotation.
Example 29.5, “Service Contract Resolver that can be Registered Using Configuration” shows a contract resolver implementation that can be added to a client using configuration.
Example 29.5. Service Contract Resolver that can be Registered Using Configuration
import javax.annotation.PostConstruct; import javax.annotation.Resource; import javax.xml.namespace.QName; import org.apache.cxf.Bus; import org.apache.cxf.BusFactory; public class UddiResolver implements ServiceContractResolver { private Bus bus; ... @PostConstruct public void init() { BusFactory bf=BusFactory.newInstance(); Bus bus=bf.createBus(); if (null != bus) { ServiceContractResolverRegistry resolverRegistry = bus.getExtension(ServiceContractResolverRegistry.class); if (resolverRegistry != null) { resolverRegistry.register(this); } } } public URI getContractLocation(QName serviceName) { ... } }
To register the contract resolver with a client you need to add a bean
element to the client’s configuration. The bean
element’s class
attribute is the name of the class implementing the contract resolver.
Example 29.6, “Bean Configuring a Contract Resolver” shows a bean for adding a configuration resolver implemented by the org.apache.cxf.demos.myContractResolver
class.
Example 29.6. Bean Configuring a Contract Resolver
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> ... <bean id="myResolver" class="org.apache.cxf.demos.myContractResolver" /> ... </beans>
Contract resolution order
When a new proxy is created, the runtime uses the contract registry resolver to locate the remote service’s WSDL contract. The contract resolver registry calls each contract resolver’s getContractLocation()
method in the order in which the resolvers were registered. It returns the first URI returned from one of the registered contract resolvers.
If you registered a contract resolver that attempted to resolve the WSDL contract at a well known shared file system, it would be the only contract resolver used. However, if you subsequently registered a contract resolver that resolved WSDL locations using a UDDI registry, the registry could use both resolvers to locate a service’s WSDL contract. The registry would first attempt to locate the contract using the shared file system contract resolver. If that contract resolver failed, the registry would then attempt to locate it using the UDDI contract resolver.