Web services in Java SE, Part 4: SOAP with Attachments API for Java

Learn about SAAJ and other advanced Java SE Web service features

1 2 3 4 5 6 Page 4
Page 4 of 6

Integrating the SOAP message handler into UCClient

After declaring the handler class, you need to instantiate it and add the instance to the client's or Web service's handler chain, either via the @HandlerChain annotation or programmatically. Listing 5 demonstrates the programmatic approach by adding a SOAPLoggingHandler instance to UCClient's (see Part 2) handler chain.

Listing 5. Adding a SOAPHandler instance to UCClient's handler chain

import java.net.URL;

import java.util.List;

import javax.xml.namespace.QName;

import javax.xml.ws.Binding;
import javax.xml.ws.BindingProvider;
import javax.xml.ws.Service;

import javax.xml.ws.handler.Handler;

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);
      BindingProvider bp = (BindingProvider) uc;
      Binding binding = bp.getBinding();
      List<Handler> hc = binding.getHandlerChain();
      hc.add(new SOAPLoggingHandler());
      binding.setHandlerChain(hc);
      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));
   }
}

Listing 5's main() method accesses UCClient's handler chain and inserts an instance of SOAPLoggingHandler into this chain by completing the following steps:

  1. Cast the proxy instance returned from getPort() to javax.xml.ws.BindingProvider because the proxy instance's class implements this interface. BindingProvider provides access to the protocol binding and associated context objects for request and response message processing.
  2. Call BindingProvider's Binding getBinding() method to return the protocol binding instance, which is an instance of a class that ultimately implements the javax.xml.ws.Binding interface – the class actually implements Binding's javax.xml.ws.soap.SOAPBinding subinterface.
  3. Invoke Binding's List<Handler> getHandlerChain() method on this instance to return a copy of the handler chain.
  4. Instantiate SOAPLoggingHandler and add this instance to the java.util.List instance of Handler instances.
  5. Pass this list of handlers to Binding's void setHandlerChain(List<Handler> chain) method.

Compile the contents of Listing 5 (see Part 2). Assuming that UCPublisher is running (see Part 2), run UCClient (see Part 2). You should observe the following output (reformatted for readbility):

Outbound message:
<S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/" 
            xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
  <SOAP-ENV:Header/>
  <S:Body xmlns:ns2="http://uc.javajeff.ca/">
    <ns2:c2f>
      <arg0>37.0</arg0>
    </ns2:c2f>
  </S:Body>
</S:Envelope>


Inbound message:
<S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/" 
            xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
  <SOAP-ENV:Header/>
  <S:Body xmlns:ns2="http://uc.javajeff.ca/">
    <ns2:c2fResponse>
      <return>98.6</return>
    </ns2:c2fResponse>
  </S:Body>
</S:Envelope>


DC to DF: 37 DC = 98.600000 DF
Outbound message:
<S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/" 
            xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
  <SOAP-ENV:Header/>
  <S:Body xmlns:ns2="http://uc.javajeff.ca/">
    <ns2:cm2in>
      <arg0>10.0</arg0>
    </ns2:cm2in>
  </S:Body>
</S:Envelope>


Inbound message:
<S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/" 
            xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
  <SOAP-ENV:Header/>
  <S:Body xmlns:ns2="http://uc.javajeff.ca/">
    <ns2:cm2inResponse>
      <return>3.937007874015748</return>
    </ns2:cm2inResponse>
  </S:Body>
</S:Envelope>


CM to IN: 10 CM = 3.937008 IN
Outbound message:
<S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/" 
            xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
  <SOAP-ENV:Header/>
  <S:Body xmlns:ns2="http://uc.javajeff.ca/">
    <ns2:f2c>
      <arg0>212.0</arg0>
    </ns2:f2c>
  </S:Body>
</S:Envelope>


Inbound message:
<S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/" 
            xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
  <SOAP-ENV:Header/>
  <S:Body xmlns:ns2="http://uc.javajeff.ca/">
    <ns2:f2cResponse>
      <return>100.0</return>
    </ns2:f2cResponse>
  </S:Body>
</S:Envelope>


DF to DC: 212 DF = 100.000000 DC
Outbound message:
<S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/" 
            xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
  <SOAP-ENV:Header/>
  <S:Body xmlns:ns2="http://uc.javajeff.ca/">
    <ns2:in2cm>
      <arg0>10.0</arg0>
    </ns2:in2cm>
  </S:Body>
</S:Envelope>


Inbound message:
<S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/" 
            xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
  <SOAP-ENV:Header/>
  <S:Body xmlns:ns2="http://uc.javajeff.ca/">
    <ns2:in2cmResponse>
      <return>25.4</return>
    </ns2:in2cmResponse>
  </S:Body>
</S:Envelope>


IN to CM: 10 IN = 25.400000 CM

The S: and ns2: namespace prefixes are generated by JAX-WS.

Creating a customized lightweight HTTP server for authentication

You can create a customized lightweight HTTP server that offers additional features for testing a Web service, and replace the default lightweight HTTP server that's started in response to an Endpoint.publish() invocation with your server. What makes this possible is that Endpoint's void publish(Object serverContext) method can accept as its argument an instance of a class that subclasses the abstract com.sun.net.httpserver.HttpContext class.

This section shows you how to create a customized Lightweight HTTP Server for the purpose of demonstrating basic authentication with a Web service.

Authentication 101

Authentication is the process or action of verifying the identity of a user or a process. RFC 1945: Hypertext Transfer Protocol – HTTP/1.0 introduced support for authentication via a simple challenge-response mechanism that a server can use to challenge a client's request to access some resource. A client can use this mechanism to provide credentials (typically username and password) that authenticate (prove) the client's identity. When the supplied credentials satisfy the server, the user is authorized (allowed) to access the resource.

HTTP 1.0 introduced the basic authentication scheme by which a client identifies itself via a username and a password. The basic authentication scheme works as follows:

  1. The WWW-Authenticate header specifies Basic as the token and a single realm="quoted string" pair that identifies the realm (a protected space to which a resource belongs, such as a specific group of Web pages) referred to by the browser address.
  2. In response to this header, the browser displays a dialog box in which a username and password are entered.
  3. Once entered, the username and password are concatenated into a string (a colon is inserted between the username and password), the string is base64-encoded, and the result is placed in an Authorization header that's sent back to the server.
  4. The server base64-decodes these credentials and compares them to values stored in its username/password database. When there's a match, the application is granted access to the resource (and any other resource belonging to the realm).

Java supports basic authentication via the abstract java.net.Authenticator and java.net.PasswordAuthentication classes. Authenticator represents an object that knows how to obtain authentication for a network connection. This task is performed by overriding its PasswordAuthentication getPasswordAuthentication() method, which returns the password and user name supplied by the user or null when no such information is provided.

Demonstrating basic authentication via a customized lightweight HTTP server

Suppose you want to test basic authentication with the UC Web service introduced in Part 2 of this series. On the client side, you install a default authenticator that supplies a username and password to the Web service. Listing 6 reveals this authenticator in the context of UCClient.

Listing 6. Supporting basic authentication with the UCClient application

import java.net.Authenticator;
import java.net.PasswordAuthentication;
import java.net.URL;

import java.util.List;

import javax.xml.namespace.QName;

import javax.xml.ws.Binding;
import javax.xml.ws.BindingProvider;
import javax.xml.ws.Service;

import javax.xml.ws.handler.Handler;

import ca.javajeff.uc.UC;

public class UCClient
{
   public static void main(String[] args) throws Exception
   {
      Authenticator auth;
      auth = new Authenticator()
      {
         @Override
         protected PasswordAuthentication getPasswordAuthentication()
         {
            return new PasswordAuthentication("x", new char[] { 'y' });
         }
      };
      Authenticator.setDefault(auth);

      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);
      BindingProvider bp = (BindingProvider) uc;
      Binding binding = bp.getBinding();
      List<Handler> hc = binding.getHandlerChain();
      hc.add(new SOAPLoggingHandler());
      binding.setHandlerChain(hc);
      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));
   }
}

For simplicity, Listing 6 embeds x as the username and y as the password in the source code. A more useful and secure application would prompt for this information. At runtime the JVM invokes getPasswordAuthentication() to obtain these credentials and make them available to the HTTP server when requested to do so.

This method will not be called if the HTTP server doesn't make a request, and Part 2's version of UCPublisher will never cause the HTTP server to make this request. However, you can install a customized server that will result in this request, and Listing 7 presents an enhanced UCPublisher application that accomplishes this task.

Listing 7. Supporting basic authentication with the UCPublisher application

import java.io.IOException;

import java.net.InetSocketAddress;

import javax.xml.ws.Endpoint;

import com.sun.net.httpserver.BasicAuthenticator;
import com.sun.net.httpserver.HttpContext;
import com.sun.net.httpserver.HttpServer;

import ca.javajeff.uc.UCImpl;

public class UCPublisher
{
   public static void main(String[] args) throws IOException
   {
      HttpServer server = HttpServer.create(new InetSocketAddress(9901), 0);
      HttpContext context = server.createContext("/UC");
      BasicAuthenticator auth;
      auth = new BasicAuthenticator("myAuth")
      {
         @Override
         public boolean checkCredentials(String username, String password)
         {
            return username.equals("x") && password.equals("y");
         }
      };
      context.setAuthenticator(auth);
      Endpoint endpoint = Endpoint.create(new UCImpl());
      endpoint.publish(context);
      server.start();
   }
}

The main() method first creates an HttpServer instance that describes an HTTP server connected to port 9901 of the local host. This method next creates the /UC context, and returns the resulting HttpContext subclass object.

Continuing, the abstract com.sun.net.httpserver.BasicAuthenticator class is anonymously subclassed to describe a server side implementation of HTTP basic authentication; its boolean checkCredentials(String username, String password) method is called to verify the given name and password in the context of the basic authenticator's realm. This method returns true for valid credentials, and false when they are invalid.

1 2 3 4 5 6 Page 4
Page 4 of 6