In Chapter 2, you saw that in order to get a stub for a web service endpoint interface, it was first necessary to obtain a reference to a Service object, using code like this:
BookService_Impl service = new BookService_Impl( ); BookQuery bookQuery = (BookQuery)service.getBookQueryPort( ); BookInfo[] books = bookQuery.getBookInfo( );
where
BookService_Impl
is a class generated by
ServiceFactory
is an abstract class that can be used to create
Service
objects in a portable manner. The public
public abstract class ServiceFactory {
public static ServiceFactory newInstance( );
public abstract Service createService(URL wsdlLocation, QName serviceName);
public abstract Service createService(QName serviceName);
}
The static newInstance( ) method returns an implementation-dependent instance of the ServiceFactory class:
ServiceFactory factory = ServiceFactory.newInstance( );
Although it is
[1] Properties such as this are used not by developers, but by third-party
vendors that provide their ownimplementations of JAX-RPC. By setting an appropriate value, they cause their own classes to beinstantiated instead of the default classes provided by the reference implementation.
The two
createService( )
methods return instances of an implementation-dependent class that implements the
Service
interface. The first variant returns a
Service
object that has access to the ports of a service described in a WSDL document whose URL is supplied as its first argument. The name of the required service is supplied in the form of a
QName
object.
QName
represents an XML namespace-qualified name and is therefore
QName serviceName = new QName("urn:jwsnut.chapter2.bookservice/wsdl/
BookQuery", "BookService");
The constructor requires the namespace and local
URL wsdlURL = new URL("http://localhost:8000/Books/BookQuery?WSDL");
Service service = factory.createService(wsdlURL, serviceName);
The second variant of createService( ) requires only a QName argument. A Service object returned by this method can only be used in conjunction with the dynamic invocation interface, which is described later in this chapter.
ServiceFactory
is intended to be used by standalone J2SE clients in the case where the target service is defined in a WSDL document. However, for container-resident clients (such as J2EE application
An object that implements the Service interface provides the client-side view of a web service (despite the name that might lead you to think that this is a server-side interface). Logically, it maps to the entity represented by the service element in a WSDL document. The methods that make up the Service interface are shown in Example 6-3.
public interface Service {
// Accessors
public HandlerRegistry getHandlerRegistry( );
public TypeMappingRegistry getTypeMappingRegistry( );
public QName getServiceName( )
public URL getWSDLDocumentLocation( );
// Access to ports
public Iterator getPorts( ) throws ServiceException;
public Remote getPort(Class endpointInterface) throws ServiceException;
public Remote getPort(QName portName, Class endpointInterface)
throws ServiceException;
// DII-related methods
public Call createCall( ) throws ServiceException;
public Call createCall(QName portName) throws ServiceException;
public Call createCall(QName portName, QName operationName)
throws ServiceException;
public Call createCall(QName portName, String operationName)
throws ServiceException;
public Call[] getCalls(QName portName);
}
When you use wscompile to create stubs from a Java interface definition or from a WSDL file, one of the files that is generated is a class that implements the Service interface. The generated Service class includes a method that can be used to get a client-side stub for the service endpoint interface. In the book service example used in Chapter 2, for example, the following method is generated:
public BookQuery getBookQueryPort( )
This method is not, of course, part of the
Service
interface and it is obviously not, therefore, available from a
Service
object returned by the
ServiceFactory
createCall( )
method. If you use a
ServiceFactory
to create your
Service
object instead of directly instantiating a generated
Service
class, you will need to use one of the
getPort( )
methods listed in Example 6-3 to get access to an object that can be used to invoke the service endpoint's methods. These methods may, according to the JAX-RPC specification, return an instance of a precompiled stub, just as the
getBookQueryPort( )
method would. It may also return an object called a dynamic proxy that is created
The various
createCall( )
methods return a
Call
object that can be used to invoke an operation that belongs to the web service's endpoint interface without requiring the generation and compilation of a stub. The
Call
object is the
The remaining four Service methods can be used to access various items of state:
Returns the name of the service.
Gets the URL for the WSDL document for the service. This is null if the WSDL document location is not known ” that is, if the Service object was generated by wscompile starting from a Java interface definition, or if it was returned by the variant of the ServiceFactory createService( ) method that does not require a WSDL document URL as one of its arguments.
Returns a reference to the configured SOAP handlers for this service. A client can install handlers to perform processing on a SOAP message created as a result of a JAX-RPC call before it is transmitted, or on a response message when it is received and before its content is used to generate the results of the JAX-RPC call. The HandlerRegistry is described in Section 6.8, later in this chapter.
Returns a reference to the TypeMappingRegistry for this service. This registry contains the serializers and deserializers that are used to convert between Java primitive types and objects and their XML representation in SOAP messages. See Section 6.9, later in this chapter, for a discussion of this registry and how its contents are determined.
Although a Service object (such as BookService_Impl ) generated by wscompile has a method (like getBookQueryPort( ) ) that can be used to get a client-side stub, it is still possible to call the getPort( ) method of such an object instead. This method has two variants, the first of which requires a QName object that identifies the port and a reference to a class object for the service endpoint interface:
public Remote getPort(QName portName, Class endpointInterface);
The port name is
For a Service generated from a Java interface definition, the namespace value is taken from the targetNamespace attribute of the configuration element in the config.xml file, while the local name is the name of the Java interface itself with the string Port appended to it. Therefore, in Example 2-9, you can see that the namespace value for the BookQuery port of the book web service should be urn:jwsnut.chapter2.bookservice/wsdl/BookQuery , while the local part of the name is BookQueryPort , since BookQuery is the name of the interface defined in Example 2-3.
For a Service created from WSDL, the namespace is the one that applies to the port as defined in the WSDL document, which is determined by the targetNamespace attribute of the definitions element enclosing the port element. The local part of the name is taken from the name attribute of the port element itself. In the case of the book web service, the namespace is therefore urn:jwsnut.chapter2.bookservice/wsdl/BookQuery (see Example 5-2) and the local name is BookQueryPort (see Example 5-8).
In both cases, then, an appropriate
QName
object for the service endpoint interface of the book web service can be created as
QName portName = new QName("urn:jwsnut.chapter2.bookservice/wsdl/BookQuery",
"BookQueryPort");
The following code sequence can therefore be used to get an object that can be used to invoke the methods of this interface:
BookService_Impl service = new BookService_Impl( );
QName portName = new QName("urn:jwsnut.chapter2.bookservice/wsdl/BookQuery",
"BookQueryPort");
BookQuery bookQuery = (BookQuery)service.getPort(portName
,
BookQuery.class);
The second variant of getPort( ) requires only the Class object for the Java interface:
BookService_Impl service = new BookService_Impl( );
BookQuery bookQuery = (BookQuery)service.getPort(BookQuery.class);
Because the getPort( ) method is defined to return only a class that implements the Remote interface, it is necessary to explicitly cast the returned value to the actual interface type before it can be used to invoke the methods of the web service's endpoint interface.
When used with a generated Service object, these two variants of the getPort( ) method are equivalent. Therefore, there is no advantage to using the variant that requires a QName to describe the port in addition to the service interface Class object. In fact, in the reference implementation, both of these methods return the same generated stub object that would be returned by the getBookQueryPort( ) method, so there is little reason to use getPort( ) when you are dealing with a generated Service object.
When you obtain your Service object from a ServiceFactory , you can't use a method like getBookQueryPort( ) to get a client-side stub. Therefore, you have to use the getPort( ) methods (or use DII, which is considerably more complex). Consider the code shown in Example 6-4.
ServiceFactory factory = ServiceFactory.newInstance( );
QName serviceName = new QName("urn:jwsnut.chapter2.bookservice/wsdl/BookQuery",
"BookService");
URL wsdlURL = new URL("http://localhost:8000/Books/BookQuery?WSDL");
Service service = factory.createService(wsdlURL, serviceName);
QName portName = new QName("urn:jwsnut.chapter2.bookservice/wsdl/BookQuery",
"BookQueryPort");
BookQuery bookQuery = (BookQuery)service.getPort(portName, BookQuery.class)
This code uses a
ServiceFactory
to obtain a
Service
object for a web service defined in a WSDL document and then calls its
getPort( )
method to get an object that refers to the service's endpoint interface. As you can see, there is no dependency on any client-side artifacts of the type created by
wscompile
(and in particular, no
The ServiceFactory createService( ) method needs to read and parse the WSDL document at runtime in order to determine the services and ports that it defines. Apart from the parsing overhead that this incurs, there may be a noticeable delay if the WSDL document resides on a host that is accessed over the Internet. By contrast, when you use wscompile to pregenerate the Service object and the client-side stubs, this overhead is incurred only while the client application is being built.
Even though the WSDL document is read at runtime, you still need to have prefetched it in order to determine the fully qualified name of the service, which the createService( ) method requires.
Although using the
ServiceFactory
class allows you to avoid using precompiled client-side stubs, you still have to create a Java interface for the service endpoint itself. In this case, this means generating and compiling the class file for the
BookQuery
interface. The
When you use wscompile to generate client-side stubs, the getBookQueryPort( ) and getPort( ) methods simply have to create and return an instance of the stub class. However, when you use the getPort( ) method of a Service object returned from a ServiceFactory , there is no stub class available. Instead, getPort( ) constructs and returns an object, called a dynamic proxy , [2] that implements the methods of the service endpoint interface ( BookQuery ), as well as implementing the javax.xml.rpc.Stub interface so that it functions properly as a client-side stub. The process of creating this object involves runtime overhead that will probably be greater than simply instantiating an existing class.
[2] Dynamic proxies were introduced in J2SE Version 1.3. You don't need to understand how dynamic proxies work in order to use the one returned by the getPort( ) method, but if you are
curious , refer to the descriptions of java.lang.reflect.Proxy and java.lang.reflect.InvocationHandler in the J2SE documentation bundle or in Java in a Nutshell , by David Flanagan (O'Reilly).
Once you have a
Service
object obtained from a
ServiceFactory
, you can use either of its two
getPort( )
methods to get a reference to a service endpoint interface. It is probably better to use the variant that supplies both the port name and the service endpoint interface class, as shown in Example 6-4. If you use the variant that
You can try out an example that uses dynamic proxies by opening a command window, making your working directory chapter6\proxybookservice relative to the installation directory of the example source code for this book, and then typing the commands:
ant generate-client ant compile-client ant run-client
As with the previous example in this chapter, this provides a client for the book web service developed in Chapter 2 by importing its WSDL definition. Therefore, you must have already started the web container and deployed the book web service before typing these commands, so that the WSDL definition can be obtained from the web container.
The generate-client target uses wscompile to access the WSDL file and generate the definitions of the BookQuery interface and the BookInfo object that it relies on, using the -import option of wscompile (see Section 6.4, later in this chapter, or Chapter 8 for a description of this option). The same config.xml file is shown in Section 6.1 earlier in this chapter. The run-client target uses a client application to invoke the getBookCount( ) method of the book web service and then prints the result. The output from this command should look like this:
run-client:
[java] Book count = 12
|
The important point to note about this example is that, as the following code extract shows, once the BookQuery object is obtained, the fact that it happens to be a dynamic proxy is not of any concern to the application, which invokes its methods in the same way as it would invoke those of a generated stub:
ServiceFactory factory = ServiceFactory.newInstance( );
QName serviceName = new QName(
"urn:jwsnut.chapter2.bookservice/wsdl/BookQuery", "BookService");
Service service = factory.createService(wsdlURL, serviceName);
QName portName = new QName("urn:jwsnut.chapter2.bookservice/wsdl/BookQuery",
"BookQueryPort");
BookQuery bookQuery = (BookQuery)service.getPort(portName
,
BookQuery.class);
int bookCount = bookQuery.getBookCount( );
Notice also that, because the BookQuery object was created from information in a WSDL document, there is no need to cast it to the type javax.xml.rpc.Stub and use the _setProperty( ) method to set the endpoint address. You could, however, perform this cast to set any of the stub properties listed in Table 2-6 if this is required, since the dynamic proxy returned by getPort( ) implements the Stub interface as well as BookQuery .
The full command line for this example application provides options that allow you to use the other methods of the service endpoint interface. The general form of the command line is as follows:
ProxyBookServiceClient wsdlURL [args]
Here, wsdlURL is the URL to be used to obtain the WSDL document for the service, and args is the additional argument that indicates which method is to be invoked and supplies any required parameters. The run-client target does not supply any additional arguments, the result of which is the getBookCount( ) method being called. Other legal argument combinations are as follows:
|
Arguments |
Description |
|---|---|
|
author title |
Gets the name of the author of the book with the given title, using the getAuthor( ) method |
|
editor title |
Gets the name of the book's editor, using the getEditor( ) method |
|
price title |
Gets the book's price, using the getTitle( ) method |
|
list |
Gets a list of all books, using the getBookInfo( ) method |
You can use the CLIENT_ARGS property to set the arguments to be passed to the application. For example, the following command line calls the getEditor( ) method to get the name of the editor of the book Java Swing :
ant -DCLIENT_ARGS="http://locahost:8000/Books/BookQuery?WSDL
editor Java Swing" run-client
The result is:
run-client:
[java] NAME = [Java Swing]
[java] Mike Loukides
|
|