Some web services are designed with a rich client user interface in mind. An example could be an Internet chat web service that can use a GUI to provide alerts, video-conferencing, and other such capabilities. Some of the business to consumer services may also benefit from rich user interfaces, for example, a mail and calendaring service. The Java[TM] Swing APIs provide a powerful, portable mechanism to create such rich stand-alone desktop clients. This solution discusses some issues to keep in mind while accessing a web service from a stand-alone Java client.
While accessing a web service from a stand-alone Java client, some considerations are
xsd:date
and xsd:dateTime
get mapped to java.util.Calendar
.
The generated service interface
also
depends on the encoding style used. For example, if document-literal
encoding is used by the web service,
the generated Java interface will contain wrapped versions of each
class. This implies that even if a method parameter was of a simple
type such as String
, it will get wrapped with an object
that will contain a single property of type String
. This
can be annoying for a developer who is using the generated interface
because the interface becomes needlessly complex. The following table summarizes the recommendations for accessing web services through standalone clients. Details on these recommendations are provided in the subsequent text.
Issue |
Recommendation |
---|---|
Choosing the communication mode | Use stubs. Generate service
interface from WSDL in WS-I basic profile compatible mode (using -f:wsi
w) with unwrapped parameters with -f:unwrap . |
Making JAXRPC runtime available | Include JAXRPC implementation jars from the application server. |
Handling errors | Involve user to retry or select alternate service for system exceptions, and to take corrective action for service-specific exceptions. |
Decoupling application logic from web service artifacts | Use a delegate to encapsulate all calls to the web service and the parameters used for the web service. |
Avoiding delays because of web service overheads to create responsive GUIs | Make coarse-grained calls to the web service, cache received data and use it for local accesses. |
Deploying clients automatically | Use Java Web Start with the stand-alone clients to make them available through a web site. |
Now let us look at each of these issues in detail and begin with the first, which is the choice of the communication mode.
The communication mode most suitable for stand-alone clients is stubs because it is easiest to use when compared with DII or dynamic proxies. See Section 5.3.1 and Section 5.3.2 of the book Designing Web Services with the J2EE 1.4 Platform for a comparison of these approaches.
The stub is generated by running the JAX-RPC compiler
on the service WSDL. While generating the stub, ensure that the
JAX-RPC compiler is configured to use WS-I basic profile compatible
document-literal encoding (achieved by using the -f:wsi
switch), and is also set to unwrap the parameters (achieved by using
the -f:unwrap
switch). This can also be done in an ant
target. For example, Code Example 1
generates the stub classes from a WSDL file:
In the example above, the client-config.xml
file
contains a pointer to the location of the WSDL file. Also note that the
keep
attribute is set to true
to ensure that
the Java source code for the generated classes for
the stub are preserved. This source code is needed because this is how
a client developer will know what the generated stub interface is, and
how it should be used. The generated Java
source code can then be used to understand the stub and its
associated classes. A client developer can either use this source code
directly, or generate Javadoc[TM] code from it to understand how to
make calls
to the service. The
The standalone client instantiates and configures the generated stub class to access the web service. Code Example 2 illustrates how to accesses a web service using stubs:
Note that the StringPurchaseOrderService_Impl
is not
portable across different JAX-RPC runtimes, but is the recommended
choice because it is significantly
easier to use than any of the portable alternatives. This is different
from the case of J2EE clients, where it is
possible to write fully portable code. It is also important to point
out that the standalone client itself is portable from Java platform
point of view: it can be used unchanged on a variety of hardware
platforms and operating systems. It just requires the same JAX-RPC
runtime environment, which must be bundled with the
application because JAX-RPC is not yet a part of the Java SE platform. The
portability of stub classes is one of the stated goals of the JAX-RPC
2.0 specification which will be available in the Java EE 5
platform.
Often a client would prefer not to hardcode the URL of the web
service it is using. The client might choose to select the service URL
at
runtime, sometimes based on user input. This can be done with stubs by
setting the Stub.ENDPOINT_ADDRESS_PROPERTY
to the desired
service URL at runtime as shown in the
example above.
Dynamic proxy should be used when stubs cannot be used because of their non-portable nature. This might happen when the JAX-RPC implementation available at the runtime is different from that of the compile time. Code Example 3 below illustrates how dynamic proxy can be used with stand-alone clients:
Note that the dynamic proxy needs to use the wrapped version of the
service endpoint interface instead of the more convenient unwrapped
version. This is because the web service is using the WS-I basic
profile compliant document
instead of the rpc
style SOAP binding. The document
binding maps to a
wrapped version of the interface, and the wscompile adds a convenient
way to access an unwrapped version through the stub class.
However, the dynamic proxy does not use the stub class and therefore
has to
use the wrapped version. The wrapped version of the service endpoint
interface can be generated by omitting the unwrap
switch
while invoking wscompile to generate the classes from the WSDL. Note
that the dynamic proxy code still requires the generation of classes
from the WSDL. However, it needs access to only the classes that relate
to the service endpoint interface such as StringPurchaseOrderServiceSEI
,
SubmitPO
, and SubmitPOResponse
.
The scenarios for using DII are rare. It can be used in situations where the client does not have access to the WSDL but somehow knows which methods to call. Code Example 4 below illustrates how DII can be used with stand-alone clients:
As shown in the example above, using DII can be very
complex and therefore should only be used in exceptional situations.
The
JAX-RPC implementation available in the J2EE 1.4 SDK also contains a bug
where it does not generate the wrapper class correctly when the
parameter type for the web service method is of the type string
.
The
developers must work around this bug by overwriting the generated
wrapper class SubmitPO with an equivalent class that renames the member
variable to be String_1
instead of string_1
.
The JAX-RPC runtime also must be included with the application. This can be done by copying the necessary classes from the application server installation. While doing this bundling, be aware of the licensing implications when redistributing your application. The ant build file fragment presented in Code Example 5 illustrates how this can be done with J2EE SDK.
A web service client must deal with two type of errors: system
exceptions and service-specific exceptions. The system
exceptions occur because of irrecoverable system errors such as a
faulty network connection, or if the web service is down. The JAX-RPC
clients receive the system exceptions as RemoteException
.
The
service-specific exceptions are thrown by the web service to indicate
an error in the application logic such as if an incorrect parameter was
passed, or if an attempt is made to create a duplicate key in a
database. The JAX-RPC clients receive the service-specific exceptions as
application exception classes. These exception classes are generated by
the JAX-RPC compiler from the faults specified in the WSDL.
Exception handling for stand-alone clients is often simpler than the case where the web service call was made by a servlet or an enterprise bean. This is because the stand-alone clients often have a human user at their end who can act on an error condition. Thus, a simple exception handling strategy may involve translating exception conditions to messages that a human user will understand, and then prompt the user to either retry the operation, or take corrective action. Often service-specific exceptions can be avoided or reduced by ensuring that the parameters are validated before making the web service call.
In a good design, the application logic of the web service client will have minimal dependencies on the service WSDL. This is useful because as the service evolves, the impact on the client code will be minimal. This decoupling can be achieved by creating a delegate class that encapsulates code that uses the stub and its generated classes. The public API of the delegate does not expose any of the stub classes. Instead, it maps them to classes that the rest of the application understands.
Typical swing clients provide a rich user interface that is very responsive to the user. The swing clients may choose to cache data locally instead of going over the network to increase the responsiveness of the user interface. However, this introduces the issue of maintaining data consistency. Both the client and the web service need to be designed to use a protocol (such as SyncML) to keep the data synchronized.
For more information about this topic, refer to the following: