In this article, we will look at JAX-WS Technology and the role in building web services. We will provide an overview of JAX-WS use as well as how it is used in the context of transactional and security requirements for application. This is second of four articles that focus on describing and publishing web services using WSDL and UDDI. This is part of a larger series of articles to help you prepare for the web service developer exam. You can review the all the articles of this series by going here if needed.

JAX-WS 2.0 provides the Java Web Services standard for both deploying and invoking Web services. In this chapter, I look at the client-side invocation capabilities provided by JAX-WS. In particular, I focus on how to use JAX-WS as a client-side tool for creating SOA components that consume Web services.

What are JAX-WS Proxies

JAX-WS is the successor to JAX-RPC. It was designed in order that programmers could invoke a Web service in the same way that they invoke a Java method. In many ways, JAX-WS imitates Java Remote Method Invocation (Java RMI) by enabling the programmer to use local method calls to invoke a service on another host. It differs in that the remote service is not a Java application but a web service on a remote host with a WSDL interface. JAX-WS provides a standard WSDL to Java mapping from a wsdl:portType to a Java interface. This type of interface is known as a service endpoint interface (SEI) because they are a Java representation of a Web service endpoint. At runtime, JAX-WS can generate an instance of a SEI that enables the programmer to invoke a Web service by invoking a method on the SEI.

The means by which this technology is employed by JAX-WS in creating a run-time instance of a SEI is via a dynamic proxy class. Dynamic proxy classes enables JAX-WS to dynamically create at runtime an instance of a SEI that can be used to invoke a Web service. The internal workings of a JAX-WS proxy are based on an instance of java.lang.reflect.InvocationHandler that implements a standard WSDL to Java and Java to WSDL mapping. When you invoke a method on the proxy, the instance of the InvocationHandler will convert the parameters into a SOAP message that is sent to the web service endpoint. Vice versa, a SOAP response message on the return is converted by the InvocationHandler into an instance of the SEI return type.

JAX-WS proxies are dynamically created at runtime. Although there is no need to generate stub classes to implement the SEI. you do need to generate the SEI prior to runtime since the dynamic proxy requires an interface definition in order to create a proxy class instance. All of the JAX-WS implementations provide tools for creating a SEI from its corresponding wsdl:portType. The figure below shows the process of creating and using a SEI and its proxy instance:

Tutorial:Review of JAX-WS Client Side for the Web Service Developer Exam-b5-javaproxyinvokewebservice.jpg
Figure: Using a Java Proxy to Invoke a Web Service

The steps required to produce a create and use a proxy instance with JBoss Tools is the following:
  1. Select the File->New->Web Service in Eclipse brings up a tool that is used to create a new web service either Top Down from a WSDL to Java implementation mapping tool or Bottom Up from a Java Implementation to WSDL. In the Top Down mode, it reads the target Web service’s WSDL and then generates a service endpoint interface (SEI). This step is done prior to compiling and running the client code that invokes the Web service.
  2. Next at runtime, one of the getPort() methods from the javax.xml.ws.Service class is used to create a proxy instance that implements the SEI.
  3. The Web service is invoked simply by invoking a method from the SEI. As shown in the above figure, the parameters are instances of JAXB-generated classes and they are to the targeted method. This is done by SEI which maps to its corresponding wsdl:portType using the standard JAXB XML Schema to Java mapping. Every SEI provided method corresponds to a wsdl:operation on the wsdl:portType.
  4. Internally the proxy instance converts the SEI method invocation parameters into a SOAP request and then send it to the Web service’s endpoint. Here our binding is SOAP over HTTP, but the JAX-WS architecture can support other bindings.

Finally the proxy instance receives the SOAP response or a fault message and deserializes it into an instance of the SEI method’s return type.
Below is a figure of the dialog that comes up in Eclipse with JBoss Tools for creating a new Web Service:

Tutorial:Review of JAX-WS Client Side for the Web Service Developer Exam-b5-createwebservicedialog.jpg
Figure: Create Web Service Dialog

Next we will show you how to invoke a Web service using a proxy instance. In this example, the Web service is used for placing an order. It is called the RequestOrder service. In this service, the client must provide a customer number, a purchase order number or credit or debit card and finally a list of items to they desire to purchase. The Web service processes and completes the order and returns the customer’s receipt. The order is represented as an instance of oms:Order .

Looking at JAX-WS WSDL to Java Mapping

We know that the SEI is mapped from a Web service’s wsdl:portType via the JAX-WS WSDL to Java mapping tool. First we will need to create the WSDL for the RequestOrder Web Service. This is considered Top Down Design of the Web Service. RequestOrder uses the document/literal wrapped style of WSDL . The element req:requestOrder is the wrapper element containing the following parameters:
  • req:CUST_NO - the customer number
  • req:PURCH_ORD_NO - the purchase order number
  • req:ccard - the credit card information
  • req:item - the items being ordered.


Similarly req:requestOrderResponse is the wrapper element that contains the following response parameter:
  • oms:Order - the customer order.

The listing of the WSDL file is shown below:
XML Code: RequestOrder Web Service WSDL File
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<wsdl:definitions targetNamespace="http://www.acme.com/req"
  xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
  xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
  xmlns:xsd="http://www.w3.org/2001/XMLSchema"
  xmlns:oms="http://www.acme.com/oms" xmlns:req="http://www.acme.com/req"
  xmlns:faults="http://www.acme.com/faults">
  <!--! <acme xn="WSDL_TYPES"> -->
  <!--! <c>chap06</c><s>proxy</s> -->
  <wsdl:types>
    <xsd:schema targetNamespace="http://www.acme.com/oms">
      <xsd:include schemaLocation="http://acmesoa.com/acme/oms/orders.xsd"/>
    </xsd:schema>
    <xsd:schema targetNamespace="http://www.acme.com/faults">
      <xsd:include schemaLocation="http://acmesoa.com/acme/faults/faults.xsd"
      />
    </xsd:schema>
    <xsd:schema elementFormDefault="qualified"
      targetNamespace="http://www.acme.com/req">
      <xsd:import namespace="http://www.acme.com/oms"/>
      <xsd:element name="requestOrder">
        <xsd:complexType>
          <xsd:sequence>
            <xsd:element name="CUST_NO">
              <xsd:simpleType>
                <xsd:restriction base="xsd:string">
                  <xsd:maxLength value="10"/>
                </xsd:restriction>
              </xsd:simpleType>
            </xsd:element>
            <xsd:element name="PURCH_ORD_NO" minOccurs="0">
              <xsd:simpleType>
                <xsd:restriction base="xsd:string">
                  <xsd:maxLength value="35"/>
                </xsd:restriction>
              </xsd:simpleType>
            </xsd:element>
            <xsd:element name="ccard" type="oms:BUSOBJ_CCARD" minOccurs="0"/>
            <xsd:element name="item" type="oms:BUSOBJ_ITEM" 
            maxOccurs="unbounded"/>
          </xsd:sequence>
        </xsd:complexType>
      </xsd:element>
      <xsd:element name="requestOrderResponse">
        <xsd:complexType>
          <xsd:sequence>
            <xsd:element ref="oms:Order"/>
          </xsd:sequence>
        </xsd:complexType>
      </xsd:element>
    </xsd:schema>
  </wsdl:types>
  <!--! </acme> -->
  <!--! <acme xn="WSDL_PORT"> -->
  <!--! <c>chap06</c><s>proxy</s> -->
  <wsdl:message name="request">
    <wsdl:part name="parameters" element="req:requestOrder"/>
  </wsdl:message>
  <wsdl:message name="response">
    <wsdl:part name="parameters" element="req:requestOrderResponse"/>
  </wsdl:message>
  <wsdl:message name="inputFault">
    <wsdl:part name="parameters" element="faults:inputMessageValidationFault"/>
  </wsdl:message>
  <wsdl:portType name="RequestOrderPort">
    <wsdl:operation name="requestOrder">
      <wsdl:input message="req:request"/>
      <wsdl:output message="req:response"/>
      <wsdl:fault name="requestOrderInputFault" message="req:inputFault"/>
    </wsdl:operation>
  </wsdl:portType>
  <!--! </acme> -->
  <wsdl:binding name="RequestOrderSOAPBinding" type="req:RequestOrderPort">
    <soap:binding style="document"
      transport="http://schemas.xmlsoap.org/soap/http"/>
    <wsdl:operation name="requestOrder">
      <wsdl:input>
        <soap:body use="literal"/>
      </wsdl:input>
      <wsdl:output>
        <soap:body use="literal"/>
      </wsdl:output>
      <wsdl:fault name="requestOrderInputFault">
        <soap:fault name="requestOrderInputFault"/>
      </wsdl:fault>
    </wsdl:operation>
  </wsdl:binding>
  <wsdl:service name="RequestOrderService">
    <wsdl:port name="RequestOrderPort" binding="req:RequestOrderSOAPBinding">
      <soap:address location="undefined"/>
    </wsdl:port>
  </wsdl:service>
</wsdl:definitions>
The JAXB 2.0 XML Schema to Java mapping is used on the parameters within these wrapper elements to map to method parameters in the SEI. The SEI method signatures are mapped from these operations defined on the wsdl:portType. The requestOrder is an wsdl:operation that is mapped to a method on the SEI named requestOrder. The input parameters for this method will be taken from the wsdl:input message (i.e. req:request). This message is a single wsdl:part that is the req:requestOrder wrapper element we spoke about before. As well the parameters for the SEI method, requestOrder are mapped from the child elements of the wrapper req:requestOrder. For the response type of the requestOrder method, this will map to oms:Order since the oms:Order is a child element of the wsdl:output message. This seems long and convoluted. It is recommended that you review the description of the document/literal wrapped style of WSDL from our previous article, “Tutorial, Review of WSDL Document for the Web Service Developer Exam”.


Below we provide some detail into the JAX-WS WSDL to Java mapping that has been implemented by the Top Down Implementation of the requestOrder Web Service in Eclipse. On the left-hand side is a snippet of the RequestOrder WSDL for the Web service. On the right are the classes that have generated by the Eclipse Web Service utility. There are a number of classes that are only the result of the JAXB XML Schema to Java mapping from the schemas defined in the wsdl:types section. An example of this is the OrderType class. There are also classes defined by JAX-WS WSDL to Java mapping. An example of this is the RequestOrderPort. These classes are all annotated with both JAXB and JAX-WS annotations. The figure for this is shown below:

Tutorial:Review of JAX-WS Client Side for the Web Service Developer Exam-b5-jaxws-javamapping.jpg
Figure: JAX-WS to Java Mapping for RequestOrder Web Service

We explain some of the detail of the mapping:
  1. The wsdl:portType element is mapped to the SEI (i.e. RequestOrderPort)
  2. The wsdl:operation requestOrder is mapped to a SEI method of the same name.
  3. The wsdl:input message which determines the parameters of the requestOrder method has the req:request, which has a single wsdl:part, a wrapper element of req:requestOrder defined in the wsdl:types section. The children of this wrapper element are the method parameters. The children are CUST_NO, PURCH_ORD_NO, ccard and item. Note that the item element gets mapped to a List because it has maxOccurs > 1.
  4. The wsdl:output message determines the response type. In this case, OrderType.
  5. As specified by the JAX-WS mapping, each wsdl:fault gets mapped to a thrown exception. Hew we map the wsdl:fault maps to the "throws InputFault"clause of the requestOrder method declaration.
  6. Next we have the wsdl:message referred to by a wsdl:fault being mapped to a class extending java.lang.Exception. In this case, the wsdl:message, inputFault is mapped to the InputFault class. This is a wrapper exception class for the fault bean.

Note that a wsdl:fault element refers to a wsdl:message with a single part. The global element declaration referred to by that part is mapped to a JavaBean that JAX-WS refers to as a fault bean. In our Web service, the element faults:inputMessageValidationFault maps to the fault bean named InputMessageValidationFaultType. These type of fault beans are properties wrapped by an exception wrapper class.
Finally the types and global elements defined in the wsdl:types section are mapped to Java classes using the JAXB XML Schema to Java mapping. In our Web service, the mapping is the following:
  • oms:Order -> OrderType,
  • oms:BUSOBJ_ITEM -> BUSOB- JITEM
  • oms:BUSOBJ_CCARD -> BUSOBJCCARD

Now that we see how JAX-WS maps WSDL to Java classes when a SEI is created from a wsdl:port- Type. Let’s now see how the JAX-WS runtime converts a method invocation on a SEI into a SOAP message. The key to this process is JAX-WS annotations.

Service Endpoint Interface Annotations

We have not discussed the JAXB runtime. We will discuss it more in depth later. For know just know that the JAXB runtime uses annotations to determine how to marshal/unmarshal a JAXB value class to/from XML. Similarly the JAX-WS runtime uses annotations on the SEI to determine the marshaling of a method invocation to a SOAP request message and the unmarshaling of a SOAP response into an instance of the method’s return type. This might be better explained by showing an example where the output message has a single part/wrapper child that maps to the SEI method’s return value. The figure below shows the SEI, RequestOrderPort with its annotation indicating how to map the SOAP request and response messages. This is the annotated code produced by Eclipse and JBoss Tools as it generated the SEI from the RequestOrderPort wsdl:portType.

Tutorial:Review of JAX-WS Client Side for the Web Service Developer Exam-b5-jaxwsannot-wsdl.jpg
Figure: Mapping from JAX-WS SEI Annotations to SOAP request/response Messages

Let’s look at the key parts of the mapping between the SEI RequestOrderPort annotations and the SOAP request and response message.
@RequestWrapper - this annotation specifies the wrapper element for the SOAP request message. As we are using a document/literal wrapped style Web service, the SOAP request parameters appear as children of the wrapper element since it is the only child of the SOAP body. We defined this wrapper element in the wsdl:types section in the listing for the RequestOrder WSDL. In the @RequestWrapper we have set the SOAP request message with a single child in its body with the local name request- Order and namespace http://www.acme.com/req.
@WebParam - these annotations map the parameters of the SEI method to the parameters of the SOAP request message. In the document/literal wrapped style Web service, these parameters are children of the wrapper element.
@ResponseWrapper - this annotation is similar to the @RequestWrapper and it defines the wrapper element for the SOAP response message. In the RequestOrder Web service, it has only one child in the SOAP response body element which is the wrapper element defined by this annotation.
@WebResult - this annotation maps the return value of a SEI method to corresponding child of the response wrapper element contained in the SOAP response message’s body. In our case, the annotation defines an element named oms:Order that is the element appearing as the grandchild of the SOAP body in the response message.

These annotations have two purposes. They annotate a SEI and determine at runtime, how to map a method invocation to SOAP request/response messages. As well, the annotations are used on the service side to deploy a Java class as a Web service. In fact, the purposes are one and the same since the annotations are mapping a wsdl:operation to a Java method. Note in the listing above how the SEI annotations are used to define a mapping from the wsdl:portType/wsdl:operation to the Java interface/method. This also allows us to see the relationship between these annotations and the structure of the SOAP request/response message by following the WSDL SOAP Binding. The table below lists the annotations and explains their general purpose:

Tutorial:Review of JAX-WS Client Side for the Web Service Developer Exam-b5-javaannotations.jpg
Table: Java Annotations

How to Invoke a Web Service Using a Proxy

We have now created all of the mappings, let’s look at invoking the Web service using a JAX-WS client using a JAX-WS proxy. There are several methods available for you to access a proxy instance. We will review look at three of them. First let’s look at the client code for this web service. It’s shown in the listing below:

Java Code: Client Using Injected Proxy Instance with @WebServiceRef
package com.acme.webclient;
import java.net.URL;

import javax.xml.namespace.QName;
import javax.xml.ws.Service;
import javax.xml.ws.WebServiceRef;
import com.acme.req.RequestOrderPort;
import com.acme.req.RequestOrderService;

public class Client {

//! <acme xn="WebServiceRef">
//! <c>retail</c><s>proxy</s>
  @WebServiceRef(RequestOrderService.class)
  public static RequestOrderPort port;
//! </acme>
  
  public static void main(String[] args) throws Exception {
    
    if (args.length != 2) {
      throw new Exception("Must specify host and port");
    }
    String hostName = args[0];
    String portVal = args[1];
    if ( port == null ) {
      System.out.println("Running in standalone mode.");
      System.out.println();
      doStaticService();
      doDynamicService(hostName, portVal);
      return;
    } else {
      System.out.println("Running inside the Application Client Container.");
      System.out.println();
    }
    (new Tester()).runTests(port);
  
  }
  
  public static void doStaticService() throws Exception {
    
    System.out.println();
    System.out.println("Getting proxy from the static service.");
    System.out.println();
//! <acme xn="StaticService">
//! <c>retail</c><s>proxy</s>
    RequestOrderService service = new RequestOrderService();
    RequestOrderPort port = service.getRequestOrderPort();
    (new Tester()).runTests(port);
//! </acme>
    
  }
 
  public static void doDynamicService(String hostName, String portVal) throws Exception {
    
    System.out.println();
    System.out.println("Getting proxy from the dynamic service.");
    System.out.println();
//! <acme xn="DynamicService">
//! <c>retail</c><s>proxy</s>
    URL wsdlURL = new URL("http://"+hostName+":"+portVal+"/retail-endpoint-endpoint-1.0/requestOrder?wsdl");
  QName serviceQName =
    new QName("http://www.acme.com/req", "RequestOrderService");
  QName portQName = 
    new QName("http://www.acme.com/req", "RequestOrderPort");
  Service service = Service.create(wsdlURL, serviceQName);
  RequestOrderPort port = 
    (RequestOrderPort) service.getPort(portQName, RequestOrderPort.class);
  (new Tester()).runTests(port);
//! </acme>
    
  }
}
In the listing above, the first thing to notice is that through the use of the annotation @WebServiceRef we are able to define a reference to a Web service through dependency injection. It is similar to the @Resource annotation that is used for web and EJB applications. When this annotation is applied on a field or method, the container injects an instance of the requested resource into the application when the application is initialized. This is highlighted in the listing below:

Java Code: Dependency Injection of Proxy Instance
@WebServiceRef(RequestOrderService.class)
  public static RequestOrderPort port;
The listing above, show the annotation used on the field port of type RequestOrderPort. If you remember, this is the SEI for the RequestOrder Web service. The variable port is the injection target for this case of dependency injection using the @WebServiceRef annotation. Note that a Java EE container must be used in order take advantage of the @WebServiceRef annotation in order to inject a proxy instance implementing the SEI into the injection target of this type.

When using the @WebServiceRef annotation to inject an instance of the SEI, the value attribute of the annotation must refer to a generated service interface type that is a subtype of javax.xml.ws.Service. The RequestOrderService.class was generated at the same time as the SEI by the Eclipse and JBoss Tools. The RequestOrderService generated class provides a getRequestOrderPort() method that returns an instance of RequestOrderPort.

For those cases where you cannot use the @WebServiceRef for injection because you are not running inside a container, you can still use the generated service class RequestOrderService by use of it’s constructor. This is shown in the listing below:

Java Code: Using the RequestOrderService Outside a Container
RequestOrderService service = new RequestOrderService();
RequestOrderPort port = service.getRequestOrderPort();
(new Tester()).runTests(port);
Now you able to construct a proxy instance for the SEI dynamically by configuring an instance of the javax.xml.ws.Service class at runtime. The listing below shows how to complete this approach:

Java Code: Constructing a Proxy Instance
URL wsdlURL = 
    new URL("http://"+hostName+":"+portVal+"/retail-endpoint-endpoint-1.0/requestOrder?wsdl");
QName serviceQName = 
		new QName("http://www.acme.com/req", "RequestOrderService");
QName portQName = 
    new QName("http://www.acme.com/req", "RequestOrderPort");
Service service = Service.create(wsdlURL, serviceQName);
RequestOrderPort port = 
    (RequestOrderPort) service.getPort(portQName, RequestOrderPort.class);
  (new Tester()).runTests(port);
This approach uses the Service.create() factory method to configure a Service instance using the WSDL’s URL and QName of the wsdl:service. Since the SEI is annotated with the WSDL’s URL, it is not necessary to provide the URL parameter but it is good practice to provide one nonetheless in case the URL has been changed since the SEI was generated.

We now have created the Service instance and now we are ready to invoke the Service.getPort() method by passing in the QName of the wsdl:portType and the RequestOrderPort.class (i.e. SEI). The Service instance uses the dynamic proxy mechanism to create a proxy instance that you can be cast to the SEI. The listing below
shows how the proxy instance can be used as well as how exceptions are handled:

Java Code: Test to Create and Use Proxy Instance
public void runTests(RequestOrderPort port) throws Exception {
    
    BUSOBJCCARD ccard = createCreditCard();
    BUSOBJCCARD expiredCCard = createExpiredCreditCard();
    ArrayList<BUSOBJITEM> itemList = createItemList();

    OrderType order;
    try {
      System.out.println("Running test with expired credit card.");
      order = port.requestOrder(
          "ENT0072123", null, expiredCCard, itemList);
    } catch (SOAPFaultException sfe) { // a runtime exception
      processSOAPFault(sfe);
    } catch (InputFault e) {  // a checked exception
      System.out.println("Error - should have thrown SOAPFault");
    }
    try {
      System.out.println("Running test with null customer number.");
      order = port.requestOrder(
          null, null, ccard, itemList);
    } catch (InputFault ife) {
      processInputFault(ife);
    }
    try {
      System.out.println("Running test with a valid request.");
      order = port.requestOrder(
          "ENT0072123", null, ccard, itemList);
      printReturnedOrder(order);
    } catch (InputFault ife) {
      processInputFault(ife);
    }
}
This listing above shows the method runTests that receives as its single parameter a proxy instance implementing the RequestOrderPort interface by using one of the creation methods we previously described. In the first part of this method, we create the JAXB instances that are passed as parameters to the proxy instance’s SEI method. As we have included exception handling, we have created an “expired” credit card and a “good” credit card in order to test this.The first try block we will invoke the dynamic proxy in the following manner:

Java Code: Dynamic Proxy Invocation
order = port.requestOrder("ENT0072123", null, expiredCCard, itemList);
As you can see, it is very straightforward to invoke the requestOrder method on the RequestOrderPort interface. The return value is of type OrderType, specified by JAXB binding of the oms:Order element in the wsdl:output message.

How is Fault Handling Done with Proxies

The WSDL for the RequestOrder Web service used in this listing specifies a wsdl:fault in the wsdl:operation definition for requestOrder. This wsdl:fault is mapped to the Java exception class InputFault. Therefore the code that executes the RequestOrderPort.requestOrder method in the listing has catch blocks to handle any InputFault exceptions that could be thrown.

Note though that the code catches and handles instances of SOAPFaultException. SOAPFaultException is the run-time exception that the JAX-WS runtime uses to carry SOAP2 protocol-specific fault information. So an invocation of the RequestOrder Web service will return a SOAP fault message if the SOAP fault message’s detail element isn’t an instance of the faults:inputMessageValidationFault element that was specified by the wsdl:fault’s corresponding wsdl:message. Instead JAX-WS converts this SOAP fault message to an instance of SOAPFaultException.

In an ideal world, all application-specific faults would be represented by a wsdl:fault. If this were true, the only SOAPFaultException you would get would be caused by system-level errors (e.g., a server error unrelated to the Web service’s business logic). But in the real world, a Web service may return SOAP fault messages that are business-logic-related and not represented by a wsdl:fault. That is why it is good practice, as shown in Example 6–8, to write catch blocks to handle SOAPFaultException.

As an example of this is shown in the listing for runTests since it first invokes the requestOrder method with an expired credit card. This causes the Web service to return a SOAP fault message that is not mapped to an InputFault resulting in the requestOrder method to throw the run-time exception SOAPFaultException. The listing below shows the SOAP-ENV:Fault element, from the SOAP message body:

XML Code: Soap Fault Exception
<SOAP-ENV:Fault xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
  <faultstring>Business logic exception</faultstring>
  <faultcode>SOAP-ENV:Client</faultcode>
  <detail>
    <expiredCC xmlns="http://www.acme.com/req"> Credit card has
    expired</expiredCC>
  </detail>
</SOAP-ENV:Fault>
Here the faultstring element indicates a business logic exception. The detail element provides information that this is the result of an expired credit card. The detail element is provides more information than is conveyed by the faultcode element. This is due to the faultcode of SOAP-ENV:Client simply indicating that there was a problem with the request message without any detail.

For application-specific faults that are represented by a wsdl:fault, the WSDL’s soap:fault element specifies the contents of the SOAP fault message’s details element. The name attribute indicates the soap:fault to the wsdl:fault defined for the operation. So, from the RequestOrder WSDL (if you go back), you will see that for those messages, the detail element contains a single faults:inputMessageValidationFault child element. With JAX-WS, that element is mapped to the FaultInfo property of the InputFault exception wrapper class. This listing below show how it processes the InputFault instance.

Java Code: Processing of InputFault Instance
private void processInputFault(InputFault e) { 
    System.out.println("Mapped Exception (InputFault)");
    System.out.println("InputFault.getMessage() =");
    System.out.println(e.getMessage());
    System.out.println("InputFault.getFaultInfo.getMsg() =");
    System.out.println(e.getFaultInfo().getMsg());
    System.out.println();
  }
Note how the handling exceptions is mapped from wsdl:fault declarations is straightforward. You just use the getFaultInfo() method to get a JAXB representation of the fault message’s detail element contents. The next listing shows the results of running the code from the processInputFault method when a SOAP request message is sent to the Web service containing a null customer number:

XML Code: InputFault Results
java - Running test with null customer number.
    java - Mapped Exception (InputFault)
    java - InputFault.getMessage() =
    java - Input parameter failed validation.
    java - InputFault.getFaultInfo.getMsg() =
    java - Customer Number cannot be null.
Closing UP

We have covered a great deal of material on JAX-WS on the client side. We have looked into using JAX-WS proxies as well as learning a great deal about the technology and standards used for such proxies. The key for working with JAX-WS is to have a decent understanding of how the WSDL to Java mapping is defined. Without this understanding of the mapping, and the annotations that control its behavior at runtime, it would be a real challenge to be prepared for the Web Service Developer exam. This is because the JAX-WS API is designed to make it look like you can do Web services invocation simply by calling a method on a SEI via dynamic proxy technology. But this ease of use comes at a cost to the developer to understand what is happening with the software behind the scenes especially when you are looking to do useful work and need to handle both mappings and SOAP fault messages.

Well we are far from finished with JAX-WS. In the next section, we will continue with JAX-WS on the client side, this time looking at working with Web services using the JAX-WS client infrastructure to send and receive XML messages. Until then.