5.3. Developing Client Applications to Use a Web Service

All client applications follow certain steps to use a Web service. In brief, they must first look up or locate the service, make a call to the service, and process any returned data. The choice of communication mode determines much of the details for performing these steps.

An application can communicate with a service using stubs, dynamic proxies, or the dynamic invocation interface (DII) Call interface. (The next section explains these modes.) With these modes, a developer relies on some form of client-side proxy class (stub, dynamic proxy, or a DII interface) representing a Web service to access the service's functionality. The client developer decides which representation to use based on the WSDL availability, the service's endpoint address, and the use cases for the service.

After deciding on a communication mode, developers need to do the following when designing and developing client applications:

  1. Assess the nature and availability of the service and the service description.

    Typically, the first step is to determine the location of the Web service's WSDL document and choose a communication mode. If you are using stubs or dynamic proxies, you must first locate and gain access to the full WSDL document representing the Web service, since you develop the client application from the stubs and supporting files generated from the WSDL. If you have access to only a partial WSDL file, then use DII, since DII lets you locate the service at runtime . See “Communication Modes for Accessing a Service” on page 212 for more information.

  2. Create a client-side Web service delegate.

    • If applicable, generate the necessary stubs and support classes— Generated classes may not be portable across implementations. If portability is important, limit your use of vendor-specific method calls in the stub classes.

    • Locate the service— Depending on the communication mode, there are different ways to locate a service. See “Locating and Accessing a Service” on page 219.

    • Configure the stub or Call objects— See “Stubs and Call Configuration” on page 223.

  3. Invoke the service.

    • When using stubs and dynamic proxies, invocations are synchronous. Use DII if you choose to have a one-way call.

    • Handle parameters, return values, and exceptions.

  4. Present the view.

    Often the client may need to generate a view for end users. There are different ways clients can handle the presentation to the user of the Web service response. In some cases, a service sends its response as an XML document, and this document may be transformed or mapped to a suitable format for presentation. Web tier clients might pass the service response to a Web component such as JSP and let the component apply a transformation to the XML document or otherwise handle the returned data. EJB tier clients may themselves apply XSLT transformations to the returned content to create the target content, which they then pass to a Web component.

The following sections examine some of the considerations for designing and implementing client applications.

5.3.1. Communication Modes for Accessing a Service

To best determine the communication mode for your client application, you need to know the answers to the following two questions. Is the Web service's full WSDL document known? Is the endpoint address for the Web service known?

As indicated, there are three principal modes for a client application's communication with a Web service: stub, dynamic proxy, and dynamic invocation interface (DII). By a communication mode, we mean the APIs for programmatically accessing a service via the javax.xml.rpc.Service interface. Clients using either the stubs or dynamic proxies to access a service require the prior definition of a service endpoint interface. Note that when we refer to the service endpoint interface, which is the interface between the stub and dynamic proxy clients and the JAX-RPC API and stub, we are referring to the interface that represents the client's view of a Web service.

When using stubs, a JAX-RPC runtime tool generates during development static stub classes that enable the service and the client to communicate. The stub, which sits between the client and the client representation of the service endpoint interface, is responsible for converting a request from a client to a SOAP message and sending it to the service. The stub also converts responses from the service endpoint, which it receives as SOAP messages, to a format understandable by the client. In a sense, a stub is a local object that acts as a proxy for the service endpoint.

Dynamic proxies provides the same functionality as the stubs, but do so in a more dynamic fashion. Stubs and dynamic proxies both provide the developer access to the javax.xml.rpc.Stub interface, which represents a service endpoint. With both models, it is easy for a developer to program against the service endpoint interface, particularly because the JAX-RPC runtime does much of the communication work behind the scenes. The dynamic proxy model differs from the stub model principally because the dynamic proxy model does not require code generation during development.

DII is a call interface that supports a programmatic invocation of JAX-RPC requests. Using DII, a client can call a service or a remote procedure on a service without knowing at compile time the exact service name or the procedure's signature. A DII client can discover this information at runtime and can dynamically look up the service and its remote procedures.

When using DII, clients can dynamically access a service at runtime. The stubs rely on a tool that uses the WSDL file to create the service endpoint interface, plus generate stub and other necessary classes. These generated classes eliminate the need for the developer to use the JAX-RPC APIs directly. By contrast, the dynamic proxy and DII approaches both require the developer to use the JAX-RPC APIs.

5.3.1.1. Using Stub Communication

J2EE clients should use generated stubs to access services, especially for services with fairly static interfaces. Stub communication easily allows Java access to a Web service. This is the recommended approach for accessing services and their WSDL files when they are unlikely to change over time.

Figure 5.5 shows how a client application might access a Web service using a stub generated by a tool prior to the client's deployment and compilation. The WSDL file for the service served as input to the tool. The client-side service endpoint interface, which implements the service endpoint interface on the client-side of the communication channel, provides the client view of the Web service.

Figure 5.5. Stub Communication Model


The stub, which implements the client-side service endpoint interface, acts as a proxy to the Web service for the client; that is, the stub is the client's view of the Web service. The tool that generates the stub class also generates all necessary helper classes for accessing the service. These helper classes define the parameters for calls to the service and the service's return values, ensuring that the parameters and return values are all of the proper types expected by the JAX-RPC runtime. To generate these stub and helper classes, the tool relies on the WSDL document as well as a JAX-RPC mapping file. The mapping file supplies information regarding the Java-to-XML bindings, such as the correct package name for generated classes.

The J2ME environment differs somewhat. Applications in J2ME environments can use only stubs to access Web services, and stubs are portable for J2ME devices. The JAX-RPC profile for J2ME environments does not support dynamic proxies and DII.

Although at this time stubs are not portable across J2EE implementations, the next version of JAX-RPC is expected to address this portability issue.

Using stubs, especially in a Java environment, is often the easiest because the developer can work with generated class files representing the service method call parameters and return values. However, the greater dependencies between the client and the Web service may lead to problems if the service interface changes frequently. This mode also requires that stub classes be generated before compiling the application.

5.3.1.2. Using Dynamic Proxy Communication

The dynamic proxies are similar in many ways to the stubs previously described. However, unlike stubs, the client developer needs only the client-side interface that matches the service endpoint interface. That is, clients using dynamic proxies program to an interface that ensures the client application is portable across other JAX-RPC runtime implementations. Developers using dynamic proxies must create Java classes to serve as JAX-RPC value types—these classes have an empty constructor and set methods for each field, similar to JavaBeans classes.

The dynamic proxy is based on the service endpoint interface, its WSDL document, and a JAX-RPC mapping file (similar to the stubs model). (See Figure 5.6.) Client applications can access the Web service ports using the javax.xml.rpc.Service method getPort.

Figure 5.6. Accessing a Service Using a Dynamic Proxy


Note in Figure 5.6 that a client accesses a Web service via the client-side representation of the service endpoint interface, while the JAX-RPC runtime handles the work of communicating with the respective service. A developer programs against an interface, which provides a flexible way to access a service in a portable fashion.

Consider using the dynamic proxy approach if portability is important to your application, since this approach uses the service's endpoint interface to communicate with a service at runtime. Dynamic proxy communication is the most portable mode across JAX-RPC implementations.

Because of how they access a service at runtime, dynamic proxies may have additional overhead when calls are made.

5.3.1.3. Using DII Call Interface

A client application may also dynamically access a Web service by locating the service at runtime from a registry. The client does not know about the service when it is compiled; instead, the client discovers the service's name from a JAXR registry at runtime. Along with the name, the client discovers the required parameters and return values for making a call to the service. Using a dynamic invocation interface, the client locates the service and calls it at runtime.

Generally, using DII is more difficult for a developer. A developer must work with a more complex interface than with stubs or dynamic proxies. Not only does this interface require more work on the part of the developer, it is more prone to class cast exceptions. In addition, the DII approach may have slower access. A developer may choose to use the DII approach when a complete WSDL document is not available or provided, particularly when the WSDL document does not specify ports. The DII approach is more suitable when used within a framework, since from within a framework, client applications can generically and dynamically access services with no changes to core application code.

Figure 5.7 shows how a client uses the JAXR API to look up the endpoint WSDL for a service in a registry. The client uses the information from the registry to construct a javax.xml.rpc.Call, which it uses to access the Web service.

Figure 5.7. DII Call Interface


Using the DII Call interface allows a client application to define at runtime the service name and the operations it intends to call on the service, thus giving the client the benefit of loosely coupling its code with that of the service. The client is less affected by changes to the service, whether those changes involve access to the service or data requirements. In addition, the DII communication model permits Web service access code to be standardized among a set of clients and reused as a component.

DII involves more work than stubs or dynamic proxies and should be used sparingly. You may consider using this mode if a service changes frequently.

5.3.1.4. Summary of Communication Model Guidelines

Table 5.1 summarizes the communication models for Web service clients.

Table 5.1. Client Communication Modes
Client ConsiderationsStubDynamic ProxyDII
Portable client code across JAX-RPC implementationsYes, in the J2EE platform when an application uses a neutral means for accessing the stubs. (For an example, see Code Example 5.2.) Since stubs are bound to a specific JAX-RPC runtime, reliance on JAX-RPC-specific access to a stub may not behave the same on all platforms. Stub code needs to be generated for an application.YesYes
Requires generation of code using a toolYesNo. A tool may be used to generate JAX-RPC value types required by a service endpoint interface (but not serializers and other artifacts).No
Ability to programmatically change the service endpoint URLYes, but the WSDL must match that used to generate the stub class and supporting classes.Yes, but the client-side service endpoint interface must match the representation on the server side.Yes
Supports service specific exceptionsYesYesNo. All are java.rmi.Remote exceptions. Checked exceptions cannot be used when calls are made dynamically.
Supports one way communication modeNoNoYes
Supports the ability to dynamically specify JAX-RPC value types at runtimeNo. Developer must program against a service endpoint interface.No. Developer must program against a service endpoint interface.Yes. However, returns Java objects which the developer needs to cast to application-specific objects as necessary.
Supported in J2ME platformYesNoNo
Supported in J2SE and J2EE platformsYesYesYes
Requires WSDLNo. A service endpoint interface may generate a stub class along with information concerning the protocol binding.No. A partial WSDL (one with the service port element undefined) may be used.No. Calls may be used when partial WSDL or no WSDL is specified. Use of methods other than the createCall method on the Call interface may result in unexpected behavior in such cases.

Choose the communication mode for your client application after considering the following:

J2EE clients should use stubs. Stubs are easiest for developers to use. Keep in mind, however, that with stubs you might lose some portability across JAX-RPC implementations.

J2EE components acting as clients should use dynamic proxies when portability is a requirement. Since dynamic proxies do not require a JAX-RPC mapping file, some ambiguity may occur, and this may result in unexpected behavior when calling a service.

DII is the most difficult to use of the three approaches. Only consider using DII if you need the runtime ability to look up operations.

All J2SE and J2ME clients should use stubs if at all possible.

5.3.2. Locating and Accessing a Service

Locating a service differs depending on the client. Client applications that run in J2EE environments use the JNDI InitialContext.lookup method to locate a service, whereas a J2SE client can use the javax.xml.rpc.ServiceFactory class or an implementation-specific stub to locate a service. Clients use the javax.xml.rpc.Service interface API to access a Web service. A stub implements the service interface.

Code Example 5.1 shows how an application in a J2EE environment might use a stub to access a service. The application locates the service using a JNDI InitialContext.lookup call. The JNDI call returns an OpcOrderTrackingService object, which is a stub.

Code example 5.1. Accessing a Service with a Stub in a J2EE Environment
   Context ic = new InitialContext();
   OpcOrderTrackingService opcOrderTrackingSvc =
          (OpcOrderTrackingService) ic.lookup(
          "java:comp/env/service/OpcOrderTrackingService");
   OrderTrackingIntf port =
          opcOrderTrackingSvc.getOrderTrackingIntfPort();
OrderDetails od = port.getOrderDetails(orderId);

Because it depends on the generated stub classes, the client code in Code Example 5.1 is not the recommended strategy for using stubs. Although this example works without problems, JAX-RPC gives you a neutral way to access a service and obtain the same results. By using the JAX-RPC javax.xml.rpc.Service interface method getPort, you can access a Web service in the same manner regardless of whether you use stubs or dynamic proxies. The getPort method returns either an instance of a generated stub implementation class or a dynamic proxy, and the client can then uses this returned instance to invoke operations on the service endpoint.

The getPort method removes the dependencies on generated service-specific implementation classes. When this method is invoked, the JAX-RPC runtime selects a port and protocol binding for communicating with the port, then configures the returned stub that represents the service endpoint interface. Furthermore, since the J2EE platform allows the deployment descriptor to specify multiple ports for a service, the container, based on its configuration, can choose the best available protocol binding and port for the service call. (See Code Example 5.2.)

Code example 5.2. Looking Up a Port Using a Stub or Dynamic Proxy
Context ic = new InitialContext();
Service service = (Service)ic.lookup(
          "java:comp/env/service/OpcPurchaseOrderService");
PurchaseOrderIntf port = (PurchaseOrderIntf)service.getPort(
          PurchaseOrderIntf.class);

Code Example 5.2 illustrates how a J2EE client might use the Service interface getPort method. Rather than cast the JNDI reference to the service implementation class, the code casts the JNDI reference to a javax.xml.rpc.Service interface. Using the Service interface in this manner reduces the dependency on generated stub classes. The client developer, by invoking the getPort method, uses the client-side representation of the service endpoint interface to look up the port. After obtaining the port, the client may make any calls desired by the application on the port.

When using stubs or dynamic proxies, the recommended strategy to reduce the dependency on generated classes is to use the java.xml.rpc.Service interface and the getPort method as a proxy for the service implementation class.

A client developer should not circumvent the J2EE platform's management of a service. A client should not create or destroy a Web service port. Instead, a client should use the standard J2EE mechanisms, such as those shown in Code Example 5.2.

A client developer should not assume that the same port instance for the service is used for all calls to the service. Port instances are stateless, and the J2EE platform is not required to return a previously used port instance to a client.

An application in a non-J2EE environment uses a stub to make a Web services call in a different manner. The client application accesses a stub for a service using the method getOrderTrackingIntfPort on the generated implementation class, OpcOrderTrackingService_Impl, which is specific to each JAX-RPC runtime. J2SE or J2ME clients use these generated _Impl files because they do not have access to the naming services available to clients in a J2EE environment through JNDI APIs. See Code Example 5.3.

Code example 5.3. Accessing a Service with a Stub in J2SE and J2ME Environments
Stub stub = (Stub)(new OpcOrderTrackingService_Impl()
   getOrderTrackingIntfPort());
OrderTrackingIntf port = (OrderTrackingIntf)stub;

In addition, a J2SE or J2ME client can access a service by using the javax.xml.rpc.ServiceFactory class to instantiate a stub object. Code Example 5.4 shows how a J2SE client might use a factory to locate the same order tracking service.

Code example 5.4. Looking Up a Service in J2SE and J2ME Environments
ServiceFactory factory =  ServiceFactory.newInstance();
Service service = factory.createService(new QName(
   "urn:OpcOrderTrackingService", "OpcOrderTrackingService"));

Similarly, Code Example 5.5 shows how a J2SE application might use a dynamic proxy instead of a stub to access a service.

Code example 5.5. J2SE Client Dynamic Proxy Service Lookup
...
ServiceFactory sf = ServiceFactory.newInstance();
String wsdlURI =
       "http://localhost:8001/webservice/OtEndpointEJB?WSDL";
URL wsdlURL = new URL(wsdlURI);
Service ots = sf.createService(wsdlURL,
       new QName("urn:OpcOrderTrackingService",
       "OpcOrderTrackingService"));
OrderTrackingIntf port = (
       OrderTrackingIntf)ots.getPort(new QName(
       "urn:OpcOrderTrackingService", "OrderTrackingIntfPort"),
       OrderTrackingIntf.class);
...

Code Example 5.5 illustrates how a J2SE client might program to interfaces that are portable across JAX-RPC runtimes. It shows how a J2SE client uses a ServiceFactory to look up and obtain access to the service, represented as a Service object. The client uses the qualified name, or QName, of the service to obtain the service's port. The WSDL document defines the QName for the service. The client needs to pass as arguments the QName for the target service port and the client-side representation of the service endpoint interface.

By contrast, Code Example 5.6 shows how a J2EE client might use a dynamic proxy to look up and access a service. These two examples show how much simpler it is for J2EE clients to look up and access a service than it is for J2SE clients, since a JNDI lookup from the IntialContext of an existing service is much simpler than configuring the parameters for ServiceFactory. The J2EE client just invokes a getPort call on the client-side representation of the service endpoint interface.

Code example 5.6. Using a Dynamic Proxy in a J2EE Environment
Context ic = new InitialContext();
Service ots =
       (Service) ic.lookup(
       "java:comp/env/service/OpcOrderTrackingService");
OrderTrackingIntf port = (OrderTrackingIntf)ots.getPort(
    OrderTrackingIntf.class);

A J2SE client using the DII approach might implement the code shown in Code Example 5.7 to look up the same service at runtime. DII communication supports two invocation modes: synchronous and one way, also called fire and forget. Both invocation modes are configured with the javax.xml.rpc.Call object. Note that DII is the only communication model that supports one-way invocation. Code Example 5.7 illustrates using the DII approach for locating and accessing a service. It shows how the Call interface used by DII is configured with the property values required to access the order tracking Web service. The values set for these properties may have been obtained from a registry. Keep in mind that using DII is complex and often requires more work on the part of the client developer.

Code example 5.7. J2SE Client Using DII to Access a Web Service
Service service = //get service
QName port = new QName("urn:OpcOrderTrackingService",
       "OrderTrackingIntfPort");
Call call = service.createCall(port);
call.setTargetEndpointAddress(
       "http://localhost:8000/webservice/OtEndpointEJB");
call.setProperty(Call.SOAPACTION_USE_PROPERTY,
       new Boolean(true));
call.setProperty(Call.SOAPACTION_URI_PROPERTY,"");
call.setProperty(ENCODING_STYLE_PROPERTY, URI_ENCODING);
QName QNAME_TYPE_STRING = new QName(NS_XSD, "string");
call.setReturnType(QNAME_TYPE_STRING);
call.setOperationName(
new QName(BODY_NAMESPACE_VALUE "getOrderDetails"));
call.addParameter("String_1", QNAME_TYPE_STRING,
       ParameterMode.IN);
String[] params = {orderId};
OrderDetails = (OrderDetails)call.invoke(params);

5.3.3. Stubs and Call Configuration

Developers may want to configure an instance of a stub or a Call interface prior to invoking a service or may prefer to allow the configuration to take place dynamically at runtime. Often, the developer configures the stub or Call interface prior to invoking the service when the service requires basic authentication. For example, a J2SE client application needs to set a user name and password in the stub or Call just before invoking the service; the service requires these two fields so that it can authenticate the client. In other cases, the developer may want flexibility in specifying the endpoint address to use for a particular service, depending on network availability and so forth. The developer might configure this endpoint address dynamically at runtime.

Stubs may be configured statically or dynamically. A stub's static configuration is set from the WSDL file description at the time the stub is generated. Instead of using this static configuration, a client may use methods defined by the javax.xml.rpc.Stub interface to dynamically configure stub properties at runtime. Two methods are of particular interest: _setProperty to configure stub properties and _getProperty to obtain stub property information. Clients can use these methods to obtain or configure such properties as the service's endpoint address, user name, and password.

Generally, it is advisable to cast vendor-specific stub implementations into a javax.xml.rpc.Stub object for configuration. This ensures that configuration is done in a portable manner and that the application may be run on other JAX-RPC implementations with minimal changes, if any.

Code Example 5.8 shows how a J2EE client might use Stub interface methods to look up and set the endpoint address of a Web service.

Code example 5.8. Setting Properties on a Stub
Service opcPurchaseOrderSvc =
       (Service) ic.lookup(AdventureKeys.PO_SERVICE);
PurchaseOrderIntf port =
       opcPurchaseOrderSvc.getPort(PurchaseOrderIntf.class);
((Stub)port)._setProperty(Stub.ENDPOINT_ADDRESS_PROPERTY,
   "http://localhost:8000/webservice/PoEndpointEJB");

In a J2EE environment, the J2EE container reserves the right to use two security-related properties for its own purposes. As a result, J2EE clients using any of the three communication modes (stub, dynamic proxy, or DII) should not configure these two security properties:

javax.xml.rpc.security.auth.username
javax.xml.rpc.security.auth.password

However, J2SE developers do need to set these two security properties if they invoke a Web service that requires basic authentication. When using DII, the client application may set the properties on the Call interface. See Code Example 5.7, which illustrates setting these properties.

J2EE client developers should avoid setting properties other than the javax.xml.rpc.endpoint.address property.

Avoid setting nonstandard properties if it is important to achieve portability among JAX-RPC runtimes. Nonstandard properties are those whose property names are not preceded by javax.xml.rpc.

Avoid using javax.xml.rpc.session.maintain property. This property pertains to a service's ability to support sessions. Unless you have control over the development of both the client and the endpoint, such as when both are developed within the same organization, you cannot be sure that the Web service endpoints support sessions, and you may get into trouble if you set this property incorrectly.

5.3.4. WSDL-to-Java Type Mapping

When working with Web services, there may be differences between the SOAP-defined types (defined in the WSDL document) used by the service and the Java-defined types used by the client application. To handle these different types, a client of a Web service cannot use the normal approach and import remote classes. Instead, the client must map the WSDL types to Java types to obtain the parameter and return types used by the service. Once the types are mapped, the client has the correct Java types to use in its code.

Generally, the JAX-RPC runtime handles the mapping of parameters, exceptions, and return values to JAX-RPC types. When a client invokes a service, the JAX-RPC runtime maps parameter values to their corresponding SOAP representations and sends an HTTP request containing a SOAP message to the service. When the service responds to the request, the JAX-RPC runtime receives this SOAP response and maps the return values to Java objects or standard types. If an exception occurs, then the runtime maps the WSDL:fault to a Java exception, or to a javax.rmi.RemoteException if a soap:fault is encountered. (This is discussed further in “Handling Exceptions” on page 230).

The JAX-RPC runtime supports the following standard value types: String, BigInteger, Calender, Date, boolean, byte, short, int, long, float, double, and arrays of these types. Services can return mime types as images mapped to the java.awt.Image class and XML text as javax.xml.transform.Source objects. (The WSDL Basic Profile 1.0 does not support javax.xml.transform.Source objects as mime types. As a result, you should avoid this usage until it is supported by a future WSDL version.) A service may also return complex types, and these are mapped to Java Object representations. For more information, see “Parameter Types for Web Service Operations” on page 72.

When stubs are used, the JAX-RPC WSDL-to-Java mapping tool maps parameter, exception, and return value types into the generated classes using information contained in the developer-provided WSDL document. Complex types defined within a WSDL document are represented by individual Java classes, as are faults. A WSDL-to-Java mapping tool included with the JAX-RPC runtime also generates classes to serialize and deserialize these values to XML. The JAX-RPC runtime uses these generated classes to serialize parameter values into a SOAP message and deserialize return values and exceptions.

Use WSDL-to-Java tools to generate support classes, even if using the dynamic proxy or DII approach.

Whenever possible, developers should try to use a tool to do the WSDL-to-Java mapping, since a tool correctly handles the WSDL format and mapping semantics. Note that the Java objects generated by these mapping tools contain empty constructors and get and set methods for elements. You should use the empty constructor to create an instance of the object and set any field or element values using the corresponding set methods. JAX-RPC does not guarantee that it will correctly map values created as part of the constructor to the corresponding fields.

Although not advisable, it is possible for a developer to work without the benefit of a mapping tool, if none are available. However, without such mapping tools the scope of the developer's work greatly expands. For example, just to compile the client code, the developer must understand the WSDL for a service and generate by hand Java classes that match the parameter and return types defined in the WSDL document or, in the case of a dynamic proxy, the client-side representation of the service endpoint interface. These classes must be set up properly so that the JAX-RPC runtime can match SOAP message types to the corresponding Java objects.

5.3.5. Processing Return Values

J2EE applications generally use Web components to generate returned data for display. For example, a client accessing an order tracking service might display tracking information in a Web page using an HTML browser. The J2EE component may take the values returned from the Web service and handle them just like any other Java object. For example, a Web component in a J2EE client application might query the status of an order from an order tracking service, which returns these values within a JavaBeans-like object. The client component places the returned object in the request scope and uses a JSP to display its contents. (See Code Example 5.9.)

Code example 5.9. JSP for Generating an HTML Document
<html>
Order ID: ${bean.orderId}
Status: ${bean.status} <br>
Name: ${bean.givenName} ${bean.familyName} <br>
</html>

The J2EE platform has a rich set of component technologies for generating Web content, including JavaServer Pages (JSP) technology for generating HTML content and Java Standard Tag Libraries (JSTL). JSTL is a set of tags that assist a client developer in formatting JSPs. For example, JSTL provides additional tags for looping, database access, object access, and XSLT stylesheet transformations. The current version of JSP (2.0), along with future versions, provides an expression language that allows a developer to access bean properties. Together, developers can use JSTL and JSP technologies to generate HTML documents from data retrieved from a service. For example, a developer might generate the following HTML document for the order details (see Code Example 5.10). This HTML document is returned to the HTML browser client that requested the service.

Code example 5.10. HTML Document
<html>
    Order: 54321<br>
    Status: SHIPPED<br>
    Name: Duke Smith<br>
</html>

A different use case might be EJB components using returned data in a workflow. For example, as the workflow progresses, they may provide order tracking updates by formatting the tracking data in HTML and attaching it to an e-mail message sent to the customer. Unless the initial request originated from a Web tier client, EJB components in a workflow situation do not have Web-tier technologies such as JSP available to them. Furthermore, the order tracking service may return results as XML documents, requiring EJB components to apply XSL transformations to these documents. Code Example 5.11 shows an XML document containing the returned data from the service.

Code example 5.11. Data Returned as XML Document
<orderdetails>
    <id>54321</id>
    <status>SHIPPED</status>
    <shippinginfo>
       <family-name>Smith</family-name>
       <given-name>Duke</given-name>
    </shippinginfo>
</orderdetails>

An EJB component may use the XSL stylesheet shown in Code Example 5.12 to transform an order details XML document to the same HTML document as in Code Example 5.10. This HTML document may be attached to an e-mail message and sent to a customer. XSLT transformations may also be used to transform a document into multiple formats for different types of clients.

Code example 5.12. XSL Stylesheet
<xsl:stylesheet version='1.0'
    xmlns:xsl='http://www.w3.org/1999/XSL/Transform'>

 <xsl:output method="html"/>
 <xsl:template match="text()"/>

 <xsl:template match="orderdetails">
   <html>
   Order: <xsl:value-of select="id/text()"/><br/>
   <xsl:apply-templates/>
   </html>
 </xsl:template>

 <xsl:template match="shippinginfo">
  Name: <xsl:value-of select="given-name/text()"/>
        <xsl:text> </xsl:text>
        <xsl:value-of select="family-name/text()"/>
  <br/>
 </xsl:template>

 <xsl:template match="status">
  Status: <xsl:value-of select="text()"/><br/>
 </xsl:template>
</xsl:stylesheet>

Technologies for handling and transforming XML documents are also available to Web components such as servlets or JSPs. Web components may use custom tag components, XSL, or the JAXP APIs to handle XML documents. (For more information on handling XML documents, see Chapter 4.)

Generally, whenever possible client developers should use JSP to generate responses by a service and to present the data as a view to clients (such as browsers that use Web tier technologies).

For clients in a non-Web tier environment where JSP technology is not available, developers should use XSLT transformations.

Developers can use JSP technology to access content in XML documents and to build an HTML page from the XML document contents. Code Example 5.13 shows how JSP technology makes it easy to parse XML content and build an HTML page from the order detail contents shown in Code Example 5.11.

Code example 5.13. JSP Generating a Web Service Response
<%@ taglib prefix="x" uri="/WEB-INF/x-rt.tld" %>
<x:parse xml="${orderDetailsXml}" var="od" scope="application"/>
<html>
  Order:<x:out select="$od/orderdetails/id"/><br>
  Status:<x:out select="$od/orderdetails/status"/><br>
  Name:<x:out select="$od/orderdetails/shippinginfo/given-name"/>
      <x:out select="$od/orderdetails/shippinginfo/family-name"/>
</html>

In this example, the J2EE application first places the order details document received from the service in the request scope using the key orderDetailsXML. The next lines are JSP code that use the x:out JSTL tag to access the order details XML content. These lines of code select fields of interest (such as order identifier, status, and name fields) using XPath expressions, and convert the data to HTML for presentation to a browser client. The JSP code relies on JSTL tags to access these portions of the order details document. For example, the JSTL tag x:out, which uses XPath expressions, accesses the order identifier, status, and name fields in the order details document. When the JSP processing completes, the result is identical to the HTML page shown in Code Example 5.10.

5.3.6. Handling Exceptions

Two types of exceptions occur for client applications that access Web services: system exceptions and service-specific exceptions, which are thrown by a service endpoint. System exceptions are thrown for such conditions as passing incorrect parameters when invoking a service method, service inaccessibility, network error, or some other error beyond the control of the application. Service exceptions, which are mapped from faults, are thrown when a service-specific error is encountered. The client application must deal with both types of exceptions.

5.3.6.1. System Exceptions

System exceptions generally involve unanticipated errors—such as a network timeout or an unavailable or unreachable server—that occur when invoking a Web service. Although system exceptions are usually out of the control of the client developer, you should still be aware that these errors may occur, and you should have a strategy for your client application to deal with these situations. For example, with these types of errors, it may be useful to have the client application prompt the user to retry the operation or redirect the user to an alternative service, if possible. Many times the only solution is to display the exception occurrence to the end user. Sometimes it may be appropriate to translate the system exception to an unchecked exception and devise a global way to handle them. The exact solution is particular to each application and situation.

System exceptions, which the client receives as RemoteException, arise from a variety of reasons. Often system exceptions happen because of network failures or server errors. They also may be the result of a SOAP fault. Since a RemoteException usually contains an explanation of the error, the application can use that message to provide its own error message to the user and can prompt the user for an appropriate action to take. If an EJB component client is doing its work on behalf of a Web tier client, the EJB client should throw an exception to the Web tier client. The Web tier client notifies the user, giving the user a chance to retry the action or choose an alternative action.

When communicating using stubs, most system exceptions involve network issues or the configuration of the stub. A configuration error is thrown from the _getProperty and _setProperty methods as a javax.xml.rpc.JAXRPCException and indicates a property name that is not supported or is invalid, a property setting value that is not allowed, or a type mismatch.

When using a dynamic proxy, the getPort method may throw a javax.xml.rpc.ServiceException if the WSDL document has insufficient metadata to properly create the proxy.

A client may receive several different exceptions when using the DII Call interface. A client may receive a RemoteException if a network failure occurs. A client may also be thrown a javax.xml.rpc.JAXRPCException if the Call set methods use invalid property names or try to set properties with invalid values.

For example, a client application accessing an order tracking service may want to use an alternative service or endpoint if it receives a RemoteException indicating the unavailability of the service.

Web components may leverage the servlet error messaging system and map the RemoteException to some exception handling instructions in the web.xml file. The client can throw its own javax.servlet.ServletException and can display a general application error message. It is possible to extend ServletException to create a set of application-specific exceptions for different errors. It may also use the file to define a specific error JSP to handle the error, as shown in Code Example 5.14.

Code example 5.14. Using the Servlet Error Messaging System
<error-page>
    <exception-type>java.lang.Runtime</exception-type>
    <location>/order_tracking_system_exception.jsp</location>
</error-page>

Although they do not directly interact with the user, workflow clients implemented as EJB components can benefit from transactional processing, particularly if using container-managed transactions. However, since you cannot assume that the Web service is also transactional, you might have to manually back out some of your changes if other Web services are involved in the processing. Keep in mind that backing out changes can be difficult to accomplish and is prone to problems.

An J2EE component that receives a RemoteException when accessing a service may retry connecting to the service a set number of times. If none of the retries are successful, then the client can log an error and quit the workflow. Or, the client may forward the unit of work to another process in the workflow and let that component access the same service at a later point or use a different service. EJB component-based clients using DII may locate an alternative service using the JAXR API from a registry.

5.3.6.2. Service Exceptions

Service exceptions occur when a Web service call results in the service returning a fault. A service throws such faults when the data presented to it does not meet the service criteria. For example, the data may be beyond boundary limits, it may duplicate other data, or it may be incomplete. These exceptions are defined in the service's WSDL file as operation elements, and they are referred to as wsdl:fault elements. These exceptions are checked exceptions in client applications. For example, a client accessing the order tracking service may pass to the service an order identifier that does not match orders kept by the service. The client may receive an OrderNotFoundException, since that is the error message defined in the WSDL document:

<fault name="OrderNotFoundException"
          message="tns:OrderNotFoundException"/>

This exception-mapping mechanism may not be used with DII, since this communication mode returns all exceptions as java.rmi.RemoteException. Note that there is no guarantee that the JAX-RPC runtime system will throw a ServiceException for a specific wsdl:fault error. The runtime may instead throw a javax.xml.rpc.soap.SOAPFaultException runtime exception.

Use the JAX-RPC tools to map faults to Java objects. (See “WSDL-to-Java Type Mapping” on page 225.) These tools generate the necessary parameter mappings for the exception classes and generate the necessary classes for the mapping. Generated exceptions classes extend java.lang.Exception. The client application developer is responsible for catching these checked exceptions in a try/catch block. The developer should also try to provide the appropriate application functionality to recover from such exceptions. For example, an order tracking client might include the code shown in Code Example 5.15 to handle cases where a matching order is not found. The order tracking service threw an OrderNotFoundException, and the client presented the user with a GUI dialog indicating that the order was not found.

Code example 5.15. J2SE Client Displaying an Error
try {
   OrderDetails od = service.getOrderDetails(orderId);
} catch (OrderNotFoundException onx) {
   JOptionPane.showMessageDialog(gui,
      "Order Not found with Order ID " + orderId, "Error",
      JOptionPane.ERROR_MESSAGE);
}

A J2EE Web component client may handle the exception using the facilities provided by the J2EE environment. The client may wrap the application exception and throw an unchecked exception, such as a javax.servlet.ServletException, or it may map the exception directly to an error page in the Web deployment descriptor. In Code Example 5.16, the client maps the OrderNotFoundException directly to a JSP. Clients should always provide human-readable messages and give the user the ability to return to an entry page.

Code example 5.16. Using the Servlet Error Mechanism
<error-page>
   <exception-type>com.sun.blueprints.adventure.
          OrderNotFoundException</exception-type>
   <location>/order_not_found_exception.jsp</location>
</error-page>

Notice that using the servlet error mechanism in this way tightly binds the Web application client to the service. If the service changes the faults it throws, or any of the fault parameters, the client is directly affected.

Client developers should isolate service-specific exceptions as much as possible by wrapping them in their own application-specific exceptions to keep the client application from being too closely tied to a service. This is especially important when the service is outside the control of the client developer or if the service changes frequently. A client may require refactoring when a service changes because the stubs and supporting Java object representations of the exceptions were generated statically.

Client developers may also generalize exception handling and instead handle all exceptions in a single point in the application. Keeping exception handling to one place in an application reduces the need to include code for handling exceptions throughout the application.

Code example 5.17. Generalized Exception Processing
try {
   OrderDetails od = stub.getOrderDetails(orderId);
} catch (OrderNotFoundException onx) {
   RuntimeException re= new RuntimeException(onx);
}

In Code Example 5.17, the exception thrown as a result of the Web service call is set as the cause of the runtime exception. This technique, known as chaining exceptions, produces a better stack trace and helps to more clearly display the root cause of the exception. Chaining exceptions in conjunction with the servlet error mechanism shown in Code Example 5.16 may provide a generalized means of displaying service exceptions.

Boundary checking can help prevent service exceptions. For example, a client of an order tracking service might first check that an order identifier is a five-digit integer, or that its value falls within a certain range. While the JAX-RPC API ensures that types are correct, the application developer is responsible for checking boundary limitations.

Clients in a J2EE environment that present HTML content can use JavaScript to validate boundaries before sending requests to a service. Clients using an HTML interface should ensure that types for entered values are correct, since HTML parameters are all treated as String objects.

Do boundary checking and other validations on the client side so that Web service calls and round-trips to the server are kept to a minimum.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset