O'Reilly logo

Java Web Services: Up and Running by Martin Kalin

Stay ahead with the world's most comprehensive technology and business learning platform.

With Safari, you learn the way you learn best. Get unlimited access to videos, live online training, learning paths, books, tutorials, and more.

Start Free Trial

No credit card required

Chapter 1. Java Web Services Quickstart

What Are Web Services?

Although the term web service has various, imprecise, and evolving meanings, a glance at some features typical of web services will be enough to get us into coding a web service and a client, also known as a consumer or requester. As the name suggests, a web service is a kind of webified application, that is, an application typically delivered over HTTP (Hyper Text Transport Protocol). A web service is thus a distributed application whose components can be deployed and executed on distinct devices. For instance, a stock-picking web service might consist of several code components, each hosted on a separate business-grade server, and the web service might be consumed on PCs, handhelds, and other devices.

Web services can be divided roughly into two groups, SOAP-based and REST-style. The distinction is not sharp because, as a code example later illustrates, a SOAP-based service delivered over HTTP is a special case of a REST-style service. SOAP originally stood for Simple Object Access Protocol but, by serendipity, now may stand for Service Oriented Architecture (SOA) Protocol. Deconstructing SOA is nontrivial but one point is indisputable: whatever SOA may be, web services play a central role in the SOA approach to software design and development. (This is written with tongue only partly in cheek. SOAP is officially no longer an acronym, and SOAP and SOA can live apart from one another.) For now, SOAP is just an XML (EXtensible Markup Language) dialect in which documents are messages. In SOAP-based web services, the SOAP is mostly unseen infrastructure. For example, in a typical scenario, called the request/response message exchange pattern (MEP), the client’s underlying SOAP library sends a SOAP message as a service request, and the web service’s underlying SOAP library sends another SOAP message as the corresponding service response. The client and the web service source code may provide few hints, if any, about the underlying SOAP (see Figure 1-1).

Architecture of a typical SOAP-based web service
Figure 1-1. Architecture of a typical SOAP-based web service

REST stands for REpresentational State Transfer. Roy Fielding, one of the main authors of the HTTP specification, coined the acronym in his Ph.D. dissertation to describe an architectural style in the design of web services. SOAP has standards (under the World Wide Web Consortium [W3C]), toolkits, and bountiful software libraries. REST has no standards, few toolkits, and meager software libraries. The REST style is often seen as an antidote to the creeping complexity of SOAP-based web services. This book covers SOAP-based and REST-style web services, starting with the SOAP-based ones.

Except in test mode, the client of either a SOAP-based or REST-style service is rarely a web browser but rather an application without a graphical user interface. The client may be written in any language with the appropriate support libraries. Indeed, a major appeal of web services is language transparency: the service and its clients need not be written in the same language. Language transparency is the key to web service interoperability; that is, the ability of web services and requesters to interact seamlessly despite differences in programming languages, support libraries, and platforms. To underscore this appeal, clients against our Java web services will be written in various languages such as C#, Perl, and Ruby, and Java clients will consume services written in other languages, including languages unknown.

There is no magic in language transparency, of course. If a SOAP-based web service written in Java can have a Perl or a Ruby consumer, there must be an intermediary that handles the differences in data types between the service and the requester languages. XML technologies, which support structured document interchange and processing, act as the intermediary. For example, in a typical SOAP-based web service, a client transparently sends a SOAP document as a request to a web service, which transparently returns another SOAP document as a response. In a REST-style service, a client might send a standard HTTP request to a web service and receive an appropriate XML document as a response.

Several features distinguish web services from other distributed software systems. Here are three:

Open infrastructure

Web services are deployed using industry-standard, vendor-independent protocols such as HTTP and XML, which are ubiquitous and well understood. Web services can piggyback on networking, data formatting, security, and other infrastructures already in place, which lowers entry costs and promotes interoperability among services.

Language transparency

Web services and their clients can interoperate even if written in different programming languages. Languages such as C/C++, C#, Java, Perl, Python, Ruby, and others provide libraries, utilities, and even frameworks in support of web services.

Modular design

Web services are meant to be modular in design so that new services can be generated through the integration and layering of existing services. Imagine, for example, an inventory-tracking service integrated with an online ordering service to yield a service that automatically orders the appropriate products in response to inventory levels.

What Good Are Web Services?

This obvious question has no simple, single answer. Nonetheless, the chief benefits and promises of web services are clear. Modern software systems are written in a variety of languages—a variety that seems likely to increase. These software systems will continue to be hosted on a variety of platforms. Institutions large and small have significant investment in legacy software systems whose functionality is useful and perhaps mission critical; and few of these institutions have the will and the resources, human or financial, to rewrite their legacy systems.

It is rare that a software system gets to run in splendid isolation. The typical software system must interoperate with others, which may reside on different hosts and be written in different languages. Interoperability is not just a long-term challenge but also a current requirement of production software.

Web services address these issues directly because such services are, first and foremost, language- and platform-neutral. If a legacy COBOL system is exposed through a web service, the system is thereby interoperable with service clients written in other programming languages.

Web services are inherently distributed systems that communicate mostly over HTTP but can communicate over other popular transports as well. The communication payloads of web services are structured text (that is, XML documents), which can be inspected, transformed, persisted, and otherwise processed with widely and even freely available tools. When efficiency demands it, however, web services also can deliver binary payloads. Finally, web services are a work in progress with real-world distributed systems as their test bed. For all of these reasons, web services are an essential tool in any modern programmer’s toolbox.

The examples that follow, in this chapter and the others, are meant to be simple enough to isolate critical features of web services but also realistic enough to illustrate the power and flexibility that such services bring to software development. Let the examples begin.

A First Example

The first example is a SOAP-based web service in Java and clients in Perl, Ruby, and Java. The Java-based web service consists of an interface and an implementation.

The Service Endpoint Interface and Service Implementation Bean

The first web service in Java, like almost all of the others in this book, can be compiled and deployed using core Java SE 6 (Java Standard Edition 6) or greater without any additional software. All of the libraries required to compile, execute, and consume web services are available in core Java 6, which supports JAX-WS (Java API for XML-Web Services). JAX-WS supports SOAP-based and REST-style services. JAX-WS is commonly shortened to JWS for Java Web Services. The current version of JAX-WS is 2.x, which is a bit confusing because version 1.x has a different label: JAX-RPC. JAX-WS preserves but also significantly extends the capabilities of JAX-RPC.

A SOAP-based web service could be implemented as a single Java class but, following best practices, there should be an interface that declares the methods, which are the web service operations, and an implementation, which defines the methods declared in the interface. The interface is called the SEI: Service Endpoint Interface. The implementation is called the SIB: Service Implementation Bean. The SIB can be either a POJO or a Stateless Session EJB (Enterprise Java Bean). Chapter 6, which deals with the GlassFish Application Server, shows how to implement a web service as an EJB. Until then, the SOAP-based web services will be implemented as POJOs, that is, as instances of regular Java classes. These web services will be published using library classes that come with core Java 6 and, a bit later, with standalone Tomcat and GlassFish.

Example 1-1 is the SEI for a web service that returns the current time as either a string or as the elapsed milliseconds from the Unix epoch, midnight January 1, 1970 GMT.

Example 1-1. Service Endpoint Interface for the TimeServer
package ch01.ts;  // time server

import javax.jws.WebService;
import javax.jws.WebMethod;
import javax.jws.soap.SOAPBinding;
import javax.jws.soap.SOAPBinding.Style;

/**
 *  The annotation @WebService signals that this is the
 *  SEI (Service Endpoint Interface). @WebMethod signals 
 *  that each method is a service operation.
 *
 *  The @SOAPBinding annotation impacts the under-the-hood
 *  construction of the service contract, the WSDL
 *  (Web Services Definition Language) document. Style.RPC
 *  simplifies the contract and makes deployment easier.
*/
@WebService
@SOAPBinding(style = Style.RPC) // more on this later
public interface TimeServer {
    @WebMethod String getTimeAsString();
    @WebMethod long getTimeAsElapsed();
}

Example 1-2 is the SIB, which implements the SEI.

Example 1-2. Service Implementation Bean for the TimeServer
package ch01.ts; 

import java.util.Date;
import javax.jws.WebService;

/**
 *  The @WebService property endpointInterface links the
 *  SIB (this class) to the SEI (ch01.ts.TimeServer).
 *  Note that the method implementations are not annotated
 *  as @WebMethods.
*/
@WebService(endpointInterface = "ch01.ts.TimeServer")
public class TimeServerImpl implements TimeServer {
    public String getTimeAsString() { return new Date().toString(); }
    public long getTimeAsElapsed() { return new Date().getTime(); }
}

The two files are compiled in the usual way from the current working directory, which in this case is immediately above the subdirectory ch01. The symbol % represents the command prompt:

% javac ch01/ts/*.java

A Java Application to Publish the Web Service

Once the SEI and SIB have been compiled, the web service is ready to be published. In full production mode, a Java Application Server such as BEA WebLogic, GlassFish, JBoss, or WebSphere might be used; but in development and even light production mode, a simple Java application can be used. Example 1-3 is the publisher application for the TimeServer service.

Example 1-3. Endpoint publisher for the TimeServer
package ch01.ts;

import javax.xml.ws.Endpoint;

/**
 * This application publishes the web service whose
 * SIB is ch01.ts.TimeServerImpl. For now, the 
 * service is published at network address 127.0.0.1.,
 * which is localhost, and at port number 9876, as this
 * port is likely available on any desktop machine. The
 * publication path is /ts, an arbitrary name.
 *
 * The Endpoint class has an overloaded publish method.
 * In this two-argument version, the first argument is the
 * publication URL as a string and the second argument is
 * an instance of the service SIB, in this case
 * ch01.ts.TimeServerImpl.
 *
 * The application runs indefinitely, awaiting service requests.
 * It needs to be terminated at the command prompt with control-C
 * or the equivalent.
 *
 * Once the applicatation is started, open a browser to the URL
 *
 *     http://127.0.0.1:9876/ts?wsdl
 *
 * to view the service contract, the WSDL document. This is an
 * easy test to determine whether the service has deployed
 * successfully. If the test succeeds, a client then can be
 * executed against the service.
*/
public class TimeServerPublisher {
    public static void main(String[ ] args) {
      // 1st argument is the publication URL
      // 2nd argument is an SIB instance
      Endpoint.publish("http://127.0.0.1:9876/ts", new TimeServerImpl());
    }
}

Once compiled, the publisher can be executed in the usual way:

% java ch01.ts.TimeServerPublisher

How the Endpoint Publisher Handles Requests

Out of the box, the Endpoint publisher handles one client request at a time. This is fine for getting web services up and running in development mode. However, if the processing of a given request should hang, then all other client requests are effectively blocked. An example at the end of this chapter shows how Endpoint can handle requests concurrently so that one hung request does not block the others.

Testing the Web Service with a Browser

We can test the deployed service by opening a browser and viewing the WSDL (Web Service Definition Language) document, which is an automatically generated service contract. (WSDL is pronounced “whiz dull.”) The browser is opened to a URL that has two parts. The first part is the URL published in the Java TimeServerPublisher application: http://127.0.0.1:9876/ts. Appended to this URL is the query string ?wsdl in upper-, lower-, or mixed case. The result is http://127.0.0.1:9876/ts?wsdl. Example 1-4 is the WSDL document that the browser displays.

Example 1-4. WSDL document for the TimeServer service
<?xml version="1.0" encoding="UTF-8"?>
<definitions 
    xmlns="http://schemas.xmlsoap.org/wsdl/" 
    xmlns:tns="http://ts.ch01/" 
    xmlns:xsd="http://www.w3.org/2001/XMLSchema" 
    xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" 
    targetNamespace="http://ts.ch01/" 
    name="TimeServerImplService">
  <types></types>

  <message name="getTimeAsString"></message>
  <message name="getTimeAsStringResponse">
    <part name="return" type="xsd:string"></part>
  </message>
  <message name="getTimeAsElapsed"></message>
  <message name="getTimeAsElapsedResponse">
    <part name="return" type="xsd:long"></part>
  </message>
  
  <portType name="TimeServer">
    <operation name="getTimeAsString" parameterOrder="">
      <input message="tns:getTimeAsString"></input>
      <output message="tns:getTimeAsStringResponse"></output>
    </operation>
    <operation name="getTimeAsElapsed" parameterOrder="">
      <input message="tns:getTimeAsElapsed"></input>
      <output message="tns:getTimeAsElapsedResponse"></output>
    </operation>
  </portType>

  <binding name="TimeServerImplPortBinding" type="tns:TimeServer">
    <soap:binding style="rpc" 
                  transport="http://schemas.xmlsoap.org/soap/http">
    </soap:binding>
    <operation name="getTimeAsString">
      <soap:operation soapAction=""></soap:operation>
      <input>
        <soap:body use="literal" namespace="http://ts.ch01/"></soap:body>
      </input>
      <output>
        <soap:body use="literal" namespace="http://ts.ch01/"></soap:body>
      </output>
    </operation>
    <operation name="getTimeAsElapsed">
      <soap:operation soapAction=""></soap:operation>
      <input>
        <soap:body use="literal" namespace="http://ts.ch01/"></soap:body>
      </input>
      <output>
        <soap:body use="literal" namespace="http://ts.ch01/"></soap:body>
      </output>
    </operation>
  </binding>

  <service name="TimeServerImplService">
      <port name="TimeServerImplPort" binding="tns:TimeServerImplPortBinding">
      <soap:address location="http://localhost:9876/ts"></soap:address>
    </port>
  </service>
</definitions>

Chapter 2 examines the WSDL in detail and introduces Java utilities associated with the service contract. For now, two sections of the WSDL (both shown in bold) deserve a quick look. The portType section, near the top, groups the operations that the web service delivers, in this case the operations getTimeAsString and getTimeAsElapsed, which are the two Java methods declared in the SEI and implemented in the SIB. The WSDL portType is like a Java interface in that the portType presents the service operations abstractly but provides no implementation detail. Each operation in the web service consists of an input and an output message, where input means input for the web service. At runtime, each message is a SOAP document. The other WSDL section of interest is the last, the service section, and in particular the service location, in this case the URL http://localhost:9876/ts. The URL is called the service endpoint and it informs clients about where the service can be accessed.

The WSDL document is useful for both creating and executing clients against a web service. Various languages have utilities for generating client-support code from a WSDL. The core Java utility is now called wsimport but the earlier names wsdl2java and java2wsdl were more descriptive. At runtime, a client can consume the WSDL document associated with a web service in order to get critical information about the data types associated with the operations bundled in the service. For example, a client could determine from our first WSDL that the operation getTimeAsElapsed returns an integer and expects no arguments.

The WSDL also can be accessed with various utilities such as curl. For example, the command:

% curl http://localhost:9876/ts?wsdl

also displays the WSDL.

A Perl and a Ruby Requester of the Web Service

To illustrate the language transparency of web services, the first client against the Java-based web service is not in Java but rather in Perl. The second client is in Ruby. Example 1-5 is the Perl client.

Example 1-5. Perl client for the TimeServer client
#!/usr/bin/perl -w

use SOAP::Lite;
my $url = 'http://127.0.0.1:9876/ts?wsdl';
my $service = SOAP::Lite->service($url);

print "\nCurrent time is: ", $service->getTimeAsString();
print "\nElapsed milliseconds from the epoch: ", $service->getTimeAsElapsed();

On a sample run, the output was:

Current time is: Thu Oct 16 21:37:35 CDT 2008
Elapsed milliseconds from the epoch: 1224211055700

The Perl module SOAP::Lite provides the under-the-hood functionality that allows the client to issue the appropriate SOAP request and to process the resulting SOAP response. The request URL, the same URL used to test the web service in the browser, ends with a query string that asks for the WSDL document. The Perl client gets the WSDL document from which the SOAP::Lite library then generates the appropriate service object (in Perl syntax, the scalar variable $service). By consuming the WSDL document, the SOAP::Lite library gets the information needed, in particular, the names of the web service operations and the data types involved in these operations. Figure 1-2 depicts the architecture.

Architecture of the Perl client and Java service
Figure 1-2. Architecture of the Perl client and Java service

After the setup, the Perl client invokes the web service operations without any fuss. The SOAP messages remain unseen.

Example 1-6 is a Ruby client that is functionally equivalent to the Perl client.

Example 1-6. Ruby client for the TimeServer client
#!/usr/bin/ruby

# one Ruby package for SOAP-based services
require 'soap/wsdlDriver' 

wsdl_url = 'http://127.0.0.1:9876/ts?wsdl'


service = SOAP::WSDLDriverFactory.new(wsdl_url).create_rpc_driver

# Save request/response messages in files named '...soapmsgs...'
service.wiredump_file_base = 'soapmsgs'

# Invoke service operations.
result1 = service.getTimeAsString
result2 = service.getTimeAsElapsed

# Output results.
puts "Current time is: #{result1}"
puts "Elapsed milliseconds from the epoch: #{result2}"

The Hidden SOAP

In SOAP-based web services, a client typically makes a remote procedure call against the service by invoking one of the web service operations. As mentioned earlier, this back and forth between the client and service is the request/response message exchange pattern, and the SOAP messages exchanged in this pattern allow the web service and a consumer to be programmed in different languages. We now look more closely at what happens under the hood in our first example. The Perl client generates an HTTP request, which is itself a formatted message whose body is a SOAP message. Example 1-7 is the HTTP request from a sample run.

Example 1-7. HTTP request for the TimeServer service
POST http://127.0.0.1:9876/ts HTTP/ 1.1
Accept: text/xml
Accept: multipart/*
Accept: application/soap
User-Agent: SOAP::Lite/Perl/0.69
Content-Length: 434
Content-Type: text/xml; charset=utf-8
SOAPAction: ""

<?xml version="1.0" encoding="UTF-8"?>
<soap:Envelope
     soap:encodingStyle="http:// schemas.xmlsoap.org/soap/encoding/"
     xmlns:soap="http://schemas.xmlsoap.org/soap/ envelope/"
     xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/"
     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     xmlns:tns="http://ts.ch01/"
     xmlns:xsd ="http://www.w3.org/2001/XMLSchema">
  <soap:Body>
    <tns:getTimeAsString xsi:nil="true" />
  </soap:Body>
</soap:Envelope>

The HTTP request is a message with a structure of its own. In particular:

  • The HTTP start line comes first and specifies the request method, in this case the POST method, which is typical of requests for dynamic resources such as web services or other web application code (for example, a Java servlet) as opposed to requests for a static HTML page. In this case, a POST rather than a GET request is needed because only a POST request has a body, which encapsulates the SOAP message. Next comes the request URL followed by the HTTP version, in this case 1.1, that the requester understands. HTTP 1.1 is the current version.

  • Next come the HTTP headers, which are key/value pairs in which a colon (:) separates the key from the value. The order of the key/value pairs is arbitrary. The key Accept occurs three times, with a MIME (Multipurpose Internet Mail Extensions) type/subtype as the value: text/xml, multipart/*, and application/soap. These three pairs signal that the requester is ready to accept an arbitrary XML response, a response with arbitrarily many attachments of any type (a SOAP message can have arbitrarily many attachments), and a SOAP document, respectively. The HTTP key SOAPAction is often present in the HTTP header of a web service request and the key’s value may be the empty string, as in this case; but the value also might be the name of the requested web service operation.

  • Two CRLF (Carriage Return Line Feed) characters, which correspond to two Java \n characters, separate the HTTP headers from the HTTP body, which is required for the POST verb but may be empty. In this case, the HTTP body contains the SOAP document, commonly called the SOAP envelope because the outermost or document element is named Envelope. In this SOAP envelope, the SOAP body contains a single element whose local name is getTimeAsString, which is the name of the web service operation that the client wants to invoke. The SOAP request envelope is simple in this example because the requested operation takes no arguments.

On the web service side, the underlying Java libraries process the HTTP request, extract the SOAP envelope, determine the identity of the requested service operation, invoke the corresponding Java method getTimeAsString, and then generate the appropriate SOAP message to carry the method’s return value back to the client. Example 1-8 is the HTTP response from the Java TimeServerImpl service request shown in Example 1-7.

Example 1-8. HTTP response from the TimeServer service
HTTP/1.1 200 OK
Content-Length: 323
Content-Type: text/xml; charset=utf-8
Client-Date: Mon, 28 Apr 2008 02:12:54 GMT
Client-Peer: 127.0.0.1:9876
Client-Response-Num: 1

<?xml version="1.0" ?>
<soapenv:Envelope
    xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
    xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <soapenv:Body>
    <ans:getTimeAsStringResponse xmlns:ans="http://ts.ch01/">
      <return>Mon Apr 28 14:12:54 CST 2008</return>
    </ans:getTimeAsStringResponse>
  </soapenv:Body>
</soapenv:Envelope>

Once again the SOAP envelope is the body of an HTTP message, in this case the HTTP response to the client. The HTTP start line now contains the status code as the integer 200 and the corresponding text OK, which signal that the client request was handled successfully. The SOAP envelope in the HTTP response’s body contains the current time as a string between the XML start and end tags named return. The Perl SOAP library extracts the SOAP envelope from the HTTP response and, because of information in the WSDL document, expects the desired return value from the web service operation to occur in the XML return element.

A Java Requester of the Web Service

Example 1-9 is a Java client functionally equivalent to the Perl and Ruby clients shown in Examples 1-5 and 1-6, respectively.

Example 1-9. Java client for the Java web service
package ch01.ts;

import javax.xml.namespace.QName;
import javax.xml.ws.Service;
import java.net.URL;
class TimeClient {
    public static void main(String args[ ]) throws Exception {
        URL url = new URL("http://localhost:9876/ts?wsdl");

        // Qualified name of the service:
        //   1st arg is the service URI
        //   2nd is the service name published in the WSDL
        QName qname = new QName("http://ts.ch01/", "TimeServerImplService");

        // Create, in effect, a factory for the service.
        Service service = Service.create(url, qname);

        // Extract the endpoint interface, the service "port".
        TimeServer eif = service.getPort(TimeServer.class);

        System.out.println(eif.getTimeAsString());
        System.out.println(eif.getTimeAsElapsed());
   }
}

The Java client uses the same URL with a query string as do the Perl and Ruby clients, but the Java client explicitly creates an XML qualified name, which has the syntax namespace URI:local name. A URI is a Uniform Resource Identifier and differs from the more common URL in that a URL specifies a location, whereas a URI need not specify a location. In short, a URI need not be a URL. For now, it is enough to underscore that the Java class java.xml.namespace.QName represents an XML-qualified name. In this example, the namespace URI is provided in the WSDL, and the local name is the SIB class name TimeServerImpl with the word Service appended. The local name occurs in the service section, the last section of the WSDL document.

Once the URL and QName objects have been constructed and the Service.create method has been invoked, the statement of interest:

TimeServer eif = service.getPort(TimeServer.class);

executes. Recall that, in the WSDL document, the portType section describes, in the style of an interface, the operations included in the web service. The getPort method returns a reference to a Java object that can invoke the portType operations. The eif object reference is of type ch01.ts.TimeServer, which is the SEI type. The Java client, like the Perl client, invokes the two web service methods; and the Java libraries, like the Perl and Ruby libraries, generate and process the SOAP messages exchanged transparently to enable the successful method invocations.

Wire-Level Tracking of HTTP and SOAP Messages

Example 1-7 and Example 1-8 show an HTTP request message and an HTTP response message, respectively. Each HTTP message encapsulates a SOAP envelope. These message traces were done with the Perl client by changing the Perl use directive in Example 1-5:

use SOAP::Lite;

to:

use SOAP::Lite +trace;

The Ruby client in Example 1-6 contains a line:

service.wiredump_file_base = 'soapmsgs'

that causes the SOAP envelopes to be saved in files on the local disk. It is possible to capture the wire-level traffic directly in Java as well, as later examples illustrate. Various options are available for tracking SOAP and HTTP messages at the wire level. Here is a short introduction to some of them.

The tcpmon utility (available at https://tcpmon.dev.java.net) is free and downloads as an executable JAR file. Its graphical user interface (GUI) is easy to use. The utility requires only three settings: the server’s name, which defaults to localhost; the server’s port, which would be set to 9876 for the TimeServer example because this is the port at which Endpoint publishes the service; and the local port, which defaults to 8080 and is the port at which tcpmon listens. With tcpmon in use, the TimeClient would send its requests to port 8080 instead of port 9876. The tcpmon utility intercepts HTTP traffic between the client and web service, displaying the full messages in its GUI.

The Metro release has utility classes for tracking HTTP and SOAP traffic. This approach does not require any change to the client or to the service code; however, an additional package must be put on the classpath and a system property must be set either at the command line or in code. The required package is in the file jaxws_ri/jaxws-rt.jar. Assuming that the environment variable METRO_HOME points to the jaxws-ri directory, here is the command that tracks HTTP and SOAP traffic between the TimeClient, which connects to the service on port 9876, and the TimeServer service. (Under Windows, $METRO_HOME becomes %METRO_HOME%.) The command is on three lines for readability:

% java -cp ".":$METRO_HOME/lib/jaxws-rt.jar \
  -Dcom.sun.xml.ws.transport.http.client.HttpTransportPipe.dump=true \
  ch01.ts.TimeClient    

The resulting dump shows all of the SOAP traffic but not all of the HTTP headers. Message tracking also can be done on the service side.

There are various other open source and commercial products available for tracking the SOAP traffic. Among the products that are worth a look at are SOAPscope, NetSniffer, and Wireshark. The tcpdump utility comes with most Unix-type systems, including Linux and OS X, and is available on Windows as WinDump. Besides being free, tcpdump is nonintrusive in that it requires no change to either a web service or a client. The tcpdump utility dumps message traffic to the standard output. The companion utility tcptrace (http://www.tcptrace.org) can be used to analyze the dump. The remainder of this section briefly covers tcpdump as a flexible and powerful trace utility.

Under Unix-type systems, the tcpdump utility typically must be executed as superuser. There are various flagged arguments that determine how the utility works. Here is a sample invocation:

% tcpdump -i lo -A -s 1024 -l 'dst host localhost and port 9876' | tee dump.log

The utility can capture packets on any network interface. A list of such interfaces is available with the tcpdump -D (under Windows, WinDump -D), which is equivalent to the ifconfig -a command on Unix-like systems. In this example, the flag/value pair -i lo means capture packets from the interface lo, where lo is short for the localhost network interface on many Unix-like systems. The flag -A means that the captured packets should be presented in ASCII, which is useful for web packets as these typically contain text. The -s 1024 flag sets the snap length, the number of bytes that should be captured from each packet. The flag -l forces the standard output to be line buffered and easier to read; and, on the same theme, the construct | tee dump.log at the end pipes the same output that shows up on the screen (the standard output) into a local file named dump.log. Finally, the expression:

'dst host localhost and port 9876'

acts as a filter, capturing only packets whose destination is localhost on port 9876, the port on which TimeServerPublisher of Example 1-3 publishes the TimeServer service.

The tcpdump utility and the TimeServerPublisher application can be started in any order. Once both are running, the TimeClient or one of the other clients can be executed. With the sample use of tcpdump shown above, the underlying network packets are saved in the file dump.log. The file does require some editing to make it easily readable. In any case, the dump.log file captures the same SOAP envelopes shown in Examples 1-7 and 1-8.

What’s Clear So Far?

The first example is a web service with two operations, each of which delivers the current time but in different representations: in one case as a human-readable string, and in the other case as the elapsed milliseconds from the Unix epoch. The two operations are implemented as independent, self-contained methods. From the service requester’s perspective, either method may be invoked independently of the other and one invocation of a service method has no impact on any subsequent invocation of the same service method. The two Java methods depend neither on one another nor on any instance field to which both have access; indeed, the SIB class TimeServerImpl has no fields at all. In short, the two method invocations are stateless.

In the first example, neither method expects arguments. In general, web service operations may be parameterized so that appropriate information can be passed to the operation as part of the service request. Regardless of whether the web service operations are parameterized, they still should appear to the requester as independent and self-contained. This design principle will guide all of the samples that we consider, even ones that are richer than the first.

Key Features of the First Code Example

The TimeServerImpl class implements a web service with a distinctive message exchange pattern (MEP)—request/response. The service allows a client to make a language-neutral remote procedure call, invoking the methods getTimeAsString and getTimeAsElapsed. Other message patterns are possible. Imagine, for example, a web service that tracks new snow amounts for ski areas. Some participating clients, perhaps snow-measuring electrical devices strategically placed around the ski slopes, might use the one-way pattern by sending a snow amount from a particular location but without expecting a response from the service. The service might exhibit the notification pattern by multicasting to subscribing clients (for instance, travel bureaus) information about current snow conditions. Finally, the service might periodically use the solicit/response pattern to ask a subscribing client whether the client wishes to continue receiving notifications. In summary, SOAP-based web services support various patterns. The request/response pattern of RPC remains the dominant one. The infrastructure needed to support this pattern in particular is worth summarizing:

Message transport

SOAP is designed to be transport-neutral, a design goal that complicates matters because SOAP messages cannot rely on protocol-specific information included in the transport infrastructure. For instance, SOAP delivered over HTTP should not differ from SOAP delivered over some other transport protocol such as SMTP (Simple Mail Transfer Protocol), FTP (File Transfer Protocol), or even JMS (Java Message Service). In practice, however, HTTP is the usual transport for SOAP-based services, a point underscored in the usual name: SOAP-based web services.

Service contract

The service client requires information about the service’s operations in order to invoke them. In particular, the client needs information about the invocation syntax: the operation’s name, the order and types of the arguments passed to the operation, and the type of the returned value. The client also requires the service endpoint, typically the service URL. The WSDL document provides these pieces of information and others. Although a client could invoke a service without first accessing the WSDL, this would make things harder than they need to be.

Type system

The key to language neutrality and, therefore, service/consumer interoperability is a shared type system so that the data types used in the client’s invocation coordinate with the types used in the service operation. Consider a simple example. Suppose that a Java web service has the operation:

boolean bytes_ok(byte[ ] some_bytes)

The bytes_ok operation performs some validation test on the bytes passed to the operation as an array argument, returning either true or false. Now assume that a client written in C needs to invoke bytes_ok. C has no types named boolean and byte. C represents boolean values with integers, with nonzero as true and zero as false; and the C type signed char corresponds to the Java type byte. A web service would be cumbersome to consume if clients had to map client-language types to service-language types. In SOAP-based web services, the XML Schema type system is the default type system that mediates between the client’s types and the service’s types. In the example above, the XML Schema type xsd:byte is the type that mediates between the C signed char and the Java byte; and the XML Schema type xsd:boolean is the mediating type for the C integers nonzero and zero and the Java boolean values true and false. In the notation xsd:byte, the prefix xsd (XML Schema Definition) underscores that this is an XML Schema type because xsd is the usual extension for a file that contains an XML Schema definition; for instance, purchaseOrder.xsd.

Java’s SOAP API

A major appeal of SOAP-based web services is that the SOAP usually remains hidden. Nonetheless, it may be useful to glance at Java’s underlying support for generating and processing SOAP messages. Chapter 3, which introduces SOAP handlers, puts the SOAP API to practical use. This section provides a first look at the SOAP API through a simulation example. The application consists of one class, DemoSoap, but simulates sending a SOAP message as a request and receiving another as a response. Example 1-10 shows the full application.

Example 1-10. A demonstration of Java’s SOAP API
package ch01.soap;

import java.util.Date;
import java.util.Iterator;
import java.io.InputStream;
import java.io.ByteArrayOutputStream;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import javax.xml.soap.MessageFactory;
import javax.xml.soap.SOAPMessage;
import javax.xml.soap.SOAPEnvelope;
import javax.xml.soap.SOAPHeader;
import javax.xml.soap.SOAPBody;
import javax.xml.soap.SOAPPart;
import javax.xml.soap.SOAPElement;
import javax.xml.soap.SOAPException;
import javax.xml.soap.Node;
import javax.xml.soap.Name;

public class DemoSoap {
    private static final String LocalName = "TimeRequest";
    private static final String Namespace = "http://ch01/mysoap/";
    private static final String NamespacePrefix = "ms";

    private ByteArrayOutputStream out;
    private ByteArrayInputStream in;

    public static void main(String[ ] args) {
       new DemoSoap().request();
    }

    private void request() {
       try {
         // Build a SOAP message to send to an output stream.
         SOAPMessage msg = create_soap_message();
   
         // Inject the appropriate information into the message. 
         // In this case, only the (optional) message header is used
         // and the body is empty.
         SOAPEnvelope env = msg.getSOAPPart().getEnvelope();
         SOAPHeader hdr = env.getHeader();
  
         // Add an element to the SOAP header. 
         Name lookup_name = create_qname(msg);
         hdr.addHeaderElement(lookup_name).addTextNode("time_request");

         // Simulate sending the SOAP message to a remote system by
         // writing it to a ByteArrayOutputStream.
         out = new ByteArrayOutputStream();
         msg.writeTo(out);

         trace("The sent SOAP message:", msg);

         SOAPMessage response = process_request();
         extract_contents_and_print(response);
       }
       catch(SOAPException e) { System.err.println(e); }
       catch(IOException e) { System.err.println(e); }
    }

    private SOAPMessage process_request() {
       process_incoming_soap();
       coordinate_streams();
       return create_soap_message(in);
    }
       
    private void process_incoming_soap() {
       try {
         // Copy output stream to input stream to simulate
         // coordinated streams over a network connection.
         coordinate_streams();

         // Create the "received" SOAP message from the
         // input stream.
         SOAPMessage msg = create_soap_message(in);

         // Inspect the SOAP header for the keyword 'time_request' 
         // and process the request if the keyword occurs.
         Name lookup_name = create_qname(msg);

         SOAPHeader header = msg.getSOAPHeader();
         Iterator it = header.getChildElements(lookup_name);
         Node next = (Node) it.next();
         String value = (next == null) ? "Error!" : next.getValue();

         // If SOAP message contains request for the time, create a
         // new SOAP message with the current time in the body.
         if (value.toLowerCase().contains("time_request")) {

           // Extract the body and add the current time as an element.
           String now = new Date().toString();
           SOAPBody body = msg.getSOAPBody();
           body.addBodyElement(lookup_name).addTextNode(now);
           msg.saveChanges();

           // Write to the output stream.
           msg.writeTo(out);
           trace("The received/processed SOAP message:", msg);
         }
       }
       catch(SOAPException e) { System.err.println(e); }
       catch(IOException e) { System.err.println(e); }
    }
    
    private void extract_contents_and_print(SOAPMessage msg) {
       try {
         SOAPBody body = msg.getSOAPBody();

         Name lookup_name = create_qname(msg);
         Iterator it = body.getChildElements(lookup_name);
         Node next = (Node) it.next();
    
         String value = (next == null) ? "Error!" : next.getValue();
         System.out.println("\n\nReturned from server: " + value);
       }
       catch(SOAPException e) { System.err.println(e); }
    }

    private SOAPMessage create_soap_message() {
       SOAPMessage msg = null;
       try {
         MessageFactory mf = MessageFactory.newInstance();
         msg = mf.createMessage();
       }
       catch(SOAPException e) { System.err.println(e); }
       return msg;
    }

    private SOAPMessage create_soap_message(InputStream in) {
       SOAPMessage msg = null;
       try {
         MessageFactory mf = MessageFactory.newInstance();
         msg = mf.createMessage(null, // ignore MIME headers
                                in);  // stream source
       }
       catch(SOAPException e) { System.err.println(e); }
       catch(IOException e) { System.err.println(e); }
       return msg;
    }

    private Name create_qname(SOAPMessage msg) {
       Name name = null;
       try {
         SOAPEnvelope env = msg.getSOAPPart().getEnvelope();
         name = env.createName(LocalName, NamespacePrefix, Namespace);
       }
       catch(SOAPException e) { System.err.println(e); }
       return name;
    }

    private void trace(String s, SOAPMessage m) {
       System.out.println("\n");
       System.out.println(s);
       try {
         m.writeTo(System.out);
       }
       catch(SOAPException e) { System.err.println(e); }
       catch(IOException e) { System.err.println(e); }
    }

    private void coordinate_streams() {
       in = new ByteArrayInputStream(out.toByteArray());
       out.reset();
    }
}   

Here is a summary of how the application runs, with emphasis on the code involving SOAP messages. The DemoSoap application’s request method generates a SOAP message and adds the string time_request to the SOAP envelope’s header. The code segment, with comments removed, is:

SOAPMessage msg = create_soap_message();
SOAPEnvelope env = msg.getSOAPPart().getEnvelope();
SOAPHeader hdr = env.getHeader();
Name lookup_name = create_qname(msg);
hdr.addHeaderElement(lookup_name).addTextNode("time_request");

There are two basic ways to create a SOAP message. The simple way is illustrated in this code segment:

MessageFactory mf = MessageFactory.newInstance();
SOAPMessage msg = mf.createMessage();

In the more complicated way, the MessageFactory code is the same, but the creation call becomes:

SOAPMessage msg = mf.createMessage(mime_headers, input_stream);

The first argument to createMessage is a collection of the transport-layer headers (for instance, the key/value pairs that make up an HTTP header), and the second argument is an input stream that provides the bytes to create the message (for instance, the input stream encapsulated in a Java Socket instance).

Once the SOAP message is created, the header is extracted from the SOAP envelope and an XML text node is inserted with the value time_request. The resulting SOAP message is:

<SOAP-ENV:Envelope
     xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
   <SOAP-ENV:Header>
      <ms:TimeRequest xmlns:ms="http://ch01/mysoap/">
        time_request
      </ms:TimeRequest>
   </SOAP-ENV:Header>
   <SOAP-ENV:Body/>
</SOAP-ENV:Envelope>

There is no need right now to examine every detail of this SOAP message. Here is a summary of some key points. The SOAP body is always required but, as in this case, the body may be empty. The SOAP header is optional but, in this case, the header contains the text time_request. Message contents such as time_request normally would be placed in the SOAP body and special processing information (for instance, user authentication data) would be placed in the header. The point here is to illustrate how the SOAP header and the SOAP body can be manipulated.

The request method writes the SOAP message to a ByteArrayOutputStream, which simulates sending the message over a network connection to a receiver on a different host. The request method invokes the process_request method, which in turn delegates the remaining tasks to other methods. The processing goes as follows. The received SOAP message is created from a ByteArrayInputStream, which simulates an input stream on the receiver’s side; this stream contains the sent SOAP message. The SOAP message now is created from the input stream:

SOAPMessage msg = null;
try {
   MessageFactory mf = MessageFactory.newInstance();
   msg = mf.createMessage(null, // ignore MIME headers
                          in);  // stream source (ByteArrayInputStream)
}  

and then the SOAP message is processed to extract the time_request string. The extraction goes as follows. First, the SOAP header is extracted from the SOAP message and an iterator over the elements with the tag name:

<ms:TimeRequest xmlns:ms="http://ch01/mysoap/>

is created. In this example, there is one element with this tag name and the element should contain the string time_request. The lookup code is:

SOAPHeader header = msg.getSOAPHeader();
Iterator it = header.getChildElements(lookup_name);
Node next = (Node) it.next();
String value = (next == null) ? "Error!" : next.getValue();

If the SOAP header contains the proper request string, the SOAP body is extracted from the incoming SOAP message and an element containing the current time as a string is added to the SOAP body. The revised SOAP message then is sent as a response. Here is the code segment with the comments removed:

if (value.toLowerCase().contains("time_request")) {
  String now = new Date().toString();
  SOAPBody body = msg.getSOAPBody();
  body.addBodyElement(lookup_name).addTextNode(now);
  msg.saveChanges();

  msg.writeTo(out);
  trace("The received/processed SOAP message:", msg);
}

The outgoing SOAP message on a sample run was:

<SOAP-ENV:Envelope 
      xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
   <SOAP-ENV:Header>
       <ms:TimeRequest xmlns:ms="http://ch01/mysoap/">
          time_request
       </ms:TimeRequest>
   </SOAP-ENV:Header>
   <SOAP-ENV:Body>
       <ms:TimeRequest xmlns:ms="http://ch01/mysoap/">
          Mon Oct 27 14:45:53 CDT 2008
       </ms:TimeRequest>
   </SOAP-ENV:Body>
</SOAP-ENV:Envelope>

This example provides a first look at Java’s API. Later examples illustrate production-level use of the SOAP API.

An Example with Richer Data Types

The operations in the TimeServer service take no arguments and return simple types, a string and an integer. This section offers a richer example whose details are clarified in the next chapter.

The Teams web service in Example 1-11 differs from the TimeServer service in several important ways.

Example 1-11. The Teams document-style web service
package ch01.team;

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

@WebService
public class Teams {
    private TeamsUtility utils;

    public Teams() { 
       utils = new TeamsUtility(); 
       utils.make_test_teams();
    }

    @WebMethod
    public Team getTeam(String name) { return utils.getTeam(name); }

    @WebMethod
    public List<Team> getTeams() { return utils.getTeams(); }
}

For one, the Teams service is implemented as a single Java class rather than as a separate SEI and SIB. This is done simply to illustrate the possibility. A more important difference is in the return types of the two Teams operations. The operation getTeam is parameterized and returns an object of the programmer-defined type Team, which is a list of Player instances, another programmer-defined type. The operation getTeams returns a List<Team>, that is, a Java Collection.

The utility class TeamsUtility generates the data. In a production environment, this utility might retrieve a team or list of teams from a database. To keep this example simple, the utility instead creates the teams and their players on the fly. Here is part of the utility:

package ch01.team;

import java.util.Set;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.util.HashMap;

public class TeamsUtility {
    private Map<String, Team> team_map;

    public TeamsUtility() {
       team_map = new HashMap<String, Team>();
    }

    public Team getTeam(String name) { return team_map.get(name); }
    public List<Team> getTeams() {
       List<Team> list = new ArrayList<Team>();
       Set<String> keys = team_map.keySet();
       for (String key : keys)
          list.add(team_map.get(key));
       return list;
    }

    public void make_test_teams() {
       List<Team> teams = new ArrayList<Team>();
       ...
       Player chico = new Player("Leonard Marx", "Chico");
       Player groucho = new Player("Julius Marx", "Groucho");
       Player harpo = new Player("Adolph Marx", "Harpo");
       List<Player> mb = new ArrayList<Player>();
       mb.add(chico); mb.add(groucho); mb.add(harpo);
       Team marx_brothers = new Team("Marx Brothers", mb);
       teams.add(marx_brothers);

       store_teams(teams);
    }

    private void store_teams(List<Team> teams) {
       for (Team team : teams)
          team_map.put(team.getName(), team);
    }
}

Publishing the Service and Writing a Client

Recall that the SEI for the TimeServer service contains the annotation:

@SOAPBinding(style = Style.RPC) 

This annotation requires that the service use only very simple types such as string and integer. By contrast, the Teams service uses richer data types, which means that Style.DOCUMENT, the default, should replace Style.RPC. The document style does require more setup, which is given below but not explained until the next chapter. Here, then, are the steps required to get the web service deployed and a sample client written quickly:

  1. The source files are compiled in the usual way. From the working directory, which has ch01 as a subdirectory, the command is:

    % javac ch01/team/*.java

    In addition to the @WebService-annotated Teams class, the ch01/team directory contains the Team, Player, TeamsUtility, and TeamsPublisher classes shown below all together:

    package ch01.team;
    public class Player {
        private String name;
        private String nickname;
    
        public Player() { }
        public Player(String name, String nickname) {
           setName(name);
           setNickname(nickname);
        }
    
        public void setName(String name) { this.name = name; }
        public String getName() { return name; }
        public void setNickname(String nickname) { this.nickname = nickname; }
        public String getNickname() { return nickname; }
    }
    // end of Player.java
    
    package ch01.team;
    
    import java.util.List;
    public class Team {
        private List<Player> players;
        private String name;
        
        public Team() { }
        public Team(String name, List<Player> players) { 
           setName(name);
           setPlayers(players); 
        }
    
        public void setName(String name) { this.name = name; }
        public String getName() { return name; }
        public void setPlayers(List<Player> players) { this.players = players; }
        public List<Player> getPlayers() { return players; }
        public void setRosterCount(int n) { } // no-op but needed for property
    	public int getRosterCount() { return (players == null) ? 0 : players.size(); }
    
        }
    }
    // end of Team.java
    
    package ch01.team;
    import javax.xml.ws.Endpoint;
    class TeamsPublisher {
        public static void main(String[ ] args) {
           int port = 8888;
           String url = "http://localhost:" + port + "/teams";
           System.out.println("Publishing Teams on port " + port);
           Endpoint.publish(url, new Teams());
        }
    } 
  2. In the working directory, invoke the wsgen utility, which comes with core Java 6:

    % wsgen -cp . ch01.team.Teams

    This utility generates various artifacts; that is, Java types needed by the method Endpoint.publish to generate the service’s WSDL. Chapter 2 looks closely at these artifacts and how they contribute to the WSDL.

  3. Execute the TeamsPublisher application.

  4. In the working directory, invoke the wsimport utility, which likewise comes with core Java 6:

    % wsimport -p teamsC -keep http://localhost:8888/teams?wsdl

    This utility generates various classes in the subdirectory teamsC (the -p flag stands for package). These classes make it easier to write a client against the service.

Step 4 expedites the coding of a client, which is shown here:

import teamsC.TeamsService;
import teamsC.Teams;
import teamsC.Team;
import teamsC.Player;
import java.util.List;
class TeamClient {
    public static void main(String[ ] args) {
        TeamsService service = new TeamsService();
        Teams port = service.getTeamsPort();
        List<Team> teams = port.getTeams();
        for (Team team : teams) {
            System.out.println("Team name: " + team.getName() +
                               " (roster count: " + team.getRosterCount() + ")");
            for (Player player : team.getPlayers())
                System.out.println("  Player: " + player.getNickname());
        }
    }
}

When the client executes, the output is:

Team name: Abbott and Costello (roster count: 2)
  Player: Bud
  Player: Lou
Team name: Marx Brothers (roster count: 3)
  Player: Chico
  Player: Groucho
  Player: Harpo
Team name: Burns and Allen (roster count: 2)
  Player: George
  Player: Gracie

This example hints at what is possible in a commercial-grade, SOAP-based web service. Programmer-defined types such as Player and Team, along with arbitrary collections of these, can be arguments passed to or values returned from a web service so long as certain guidelines are followed. One guideline comes into play in this example. For the Team and the Player classes, the JavaBean properties are of types String or int; and a List, like any Java Collection, has a toArray method. In the end, a List<Team> reduces to arrays of simple types; in this case String instances or int values. The next chapter covers the details, in particular how the wsgen and the wsimport utilities facilitate the development of JWS services and clients.

Multithreading the Endpoint Publisher

In the examples so far, the Endpoint publisher has been single-threaded and, therefore, capable of handling only one client request at a time: the published service completes the processing of one request before beginning the processing of another request. If the processing of the current request hangs, then no subsequent request can be processed unless and until the hung request is processed to completion.

In production mode, the Endpoint publisher would need to handle concurrent requests so that several pending requests could be processed at the same time. If the underlying computer system is, for example, a symmetric multiprocessor (SMP), then separate CPUs could process different requests concurrently. On a single-CPU machine, the concurrency would occur through time sharing; that is, each request would get a share of the available CPU cycles so that several requests would be in some stage of processing at any given time. In Java, concurrency is achieved through multithreading. At issue, then, is how to make the Endpoint publisher multithreaded. The JWS framework supports Endpoint multithreading without forcing the programmer to work with difficult, error-prone constructs such as the synchronized block or the wait and notify method invocations.

An Endpoint object has an Executor property defined with the standard get/set methods. An Executor is an object that executes Runnable tasks; for example, standard Java Thread instances. (The Runnable interface declares only one method whose declaration is public void run().) An Executor object is a nice alternative to Thread instances, as the Executor provides high-level constructs for submitting and managing tasks that are to be executed concurrently. The first step to making the Endpoint publisher multithreaded is thus to create an Executor class such as the following very basic one:

package ch01.ts;

import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.locks.Condition;

public class MyThreadPool extends ThreadPoolExecutor {
    private static final int pool_size = 10;
    private boolean is_paused;
    private ReentrantLock pause_lock = new ReentrantLock();
    private Condition unpaused = pause_lock.newCondition();
    
    public MyThreadPool(){
       super(pool_size,        // core pool size
             pool_size,        // maximum pool size
             0L,               // keep-alive time for idle thread
             TimeUnit.SECONDS, // time unit for keep-alive setting
             new LinkedBlockingQueue<Runnable>(pool_size)); // work queue 
    }

    // some overrides
    protected void beforeExecute(Thread t, Runnable r) {
       super.beforeExecute(t, r);
       pause_lock.lock();
       try {
          while (is_paused) unpaused.await();
       }
       catch (InterruptedException e) { t.interrupt(); } 
       finally { pause_lock.unlock(); }
    }
    
    public void pause() {
       pause_lock.lock();
       try {
          is_paused = true;
       } 
       finally { pause_lock.unlock(); }
    }
    
    public void resume() {
       pause_lock.lock();
       try {
          is_paused = false;
          unpaused.signalAll();
       } 
       finally { pause_lock.unlock(); }
    }
}

The class MyThreadPool creates a pool of 10 threads, using a fixed-size queue to store the threads that are created under the hood. If the pooled threads are all in use, then the next task in line must wait until one of the busy threads becomes available. All of these management details are handled automatically. The MyThreadPool class overrides a few of the available methods to give the flavor.

A MyThreadPool object can be used to make a multithreaded Endpoint publisher. Here is the revised publisher, which now consists of several methods to divide the work:

package ch01.ts;

import javax.xml.ws.Endpoint;

class TimePublisherMT { // MT for multithreaded
    private Endpoint endpoint;

    public static void main(String[ ] args) {
        TimePublisherMT self = new TimePublisherMT();
        self.create_endpoint();
        self.configure_endpoint();
        self.publish();
    }
    private void create_endpoint() {
        endpoint = Endpoint.create(new TimeServerImpl());
    }
    private void configure_endpoint() {
        endpoint.setExecutor(new MyThreadPool());
    }
    private void publish() {
        int port = 8888;
        String url = "http://localhost:" + port + "/ts";
        endpoint.publish(url);
        System.out.println("Publishing TimeServer on port " + port);
    }
}    

Once the ThreadPoolExecutor has been coded, all that remains is to set the Endpoint publisher’s executor property to an instance of the worker class. The details of thread management do not intrude at all into the publisher.

The multithreaded Endpoint publisher is suited for lightweight production, but this publisher is not a service container in the true sense; that is, a software application that readily can deploy many web services at the same port. A web container such as Tomcat, which is the reference implementation, is better suited to publish multiple web services. Tomcat is introduced in later examples.

What’s Next?

A SOAP-based web service should provide, as a WSDL document, a service contract for its potential clients. So far we have seen how a Perl, a Ruby, and a Java client can request the WSDL at runtime for use in the underlying SOAP libraries. Chapter 2 studies the WSDL more closely and illustrates how it may be used to generate client-side artifacts such as Java classes, which in turn ease the coding of web service clients. The Java clients in Chapter 2 will not be written from scratch, as is our first Java client. Instead such clients will be written with the considerable aid of the wsimport utility, as was the TeamClient shown earlier. Chapter 2 also introduces JAX-B (Java API for XML-Binding), a collection of Java packages that coordinate Java data types and XML data types. The wsgen utility generates JAX-B artifacts that play a key role in this coordination; hence, wsgen also will get a closer look.

With Safari, you learn the way you learn best. Get unlimited access to videos, live online training, learning paths, books, interactive tutorials, and more.

Start Free Trial

No credit card required