Web services in Java SE, Part 2: Creating SOAP web services

Learn how to create SOAP-based Web services

1 2 3 Page 2
Page 2 of 3
  • types presents user-defined data types (used in the context of message elements) under a data type system. Although any type definition language can be used, XML Schema is mandated by the WS-I in Basic Profile 1.0. The types element can contain zero or more schema elements; this example has a single schema element, which imports an external schema. Furthermore, types is optional. It's not present when the service uses only XML Schema builtin simple types, such as strings and integers.
  • message defines a one-way request or response message (conceptually a function invocation or an invocation response) that may consist of one or more parts (conceptually equivalent to function parameters or return values). Each part is described by a part element whose name attribute identifies a parameter/return value element. The element attribute identifies another element (defined elsewhere) whose value is passed to this parameter or which provides the response value. Zero or more part elements, and zero or more message elements may be specified.
  • portType describes a Web service interface via its operations. Each operation element contains input and/or output elements based on the Message-Exchange Pattern. Listing 4 includes both elements. (A fault element for communicating error information can be specified when there is an output element.) The wsam:Action attribute is used with message routing in the context of WS-Addressing. The message attribute identifies the message element that describes the message via its name attribute (and also provides the part elements describing parameters and return value). operation elements are optional; at least one portType element must be specified.
  • binding provides details on how a portType operation (such as c2f or f2c) is transmitted over the wire. This element's type attribute identifies the portType element defined earlier in the document. The nested soap:binding element indicates that a SOAP 1.1 binding is being used. Its transport attribute's URI value identifies HTTP as the transport protocol (SOAP over HTTP), and its style attribute identifies document as the default service style. Each operation element consists of soap:operation, input, and output elements. The soap:operation element is a SOAP extension element that provides extra binding information at the operation level. Servers (such as firewalls) can use the SOAPAction attribute's URI value (when present) to filter SOAP request messages sent via HTTP. The input and output elements contain soap:body elements whose use attributes indicate how message parts appear inside of SOAP's Body element –- I present an overview of SOAP later in this series. The literal value means that these parts appear literally instead of being encoded. Multiple binding elements can be specified.
  • service defines a collection of endpoints in terms of nested port elements that expose bindings –- a port element's binding attribute identifies a binding element. Furthermore, the port element identifies the service's address; because we are dealing with a SOAP service, port contains a soap:address element whose location attribute specifies this address.

The types, message, and portType elements are abstract definitions of the Web service's interface. They form the interface between the Web service and an application. The binding and service elements provide concrete details on how this interface is mapped to messages transmitted over the wire. JAX-WS handles these details on behalf of the application.

Understanding style and use

The soap:binding element's style attribute affects how a SOAP message's Body element is built by indicating whether the operation is document-oriented (messages contain documents) -- the value is document -- or Remote Procedure Call (RPC)-oriented (messages contain parameters and return values) -- the value is rpc. I discuss SOAP message architecture later in this series.

The soap:body element's use attribute indicates whether the WSDL document's message element's part child elements define the concrete schema of the message -- the value is literal -- or are encoded via certain encoding rules -- the value is encoded.

When use is set to literal, each part element references a concrete schema definition using either the element or type attribute. For element, the referenced element will appear directly under the SOAP message's Body element (for document style bindings) or under an accessor element named after the message part (for rpc style bindings). For type, the referenced type becomes the schema type of the enclosing element (Body for document style or part accessor element for rpc style).

When use is set to encoded, each part element references an abstract type using the type attribute. These abstract types are used to produce a concrete message by applying an encoding specified by the SOAP message's encodingStyle attribute.

To learn more about the style and use attributes, check out Which style of WSDL should I use?.

Exploring the XML schema document

The types element's schema element identifies the location of the schema where each operation's return and parameter types are described. The xsd:import tag's schemaLocation attribute identifies this location as http://localhost:9901/UC?xsd=1. When you point your Google Chrome browser to this location, you observe Listing 5 (Mozilla Firefox hides the xmlns attributes).

Listing 5. The WSDL document's referenced XML Schema document

<xs:schema xmlns:tns="http://uc.javajeff.ca/" 
           xmlns:xs="http://www.w3.org/2001/XMLSchema" 
           version="1.0" targetNamespace="http://uc.javajeff.ca/">
  <xs:element name="c2f" type="tns:c2f"/>
  <xs:element name="c2fResponse" type="tns:c2fResponse"/>
  <xs:element name="cm2in" type="tns:cm2in"/>
  <xs:element name="cm2inResponse" type="tns:cm2inResponse"/>
  <xs:element name="f2c" type="tns:f2c"/>
  <xs:element name="f2cResponse" type="tns:f2cResponse"/>
  <xs:element name="in2cm" type="tns:in2cm"/>
  <xs:element name="in2cmResponse" type="tns:in2cmResponse"/>
  <xs:complexType name="cm2in">
    <xs:sequence>
      <xs:element name="arg0" type="xs:double"/>
    </xs:sequence>
  </xs:complexType>
  <xs:complexType name="cm2inResponse">
    <xs:sequence>
      <xs:element name="return" type="xs:double"/>
    </xs:sequence>
  </xs:complexType>
  <xs:complexType name="in2cm">
    <xs:sequence>
      <xs:element name="arg0" type="xs:double"/>
    </xs:sequence>
  </xs:complexType>
  <xs:complexType name="in2cmResponse">
    <xs:sequence>
      <xs:element name="return" type="xs:double"/>
    </xs:sequence>
  </xs:complexType>
  <xs:complexType name="c2f">
    <xs:sequence>
      <xs:element name="arg0" type="xs:double"/>
    </xs:sequence>
  </xs:complexType>
  <xs:complexType name="c2fResponse">
    <xs:sequence>
      <xs:element name="return" type="xs:double"/>
    </xs:sequence>
  </xs:complexType>
  <xs:complexType name="f2c">
    <xs:sequence>
      <xs:element name="arg0" type="xs:double"/>
    </xs:sequence>
  </xs:complexType>
  <xs:complexType name="f2cResponse">
    <xs:sequence>
      <xs:element name="return" type="xs:double"/>
    </xs:sequence>
  </xs:complexType>
</xs:schema>

For brevity, I won't delve into Listing 5. Instead, I refer you to Chapter 1 in my Java XML and JSON book (see the advertisement at the end of this post). This chapter explores XML Schema and makes sense of the various elements in the listing.

Accessing the web service from a simple client

I've created a simple UCClient Java application that demonstrates the UC Web service. Listing 6 presents this application's source code.

Listing 6. A client for accessing the UC Web service

import java.net.URL;

import javax.xml.namespace.QName;

import javax.xml.ws.Service;

import ca.javajeff.uc.UC;

public class UCClient
{
   public static void main(String[] args) throws Exception
   {
      URL url = new URL("http://localhost:9901/UC?wsdl");
      QName qname = new QName("http://uc.javajeff.ca/",
                              "UCImplService");
      Service service = Service.create(url, qname);
      qname = new QName("http://uc.javajeff.ca/", "UCImplPort");
      UC uc = service.getPort(qname, UC.class);
//      UC uc = service.getPort(UC.class);
      System.out.printf("DC to DF: 37 DC = %f DF%n", uc.c2f(37.0));
      System.out.printf("CM to IN: 10 CM = %f IN%n", uc.cm2in(10));
      System.out.printf("DF to DC: 212 DF = %f DC%n", uc.f2c(212.0));
      System.out.printf("IN to CM: 10 IN = %f CM%n", uc.in2cm(10));
   }
}

UCClient first creates a java.net.URL instance that identifies the Web service's WSDL file. It then creates a javax.xml.namespace.QName instance that identifies the endpoint's qualified service name (see Figure 1). These instances are passed to the javax.xml.ws.Service class's Service create(URL wsdlDocumentLocation, QName serviceName) class method to return a Service instance that provides a client view of a Web service.

Service's T getPort(QName portName, Class<T> serviceEndpointInterface) method is then called on the Service instance to return a proxy for communicating with the Web service via its endpoint. The qualified name passed to portName identifies the endpoint's qualified port name (see Figure 1), which identifies the Web service interface whose operations are to be accessed -- there's only one interface in this example. The java.lang.Class instance passed to serviceEndpointInterface identifies the UC SEI. This method returns a proxy object whose class implements UC, or throws javax.xml.ws.WebServiceException when something goes wrong (such as when not specifying endpointInterface in the UCImpl SIB's @WebService annotation, and calling Service's T getPort(Class<T> serviceEndpointInterface) method, which uses endpointinterface to access the SEI).

Assuming that getPort() succeeds, the returned object is used to invoke the c2f(), cm2in(), f2c(), and in2cm() methods with arguments representing body temperature in degrees Celsius, a specific number of centimeters, the boiling point of water in degrees Fahrenheit, and a specfic number of inches, respectively.

Building and running the web service client

Assuming that the current directory contains UCClient.java and that the parent directory includes a uc directory containing UC's files, compile Listing 6 in Java 9 via the following command:

javac -cp ..\uc --add-modules java.xml.ws UCClient.java

Assuming successful compilation, execute the following command to run the client against the UC Web service, which must be running:

java -cp ..\uc;. --add-modules java.xml.ws UCClient

You should observe the following output:

DC to DF: 37 DC = 98.600000 DF
CM to IN: 10 CM = 3.937008 IN
DF to DC: 212 DF = 100.000000 DC
IN to CM: 10 IN = 25.400000 CM

Working with wsimport

Because the WSDL document in Listing 4 contains enough information to let clients communicate with the Web service, you can alternatively use the wsimport tool to generate client-support code from this document, to facilitate creating the client. In the context of UC, you would use this tool as follows:

wsimport -keep –p client http://localhost:9901/UC?wsdl

wsimport outputs parsing WSDL..., Generating code..., and Compiling code... messages; and generates the class files that a client needs to access this Web service. The -keep option causes wsimport to save the source code for these class files as well, which helps us learn how clients access the Web service, and makes it possible to add client-side handlers for intercepting messages (discussed later in this series).

The -p option identifies the package directory in which to store the generated source and/or class files. You can specify any meaningful name (such as client) and wsimport will create a package directory with this name, and store the package directory structure underneath.

Along with class files, wsimport stores UC.java, UCImplService.java, and other source files in the client directory. The former source file's Java interface declares the same methods as Listing 1's UC SEI interface, but with c2F, cm2In, f2C, and in2Cm method names replacing c2f, cm2in, f2c, and in2cm to adhere to a JAXB naming convention where the first letter of each subsequent word in a method name is capitalized.

The latter file's class, which is presented in Listing 7, provides a noargument constructor for instantiating this class, and a getUCImplPort() method that returns an instance of the generated UC interface; the client executes the Web service's operations on this instance.

Listing 7. A cleaned up service implementation class for accessing the UC Web service

package client;

import java.net.MalformedURLException;
import java.net.URL;

import javax.xml.namespace.QName;

import javax.xml.ws.Service;
import javax.xml.ws.WebEndpoint;
import javax.xml.ws.WebServiceClient;
import javax.xml.ws.WebServiceException;
import javax.xml.ws.WebServiceFeature;

/**
 * This class was generated by the JAX-WS RI.
 * JAX-WS RI 2.3.0-SNAPSHOT
 * Generated source version: 2.2
 * 
 */
@WebServiceClient(name = "UCImplService", 
                  targetNamespace = "http://uc.javajeff.ca/", 
                  wsdlLocation = "http://localhost:9901/UC?wsdl")
public class UCImplService extends Service
{
   private final static URL UCIMPLSERVICE_WSDL_LOCATION;
   private final static WebServiceException UCIMPLSERVICE_EXCEPTION;
   private final static QName UCIMPLSERVICE_QNAME = 
      new QName("http://uc.javajeff.ca/", "UCImplService");

   static 
   {
      URL url = null;
      WebServiceException e = null;
      try 
      {
         url = new URL("http://localhost:9901/UC?wsdl");
      } 
      catch (MalformedURLException ex) 
      {
         e = new WebServiceException(ex);
      }
      UCIMPLSERVICE_WSDL_LOCATION = url;
      UCIMPLSERVICE_EXCEPTION = e;
   }

   public UCImplService()
   {
      super(__getWsdlLocation(), UCIMPLSERVICE_QNAME);
   }

   public UCImplService(WebServiceFeature... features) 
   {
      super(__getWsdlLocation(), UCIMPLSERVICE_QNAME, features);
   }

   public UCImplService(URL wsdlLocation) 
   {
      super(wsdlLocation, UCIMPLSERVICE_QNAME);
   }

   public UCImplService(URL wsdlLocation, WebServiceFeature... features) 
   {
      super(wsdlLocation, UCIMPLSERVICE_QNAME, features);
   }

   public UCImplService(URL wsdlLocation, QName serviceName) 
   {
      super(wsdlLocation, serviceName);
   }

   public UCImplService(URL wsdlLocation, QName serviceName, 
                        WebServiceFeature... features) 
   {
      super(wsdlLocation, serviceName, features);
   }

   /**
    * @return
    *    returns UC
    */
   @WebEndpoint(name = "UCImplPort")
   public UC getUCImplPort() 
   {
      return super.getPort(new QName("http://uc.javajeff.ca/", "UCImplPort"), 
                           UC.class);
   }

   /**
    * @param features
    *     A list of {@link javax.xml.ws.WebServiceFeature} to configure on the
    *     proxy. Supported features not in the features parameter 
    *     will have their default values.
    * @return
    *     returns UC
    */
   @WebEndpoint(name = "UCImplPort")
   public UC getUCImplPort(WebServiceFeature... features) 
   {
      return super.getPort(new QName("http://uc.javajeff.ca/", "UCImplPort"), 
                           UC.class, features);
   }

   private static URL __getWsdlLocation() 
   {
     if (UCIMPLSERVICE_EXCEPTION!= null) 
     {
        throw UCIMPLSERVICE_EXCEPTION;
     }
     return UCIMPLSERVICE_WSDL_LOCATION;
   }
}

UCImplService extends the Service class to provide the client view of a Web service. There are two items to note:

  • The noargument constructor is equivalent to Listing 6's Service.create() method call.
  • getUCImplPort() is equivalent to Listing 6's getPort() method call.
1 2 3 Page 2
Page 2 of 3