Book HomeJava and XML, 2nd EditionSearch this book

3.4. Error Handlers

In addition to providing the ContentHandler interface for handling parsing events, SAX provides an ErrorHandler interface that can be implemented to treat various error conditions that may arise during parsing. This class works in the same manner as the document handler already constructed, but defines only three callback methods. Through these three methods, all possible error conditions are handled and reported by SAX parsers. Here's a look at the ErrorHandler interface:

public interface ErrorHandler {
    public abstract void warning (SAXParseException exception)
		throws SAXException;
    public abstract void error (SAXParseException exception)
		throws SAXException;
    public abstract void fatalError (SAXParseException exception)
		throws SAXException;
}

Each method receives information about the error or warning that has occurred through a SAXParseException. This object holds the line number where the trouble was encountered, the URI of the document being treated (which could be the parsed document or an external reference within that document), and normal exception details such as a message and a printable stack trace. In addition, each method can throw a SAXException. This may seem a bit odd at first; an exception handler that throws an exception? Keep in mind that each handler receives a parsing exception. This can be a warning that should not cause the parsing process to stop or an error that needs to be resolved for parsing to continue; however, the callback may need to perform system I/O or another operation that can throw an exception, and it needs to be able to send any problems resulting from these actions up the application chain. It can do this through the SAXException the error handler callback is allowed to throw.

As an example, consider an error handler that receives error notifications and writes those errors to an error log. This callback method needs to be able to either append to or create an error log on the local filesystem. If a warning were to occur within the process of parsing an XML document, the warning would be reported to this method. The intent of the warning is to give information to the callback and then continue parsing the document. However, if the error handler could not write to the log file, it might need to notify the parser and application that all parsing should stop. This can be done by catching any I/O exceptions and rethrowing these to the calling application, thus causing any further document parsing to stop. This common scenario is why error handlers must be able to throw exceptions (see Example 3-2).

Example 3-2. Error handler that may throw a SAXException

public void warning(SAXParseException exception)
    throws SAXException {

    try {
        FileWriter fw = new FileWriter("error.log");
        BufferedWriter bw = new BufferedWriter(fw);
        bw.write("Warning: " + exception.getMessage( ) + "\n");
        bw.flush( );
        bw.close( );
        fw.close( );
    } catch (IOException e) {
        throw new SAXException("Could not write to log file", e);
    }
}

With this in mind, it's possible to define the skeleton of an ErrorHandler implementation and register it with the reader implementation in the same way that the content handler was registered. In the interests of keeping this book from becoming a treatise on Swing, these methods will just stop parsing and report warnings and errors through the command line. First, add another nonpublic class to the end of the SAXTreeViewer.java source file:

class JTreeErrorHandler implements ErrorHandler {

    // Method implementations

}

Next, in order to actually use the custom error handler, you need to register this error handler with your SAX reader. This is done with the setErrorHandler( ) method on the XMLReader instance, and needs to occur in the example's buildTree( ) method:

    public void buildTree(DefaultTreeModel treeModel, 
                          DefaultMutableTreeNode base, String xmlURI) 
        throws IOException, SAXException {

        // Create instances needed for parsing
        XMLReader reader = 
            XMLReaderFactory.createXMLReader(vendorParserClass);
        ContentHandler jTreeContentHandler = 
            new JTreeContentHandler(treeModel, base);
        ErrorHandler jTreeErrorHandler = new JTreeErrorHandler( );

        // Register content handler
        reader.setContentHandler(jTreeContentHandler);

        // Register error handler
        reader.setErrorHandler(jTreeErrorHandler);

        // Parse
        InputSource inputSource = 
            new InputSource(xmlURI);
        reader.parse(inputSource);
    }

Finally, let's take a look at coding the three methods required by the ErrorHandler interface.

3.4.1. Warnings

Any time a warning (as defined by the XML 1.0 specification) occurs, this method is invoked in the registered error handler. There are several conditions that can generate a warning; however, all of them are related to the DTD and validity of a document, and I will discuss them in the next chapter. For now, you just need to define a simple method that prints out the line number, URI, and warning message when a warning occurs. Because (for demonstration purposes) I want any warnings to stop parsing, this code throws a SAXException and lets the wrapping application exit gracefully, cleaning up any used resources:

    public void warning(SAXParseException exception)
        throws SAXException {
            
        System.out.println("**Parsing Warning**\n" +
                           "  Line:    " + 
                              exception.getLineNumber( ) + "\n" +
                           "  URI:     " + 
                              exception.getSystemId( ) + "\n" +
                           "  Message: " + 
                              exception.getMessage( ));        
        throw new SAXException("Warning encountered");
    }

3.4.2. Nonfatal Errors

Errors that occur within parsing that can be recovered from, but constitute a violation of some portion of the XML specification, are considered nonfatal errors. An error handler should always at least log these, as they are typically serious enough to merit informing the user or administrator of the application, if not so critical as to cause parsing to cease. Like warnings, most nonfatal errors are concerned with validation, and will be covered in the next chapter in more detail. Also like warnings, in the example this error handler just reports the information sent to the callback method and exits the parsing process:

    public void error(SAXParseException exception)
        throws SAXException {
        
        System.out.println("**Parsing Error**\n" +
                           "  Line:    " + 
                              exception.getLineNumber( ) + "\n" +
                           "  URI:     " + 
                              exception.getSystemId( ) + "\n" +
                           "  Message: " + 
                              exception.getMessage( ));
        throw new SAXException("Error encountered");
    }

3.4.3. Fatal Errors

Fatal errors are those that necessitate stopping the parser. These are typically related to a document not being well-formed, and make further parsing either a complete waste of time or technically impossible. An error handler should almost always notify the user or application administrator when a fatal error occurs; without intervention, these can bring an application to a shuddering halt. For the example, I'll just emulate the behavior of the other two callback methods, stopping the parsing and writing an error message to the screen when a fatal error is encountered:

    public void fatalError(SAXParseException exception)
        throws SAXException {
    
        System.out.println("**Parsing Fatal Error**\n" +
                           "  Line:    " + 
                              exception.getLineNumber( ) + "\n" +
                           "  URI:     " + 
                              exception.getSystemId( ) + "\n" +
                           "  Message: " + 
                              exception.getMessage( ));        
        throw new SAXException("Fatal Error encountered");
    }

With this third error handler coded, you should be able to compile the example source file successfully and run it on the XML document again. Your output should not be any different than it was earlier, as there are no reportable errors in the XML. Next, I'll show you how to make some of these errors occur (for testing purposes, of course!).

3.4.4. Breaking the Data

Now that some error handlers are in place, it is worthwhile to generate some problems and see these handlers in action. Most warnings and nonfatal errors are associated with document validity issues, which I will address in the next chapter (when turning on validation is covered in detail). However, there is one nonfatal error that results from a nonvalidated XML document, involving the version of XML that a document reports. To view this error, make the following change to the first line of the XML table of contents example:

<?xml version="1.2"?>

Now run the Java SAX viewer program on the modified XML document. Your output should be similar to that shown here:

C:\javaxml2\build>java javaxml2.SAXTreeViewer ..\ch03\xml\contents.xml
**Parsing Error**
  Line:    1
  URI:     file:///C:/javaxml2/ch03/xml/contents.xml
  Message: XML version "1.2" is not supported.
org.xml.sax.SAXException: Error encountered

When an XML parser is operating upon a document that reports a version of XML greater than that supported by the parser, a nonfatal error is reported, in accordance with the XML 1.0 specification. This tells an application that newer features expected to be utilized by the document may not be available within the parser and the version that it supports. Because parsing continues, this is a nonfatal error. However, because it signifies a major impact on the document (such as newer syntax possibly generating subsequent errors), it is considered more important than a warning. This is why the error( ) method is invoked and triggers the error message and parsing halt in the example program.

All other meaningful warnings and nonfatal errors will be discussed in the next chapter; still, there is a variety of fatal errors that a nonvalidated XML document may have. These are related to an XML document not being well-formed. There is no logic built into XML parsers to try to resolve or estimate fixes to malformed XML, so an error in syntax results in the parsing process halting. The easiest way to demonstrate one of these errors is to introduce problems within your XML document. Reset the XML declaration to specify an XML version of 1.0, and make the following change to the XML document:

<?xml version="1.0"?>
<!DOCTYPE Book SYSTEM "DTD/JavaXML.dtd">

<!-- Java and XML Contents -->
<book xmlns="http://www.oreilly.com/javaxml2"
      xmlns:ora="http://www.oreilly.com"
>
  <!-- Note the missing end slash on the title element -->
  <title ora:series="Java">Java and XML<title>

  <!-- Rest of content -->
</book>

This is no longer a well-formed document. To see the fatal error that parsing this document generates, run the SAXVTreeViewer program on this modified file to get the following the output:

C:\javaxml2\build>java javaxml2.SAXTreeViewer ..\ch03\xml\contents.xml
**Parsing Fatal Error**
  Line:    23
  URI:     file:///C:/javaxml2/ch03/xml/contents.xml
  Message: The element type "title" must be terminated by the matching 
           end-tag "</title>".
org.xml.sax.SAXException: Fatal Error encountered

The parser reports an incorrect ending to the title element. This fatal error is exactly as expected; parsing could not continue beyond this error. With this error handler, you begin to see what can go wrong within the parsing process, as well as how to handle those events. In Chapter 4, "Advanced SAX " I will revisit the error handler and its methods and look at the problems that can be reported by a validating parser.



Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.