Web services in Java SE, Part 1: Tools overview

Learn all about Web services and how they work in Java SE

1 2 Page 2
Page 2 of 2
  • Java API for XML Web Services (JAX-WS): The main API for building Web services and clients (in Java) that communicate via XML. JAX-WS replaces the older Java API for Remote Procedure Call Web Services (JAX-RPC) API, and is assigned package javax.xml.ws and various subpackages.
  • Java Architecture for XML Binding (JAXB): The API for mapping XML Schema-based data types to Java objects and vice-versa. JAX-WS delegates data-binding tasks to JAXB. This API is assigned package javax.xml.bind and various subpackages.
  • Soap with Attachments API for Java (SAAJ): The API for creating, sending, and receiving SOAP messages with/without attachments. JAX-WS's dependency on SAAJ for SOAP messages was removed in the reference implementation of JAX-WS 2.1.3 (known as Metro). This API is assigned the javax.xml.soap package.

I will explore JAX-WS and SAAJ in this series, but (for brevity) won't be exploring JAXB. If you want a detailed tutorial on JAXB, I recommend that you check out Lesson: Introduction to JAXB at The Java Tutorials.

Annotations

Java SE 6 introduced several Web service annotation types that facilitate Web service development, by letting you describe Web services declaratively via metadata. You can still develop Web services without these annotation types, but you'll soon appreciate their convenience if you decide not to use them.

Most Web service annotation types are either part of the Web Services MetaData API, which is assigned packages javax.jws and javax.jws.soap, or belong to the javax.xml.ws package. The javax.jws package provides the following annotation types:

  • HandlerChain: Associate the Web service with an externally defined handler chain. I'll discuss handler chains from the client perspective later in this series.
  • Oneway: Indicate that a given @WebMethod annotation has only an input message and no output message.
  • WebMethod: Customize a method that's exposed as a Web service operation.
  • WebParam: Customize the mapping of an individual parameter to a WSDL message element's part element.
  • WebResult: Customize the mapping of the return value to a WSDL message element's part element.
  • WebService: Mark a Java class as implementing a Web service, or a Java interface as defining a service endpoint interface.

The following annotation types (three of which are deprecated in favor of using the HandlerChain annotation type) belong to the javax.jws.soap package:

  • InitParam: Describe an initialization parameter (a name/value pair passed to the handler during initialization). This annotation type is deprecated.
  • SOAPBinding: Specify the mapping of the Web service onto the SOAP protocol.
  • SOAPMessageHandler: Specify a single SOAP message handler that runs before and after the Web service's business methods. This handler is called in response to SOAP messages targeting the service. This annotation type is deprecated.
  • SOAPMessageHandlers: Specify a list of SOAP protocol handlers that run before and after the Web service's business methods. These handlers are called in response to SOAP messages targeting the service. This annotation type is deprecated.

Finally, javax.xml.ws's most important annotation types from a RESTful Web service perspective are WebServiceProvider and BindingType. I'll discuss these annotation types later in this series.

Tools

Java provides four command-line-based tools that facilitate Web service development. Two of these tools are used to convert between XML Schema-based schemas and Java classes, and the other pair of tools is used in the context of WSDL documents:

  • schemagen: WSDL documents use XML Schema data types to describe Web service function return and parameter types. This tool generates a schema (often stored in a file with a .xsd extension) from Java classes -- one schema file is created for each referenced namespace. After the schema has been created, XML instance documents (XML documents that adhere to their schemas) can be converted to and from Java objects via JAXB. The classes contain all of the information needed by JAXB to parse the XML for marshaling (converting Java objects to XML) and unmarshaling (converting XML to Java objects) -- the application doesn't perform XML parsing.
  • wsgen: This tool reads a compiled Web service endpoint interface and generates JAX-WS portable artifacts for Web service deployment and invocation. It can alternatively generate a WSDL file and corresponding XML Schema document (when its -wsdl option is specified). This tool isn't required when publishing a Web service via Endpoint.publish(), which automatically generates the artifacts and WSDL/schema. You'll learn about Endpoint.publish() later in this series.
  • wsimport: This tool generates client-support Java classes (artifacts) from a given WSDL document. These classes facilitate writing a client against the service.
  • xjc: This tool generates Java classes from a schema. The generated classes contain properties mapped to the XML elements and attributes defined in the schema.

For brevity, I demonstrate only wsimport in this series. For demonstrations of schemagen and xjc, check out Using JAXB schemagen tooling to generate an XML schema file from a Java class and Java Architecture for XML Binding (JAXB), respectively.

Lightweight HTTP Server

The Java SE reference implementation includes a lightweight HTTP server for deploying and testing Web services. The server implementation supports the HTTP and Hypertext Transfer Protocol Secure (HTTPS) protocols. Furthermore, its associated API can be used to create a customized Web server to enhance your Web service testing or for other purposes.

Exploring the Lightweight HTTP Server API

The Lightweight HTTP Server API is distributed in the following pair of packages where the first package contains the main API and the second package allows new lightweight HTTP server implementations to be installed:

  • com.sun.net.httpserver: This package provides a high-level HTTP server API for building embedded HTTP servers.
  • com.sun.net.httpserver.spi: This package provides a pluggable service provider API for installing HTTP server replacement implementations.

The com.sun.net.httpserver package contains an HttpHandler interface, which you must implement to handle HTTP request-response exchanges. This package also declares several classes, including the following key classes:

  • HttpServer: Implement a simple HTTP server bound to an IP address/port number, and listen for incoming TCP connections from clients. One or more associated HttpHandlers process requests and create responses.
  • HttpsServer: An HttpServer subclass that supports HTTPS. It must be associated with an HttpsConfigurator object to configure the HTTPS parameters for each incoming Secure Sockets Layer (SSL) connection.
  • HttpContext: Describe a mapping between a root URI path and an HttpHandler implementation that's invoked to handle those requests targeting the path.
  • HttpExchange: Encapsulate an HTTP request and its response. An instance of this class is passed to an HttpHandler instance to handle the request and generate a response.

This interface and these classes provide various methods that you'll use when implementing a lightweight HTTP server. Creating this implementation will involve the following tasks:

  1. Create a handler class that implements HttpHandler. The overriding void handle(HttpExchange exchange) method will access its exchange argument and invoke various HttpExchange methods, such as the following:
    • Headers getRequestHeaders(): Return an immutable map of an HTTP request's headers.
    • void sendResponseHeaders(int rCode, long responseLength): Begin to send a response back to the client using the current set of response headers and rCode's numeric code. For example, 200 indicates success.
    • OutputStream getResponseBody(): Return an output stream to which the response's body is output. This method must be called after calling sendResponseHeaders().
  2. Create the server. The abstract HttpServer class provides an HttpServer create(InetSocketAddress addr, int backlog) class method for creating a server that handles the HTTP protocol. This method's addr argument specifies a java.net.InetSocketAddress object containing an IP address and port number for the server's listening socket. The backlog argument specifies the maximum number of TCP connections that can be queued while waiting for acceptance by the server; a value less than or equal to zero causes a system default value to be used. Alternatively, you can pass null to addr or invoke HttpServer's HttpServer create() class method to create a server not bound to an address/port. If you choose this alternative, you will need to invoke HttpServer's void bind(InetSocketAddress addr, int backlog) method before you can use the server.
  3. Create a context. After creating the server, you need to create at least one context (an instance of a subclass of the abstract HttpContext class) that maps a root URI path to an implementation of HTTPHandler. Contexts help you organize the applications run by the server (via HTTP handlers). (The HttpServer Java documentation shows how incoming request URIs are mapped to HttpContext paths.) You create a context by invoking HttpServer's HttpContext createContext(String path, HttpHandler handler) method, where path specifies the root URI path, and handler specifies the HttpHandler implementation that handles all requests that target this path. If you prefer, you can invoke HttpContext createContext(String path) without specifying an initial handler. You would later specify the handler by calling HttpContext's void setHandler(HttpHandler h) method.
  4. Start the server. After you have created the server and at least one context (including a suitable handler), the final task is to start the server. Accomplish this task by calling HttpServer's void start() method.

Before invoking start(), you can specify a java.util.concurrent.Executor instance that handles all HTTP requests. Accomplish this task by calling HttpServer's void setExecutor(Executor executor) method. You can also call Executor getExecutor() to return the current executor (the return value is null when no executor has been set). If you don't call setExecutor() before starting the server, or if you pass null to this method, a default implementation based on the thread created by start() is used.

You can stop a started server by invoking HttpServer's void stop(int delay) method, which closes the listening socket and prevents any queued exchanges from being processed. It then blocks until all current exchange handlers have finished or delay seconds have elapsed (whichever comes first). A java.lang.IllegalArgumentException object is thrown when delay is less than zero. Continuing, all open TCP connections are closed, and the thread created by the start() method finishes. A stopped HttpServer instance cannot be restarted.

Demonstrating the Lightweight HTTP Server API

I've created a minimal HTTP server application that demonstrates the LightWeight HTTP Server API along with the aforementioned tasks. This application's source code appears in Listing 1.

Listing 1. A minimal HTTP server implementation

import java.io.IOException;
import java.io.OutputStream;

import java.net.InetSocketAddress;

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

import com.sun.net.httpserver.Headers;
import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpHandler;
import com.sun.net.httpserver.HttpServer;

public final class MinimalHTTPServer
{
   public static void main(String[] args) throws IOException
   {
      HttpServer server = HttpServer.create(new InetSocketAddress(8000), 0);
      server.createContext("/echo", new Handler());
      server.start();
   }
}

class Handler implements HttpHandler
{
   @Override
   public void handle(HttpExchange xchg) throws IOException
   {
      Headers headers = xchg.getRequestHeaders();
      Set<Map.Entry<String, List<String>>> entries = headers.entrySet();
      StringBuffer response = new StringBuffer();
      for (Map.Entry<String, List<String>> entry: entries)
         response.append(entry.toString() + "\n");
      xchg.sendResponseHeaders(200, response.length());
      OutputStream os = xchg.getResponseBody();
      os.write(response.toString().getBytes());
      os.close();
   }
}

Listing 1's handler echos an incoming request's headers back to the client.

Compile Listing 1 as follows:

javac MinimalHTTPServer.java

Run the resulting application as follows:

java MinimalHTTPServer

This application runs continously without displaying anything on the screen.

Start a Web browser and enter http://localhost:8000/echo. Don't forget that placing any path items before echo results in a 404 Not Found page. Figure 5 shows the echoed headers.

Figure 5. Echoing an incoming request's headers back to the client

Echoing an incoming request's headers back to the client

Conclusion

Now that you have a basic understanding of Web services and the support that Java offers for developing them, it's time to get practical. Part 2 of this series shows you how to build your own SOAP-based Web services in Java SE.

download
Get the source code for this post's applications. Created by Jeff Friesen for JavaWorld

The following software was used to develop the post's code:

  • 64-bit JDK 9ea+154

The post's code was tested on the following platform(s):

  • JVM on 64-bit Windows 8.1
1 2 Page 2
Page 2 of 2