CHAPTER 14

image

SOAP Web Services

The term web service implies “something” accessible on the “web” that gives you a “service.” The first example that comes to our mind is an HTML page: it’s accessible online and, once read, it gives you the information you were looking for. Another kind of web service is the Servlets. They are bound to a URL, therefore accessible on the web, and they perform any kind of processing. But the term “web services” quickly became a buzzword, got assimilated to Service Oriented Architecture (SOA) and today web services are part of our day-to-day architectural life. Web services applications can be implemented with different technologies such as SOAP, described in this chapter, or REST (see next chapter).

SOAP (Simple Object Access Protocol) web services are said to be “loosely coupled” because the client, a.k.a. the consumer, of a web service doesn’t have to know its implementation details (such as the language used to develop it, the method signature or the platform it runs on). The consumer is able to invoke a SOAP web service using a self-explanatory interface describing the available business methods (parameters and return value). The underlying implementation can be done in any language (Visual Basic, C#, C, C++, Java, etc.). A consumer and a service provider will still be able to exchange data in a loosely coupled way: using XML documents. A consumer sends a request to a SOAP web service in the form of an XML document, and, optionally, receives a reply, also in XML.

SOAP web services are also about distribution. Distributed software has been around for a long time, but, unlike existing distributed systems, SOAP web services are adapted to the Web. The default network protocol is HTTP, a well-known and robust stateless protocol.

SOAP web services are everywhere. They can be invoked from a simple desktop or used for business-to-business (B2B) integration so that operations that previously required manual intervention are performed automatically. SOAP web services integrate applications run by various organizations through the Internet or within the same company (which is known as Enterprise Application Integration, or EAI). In all cases, they provide a standard way to connect diverse pieces of software.

This chapter will first introduce some important notions to understand SOAP Web Services such as WSDL or SOAP. Then it will show how to write a SOAP Web Service before explaining how to consume it.

Understanding SOAP Web Services

Simply put, SOAP web services constitute a kind of business logic exposed via a service (i.e., the service provider) to a client (i.e., the service consumer). However, unlike objects or EJBs, SOAP web services provide a loosely coupled interface using XML. SOAP web service standards specify that the interface to which a message is sent should define the format of the message request and response, and mechanisms to publish and to discover web service interfaces (the service registry).

In Figure 14-1, you can see a high-level picture of a SOAP web service interaction. The SOAP web service can optionally register its interface into a registry (Universal Description Discovery and Integration, or UDDI) so a consumer can discover it. Once the consumer knows the interface of the service and the message format, it can send a request to the service provider and receive a response.

9781430246268_Fig14-01.jpg

Figure 14-1. The consumer discovers the service through a registry

SOAP web services depend on several technologies and protocols to transport and to transform data from a consumer to a service provider in a standard way. The ones that you will come across more often are the following:

  • Extensible Markup Language (XML) is the basic foundation on which SOAP web services are built and defined (SOAP, WSDL, and UDDI).
  • Web Services Description Language (WSDL) defines the protocol, interface, message types, and interactions between the consumer and the provider.
  • Simple Object Access Protocol (SOAP) is a message-encoding protocol based on XML technologies, defining an envelope for web services communication.
  • Messages are exchanged using a transport protocol. Although Hypertext Transfer Protocol (HTTP) is the most widely adopted transport protocol, others such as SMTP or JMS can also be used.
  • Universal Description Discovery, and Integration (UDDI) is an optional service registry and discovery mechanism, similar to the Yellow Pages; it can be used for storing and categorizing SOAP web services interfaces (WSDL).

With these standard technologies, SOAP web services provide almost unlimited potential. Clients can call a service, which can be mapped to any program and accommodate any data type and structure to exchange messages through XML.

XML

I’ve already described XML in Chapter 12 and you now know how to manipulate, parse, and bind XML documents. Because XML is the perfect integration technology that solves the problem of data independence and interoperability, it is the DNA of SOAP web services. It is used not only as the message format but also as the way the services are defined (WSDL) or exchanged (SOAP). Associated with these XML documents, schemas (XSD) are used to validate the data exchanged between the consumer and the provider. Historically, SOAP web services evolved from the basic idea of “RPC (Remote Procedure Call) using XML.”

WSDL

WSDL is the interface definition language (IDL) that defines the interactions between consumers and SOAP web services (see Figure 14-2). It is central to a SOAP web service as it describes the message type, port, communication protocol, supported operations, location, and what the consumer should expect in return. It defines the contract to which the service guarantees it will conform. You can think of WSDL as a Java interface but written in XML.

9781430246268_Fig14-02.jpg

Figure 14-2. WSDL interface between the consumer and the web service

To ensure interoperability, a standard web service interfaceis needed for a consumer and a producer to share and understand a message. That’s the role of WSDL. Listing 14-1 shows you an example of a WSDL that represents a credit card validation SOAP web service (this service takes a credit card as an input and validates it). This WSDL is pure XML and, if you read carefully, you will see all the information needed for a consumer to locate a web service (soap:address location), invoke a method (operation name="validate"), and use an appropriate transport protocol (soap:binding transport).

Listing 14-1.  A WSDL File Representing a Credit Card Validation Service

<?xml version="1.0" encoding="UTF-8" ?>
<definitions →
        xmlns:wsam=" http://www.w3.org/2007/05/addressing/metadata "
        xmlns:soap=" http://schemas.xmlsoap.org/wsdl/soap/ "
        xmlns:tns=" http://chapter14.javaee7.book.agoncal.org/ "
        xmlns:xsd=" http://www.w3.org/2001/XMLSchema "
        xmlns=" http://schemas.xmlsoap.org/wsdl/ "
        targetNamespace=" http://chapter14.javaee7.book.agoncal.org/ "
        name="CardValidatorService">
  < types >
    <xsd:schema>
      <xsd:import namespace=" http://chapter14.javaee7.book.agoncal.org/ "            schemaLocation=" http://localhost:8080/chapter14/CardValidatorService?xsd=1 "/>
    </xsd:schema>
  </types>
  < message name=" validate ">
    <part name="parameters" element="tns:validate"/>
  </message>
  < message name=" validateResponse ">
    <part name="parameters" element="tns:validateResponse"/>
  </message>
  < portType name="CardValidator">
    <operation name="validate">
      <input message="tns: validate "/>
      <output message="tns: validateResponse "/>
    </operation>
  </portType>
  < binding name="CardValidatorPortBinding" type="tns:CardValidator">
    <soap:binding transport=" http://schemas.xmlsoap.org/ soap /http " style="document"/>
    < operation name="validate">
      <soap:operation soapAction=""/>
      <input>
        <soap:body use="literal"/>
      </input>
      <output>
        <soap:body use="literal"/>
      </output>
    </operation>
  </binding>
  < service name="CardValidatorService">
    < port name="CardValidatorPort" binding="tns:CardValidatorPortBinding">
      <soap:address location=" http://localhost:8080/chapter14/CardValidatorService "/>
    </port>
  </service>
</definitions>

WSDL uses XML to describe what a service does, how to invoke its operations, and where to find it. It follows a fixed structure containing several parts (types, message, portType, binding, service). Table 14-1 lists a subset of the WSDL elements and attributes. WSDL is a much richer language than what’s listed in Table 14-1 and is defined by the W3C. If you want to know more about WSDL, its structure, and its datatypes, you should check the related W3C website.

Table 14-1. WSDL Elements and Attributes

Element Description
definitions Is the root element of the WSDL, and it specifies the global declarations of namespaces that are visible throughout the document
types Defines the data types to be used in the messages. In this example, it is the XML Schema Definition (CardValidatorService?xsd=1) that describes the parameters passed to the web service request and the response
message Defines the format of data being transmitted between a web service consumer and the web service itself. Here you have the request (the validate method) and the response (validateResponse)
portType Specifies the operations of the web service (the validate method). Each operation refers to an input and output message
binding Describes the concrete protocol (here SOAP) and data formats for the operations and messages defined for a particular port type
service Contains a collection of <port> elements, where each port is associated with an endpoint (a network address location or URL)
port Specifies an address for a binding, thus defining a single communication endpoint

The <xsd:import namespace> element refers to an XML Schema that has to be available on the network to the consumers of the WSDL. Listing 14-2 shows this schema defining the data types used in the web service: the structure of the CreditCard object (with number, expiry date, and so on) sent in the request (validate), and a boolean (the credit card is valid or not) received in the response (validateResponse).

Listing 14-2.  The Schema Imported by the WSDL File

<xs:schema xmlns:tns=" http://chapter14.javaee7.book.agoncal.org/ " 
           xmlns:xs=" http://www.w3.org/2001/XMLSchema "
           targetNamespace=" http://chapter14.javaee7.book.agoncal.org/ " version="1.0">
  <xs:element name=" validate " type="tns:validate"/>
  <xs:element name=" validateResponse " type="tns:validateResponse"/>
  <xs:complexType name=" validate ">
    <xs:sequence>
      <xs:element name="arg0" type="tns: creditCard " minOccurs="0"/>
    </xs:sequence>
  </xs:complexType>
  <xs:complexType name=" creditCard ">
    <xs:sequence/>
    <xs:attribute name="number" type="xs:string" use="required"/>
    <xs:attribute name="expiry_date" type="xs:string" use="required"/>
    <xs:attribute name="control_number" type="xs:int" use="required"/>
    <xs:attribute name="type" type="xs:string" use="required"/>
  </xs:complexType>
  <xs:complexType name=" validateResponse ">
    <xs:sequence>
      <xs:element name="return" type=" xs:boolean "/>
    </xs:sequence>
  </xs:complexType>
</xs:schema>

If you remember JAXB described in Chapter 12, you will realize that the XSD in Listing 14-2 has all the information needed to be bound to Java classes. With a WSDL and a schema any consumer can then generate the needed artifacts to invoke the web service (as you’ll see later, JAX-WS comes with utilities that automatically generate these artifacts).

SOAP

WSDL describes an abstract interface of the web service while SOAP provides a concrete implementation, defining the XML messages exchanged between the consumer and the provider (see Figure 14-3). SOAP is the standard web services application protocol. It provides the communication mechanism to connect web services, exchanging formatted XML data across a network protocol, commonly HTTP. Like WSDL, SOAP relies heavily on XML because a SOAP message is an XML document containing several elements (an envelope, a header, a body, etc.). Instead of using HTTP to request a web page from a browser, SOAP sends an XML message via a HTTP request and receives a reply via a HTTP response.

9781430246268_Fig14-03.jpg

Figure 14-3. A consumer invoking a SOAP web service

SOAP is designed to provide an independent, abstract communication protocol capable of connecting distributed services. The connected services can be built using any combination of hardware and software that supports a given transport protocol.

Let’s use the credit card validation example shown in Figure 14-3. The consumer calls the SOAP web service to validate a credit card passing all the needed parameters (credit card number, expiry date, type and control number) and receives a Boolean that informs the consumer if the card is valid or not. Listings 14-3 and 14-4, respectively, show the structure of these two SOAP messages.

Listing 14-3.  The SOAP Envelope Sent for the Request

<soap: Envelope xmlns:soap=" http://schemas.xmlsoap.org/soap/envelope/ " 
               xmlns:cc=" http://chapter14.javaee7.book.agoncal.org/ ">
  <soap: Header />
  <soap: Body >
    <cc: validate >
      <arg0 number="123456789011" expiry_date="10/12" control_number="544" type="Visa"/>
    </cc:validate>
  </soap:Body>
</soap:Envelope>

Listing 14-4.  The SOAP Envelope Received for the Response

<soap: Envelope xmlns:soap=" http://schemas.xmlsoap.org/soap/envelope/ "
               xmlns:cc=" http://chapter14.javaee7.book.agoncal.org/ ">
  <soap: Body >
    <cc: validateResponse >
      <return> true </return>
    </cc:validateResponse>
  </soap:Body>
</soap:Envelope>

The consumer sends all the credit card information within a SOAP envelope (Listing 14-3) to the validate method of the credit card validator web service. The service returns another SOAP envelope (Listing 14-4) with the result of the validation (true or false).

Table 14-2 lists a subset of the SOAP elements and attributes. Like WSDL, SOAP is defined by the W3C standard body.

Table 14-2. SOAP Elements and Attributes

Element Description
Envelope Defines the message and the namespace used in the document. This is a required root element
Header Contains any optional attributes of the message or application-specific infrastructure such as security information or network routing
Body Contains the message being exchanged between applications
Fault Provides information about errors that occur while the message is processed. This element is optional

UDDI

Consumers and providers that interact with one another over the Web need to be able to find information that allows them to interconnect. Either the consumer knows the exact location of the service it wants to invoke or it has to find it. UDDI provides a standard approach to locating information about a web service and how to invoke it. The service provider publishes a WSDL into a UDDI registry available on the Internet. Then it can be discovered and downloaded by potential consumers. It is optional as you can invoke a web service without UDDI if you already know the web service’s location.

UDDI is an XML-based registry of web services, similar to a Yellow Pages directory, where businesses can register their services. This registration includes the business type, geographical location, web site, phone number, and so on. Other businesses can then search the registry and discover information about specific web services. This information provides additional metadata about the service, describing its behavior and the actual location of the WSDL document.

image Note  UDDI has not been as widely adopted as its designers (IBM, Microsoft, and SAP) had hoped. In January 2006 the three companies announced they were closing their public UDDI registries. In late 2007, the group defining UDDI at OASIS announced the closure of the Technical Committee.

Transport Protocol

For a consumer to communicate with a web service it needs a way to send messages. SOAP messages can be transported over a network using a protocol that both parties can support. Given that web services are used mostly on the Web, they usually use HTTP, but they can also use other network protocols such as HTTPS (HTTP Secure), TCP/IP, SMTP (Simple Mail Transport Protocol), FTP (File Transfer Protocol), and so on.

SOAP Web Services Specifications Overview

As seen in Chapter 4, persistence is mostly covered by one specification: JPA. For web services, the situation is more complex, as you have to deal with many specifications coming from different standard bodies. Moreover, because other programming languages use web services, these specifications are not all directly related to the Java Community Process (JCP).

A Brief History of SOAP Web Services Specifications

SOAP web services are a standard way for businesses to communicate over a network. There have been precursors:Common Object Request Broker Architecture (CORBA), initially used by Unix systems, and Distributed Component Object Model (DCOM), its Microsoft competitor. On a lower level, there is Remote Procedure Call (RPC) and, closer to our Java world, Remote Method Invocation (RMI).

Before the Web, it was difficult to get all major software vendors to agree on a transport protocol. When the HTTP protocol became a mature standard, it gradually became a universal business medium of communication. At about the same time, XML officially became a standard when the World Wide Web Consortium (W3C) announced that XML 1.0 was suitable for deployment in applications. By 1998, both ingredients, HTTP and XML, were ready to work together.

SOAP 1.0, started in 1998 by Microsoft, was finally shipped at the end of 1999, and modeled typed references and arrays in XML Schema. By 2000, IBM started working on SOAP 1.1, and WSDL was submitted to the W3C in 2001. UDDI was written in 2000 by the Organization for the Advancement of Structured Information Standards (OASIS) to allow businesses to publish and discover web services. With SOAP, WSDL, and UDDI in place, the de facto standards to create web services had arrived with the support of major IT companies.

Java introduced web services capabilities with the Java API for XML-based RPC 1.0 (JAX-RPC 1.0) in June 2002 and added JAX-RPC 1.1 to J2EE 1.4 in 2003. This specification was quite verbose and not easy to use. With the arrival of Java EE 5 and annotations in Java, the brand new Java API for XML-based Web Services 2.0 (JAX-WS 2.0) specification was introduced as the preferred SOAP web service model. Today Java EE 7 is shipped with JAX-WS 2.2a.

SOAP Web Services Related Specifications

To master all web services standards, you would have to spend some time reading all the specifications listed in Table 14-3 coming from the W3C, the JCP, and OASIS.

Table 14-3. SOAP Web Services Related Specifications and Standard Bodies

Table1-5.jpg

The W3C is a consortium that develops and maintains web technologies such as HTML, XHTML, RDF, CSS, and so forth and, more interestingly for web services, XML, XML Schemas, SOAP, and WSDL.

OASIS hosts several web service-related standards such as UDDI, WS-Addressing, WS-Security, WS-Reliability, and many others (known as WS-*).

Returning to Java, the JCP has a set of specifications that are part of Java EE 7 and Java SE 7. They include JAX-WS 2.2a (JSR 224), Web Services 1.3 (JSR 109), Web Services Metadata 2.3 (JSR 181) and JAXB 2.2 (JSR 222). Taken together, these specifications are usually referred to by the informal term Java Web Services (JWS). SAAJ (SOAP with Attachments API for Java), defined in JSR 67, is part of Java SE and enables developers to produce and consume SOAP messages with attachments.

Other related specifications have been part of previous Java EE versions and have been pruned or evolve now separately from Java EE. JAX-WS is the successor of JAX-RPC (JSR 101) which was too verbose and complex. JAX-RPC has been pruned in Java EE 6, meaning that it has been removed from Java EE 7. The Java API for XML Registries (JAXR) specification defines a standard set of APIs that allow Java clients to access UDDI. Because UDDI has not had the momentum initially expected, this JSR 93 has been pruned and is not included in Java EE 7.

Looking at this huge list of specifications may make you think that writing a SOAP web service in Java is difficult, especially when it comes to getting your head around the APIs. However, the beauty of it is that you don’t need to worry about the underlying technologies (XML, WSDL, SOAP, HTTP, etc.), as just a few JWS standards will do the work for you as you’ll see in a few paragraphs.

JAX-WS 2.2a

JAX-WS 2.2a defines a set of APIs (main packages listed in Table 14-4) and annotations that allow you to build and consume web services with Java. It provides the consumer and service facilities to send and receive web service requests via SOAP, masking the complexity of the protocol. Therefore, neither the consumer nor the service has to generate or parse SOAP messages, as JAX-WS deals with the low-level processing. The JAX-WS specification depends on other specifications such as Java Architecture for XML Binding (JAXB) that you saw in Chapter 12.

Table 14-4. Main JAX-WS Packages

Package Description
javax.xml.ws This package contains the core JAX-WS APIs
javax.xml.ws.http Defines APIs specific to the XML/HTTP binding
javax.xml.ws.soap Defines APIs specific to the SOAP 1.1/HTTP or SOAP 1.2/HTTP binding
javax.xml.ws.handler This package defines APIs for message handlers

Web Services 1.3

JSR 109 (“Implementing Enterprise Web Services”) defines the programming model and runtime behavior of web services in the Java EE container. It also defines packaging to ensure portability of web services across application server implementations.

WS-Metadata 2.3

Web Services Metadata (WS-Metadata, specification JSR 181) provides annotations to facilitate the definition and deployment of web services (main packages listed in Table 14-5). The primary goal of JSR 181 is to simplify the development of web services. It provides mapping facilities between WSDL and Java interfaces, and vice versa, through annotations. These annotations can be used within simple Java classes or EJBs.

Table 14-5. Main WS-Metadata Packages

Package Description
javax.jws This package contains the Java to WSDL mapping annotations
javax.jws.soap APIs to specify the mapping of the web service onto the SOAP message protocol

What’s New in SOAP Web Services Specifications?

Unfortunately there is nothing new in JAX-WS or WS-Metadata in Java EE 7. SOAP web services specifications have not been updated in this Java EE release. For instance, in Java EE 7, SOAP web services are not considered Managed Beans so you can’t use interceptors, interceptor binding, injection of context, and so on. Bean Validation hasn’t been integrated either, meaning you cannot use method level validation (see Chapter 3).

Reference Implementation

Metro is the open source reference implementation of the Java web services specifications. It consists of JAX-WS and JAXB, and also supports the legacy JAX-RPC APIs. It allows you to create and deploy secure, reliable, transactional, interoperable SOAP web services as well as SOAP web service consumers. The Metro stack is produced by the GlassFish community, but it can also be used outside GlassFish in a Java SE environment or other web containers (e.g. Tomcat, Jetty).

Apache CXF (formerly known as XFire) and Apache Axis2 also implement the JWS stack. In addition to not being the reference implementation, both frameworks are also heavily used in SOAP web services.

Writing SOAP Web Services

So far you’ve seen lots of low-level concepts such as HTTP protocol, WSDL documents, SOAP messages, or XML. But how do you write a SOAP web service with all the specifications that have been previously presented? You can either start from the WSDL or go straight to coding some Java.

Because the WSDL document is the contract between the consumer and the service, it can be used to generate the Java code for the consumer and the service. This is the top-down approach, also known as contract first. This approach starts with the contract (the WSDL) by defining operations, messages, and so forth. When both the consumer and provider agree on the contract, you can then implement the Java classes based on that contract. Metro provides some tools (wsimport) that generate classes from a WSDL.

With the other approach, called bottom-up, the implementation class already exists, and all that is needed is to create the WSDL. Again, Metro provides utilities (wsgen) to generate a WSDL from existing classes. In both cases, the code may need to be adjusted to fit the WSDL or vice versa. That is when JAX-WS comes to your aid. With a simple development model and a few annotations, Java-to-WSDL mapping can be adjusted. But be careful, the bottom-up approach can result in very inefficient applications, as the Java methods and classes have no bearing on the ideal granularity of messages crossing the network. If latency is high and/or bandwidth low, it pays to use the fewer, larger messages, and this can be done more efficiently by using the contract-first approach.

Despite all these specifications, concepts, standards, and organizations, writing and consuming a web service in the bottom-up approach is very easy. SOAP web services follow the “ease of development” paradigm of Java EE 7 and do not require you to write any WSDL or SOAP. The web service is just an annotated POJO that needs to be deployed in a web service container. Listing 14-5 shows you the code of a web service that validates a credit card.

Listing 14-5.  The CardValidator Web Service

@WebService
public class CardValidator {
 
  public boolean validate (CreditCard creditCard) {
    Character lastDigit = creditCard.getNumber().charAt(
                          creditCard.getNumber().length() - 1);
 
    if (Integer.parseInt(lastDigit.toString()) % 2 != 0) {
      return true;
    } else {
      return false;
    }
  }
}

Like entities or EJBs, a SOAP web service uses the annotated POJO model with the configuration-by-exception policy. This means that a web service can just be a Java class annotated with @javax.jws.WebService if all the defaults suit you. Then, the JAX-WS runtime will generate all the plumbing that will transform a Java method invocation into a XML over HTTP call. This CardValidator SOAP web service has one method to validate a credit card. It takes a credit card as a parameter and returns true or false according to whether the card is valid or not. In this instance, it assumes that credit cards with an even number are valid, and those with an odd number are not.

A CreditCard object (see Listing 14-6) is exchanged between the consumer and the SOAP web service. When describing the web service architecture, the data exchanged needs to be an XML document, so a method to transform a Java object into an XML document is needed. This is where JAXB comes into play with its simple annotations and powerful API. As seen in Chapter 12, the CreditCard object has to be annotated with @javax.xml.bind.annotation.XmlRootElement, and a few other mapping annotations (e.g. @XmlAttribute) if you need to customize the mapping, and JAXB will transform it back and forth from XML to Java.

Listing 14-6.  The CreditCard Class with JAXB Annotations

@XmlRootElement
public class CreditCard {
 
  @XmlAttribute(required = true)
  private String number;
  @XmlAttribute(name = "expiry_date", required = true)
  private String expiryDate;
  @XmlAttribute(name = "control_number", required = true)
  private Integer controlNumber;
  @XmlAttribute(required = true)
  private String type;
 
  // Constructors, getters, setters
}

With JAXB annotations, you avoid developing all the low-level XML parsing, as it happens behind the scenes. The web service manipulates a Java object, and the same is true for the consumer.

Anatomy of a SOAP Web Service

Like most of the Java EE 7 components, SOAP web services rely on the configuration-by-exception paradigm, which specifies that configuring a component is the exception. Only one annotation is actually needed to turn a POJO into a SOAP web service @WebService. The requirements to write a web service are as follows:

  • The class must be annotated with @javax.jws.WebService or the XML equivalent in a deployment descriptor (webservices.xml).
  • The class (a.k.a service implementation bean) can implement zero or more interfaces (a.k.a service endpoint interface) that have to be annotated with @WebService.
  • The class must be defined as public, and it must not be final or abstract.
  • The class must have a default public constructor.
  • The class must not define the finalize() method.
  • To turn a SOAP web service into an EJB endpoint, the class has to be annotated with @javax.ejb.Stateless or @javax.ejb.Singleton (see Chapter 7).
  • A service must be a stateless object and should not save client-specific state across method calls.

The WS-Metadata specification (JSR 181) says that, as long as it meets these requirements, a POJO can be used to implement a web service deployed to the servlet container. This is commonly referred to as a servlet endpoint. A stateless or singleton session bean can also be used to implement a web service that will be deployed in an EJB container (a.k.a. an EJB endpoint).

SOAP Web Service Endpoints

JAX-WS allows both regular Java classes and EJBs to be exposed as web services. We call it service endpoint interfaces (SEI). Referring to the code for a POJO (Listing 14-5) and for an EJB web service (Listing 14-7) reveals hardly any differences, with the exception that the EJB web service has the extra annotation @Stateless (or @Singleton). Although, the packaging can be different as you’ll later see.

Listing 14-7.  The CardValidator Web Service as an EJB Endpoint

@WebService
@Stateless
public class CardValidator {
 
  public boolean validate(CreditCard creditCard) {
    Character lastDigit = creditCard.getNumber().charAt(
                          creditCard.getNumber().length() - 1);
 
    if (Integer.parseInt(lastDigit.toString()) % 2 != 0) {
      return true;
    } else {
      return false;
    }
  }
}

Both endpoints have almost identical behavior, but a few extra benefits are gained from using EJB endpoints. As the web service is also an EJB, the benefits of transaction and security being managed by the container are automatic, and interceptors can be used, which is not possible with servlet endpoints. The business code can be exposed as a web service and as an EJB at the same time, meaning that the business logic can be exposed through SOAP and also through RMI by adding a remote interface.

WSDL Mapping

At the service level, systems are defined in terms of XML messages, WSDL operations, and SOAP messages. Meanwhile, at the Java level, applications are defined in terms of objects, interfaces, and methods. A translation from Java objects to WSDL operations is needed. The JAXB runtime uses annotations to determine how to marshal/unmarshal a class to/from XML. Similarly, JWS uses annotations to map Java classes into WSDL and to determine how to marshal a method invocation to a SOAP request and unmarshal a SOAP response into an instance of the method’s return type.

The JAX-WS (JSR 224) and WS-Metadata specifications (JSR 181) define two different kinds of annotations:

  • WSDL mapping annotations: These annotations belong to the javax.jws package and allow you to change the WSDL/Java mapping. The @WebMethod, @WebResult, @WebParam, and @OneWay annotations are used on the web service to customize the signature of the exposed methods.
  • SOAP binding annotations: These annotations belong to the javax.jws.soap package and allow customizing of the SOAP binding (@SOAPBinding and @SOAPMessageHandler).

Like all the other Java EE 7 specifications, web services annotations can be overridden by an XML deployment descriptor (webservices.xml), which is optional. Let’s take a closer look at the WSDL mapping annotations.

@WebService

The @javax.jws.WebService annotation marks a Java class or interface as being a web service. If used directly on the class (as in all the examples so far), the annotation processor of the container will generate the interface, so the following snippets of code are equivalent. Here is the annotation on the class:

@WebService
public class CardValidator {...}

And the following snippet shows the annotation on an interface implemented by a class. As you can see, the implementation needs to define the fully qualified name of the interface in the endpointInterface attribute:

@WebService
public interface Validator {...}
 
@WebService (endpointInterface = "org.agoncal.book.javaee7.chapter14. Validator ")
public class CardValidator implements Validator {...}

The @WebService annotation has a set of attributes (see Listing 14-8) that allow you to customize the name of the web service in the WSDL file (the <wsdl:portType> or <wsdl:service> element) and in its namespace, as well as change the location of the WSDL itself (the wsdlLocation attribute).

Listing 14-8.  The @WebService API

@Retention(RUNTIME) @Target(TYPE)
public @interface WebService {
  String name() default "";
  String targetNamespace() default "";
  String serviceName() default "";
  String portName() default "";
  String wsdlLocation() default "";
  String endpointInterface() default "";
}

So when the default WSDL mapping rules are not appropriate for your SOAP web service, just use the needed attributes. The code below changes the port and service name:

@WebService(portName = " CreditCardValidator ", serviceName = " ValidatorService ")
public class CardValidator {...}

If you compare with the default WSDL described in Listing 14-1 you’ll see the following changes:

<service name=" ValidatorService ">
 <port name=" CreditCardValidator " binding="tns: CreditCardValidator Binding">
 <soap:address location=" http://localhost:8080/chapter14/ ValidatorService "/>
 </port>
</service>

When you use the @WebService annotation, all public methods of the web service are exposed except when using the @WebMethod annotation.

@WebMethod

By default, all the public methods of a SOAP web service are exposed in the WSDL and use all the default mapping rules. To customize some elements of this mapping, you can apply the @javax.jws.WebMethod annotation on methods. The API of this annotation is quite simple as it allows the renaming of a method or exclusion of it from the WSDL. Listing 14-9 shows how the CardValidator web service renames the two first methods and excludes the last one.

Listing 14-9.  Two Methods Are Renamed, the Last One Excluded

@WebService
public class CardValidator {
 
  @WebMethod(operationName = "ValidateCreditCard")
  public boolean validate(CreditCard creditCard) {
    // Business logic
  }
 
  @WebMethod(operationName = "ValidateCreditCardNumber")
  public void validate(String creditCardNumber) {
    // Business logic
  }
 
  @WebMethod(exclude = true)
  public void validate(Long creditCardNumber) {
    // Business logic
  }
}

As you can see in the WSDL fragment described in Listing 14-10, only the two methods ValidateCreditCard and ValidateCreditCardNumber are defined. The last method got excluded from the WSDL.

Listing 14-10.  Fragment of WSDL with Renamed Methods

<message name=" ValidateCreditCard ">
  <part name="parameters" element="tns:ValidateCreditCard"/>
</message>
<message name="ValidateCreditCardResponse">
  <part name="parameters" element="tns:ValidateCreditCardResponse"/>
</message>
<message name=" ValidateCreditCardNumber ">
  <part name="parameters" element="tns:ValidateCreditCardNumber"/>
</message>
<message name="ValidateCreditCardNumberResponse">
  <part name="parameters" element="tns:ValidateCreditCardNumberResponse"/>
</message>
<portType name="CardValidator">
  <operation name="ValidateCreditCard">
    <input message="tns: ValidateCreditCard "/>
    <output message="tns:ValidateCreditCardResponse"/>
  </operation>
  <operation name="ValidateCreditCardNumber">
    <input message="tns: ValidateCreditCardNumber "/>
    <output message="tns:ValidateCreditCardNumberResponse"/>
  </operation>
</portType>

@WebResult

The @javax.jws.WebResult annotation controls the generated name of the message returned value in the WSDL. In Listing 14-11, the returned result of the validate() method is renamed to IsValid.

Listing 14-11.  The Return Result of the Method Is Renamed

@WebService
public class CardValidator {
 
  @WebResult(name = "IsValid")
  public boolean validate(CreditCard creditCard) {
    // Business logic
  }
}

By default the name of the returned value in the WSDL is set to return. But with the @WebResult annotation you can be more specific and have a more expressive contract:

<!-- Default -->
<xs:element name=" return " type="xs:boolean"/>
<!-- Renamed to IsValid -->
<xs:element name=" IsValid " type="xs:boolean"/>

This annotation also has other elements to customize the WSDL, such as the XML namespace for the returned value, and looks like the @WebParam annotation shown in Listing 14-12.

Listing 14-12.  The @WebParam API

@Retention(RUNTIME) @Target(PARAMETER)
public @interface WebParam {
  String name() default "";
  public enum Mode {IN, OUT, INOUT};
  String targetNamespace() default "";
  boolean header() default false;
  String partName() default "";
};

@WebParam

The @javax.jws.WebParam annotation, shown in Listing 14-12, is similar to @WebResult as it customizes the parameters for the web service methods. Its API permits changing the name of the parameter in the WSDL (see Listing 14-13), the namespace, and the type. Valid types are IN, OUT, or INOUT (both), which determine how the parameter is flowing (default is IN).

Listing 14-13.  The Method Parameter Is Renamed

@WebService
public class CardValidator {
 
  public boolean validate( @WebParam (name= "Credit-Card", mode = IN) CreditCard creditCard) {
    // Business logic
  }
}

Again, if you compare this with the default XSD defined in Listing 14-2 you’ll notice that, by default, the name of the parameter is arg0. The @WebParam annotation overrides this default with the name Credit-Card:

<!-- Default -->
<xs:element name=" arg0 " type="tns:creditCard" minOccurs="0"/>
<!-- Renamed to Credit-Card -->
<xs:element name=" Credit-Card " type="tns:creditCard" minOccurs="0"/>

@OneWay

The @OneWay annotation can be used on methods that do not have a return value such as methods returning void. This annotation has no elements and can be seen as a markup interface that informs the container that invocation can be optimized, as there is no return (using an asynchronous invocation, for example).

@SOAPBinding

A binding describes how the web service is bound to a messaging protocol, particularly the SOAP messaging protocol. There are two programming styles for SOAP binding defined in WSDL 1.1: RPC and document (a.k.a. messaging). This choice corresponds to how the SOAP body content is structured:

  • Document: The SOAP message contains the document. It is sent as one document in the <soap:Body> element without additional formatting rules; it contains whatever the sender and the receiver agree upon. Document style is the default choice.
  • RPC: The SOAP message contains the parameters and the return values. The <soap:Body> contains an element with the name of the method or remote procedure being invoked. This element in turn contains an element for each parameter of that procedure.

A SOAP binding (Document or RPC) has then to choose from two different serialization/deserialization formats:

  • Literal: Data is serialized according to an XML schema.
  • Encoded: SOAP encoding specifies how objects, structures, arrays, and object graphs should be serialized.

This gives you four style/use models:

  • Document/Literal (default)
  • Document/Encoded (not WS-* compliant)
  • RPC/Literal
  • RPC/Encoded

By default the generated WSDL that you’ve seen so far uses the document/literal style of binding. Specifying the @SOAPBinding annotation on the class as seen in Listing 14-14 can change this.

Listing 14-14.  Web Service Using an RPC/Literal Binding

@WebService
@SOAPBinding (style = RPC , use = LITERAL)
public class CardValidator {
 
  public boolean validate(CreditCard creditCard) {
    // Business logic
  }
}

Listing 14-14 overrides the default binding by using the RPC style instead of Document. This has an effect on the WSDL as well as the XML schema generated for the SOAP web service provider and consumer. Listing 14-15 shows these differences.

Listing 14-15.  WSDL Differences Between Document and RPC Style

<!-- Document style -->
<message name="validate">
  <part name="parameters" element="tns:validate"/>
</message>
<message name="validateResponse">
  <part name="parameters" element="tns:validateResponse"/>
</message>
...
<soap:binding transport=" http://schemas.xmlsoap.org/soap/http " style=" document "/>
 
<!-- RPC Style -->
<message name="validate">
  <part name="arg0" type="tns:creditCard"/>
</message>
<message name="validateResponse">
  <part name="return" type="xsd:boolean"/>
</message>
...
<soap:binding transport=" http://schemas.xmlsoap.org/soap/http " style=" rpc "/>

Putting the Mapping All Together

To have a better understanding of what influence these annotations have on the SOAP web service, let’s put them all together and look at the different artifacts. I’ll use the basic CardValidator web service defined in Listing 14-5 and add most of the mapping annotations we’ve seen so far (see Listing 14-16).

Listing 14-16.  The CardValidator Web Service with Mapping Annotations

@WebService(portName = "CreditCardValidator", serviceName = "ValidatorService")
@SOAPBinding(style = RPC, use = LITERAL)
public class CardValidator {
 
  @WebResult(name = "IsValid")
  @WebMethod(operationName = "ValidateCreditCard")
  public boolean validate( @WebParam(name = "Credit-Card") CreditCard creditCard) {
    // Business logic
  }
 
  @WebResult(name = "IsValid")
  @WebMethod(operationName = "ValidateCreditCardNumber")
  public void validate( @WebParam(name = "Credit-Card-Number") String creditCardNumber) {
    // Business logic
  }
 
  @WebMethod(exclude = true)
  public void validate(Long creditCardNumber) {
    // Business logic
  }
}

Listing 14-16 defines an RPC/Literal web services with only two methods exposed (note that the method validate(Long creditCardNumber) is not exposed because of @WebMethod(exclude = true)). Every method parameter and returned value is renamed to have a more expressive WSDL. Listing 14-17 shows the resulting WSDL document that you can compare with the original one in Listing 14-1 (the differences are highlighted in bold in the code).

Listing 14-17.  The WSDL After Customization

<?xml version="1.0" encoding="UTF-8" ?>
<definitions
        xmlns:soap=" http://schemas.xmlsoap.org/wsdl/soap/ "
        xmlns:tns=" http://chapter14.javaee7.book.agoncal.org/ "
        xmlns:xsd=" http://www.w3.org/2001/XMLSchema "
        xmlns=" http://schemas.xmlsoap.org/wsdl/ "
        targetNamespace=" http://chapter14.javaee7.book.agoncal.org/ "
        name ="ValidatorService">
  <types>
    <xsd:schema>
      <xsd:import namespace=" http://chapter14.javaee7.book.agoncal.org/ "
                  schemaLocation=" http://localhost:8080/chapter14/ValidatorService?xsd=1 "/>
    </xsd:schema>
  </types>
  <message name=" ValidateCreditCard ">
    <part name="Credit-Card" type="tns:creditCard" />
  </message>
  <message name="ValidateCreditCardResponse">
    <part name="IsValid" type="xsd:boolean" />
  </message>
  <message name=" ValidateCreditCardNumber ">
    <part name=" Credit-Card-Number " type="xsd:string"/>
  </message>
  <message name="ValidateCreditCardNumberResponse"/>
  <portType name=" CardValidator ">
    <operation name="ValidateCreditCard">
      <input message="tns:ValidateCreditCard"/>
      <output message="tns:ValidateCreditCardResponse"/>
    </operation>
    <operation name="ValidateCreditCardNumber">
      <input message="tns:ValidateCreditCardNumber"/>
      <output message="tns:ValidateCreditCardNumberResponse"/>
    </operation>
  </portType>
  <binding name=" CreditCardValidatorBinding " type="tns:CardValidator">
    <soap:binding transport=" http://schemas.xmlsoap.org/soap/http " style=" rpc "/>
    <operation name=" ValidateCreditCard ">
      <soap:operation soapAction=""/>
      <input>
        <soap:body use="literal"/>
      </input>
      <output>
        <soap:body use="literal"/>
      </output>
    </operation>
    <operation name=" ValidateCreditCardNumber ">
      <soap:operation soapAction=""/>
      <input>
        <soap:body use="literal"/>
      </input>
      <output>
        <soap:body use="literal"/>
      </output>
    </operation>
  </binding>
  <service name=" ValidatorService ">
    <port name=" CreditCardValidator " binding="tns:CreditCardValidatorBinding">
      <soap:address location=" http://localhost:8080/chapter14/ValidatorService "/>
    </port>
  </service>
</definitions>

The @WebService annotation renames the <portType name> and <port name> elements of the WSDL. @WebMethod renames the <operation name> element, @WebResult and @WebParam rename the <part name> which is part of the <message> element.

The XML schema also gets customized as both the request and the response, are defined in the <xs:complexType> element. Compared with the XSD defined in Listing 14-2 you can see that the one in Listing 14-18 is very different. That’s because the SOAP web service uses an RPC style (no request/response documents are defined, but instead it uses the types creditCard and boolean).

Listing 14-18.  The XML Schema After Customization

<xs:schema xmlns:xs=" http://www.w3.org/2001/XMLSchema "
           targetNamespace=" http://chapter14.javaee7.book.agoncal.org/ " version="1.0">
  <xs:complexType name=" creditCard ">
    <xs:sequence/>
    <xs:attribute name="number" type="xs:string" use="required"/>
    <xs:attribute name="expiry_date" type="xs:string" use="required"/>
    <xs:attribute name="control_number" type="xs:int" use="required"/>
    <xs:attribute name="type" type="xs:string" use="required"/>
  </xs:complexType>
</xs:schema>

The WSDL (Listing 14-17) and the XSD (Listing 14-18) are used to define the contract between the service consumer and provider. But at runtime they are not used anymore and only SOAP envelopes are exchanged between the consumer and the provider. Listing 14-19 shows the SOAP request that is sent to the web service. It defines the method to be called (ValidateCreditCard) and the parameters to pass to this method (Credit-Card).

Listing 14-19.  The SOAP Envelope for the ValidateCreditCard Request After Customization

<soap:Envelope xmlns:soap=" http://schemas.xmlsoap.org/soap/envelope/ "
               xmlns:cc=" http://chapter14.javaee7.book.agoncal.org/ ">
  <soap:Header/>
  <soap:Body>
    <cc: ValidateCreditCard >
      < Credit-Card number="123456789011" expiry_date="10/12" control_number="544"
                                                             type="Visa"/>
    </cc:ValidateCreditCard>
  </soap:Body>
</soap:Envelope>

Listing 14-20 shows the SOAP response that is sent back to the consumer. It indicates that the credit card sent in the request is valid (<IsValid>true</IsValid>).

Listing 14-20.  The SOAP Envelope for the ValidateCreditCard Response After Customization)

<soap:Envelope xmlns:soap=" http://schemas.xmlsoap.org/soap/envelope/ "
               xmlns:cc=" http://chapter14.javaee7.book.agoncal.org/ ">
  <soap:Body>
    <cc: ValidateCreditCardResponse >
      < IsValid >true</IsValid>
    </cc:ValidateCreditCardResponse>
  </soap:Body>
</soap:Envelope>

Handling Exceptions

So far everything is working well: the data exchanged between the consumer and the provider is valid, the web service doesn’t crash, and the network is reliable. But that’s not always the case. In Java, when something goes wrong, an exception is thrown and some other class inside the JVM has to handle it. With SOAP web services this mechanism cannot work as the consumer and the service may not be written in the same language and are separated by a network. So the idea is to use a SOAP fault in the SOAP message. The JAX-WS runtime automatically converts Java exceptions into SOAP fault messages that are returned to the client. This feature saves you a lot of time and energy by eliminating the need to write code that maps your service exceptions to SOAP faults.

If you look at the validate method of the CardValidator web service defined in Listing 14-5, you’ll notice that if the CreditCard parameter is null, the validation crashes with a NullPointerException. When this happens, the JAX-WS runtime catches the NullPointerException exception on the server, creates a SOAP Fault message (Listing 14-21) and sends it back to the consumer.

Listing 14-21.  A SOAP Fault is Sent in the SOAP Response

<soap:Envelope xmlns:soap=" http://schemas.xmlsoap.org/soap/envelope/ ">
  <soap:Body>
    <soap:Fault>
      <faultcode> soap:Server</faultcode>
      <faultstring> java.lang.NullPointerException</faultstring>
    </soap:Fault>
  </soap:Body>
</soap:Envelope>

As you can see in Listing 14-21, the JAX-WS runtime automatically sets the faultstring to the qualified name of the Java exception. The specification also provides a mechanism to differentiate between the types of fault using the faultcode element. In this case it is set to soap:Server indicating that the server is responsible for the fault (soap:Client is the other option).

One other option for returning a SOAP fault is to throw an application exception as shown in Listing 14-22. Here the web service throws a check exception (but same mechanism applies to uncheck exceptions) if the credit card number is odd. This application exception will automatically be translated into the appropriate soap:Fault message, wrapped in the SOAP body, and returned to the client.

Listing 14-22.  Validation Throws an Exception

@WebService
public class CardValidator throws CardValidatorException {
 
public boolean validate(CreditCard creditCard) {
    Character lastDigit = creditCard.getNumber().charAt(
                          creditCard.getNumber().length() - 1);
 
    if (Integer.parseInt(lastDigit.toString()) % 2 == 0) {
      return true;
    } else {
      throw new CardValidatorException("The credit card number is invalid");
    }
  }
}

The application exception can inherit from Exception, RuntimeException, or a SOAP web service exception such as javax.xml.ws.WebServiceException or one of its subclasses (e.g., javax.xml.ws.soap.SOAPFaultException). These exceptions can also be annotated with @WebFault to have a more explicit SOAP envelope as show in Listing 14-23.

Listing 14-23.  Exception with a WebFault Annotation

@WebFault(name = "CardValidationFault")
public class CardValidatorException extends Exception {
 
  public CardValidatorRTException() {
    super();
  }
 
  public CardValidatorRTException(String message) {
    super(message);
  }
}

When the SOAP web service throws the exception defined in Listing 14-23, the JAX-WS runtime generates the SOAP fault message defined in Listing 14-24.

Listing 14-24.  SOAP Fault in a SOAP Envelope

<soap:Envelope xmlns:soap=" http://schemas.xmlsoap.org/soap/envelope/ ">
  <soap:Body>
    <soap:Fault>
      <faultcode>soap:Server</faultcode>
      <faultstring>org.agoncal.book.javaee7.chapter14. CardValidatorException </faultstring>
      <detail>
        <ns2: CardValidationFault xmlns:ns2=" http://chapter14.javaee7.book.agoncal.org/ ">
          <message> The credit card number is invalid </message>
        </ns2:CardValidationFault>
      </detail>
    </soap:Fault>
  </soap:Body>
</soap:Envelope>

But if you want to produce a more accurate SOAP fault message with a different faultcode and so on, then you can use the javax.xml.soap.SOAPFactory API to create a javax.xml.soap.SOAPFault object (shown in Listing 14-25).

Listing 14-25.  Validation Uses a SOAPFactory to Create a SOAPFault

@WebService
public class CardValidator {
 
public boolean validate(CreditCard creditCard) {
    Character lastDigit = creditCard.getNumber().charAt(
                          creditCard.getNumber().length() - 1);
 
    if (Integer.parseInt(lastDigit.toString()) % 2 == 0) {
      return true;
    } else {
      SOAPFactory soapFactory = SOAPFactory.newInstance();
      SOAPFault fault = soapFactory.createFault("The credit card number is invalid", →new QName("ValidationFault"));
      throw new CardValidatorException(fault);
    }
  }
}

Listing 14-25 creates a SOAPFault with a reason text ("The credit card number is invalid") and a fault code (ValidationFault) that will produce the SOAP envelope shown in Listing 14-26.

Listing 14-26.  SOAP Fault in a SOAP Envelope

<soap:Envelope xmlns:soap=" http://schemas.xmlsoap.org/soap/envelope/ ">
  <soap:Body>
    <soap:Fault>
      <faultcode> ValidationFault </faultcode>
      <faultstring> The credit card number is invalid </faultstring>
    </soap:Fault
  </soap:Body>
</soap:Envelope>

Life Cycle and Callback

As you can see in Figure 14-4, SOAP web services also have a life cycle that resembles managed beans. It is the same life cycle found for components that do not hold any state: either they do not exist or they are ready to process a request. The container manages this life cycle.

9781430246268_Fig14-04.jpg

Figure 14-4. SOAP web service life cycle

Both the servlet and EJB endpoint support dependency injection (because they run in a container) and life-cycle methods such as @PostConstruct and @PreDestroy. The container calls the @PostConstruct callback method, if any, when it creates an instance of a web service, and calls the @PreDestroy callback when it destroys it. One difference between servlet and EJB endpoints is that EJBs can use interceptors (described in Chapter 2).

WebServiceContext

A SOAP web service has an environment context and can access it by injecting a reference of javax.xml.ws.WebServiceContext with a @Resource annotation. Within this context, the web service can obtain runtime information such as the endpoint implementation class, the message context, and security information relative to a request being served.

image Note  JAX-WS and WS-Metadata specifications haven’t been updated in Java EE 7 so, even if your application uses CDI, you cannot use the @Inject annotation to inject the WebServiceContext, you still need to use @Resource.

The code in Listing 14-27 uses the WebServiceContext to check if the caller has the Admin role to validate a credit card. Table 14-6 lists the methods defined in the javax.xml.ws.WebServiceContext interface.

Listing 14-27.  SOAP Web Service Using the WebServiceContext

@WebService
public class CardValidator {
 
  @Resource
  private WebServiceContext context;
 
  public boolean validate(CreditCard creditCard) {
 
    if (! context .isUserInRole("Admin"))
      throw new SecurityException("Only Admins can validate cards");
 
    // Business logic
    }
  }
}

Table 14-6. Methods of the WebServiceContext Interface

Method Description
getMessageContext Returns the MessageContext for the request being served at the time this method is called. It can be used to access the SOAP message headers, body, and so on.
getUserPrincipal Returns the Principal that identifies the sender of the request currently being serviced.
isUserInRole Returns a Boolean indicating whether the authenticated user is included in the specified logical role.
getEndpointReference Returns the EndpointReference associated with this endpoint.

Deployment Descriptor

Like most Java EE 7 technologies, SOAP web services allow you to define metadata using annotations (what I’ve been doing so far in all the examples) as well as XML. Located under the WEB-INF directory, the webservices.xml file overrides or augments the annotations. Like most deployment descriptors in Java EE 7, webservices.xml is optional since the annotations can be used to specify most of the information specified in this deployment descriptor. For example, Listing 14-28 shows how you can override the WSDL port (OverriddenPort) for the CardValidator web service.

Listing 14-28.  A webservices.xml Deployment Descriptor

<webservices xmlns=" http://java.sun.com/xml/ns/javaee " 
             xmlns:xsi=" http://www.w3.org/2001/XMLSchema-instance "
             xsi:schemaLocation=" http://java.sun.com/xml/ns/javaee
             http://java.sun.com/xml/ns/javaee/javaee_web_services_1_3.xsd "
             version="1.3">
 
  <webservice-description>
    <webservice-description-name>CardValidatorWS</webservice-description-name>
 
    <port-component>
      <port-component-name>CardValidator</port-component-name>
      <wsdl-port> OverriddenPort </wsdl-port>
      <service-endpoint-interface>
        org.agoncal.book.javaee7.chapter14.Validator
      </service-endpoint-interface>
      <service-impl-bean>
        <servlet-link>CardValidatorServlet</servlet-link>
      </service-impl-bean>
    </port-component>
  </webservice-description>
</webservices>

Packaging

SOAP web services may be packaged in a war or EJB jar file. Those packaged in a war file can use servlet or EJB Lite endpoints. SOAP web services packaged in an EJB jar file can only use Stateless/Singleton session beans. The developer is responsible for packaging:

  • The service implementation bean and its dependent classes
  • The service endpoint interfaces (optional)
  • The WSDL file either by containment or reference (not required when annotations are used because the WSDL can be automatically generated by the JAX-WS runtime)
  • Generated artifacts for the SOAP request and response (optional as they are automatically generated by the JAX-WS runtime)
  • An optional deployment descriptor

Publishing a SOAP Web Service

Once packaged in a war/jar file, publishing a SOAP web service is just a matter of deploying the archive into a Java EE container such as GlassFish or JBoss. That’s because JAX-WS is part of Java EE 7 and the runtime is bundled in the application server. But you can also publish a SOAP web service in a web container such as Tomcat or Jetty if you embed a JAX-WS implementation such as Metro, CXF, or Axis2.

But remember that JAX-WS also comes in Java SE and sometimes you do not require the full power of a servlet or EJB container (for example when testing your web service). In this case you can use the javax.xml.ws.Endpoint API to programmatically publish a SOAP web service. The method Endpoint.publish (see Listing 14-29) uses by default a lightweight HTTP server that is included in the Oracle’s JVM (defined in the package com.sun.net.httpserver).

Listing 14-29.  A SOAP Web Service Publishing Itself and Accepting Incoming Requests

@WebService
public class CardValidator {
 
  public boolean validate(CreditCard creditCard) {
    // Business logic
  }
 
  public static void main(String[] args) {
    Endpoint.publish("http://localhost:8080/cardValidator", new CardValidator());
  }
}

In Listing 14-29 the publish method is used to publish the CardValidator SOAP web service, at which point it starts accepting incoming requests at the address http://localhost:8080/cardValidator. You can then invoke methods on your web service. There is also a stop method that can be used to stop accepting incoming requests and to take the endpoint down (as you’ll later see when developing an integration test).

Invoking SOAP Web Services

So far you’ve seen how to write, package, and publish a SOAP web service. Now let’s look at how to invoke such a service. With the WSDL and some tools to generate the Java client stubs (or proxies), you can easily invoke a web service without caring about all the plumbing around HTTP or SOAP. Invoking a web service is similar to invoking a distributed object with RMI. Like RMI, JAX-WS enables the programmer to use a local method call to invoke a distributed service. The difference is that, on the remote host, the web service can be written in another programming language (note that you can also invoke non-Java code using RMI-IIOP). The WSDL is the standard contract between the consumer and the service. Metro provides a WSDL-to-Java utility tool (wsimport) that generates Java interfaces and classes from a WSDL. Such proxies give you a Java representation of a web service endpoint (servlet or EJB). This proxy then routes the local Java call to the remote web service using HTTP.

When a method on this proxy is invoked (see Figure 14-5), it converts the parameters of the method into a SOAP message (the request) and sends it to the web service endpoint. To obtain the result, the SOAP response is converted back into an instance of the returned type. You don’t need to understand the internal work of the proxy nor even look at the code. Before compiling your client consumer, you need to generate the SEI to get the proxy class to call it in your code.

9781430246268_Fig14-05.jpg

Figure 14-5. A consumer invoking a web service through a proxy

When developing SOAP consumers using a contract first, the client has to get the WSDL, generate the needed artifacts, invoke the CardValidator web service to validate a credit card (the validate SOAP message), and receive a response (the validateResponse SOAP message).

image Note  The wsimport and wsgen tools are shipped with JDK 1.7 as well as GlassFish or Metro. wsimport takes a WSDL as an input and generates JAX-WS artifacts, such as an SEI. wsgen reads a web service endpoint class and generates the WSDL. You can access these tools directly with a Java SE 7 installation, or through the GlassFish command-line interface (CLI), an Ant task, or a Maven plug-in.

Anatomy of a SOAP Consumer

Because JAX-WS is available in Java SE, a SOAP web service consumer can be any kind of Java code from a main class running on the JVM to any Java EE component running in a container (Web, EJB or application client container). If it runs in a container the consumer can get an instance of the proxy either through injection or by programmatically creating it. To inject a web service, you need to use the @javax.xml.ws.WebServiceRef annotation or a CDI producer.

Invoking Programmatically

If your consumer is running outside a container, you need to programmatically invoke your SOAP web service. As you can see in Listing 14-30, the CardValidator web service is not directly invoked. The consumer uses an instance of CardValidatorService (which has been generated from the WSDL thanks to wsimport) using the new keyword. It then has to get the proxy CardValidator class (getCardValidatorPort()) to invoke business methods locally. A local call is made on the validate() method of the proxy, which in turn will invoke the remote web service, create the SOAP request, marshal the credit card messages, and so on. The proxy finds the target service because the default endpoint URL is embedded in the WSDL file, and is subsequently integrated into the proxy implementation.

Listing 14-30.  A Java SE Class Invoking a SOAP Web Service Programmatically

public class WebServiceConsumer {
 
  public static void main(String[] args) {
 
    CreditCard creditCard = new CreditCard();
    creditCard.setNumber("12341234");
    creditCard.setExpiryDate("10/12");
    creditCard.setType("VISA");
    creditCard.setControlNumber(1234);
 
    CardValidator cardValidator = new CardValidatorService().getCardValidatorPort();
    cardValidator.validate(creditCard);
  }
}

Although this code is straightforward, there’s a lot of magic happening behind the scenes. Several artifacts have been generated from a WSDL file to make this work. They contain all the information required to connect to the URL where the web service is located, marshal the CreditCard object into XML, invoke the web service through a SOAP request, and obtain a result from the SOAP response.

Invoking with Injection

On the other hand, if your consumer runs in a container you can use injection to get a reference to the SOAP web service client proxy. Listing 14-31 shows a simple main Java class running in an application client container (ACC) and using the @WebServiceRef annotation. It follows the @Resource or @EJB annotation pattern shown in previous chapters, but for web services. When this annotation is applied on an attribute (or a getter method), the container will inject an instance of the web service client proxy when the application is initialized.

Listing 14-31.  A Java SE Class Running in ACC and Using Injection

public class WebServiceConsumer {
 
  @WebServiceRef
  private static CardValidatorService cardValidatorService;
 
  public static void main(String[] args) {
 
    CreditCard creditCard = new CreditCard();
    creditCard.setNumber("12341234");
    creditCard.setExpiryDate("10/12");
    creditCard.setType("VISA");
    creditCard.setControlNumber(1234);
 
    CardValidator cardValidator = cardValidatorService .getCardValidatorPort();
    cardValidator.validate(creditCard);
  }
}

Note that the code in Listing 14-31 would be very similar if your consumer is an EJB, a Servlet or a JSF backing bean. To get injection you need to run your code in a container otherwise the @WebServiceRef annotation cannot be used.

Invoking with CDI

The JAX-WS specification hasn’t been updated in Java EE 7 and therefore does not embrace all the CDI goodies. For example, you cannot directly inject a reference of the web service proxy using the @Inject annotation. But as seen in Chapter 2, you can use the CDI producers to produce such a reference (see Listing 14-32).

Listing 14-32.  Utility Class Producing a Web Service Reference

public class WebServiceProducer {
 
  @Produces
  @WebServiceRef
  private CardValidatorService cardValidatorService;
 
}

Thanks to the WebServiceProducer utility class in Listing 14-32, any EJB or Servlet can now inject the produced CardValidatorService with @Inject and invoke a business method on it (Listing 14-33). With this mechanism you can even use the CDI alternatives to route your method invocation to an alternative SOAP web service as explained in Chapter 2.

Listing 14-33.  An EJB Using a CDI Producer to Inject a Web Service Reference

@Stateless
public class EJBConsumerWithCDI {
 
  @Inject
  private CardValidatorService cardValidatorService;
 
  public boolean validate(CreditCard creditCard) {
 
    CardValidator cardValidator = cardValidatorService .getCardValidatorPort();
    return cardValidator.validate(creditCard);
  }
}

Putting It All Together

Now let’s put all these concepts together and write a consumer, a SOAP web service, test it, deploy it in GlassFish and invoke it. We will use JAXB and JAX-WS annotations, as well as generate a service endpoint interface with the wsimport Maven goal. To write a web service, several steps are needed. I’ll demonstrate these steps by revisiting the CardValidator web service.

The CardValidator SOAP web service checks that a credit card is valid. It has one method that takes a CreditCard object as a parameter, applies some algorithm, and returns true if the card is valid, false if not. This business logic will be unit tested but also tested in integration (with an embedded HTTP server). Once this web service is tested and deployed on GlassFish, wsimport is used to generate all the needed artifacts for the consumer. The consumer can then invoke the web service to validate credit cards.

As shown in Figure 14-6, you will use two Maven projects: one to package the web service into a war file (chapter14-service-1.0.war) and another to package the consumer into a jar file (chapter14-consumer-1.0.jar). These archives will have code developed by you but also generated code on both the consumer (generated by wsimport) and the service (generated by the JAX-WS runtime).

9781430246268_Fig14-06.jpg

Figure 14-6. Putting It All Together

Writing the CreditCard Class

The CreditCard class, shown in Listing 14-34, is the POJO used as a parameter of the web service validate() method. SOAP web services exchange XML messages, not Java objects. The CreditCard class is annotated with several JAXB annotations (e.g., @XmlRootElement) allowing it to be marshaled into XML so that it can be sent in a SOAP request. The CreditCard object has some basic required attributes such as the credit card number, the expiry date (formatted as MM/YY), a credit card type (Visa, MasterCard, American Express, etc.), and a control number.

Listing 14-34.  The CreditCard Class with JAXB Annotations

@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public class CreditCard {
 
  @XmlAttribute(required = true)
  private String number;
  @XmlAttribute(name = "expiry_date", required = true)
  private String expiryDate;
  @XmlAttribute(name = "control_number", required = true)
  private Integer controlNumber;
  @XmlAttribute(required = true)
  private String type;
 
  // Constructors, getters, setters
}

Writing the CardValidator SOAP Web Service

The CardValidator (see Listing 14-36) implements the Validator interface (see Listing 14-35) and both are annotated with the JAX-WS @WebService annotation. CardValidator is just a POJO so it is considered a Servlet endpoint and not an EJB endpoint (as it doesn’t have the @Stateless or @Singleton annotation). It has a validate() method that takes a CreditCard object as a parameter. The algorithm to check whether the card is valid or not is based on the card number: even numbers are valid, odd numbers are not. The method returns a boolean. Both the class and the interface will be packaged in the war file (chapter14-service-1.0.war).

Listing 14-35.  The Validator Web Service Interface

@WebService
public interface Validator {
 
  public boolean validate(CreditCard creditCard);
}

Listing 14-36.  The CardValidator Web Service Bean

@WebService(endpointInterface = "org.agoncal.book.javaee7.chapter14.Validator")
public class CardValidator implements Validator {
 
  public boolean validate(CreditCard creditCard) {
 
    Character lastDigit = creditCard.getNumber().charAt(
                          creditCard.getNumber().length() - 1);
 
    if (Integer.parseInt(lastDigit.toString()) % 2 == 0) {
      return true;
    } else {
      return false;
    }
  }
}

For simplicity, no extra Java-to-WSDL mapping is used, so there are no @WebMethod, @WebResult, or @WebParam annotations, allowing you to see how easy it is to write a web service using all the default mapping rules.

Writing the CardValidatorTest Unit Test

Unit testing a SOAP web service can be pretty simple if you only test the business logic in isolation (i.e., with no container services such as injection, security, and so on). Unit testing the CardValidator POJO means testing the credit card validation algorithm. As shown in Listing 14-37, the test case creates an instance of the CardValidator POJO, a CreditCard with an even number, validates it, and checks that the card is valid. Then it changes the number to odd, validates the card again, and checks it is invalid.

Listing 14-37.  The CardValidatorTest Unit Test

public class CardValidatorTest {
 
  @Test
  public void shouldCheckCreditCardValidity() {
 
    CardValidator cardValidator = new CardValidator();
 
    CreditCard creditCard = new CreditCard("12341234", "10/10", 1234, "VISA");
    assertTrue("Credit card should be valid", cardValidator.validate(creditCard) );
 
    creditCard.setNumber("12341233");
    assertFalse("Credit card should not be valid", cardValidator.validate(creditCard) );
  }
}

But what do you do if your SOAP web service needs injection, for example? If you review the code in Listing 14-27, you’ll notice that the container injects the WebServiceContext so the bean can check if the user is in role Admin or not. Without the container the WebServiceContext will be null and the test will fail. To solve this problem you can either mock the container’s services (e.g., mock the WebServiceContext injection) or test your SOAP web service in integration mode, that is, inside the container.

Writing the CardValidatorIT Integration Test

Integration testing a SOAP web service means deploying it to a web server, accessing its WSDL, having full container services, and so on. This used to be a tedious task, as you had to start a runtime container, package your application, and deploy and test it. But since Java SE 6, you can now use an embedded web server thanks to the javax.xml.ws.Endpoint API.

Listing 14-38 shows a JUnit test class that publishes our CardValidator SOAP web service at a given URL (http://localhost:8080/cardValidator). Once published (endpoint.isPublished()), it can access the generated WSDL at URL http://localhost:8080/cardValidator?wsdl and use it to create a Service (Service.create(wsdlDocumentLocation, serviceQN)). The service.getPort method returns the client proxy to the SOAP web service where you can then invoke a method (cardValidator.validate(creditCard)). Before finishing the test you need to undeploy the web service (endpoint.stop()).

Listing 14-38.  The CardValidatorIT Integration Test

public class CardValidatorIT {
 
  @Test
  public void shouldCheckCreditCardValidity() throws MalformedURLException {
 
    // Publishes the SOAP Web Service
    Endpoint endpoint = Endpoint.publish (" http://localhost:8080/cardValidator ",
                                          new CardValidator());
    assertTrue( endpoint.isPublished() );
    assertEquals(" http://schemas.xmlsoap.org/wsdl/soap/http ",
                  endpoint.getBinding().getBindingID());
 
    // Needed properties to access the web service
    URL wsdlDocumentLocation = new URL(" http://localhost:8080/cardValidator?wsdl ");
    String namespaceURI = " http://chapter14.javaee7.book.agoncal.org/ ";
    String servicePart = "CardValidatorService";
    String portName = "CardValidatorPort";
    QName serviceQN = new QName(namespaceURI, servicePart);
    QName portQN = new QName(namespaceURI, portName);
 
    // Creates a service instance
    Service service = Service.create(wsdlDocumentLocation, serviceQN);
    Validator cardValidator = service.getPort(portQN, Validator.class);
 
    // Invokes the web service
    CreditCard creditCard = new CreditCard("12341234", "10/10", 1234, "VISA");
    assertTrue("Credit card should be valid", cardValidator.validate(creditCard) );
 
    creditCard.setNumber("12341233");
    assertFalse("Credit card should not be valid", cardValidator.validate(creditCard) );
 
    // Unpublishes the SOAP Web Service
    endpoint.stop();
    assertFalse(endpoint.isPublished());
  }
}

Now that the CardValidator SOAP web service is developed and tested, we can package it and deploy it to GlassFish.

Compiling, Testing and Packaging with Maven

The CardValidator web service (interface in Listing 14-35 and implementation in Listing 14-36) needs to be compiled, tested and packaged in a war file (<packaging> war </packaging>). The pom.xml file (see Listing 14-39) declares the glassfish-embedded-all dependency, which is a convenient way to have access to all the Java EE 7 specifications including JAX-WS and Web Services Metadata. In fact, glassfish-embedded-all contains the full GlassFish 4.0 implementation in a single JAR, useful for compilation and embedded use. Setting the version to 1.7 in the maven-compiler-plugin explicitly specifies that you want to use Java SE 7 (<source> 1.7 </source>).

Listing 14-39.  The pom.xml File to Compile, Test and Package the Web Service

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns=" http://maven.apache.org/POM/4.0.0 "
         xmlns:xsi=" http://www.w3.org/2001/XMLSchema-instance "
         xsi:schemaLocation=" http://maven.apache.org/POM/4.0.0
         http://maven.apache.org/xsd/maven-4.0.0.xsd ">
  <modelVersion>4.0.0</modelVersion>
 
  <parent>
    <artifactId>chapter14</artifactId>
    <groupId>org.agoncal.book.javaee7</groupId>
    <version>1.0</version>
  </parent>
 
  <groupId>org.agoncal.book.javaee7.chapter14</groupId>
  <artifactId>chapter14-service</artifactId>
  <version>1.0</version>
  <packaging>war</packaging>
 
  <dependencies>
    <dependency>
      <groupId>org.glassfish.main.extras</groupId>
      <artifactId> glassfish-embedded-all </artifactId>
      <version>4.0</version>
      <scope>provided</scope>
    </dependency>
 
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.11</version>
      <scope>test</scope>
    </dependency>
  </dependencies>
 
  <build>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-compiler-plugin</artifactId>
        <version>2.5.1</version>
        <configuration>
          <source> 1.7 </source>
          <target>1.7</target>
        </configuration>
      </plugin>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-war-plugin</artifactId>
        <version>2.2</version>
        <configuration>
          <failOnMissingWebXml>false</failOnMissingWebXml>
        </configuration>
      </plugin>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId> maven-failsafe-plugin </artifactId>
        <version>2.12.4</version>
        <executions>
          <execution>
            <id>integration-test</id>
            <goals>
              <goal> integration-test </goal>
              <goal>verify</goal>
            </goals>
          </execution>
        </executions>
      </plugin>
    </plugins>
  </build>
</project>

With Java EE 7, deployment descriptors are optional; therefore, you don’t need a web.xml or webservices.xml file. However, as Maven still obliges you to add a web.xml file into a war by default, you need to change the failOnMissingWebXml attribute of the maven-war-plugin to false; otherwise, Maven will fail the build.

To compile and package the web service, open a command-line in the root directory containing the pom.xml file and enter the following Maven command:

$ mvn package

Go to the target directory, where you should see a file named chapter14-service-1.0.war. If you open it, you will see that Validator.class, CardValidator.class and CreditCard.class are under the WEB-INFclasses directory. The war file doesn’t contain anything else, not even a WSDL file (which will be generated by the JAX-WS runtime).

You can execute the unit test (Listing 14-37) and integration test (Listing 14-38) with the Maven Surefire and Failsafe plugin by enter the following Maven command:

$ mvn integration-test

Now that the CardValidator SOAP web service is developed and tested we can deploy it to GlassFish.

Deploying on GlassFish

Once the web service is packaged in the war file, it needs to be deployed into GlassFish. This can be done using the asadmin command line. Open a console and go to the target directory of your project where the chapter14-service-1.0.war file is located, make sure GlassFish is running, and enter the following command:

$ asadmin deploy chapter14-service-1.0.war
Application deployed with name chapter14-service-1.0.
Command deploy executed successfully.

If the deployment is successful, the following command should return the name of the deployed components and their types:

$ asadmin list-components
chapter14-service-1.0 < webservices , web>

It’s interesting to note that GlassFish recognizes the web module (the war file could have contained web pages, servlets, and so on) as being a web service. If you go to the GlassFish administration console shown in Figure 14-7 (http://localhost:4848/), you will see that chapter14-service-1.0 is deployed under the Applications menu.

9781430246268_Fig14-07.jpg

Figure 14-7. Web services deployed in the GlassFish administration console

On this page, if you click the WSDL link, it will open the browser at the following URL and show the generated WSDL of the CardValidator SOAP web service (see Figure 14-8):

http://localhost:8080/chapter14-service-1.0/CardValidatorService?wsdl

9781430246268_Fig14-08.jpg

Figure 14-8. The WSDL has been generated by Metro

It is interesting to note that you didn’t create this WSDL or deploy it in the war file. The Metro stack automatically generates the WSDL based on the annotations contained in the web service (as well as the Validate SOAP request and ValidateResponse SOAP response shown in Figure 14-6). That’s the WSDL we’ll use to generate the client proxy so that the consumer can remotely access the web service.

Writing the WebServiceConsumer Class

The web service is now deployed, GlassFish is up and running, and you know which URL the WSDL has been generated to. Thanks to this WSDL, the consumer will be able to generate the necessary artifacts to invoke the web service with the wsimport tool. First, let’s write the consumer code, as shown in Listing 14-40.

Listing 14-40.  The WebServiceConsumer Class Invokes the Web Service Using Injection

public class WebServiceConsumer {
 
  @WebServiceRef
  private static CardValidatorService cardValidatorService;
 
  public static void main(String[] args) {
 
    CreditCard creditCard = new CreditCard();
    creditCard.setNumber("12341234");
    creditCard.setExpiryDate("10/12");
    creditCard.setType("VISA");
    creditCard.setControlNumber(1234);
 
    CardValidator cardValidator = cardValidatorService .getCardValidatorPort();
 
    System.out.println(cardValidator.validate(creditCard));
  }
}

This WebServiceConsumer class creates an instance of the CreditCard object, sets some data, injects a reference to the web service, invokes the validate() method, and displays the result (true or false depending on whether the credit card is valid). The interesting thing is that the consumer does not have any of these classes. The CardValidatorService, CardValidator, and CreditCard are totally unknown to the consumer. This code will not compile until all of these classes are generated.

Generating Consumer’s Artifacts and Packaging with Maven

Before compiling the WebServiceConsumer class, you need to generate the artifacts with the wsimport tool. The good news is that Maven has a JAX-WS plugin with a wsimport goal, and this goal is executed automatically during the generate-sources life-cycle phase. As described in Appendix A, Maven uses a rich life cycle to build applications. The generate-sources phase is used to generate code and is executed before compilation. The only thing to do is tell this wsimport goal where to find the WSDL document. You have this information because you’ve deployed the web service into GlassFish, and you have displayed the content of the WSDL. Its location is:

http://localhost:8080/chapter14-service-1.0/CardValidatorService?wsdl

The pom.xml file in Listing 14-41 specifies the needed dependencies, the glassfish-embedded-all version 4.0, as well as the JDK version (1.7). The WebServiceConsumer class is packaged in the chapter14-consumer-1.0.jar file, and, like every jar file, it has a META-INFMANIFEST.MF file. This file can be used to define some metadata about the jar, and that’s what you do when you use the maven-jar-plugin. You add a Main-Class element to the MANIFEST pointing to the WebServiceConsumer class. This will allow execution of the jar file (with the java –jar command, for example).

Listing 14-41.  The pom.xml File Generates and Packages the Consumer’s Artifacts

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns=" http://maven.apache.org/POM/4.0.0 "
         xmlns:xsi=" http://www.w3.org/2001/XMLSchema-instance "
         xsi:schemaLocation=" http://maven.apache.org/POM/4.0.0
         http://maven.apache.org/xsd/maven-4.0.0.xsd ">
  <modelVersion>4.0.0</modelVersion>
 
  <parent>
    <artifactId>chapter14</artifactId>
    <groupId>org.agoncal.book.javaee7</groupId>
    <version>1.0</version>
  </parent>
 
  <groupId>org.agoncal.book.javaee7.chapter14</groupId>
  <artifactId>chapter14-consumer</artifactId>
  <packaging> jar </packaging>
  <version>1.0</version>
 
  <dependencies>
    <dependency>
      <groupId>org.glassfish.main.extras</groupId>
      <artifactId> glassfish-embedded-all </artifactId>
      <version>4.0</version>
      <scope>provided</scope>
    </dependency>
  </dependencies>
 
  <build>
 
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-jar-plugin</artifactId>
        <version>2.4</version>
        <configuration>
          <archive>
            <manifest>
              <mainClass> org.agoncal.book.javaee7.chapter14.WebServiceConsumer </mainClass>
            </manifest>
          </archive>
        </configuration>
      </plugin>
 
      <plugin>
        <groupId>org.jvnet.jax-ws-commons</groupId>
        <artifactId>jaxws-maven-plugin</artifactId>
        <version>2.2</version>
        <executions>
          <execution>
            <goals>
              <goal> wsimport </goal>
            </goals>
            <configuration>
              <wsdlUrls>
                <wsdlUrl>
                  http://localhost:8080/chapter14-service-1.0/CardValidatorService?wsdl
                </wsdlUrl>
              </wsdlUrls>
              <keep>true</keep>
            </configuration>
          </execution>
        </executions>
      </plugin>
 
      <plugin>
        <artifactId>maven-compiler-plugin</artifactId>
        <version>2.5.1</version>
        <configuration>
          <source> 1.7 </source>
          <target>1.7</target>
        </configuration>
      </plugin>
 
    </plugins>
  </build>
</project>

To have a better understanding of what happens behind the scenes, first generate the artifacts by entering the following Maven command:

$ mvn generate-sources

This command executes the Maven generate-sources life-cycle phase, and thus the wsimport goal that is defined with it. wsimport connects to the web service WSDL URL, downloads it, and generates all the artifacts. Here is the output of the Maven command:

[INFO] --- jaxws-maven-plugin:2.2:wsimport (default) @ chapter14-consumer ---
[INFO] Processing: http://localhost:8080/chapter14-service-1.0/CardValidatorService?wsdl
[INFO] jaxws:wsimport args: [-keep, chapter14-consumer/ target/generated-sources/wsimport ,
 -encoding, UTF-8, -Xnocompile,
 http://localhost:8080/chapter14-service-1.0/CardValidatorService?wsdl ]
 
parsing WSDL...
Generating code...
 
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------

If you are curious, you can go to the target/generated-sources/wsimport directory and check the classes that have been generated. You will find the CardValidator, CardValidatorService, and CreditCard classes, of course, but also one class for the SOAP request (Validate) and another one for the response (ValidateResponse). These classes are full of JAXB and JAX-WS annotations as they marshal the CreditCard object and connect to the remote web service. You don’t have to worry about the generated code. The jar file can be compiled and packaged:

$ mvn package

This creates the chapter14-consumer-1.0.jar file, which contains the WebServiceConsumer class you wrote plus all the generated classes (see Figure 14-6 to have a better overview of all the generated classes). This jar is self-contained and can now be executed to invoke the web service.

Running the WebServiceConsumer Class

Remember that the WebServiceConsumer class uses the @WebServiceRef annotation to get injected a reference to the web service endpoint interface. This means that the code needs to be executed in the application client container (ACC). Also remember that the chapter14-consumer-1.0.jar file is executable, as you’ve added a Main-Class element to the MANIFEST.MF file. The only thing that you have to do is to invoke the appclient utility that comes with GlassFish and pass it the jar file as follows:

$ appclient -client chapter14-consumer-1.0.jar

This will invoke the web service through HTTP and get a response back telling you whether the credit card is valid or not.

Summary

Companies need to exchange data in a secure, reliable, transactional, and interoperable manner. That’s why distribution history has seen technologies such as CORBA, DCOM, RPC, or RMI. With the wide adoption of HTTP, several standard bodies (W3C, OASIS) have developed SOAP web services as a loosely (XML) standard way for businesses to communicate over a network. Today, many organizations use SOAP web services heavily to integrate applications run by various external organizations (Internet) or internal departments (intranet).

This chapter has introduced some relevant SOAP web service standards (WSDL, SOAP, etc.) and has focused on the Java EE specifications that cover these standards (JAX-WS, JAXB, WS-Metadata, etc.). These specifications are vital if you want to hide network complexity and simplify development. Leaving these specifications behind, JAX-WS follows a simple development model and uses only a small set of annotations to adjust the Java-to-WSDL mapping. It is then easy to write a web service (servlet or EJB endpoint) or a consumer as simple annotated POJOs with optional deployment descriptors.

This chapter ended with an example of how to write a web service, compile, test (unit testing and integration testing), and package it with Maven. Thanks to the WSDL and some tooling, you can generate the consumer’s artifacts to remotely invoke the SOAP web service.

Amazon, eBay, Google, Yahoo!, and many others have provided SOAP web services for the use of their customers. But in recent years they have all shifted to RESTful web service (described in the next chapter), mainly for performance reasons.

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

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