JAXP

Starting in Java 1.4, Sun bundled the Crimson XML parser and the SAX2, DOM2, and TrAX APIs into the standard Java class library. (TrAX is an XSLT API that sits on top of XML APIs like SAX and DOM. We’ll get to it in the last chapter.) They also threw in a couple of factory classes, and called the whole thing the “Java API for XML Processing” (JAXP).

Crimson’s a reasonably fast parser and worth your consideration. The fact that this implementation is bundled with Sun’s VM is a major plus. It allows you to distribute Java programs that use XML without having to add several megabytes of your own parser and interface classes. However, API-wise there isn’t a whole lot new here. When starting a new program you ask yourself whether you should choose SAX or DOM. You don’t ask yourself whether you should use SAX or JAXP, or DOM or JAXP. SAX and DOM are part of JAXP. If you know SAX, DOM, and TrAX, you know 99% of JAXP.

The only public part of JAXP that isn’t part of its component APIs are the factory classes in javax.xml.parsers. You can use these to create new documents in memory and load existing documents from text files and streams. You can also use the TrAX API to do some simple serialization by copying a document from a DOM object to a stream. Putting them all together, JAXP can replace most of the parser-dependent part of DOM. Example 5.6 is a JAXP client for the XML-RPC server. All the DOM standard code is the same as before in Example 5.5. However, the parser-dependent parts from the org.apache packages have been replaced with JAXP classes instead.

Example 5.6. A JAXP based client for the Fibonacci XML-RPC server

import java.net.*;
import java.io.*;
import org.w3c.dom.*;
import javax.xml.parsers.*;
import javax.xml.transform.*;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.dom.DOMSource;


public class FibonacciJAXPClient {

  private static String DEFAULT_SERVER 
   = "http://www.elharo.com/fibonacci/XML-RPC";  
  
  public static void main(String[] args) {
      
    if (args.length <= 0) {
      System.out.println(
       "Usage: java FibonacciJAXPClient number url"
      );
      return;
    }
    
    String server = DEFAULT_SERVER;
    if (args.length >= 2) server = args[1];
      
    try {       
      // Build the request document
      DocumentBuilderFactory builderFactory 
       = DocumentBuilderFactory.newInstance();
      DocumentBuilder builder 
       = builderFactory.newDocumentBuilder();
      Document request = builder.newDocument();

      Element methodCall = request.createElement("methodCall");
      request.appendChild(methodCall);
       
      Element methodName = request.createElement("methodName");
      Text text = request.createTextNode("calculateFibonacci");
      methodName.appendChild(text);
      methodCall.appendChild(methodName);
      
      Element params = request.createElement("params");
      methodCall.appendChild(params);
      
      Element param = request.createElement("param");
      params.appendChild(param);
      
      Element value = request.createElement("value");
      param.appendChild(value);
      
      // Had to break the naming convention here because of a 
      // conflict with the Java keyword int
      Element intElement = request.createElement("int");
      Text index = request.createTextNode(args[0]);
      intElement.appendChild(index);
      value.appendChild(intElement);  
      
      // Transmit the request document
      URL u = new URL(server);
      URLConnection uc = u.openConnection();
      HttpURLConnection connection = (HttpURLConnection) uc;
      connection.setDoOutput(true);
      connection.setDoInput(true); 
      connection.setRequestMethod("POST");
      OutputStream out = connection.getOutputStream();
      
      TransformerFactory xformFactory 
       = TransformerFactory.newInstance();
      Transformer idTransform = xformFactory.newTransformer();
      Source input = new DOMSource(request);
      Result output = new StreamResult(out);
      idTransform.transform(input, output);
      
      out.flush();
      out.close();        

      // Read the response
      InputStream in = connection.getInputStream();
      Document response = builder.parse(in);
      in.close();
      connection.disconnect();
      
      NodeList doubles = response.getElementsByTagName("double");
      Node datum = doubles.item(0);
      Text result = (Text) datum.getFirstChild();
      System.out.println(result.getNodeValue());
    }
    catch (Exception e) {
      System.err.println(e); 
    }
  
  } 

}

Once again, the request document is built as a tree. However, this time a DocumentBuilderFactory from JAXP does the building instead of the Xerces-specific DOMImplementationImpl class. Unlike DOMImplementation, DocumentBuilder creates the Document without a root element. Consequently the next step is to create the root methodCall element and append it to the Document’s list of children. The next lines after this are the same as in the FibonacciDOMClient.

When it becomes time to serialize the Document, the JAXP solution once again diverges. Here, FibonacciDOMClient used Xerces-specific classes. FibonacciJAXPClient uses TrAX. Specifically it creates a Transformer object initialized to perform an identity transformation. It sets the source for the transform to the original DOM request document and the result to the output stream connected to the server, and the document is transformed from one onto the other. It’s a little round-about, but it works.

Finally, parsing the server response is much the same as before. However, this time instead of using the Xerces-specific DOMParser class, we use the same DocumentBuilder that created the request document. DocumentBuilder may delegate the parsing to Xerces anyway, depending on which classes are where in your class path, and how certain environment variables are set. However, there’s no need for code at this level to know that implementation detail. If it becomes important to specify the parser for reasons of performance or conformance, you can set the javax.xml.parsers.DocumentBuilderFactory system property to the name of the factory class you want to load, which then chooses the parser class. If this property is not set, a reasonable default class will be used (most likely org.apache.crimson.jaxp.DocumentBuilderFactoryImpl from Crimson or org.apache.xerces.jaxp.DocumentBuilderFactoryImpl from Xerces).

The javax.xml.parsers package does fill a hole in DOM Level 2. However, it’s not as well designed as the similar classes that are coming down the road in DOM Level 3. The SAXBuilderFactory class is completely redundant with the much better designed XMLReaderFactory class that’s a standard part of SAX2. Frankly, javax.xml.parsers is just a legacy package dating from the days of SAX1 and DOM2, which did not provide this functionality. Forward-looking programmers can ignore it. What JAXP really is, is a bundle of the standard APIs. However, it is not a new API itself; and it is not an alternative to SAX or DOM.


Copyright 2001, 2002 Elliotte Rusty Haroldelharo@metalab.unc.eduLast Modified May 25, 2002
Up To Cafe con Leche