The JavaTM Web Services Tutorial
Home
TOC
Index
PREV TOP NEXT
Divider

Handling Errors with the Nonvalidating Parser

This version of the Echo program uses the nonvalidating parser. So it can't tell if the XML document contains the right tags, or if those tags are in the right sequence. In other words, it can't tell you if the document is valid. It can, however, tell whether or not the document is well-formed.

In this section of the tutorial, you'll modify the slideshow file to generate different kinds of errors and see how the parser handles them. You'll also find out which error conditions are ignored, by default, and see how to handle them.

Introducing an Error

The parser can generate one of three kinds of errors: fatal error, error, and warning. In this exercise, you'll make a simple modification to the XML file to introduce a fatal error. Then you'll see how it's handled in the Echo app.


Note: The XML structure you'll create in this exercise is in slideSampleBad1.xml. The output is in Echo05-Bad1.txt. (The browsable versions are slideSampleBad1-xml.html and Echo05-Bad1.html.)

One easy way to introduce a fatal error is to remove the final "/" from the empty item element to create a tag that does not have a corresponding end tag. That constitutes a fatal error, because all XML documents must, by definition, be well formed. Do the following:

  1. Copy slideSample.xml to badSample.xml.
  2. Edit badSample.xml and remove the character shown below:
    ...
    <!-- OVERVIEW -->
    <slide type="all">
      <title>Overview</title>
      <item>Why <em>WonderWidgets</em> are great</item>
      <item/>
      <item>Who <em>buys</em> WonderWidgets</item>
    </slide>
    ...
     
    

to produce:

...
<item>Why <em>WonderWidgets</em> are great</item>
<item>
<item>Who <em>buys</em> WonderWidgets</item> 
...
 
  1. Run the Echo program on the new file.

The output now gives you an error message that looks like this (after formatting for readability):

org.xml.sax.SAXParseException: 
  The element type "item" must be terminated by the
  matching end-tag "</item>".
...
at org.apache.xerces.parsers.AbstractSAXParser...
...
at Echo.main(...)
 

Note: The message above was generated by the JAXP 1.2 libraries. If you are using a different parser, the error message is likely to be somewhat different.

When a fatal error occurs, the parser is unable to continue. So, if the application does not generate an exception (which you'll see how to do a moment), then the default error-event handler generates one. The stack trace is generated by the Throwable exception handler in your main method:

    ...
} catch (Throwable t) {
  t.printStackTrace();
}
 

That stack trace is not too useful, though. Next, you'll see how to generate better diagnostics when an error occurs.

Handling a SAXParseException

When the error was encountered, the parser generated a SAXParseException--a subclass of SAXException that identifies the file and location where the error occurred.


Note: The code you'll create in this exercise is in Echo06.java. The output is in Echo06-Bad1.txt. (The browsable version is Echo06-Bad1.html.)

Add the code highlighted below to generate a better diagnostic message when the exception occurs:

...
} catch (SAXParseException spe) {
  // Error generated by the parser
  System.out.println("\n** Parsing error" 
    + ", line " + spe.getLineNumber()
    + ", uri " + spe.getSystemId());
  System.out.println("   " + spe.getMessage() );

} catch (Throwable t) {
  t.printStackTrace();
}
 

Running the program now generates an error message which is a bit more helpful, like this:

** Parsing error, line 22, uri file:<path>/slideSampleBad1.xml
  The element type "item" must be ...
 

Note: The text of the error message depends on the parser used. This message was generated using JAXP 1.2.


Note: Catching all throwables like this is not generally a great idea for production applications. We're doing it now so we can build up to full error handling gradually. In addition, it acts as a catch-all for null pointer exceptions that can be thrown when the parser is passed a null value.

Handling a SAXException

A more general SAXException instance may sometimes be generated by the parser, but it more frequently occurs when an error originates in one of application's event handling methods. For example, the signature of the startDocument method in the ContentHandler interface is defined as returning a SAXException:

public void startDocument() throws SAXException
 

All of the ContentHandler methods (except for setDocumentLocator) have that signature declaration.

A SAXException can be constructed using a message, another exception, or both. So, for example, when Echo.startDocument outputs a string using the emit method, any I/O exception that occurs is wrapped in a SAXException and sent back to the parser:

private void emit(String s)
throws SAXException
{
  try {
    out.write(s);
    out.flush();
  } catch (IOException e) {
    throw new SAXException("I/O error", e);
  }
}
 

Note: If you saved the Locator object when setDocumentLocator was invoked, you could use it to generate a SAXParseException, identifying the document and location, instead of generating a SAXException.

When the parser delivers the exception back to the code that invoked the parser, it makes sense to use the original exception to generate the stack trace. Add the code highlighted below to do that:

   ...
} catch (SAXParseException err) {
  System.out.println("\n** Parsing error" 
    + ", line " + err.getLineNumber()
    + ", uri " + err.getSystemId());
  System.out.println("   " + err.getMessage());

} catch (SAXException sxe) {
  // Error generated by this application
  // (or a parser-initialization error)
  Exception  x = sxe;
  if (sxe.getException() != null)
    x = sxe.getException();
  x.printStackTrace();

} catch (Throwable t) {
  t.printStackTrace();
}
 

This code tests to see if the SAXException is wrapping another exception. If so, it generates a stack trace originating from where that exception occurred to make it easier to pinpoint the code responsible for the error. If the exception contains only a message, the code prints the stack trace starting from the location where the exception was generated.

Improving the SAXParseException Handler

Since the SAXParseException can also wrap another exception, add the code highlighted below to use the contained exception for the stack trace:

    ...
} catch (SAXParseException err) {
  System.out.println("\n** Parsing error" 
    + ", line " + err.getLineNumber()
    + ", uri " + err.getSystemId());
  System.out.println("   " + err.getMessage());

  // Use the contained exception, if any
  Exception  x = spe;
  if (spe.getException() != null)
    x = spe.getException();
  x.printStackTrace();
 
} catch (SAXException sxe) {
  // Error generated by this application
  // (or a parser-initialization error)
  Exception      x = sxe;
  if (sxe.getException() != null)
    x = sxe.getException();
  x.printStackTrace();
 
} catch (Throwable t) {
  t.printStackTrace();
}      
 

The program is now ready to handle any SAX parsing exceptions it sees. You've seen that the parser generates exceptions for fatal errors. But for nonfatal errors and warnings, exceptions are never generated by the default error handler, and no messages are displayed. In a moment, you'll learn more about errors and warnings and find out how to supply an error handler to process them.

Handling a ParserConfigurationException

Finally, recall that the SAXParserFactory class could throw an exception if it were for unable to create a parser. Such an error might occur if the factory could not find the class needed to create the parser (class not found error), was not permitted to access it (illegal access exception), or could not instantiate it (instantiation error).

Add the code highlighted below to handle such errors:

} catch (SAXException sxe) {
  Exception      x = sxe;
  if (sxe.getException() != null)
    x = sxe.getException();
  x.printStackTrace();

} catch (ParserConfigurationException pce) {
  // Parser with specified options can't be built
  pce.printStackTrace();

} catch (Throwable t) {
  t.printStackTrace();
 

Admittedly, there are quite a few error handlers here. But at least now you know the kinds of exceptions that can occur.


Note: A javax.xml.parsers.FactoryConfigurationError could also be thrown if the factory class specified by the system property cannot be found or instantiated. That is a non-trappable error, since the program is not expected to be able to recover from it.

Handling an IOException

Finally, while we're at it, let's add a handler for IOExceptions:

} catch (ParserConfigurationException pce) {
  // Parser with specified options can't be built
  pce.printStackTrace();

} catch (IOException ioe) {
  // I/O error
  ioe.printStackTrace();
}

} catch (Throwable t) {
  ...
 

We'll leave the handler for Throwables to catch null pointer errors, but note that at this point it is doing the same thing as the IOException handler. Here, we're merely illustrating the kinds of exceptions that can occur, in case there are some that your application could recover from.

Handling NonFatal Errors

A nonfatal error occurs when an XML document fails a validity constraint. If the parser finds that the document is not valid, then an error event is generated. Such errors are generated by a validating parser, given a DTD or schema, when a document has an invalid tag, or a tag is found where it is not allowed, or (in the case of a schema) if the element contains invalid data.

You won't actually dealing with validation issues until later in this tutorial. But since we're on the subject of error handling, you'll write the error-handling code now.

The most important principle to understand about non-fatal errors is that they are ignored, by default.

But if a validation error occurs in a document, you probably don't want to continue processing it. You probably want to treat such errors as fatal. In the code you write next, you'll set up the error handler to do just that.


Note: The code for the program you'll create in this exercise is in Echo07.java.

To take over error handling, you override the DefaultHandler methods that handle fatal errors, nonfatal errors, and warnings as part of the ErrorHandler interface. The SAX parser delivers a SAXParseException to each of these methods, so generating an exception when an error occurs is as simple as throwing it back.

Add the code highlighted below to override the handler for errors:

public void processingInstruction(String target, String data)
throws SAXException
{
  ...
}
 
// treat validation errors as fatal
public void error(SAXParseException e)
throws SAXParseException
{
  throw e;
}
 

Note: It can be instructive to examine the error-handling methods defined in org.xml.sax.helpers.DefaultHandler. You'll see that the error() and warning() methods do nothing, while fatalError() throws an exception. Of course, you could always override the fatalError() method to throw a different exception. But if your code doesn't throw an exception when a fatal error occurs, then the SAX parser will -- the XML specification requires it.

Handling Warnings

Warnings, too, are ignored by default. Warnings are informative, and require a DTD. For example, if an element is defined twice in a DTD, a warning is generated--it's not illegal, and it doesn't cause problems, but it's something you might like to know about since it might not have been intentional.

Add the code highlighted below to generate a message when a warning occurs:

// treat validation errors as fatal
public void error(SAXParseException e)
throws SAXParseException
{
  throw e;
}
 
// dump warnings too
public void warning(SAXParseException err)
throws SAXParseException
{
  System.out.println("** Warning"
    + ", line " + err.getLineNumber()
    + ", uri " + err.getSystemId());
  System.out.println("   " + err.getMessage());
}
 

Since there is no good way to generate a warning without a DTD or schema, you won't be seeing any just yet. But when one does occur, you're ready!

Divider
Home
TOC
Index
PREV TOP NEXT
Divider

This tutorial contains information on the 1.0 version of the Java Web Services Developer Pack.

All of the material in The Java Web Services Tutorial is copyright-protected and may not be published in other works without express written permission from Sun Microsystems.