A SOAP-Based Web Service

JAX-WS, like JAX-RS, uses annotations, and machine-generated JAX-WS code is awash with these. The first example is stingy in its use of annotations in order to underscore exactly what is required for a SOAP-based service. Later examples introduce additional annotations.:

The RandService class (see Example 4-1) defines a SOAP-based service with two operations, each an annotated Java method:

  • Operation next1 takes no arguments and returns one randomly generated integer.
  • Operation nextN takes one argument, the number of randomly generated integers desired, and returns a list (in this implementation, an array) of integers.

Example 4-1. A SOAP-based service with two operations

package rand;

import javax.jws.WebService;
import javax.jws.WebMethod;
import java.util.Random;

@WebService                                                       1
public class RandService {
    private static final int maxRands = 16;

    @WebMethod // optional but helpful annotation                 2
    public int next1() { return new Random().nextInt(); }
    @WebMethod // optional but helpful annotation                 3
    public int[ ] nextN(final int n) {
        final int k = (n > maxRands) ? maxRands : Math.abs(n);
        int[ ] rands = new int[k];
        Random r = new Random();
        for (int i = 0; i < k; i++) rands[i] = r.nextInt();
        return rands;
    }
}

The @WebService annotation (line 1) marks the RandService POJO class as a web service, and the @WebMethod annotation (lines 2 and 3) specifies which of the encapsulated methods is a service operation. In this example, the RandService class has only two methods and each of these is annotated as @WebMethod. The @WebMethod annotation is optional but recommended. In a class annotated as a @WebService, a public instance method is thereby a service operation even if the method is not annotated. This SOAP service code is compiled in the usual way, assuming JDK 1.6 or greater.

Recall that core Java 6 or greater includes the Endpoint class for publishing web services, SOAP-based (@WebService) and REST-style (@WebServiceProvider) alike. The class RandPublisher (see Example 4-2) is the Endpoint publisher for the RandService.

Example 4-2. An Endpoint published for the RandService SOAP-based web service

package rand;

import javax.xml.ws.Endpoint;
public class RandPublisher {
    public static void main(String[ ] args) {
        final String url = "http://localhost:8888/rs";                    1
        System.out.println("Publishing RandService at endpoint " + url);
        Endpoint.publish(url, new RandService());                         2
    }
}

The publish method used here (line 2) takes two arguments: a URL that specifies the service endpoint (line 1) and an instance of the service implementation class, in this case the RandService class (line 2). In the URL, the port number 8888 and the URI /rs are arbitrary, although a port number greater than 1023 is recommended because modern operating systems typically reserve port numbers below 1024 for particular applications (e.g., port 80 is typically reserved for HTTP requests to a web server). The RandPublisher as coded here runs indefinitely, but there are various way to control an Endpoint publisher’s life span.

The web service publisher can be executed in the usual way:

% java rand.RandPublisher

The output should be similar to this:

Publishing RandService at endpoint http://localhost:8888/rs

com.sun.xml.internal.ws.model.RuntimeModeler getRequestWrapperClass
INFO: Dynamically creating request wrapper Class rand.jaxws.Next1
com.sun.xml.internal.ws.model.RuntimeModeler getResponseWrapperClass
Dynamically creating response wrapper bean Class rand.jaxws.Next1Response
com.sun.xml.internal.ws.model.RuntimeModeler getRequestWrapperClass
INFO: Dynamically creating request wrapper Class rand.jaxws.NextN
com.sun.xml.internal.ws.model.RuntimeModeler getResponseWrapperClass
INFO: Dynamically creating response wrapper bean Class rand.jaxws.NextNResponse

The first line of output is from the RandPublisher but the others are from the Java run-time. The dynamically created wrapper classes such as Next1 and Next1Response are JAX-B artifacts that represent the incoming SOAP request (Next1) and the outgoing SOAP response (Next1Response).

Once the service is published, a utility such as curl can be used to confirm that the service is indeed up and running:

% curl http://localhost:8888/rs?xsd=1

This curl request contains the query string entry xsd=1 that asks for the XML Schema associated with this service; the schema, like the JAX-B artifacts, is generated dynamically (see Example 4-3).

Example 4-3. The XML Schema generated dynamically for the RandService

<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:tns="http://rand/" xmlns:xs="http://www.w3.org/2001/XMLSchema"
           version="1.0" targetNamespace="http://rand/">
  <xs:element name="next1" type="tns:next1"></xs:element>
  <xs:element name="next1Response" type="tns:next1Response"></xs:element>
  <xs:element name="nextN" type="tns:nextN"></xs:element>
  <xs:element name="nextNResponse" type="tns:nextNResponse"></xs:element>
  <xs:complexType name="next1"><xs:sequence></xs:sequence></xs:complexType>
  <xs:complexType name="next1Response">
    <xs:sequence>
      <xs:element name="return" type="xs:int"></xs:element>
    </xs:sequence>
  </xs:complexType>
  <xs:complexType name="nextN">
    <xs:sequence>
      <xs:element name="arg0" type="xs:int"></xs:element>
    </xs:sequence>
  </xs:complexType>
  <xs:complexType name="nextNResponse">
    <xs:sequence>
      <xs:element name="return" type="xs:int" minOccurs="0" maxOccurs="unbounded">
      </xs:element>
    </xs:sequence>
  </xs:complexType>
</xs:schema>

The schema will be studied carefully later. For now, the point of interest is that the schema provides a data type for each SOAP message that travels, in either direction, between the service and the client. Each message is of an XML Schema complexType as opposed to an simple type such as xsd:date, xsd:string, or xsd:integer.

In the RandService there are two SOAP messages (for instance, the messages Next1 and Next1Response) per web service operation (in this case, the next1 operation) because each operation implements the familiar request/response pattern: a client issues a request, delivered to the service as a Next1 SOAP message, and gets a response, in this case a Next1Response message, in return. Accordingly, the schema contains four typed SOAP messages because the RandService has two operations in the request/response pattern, which means two messages per operation. The number of complexType occurrences in the XML Schema may exceed the total number of messages needed to implement the service’s operations because special error messages, SOAP faults, also may be defined in the XML Schema. SOAP faults are covered in the next chapter.

The XML Schema types such as Next1 and Next1Response are the XML counterparts to the JAX-B artifacts, noted earlier, with the same names. The schema types and the JAX-B types together allow the SOAP libraries to transform Java objects into XML documents (in particular, SOAP Envelope instances) and SOAP Envelope instances into Java objects. The Endpoint publisher’s underlying SOAP libraries handle the generation of the JAX-B artifacts and the generation of the XML Schema.

Even this first and rather simple example underscores a major appeal of SOAP-based services: underlying SOAP libraries handle the conversions between native language types (in this case, Java types) and XML Schema types. Figure 4-1 depicts the architecture.

The architecture of a typical SOAP-based service

Figure 4-1. The architecture of a typical SOAP-based service

Get Java Web Services: Up and Running, 2nd Edition now with the O’Reilly learning platform.

O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.