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 (see ).

ErrorHandler defines only three methods, but how you implement these methods can have a huge impact on the user experience

Java ScreenShot

This interface works in the same manner as the document handler already constructed, but defines only three callback methods. Through these three methods, all error conditions are handled and reported by SAX parsers. 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 of these methods 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 might 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 another exception, and the method needs to be able to send any problems resulting from these actions up the app chain. 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 occurs 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 cannot write to the logfile, it should notify the parser and app that all parsing should stop. This can be done by catching any I/O exceptions and rethrowing these to the calling app, thus causing any further document parsing to stop. This common scenario is why error handlers must be able to throw exceptions (see ).

Example Logging a problem in an error handler is a good way to find out what problems might have occurred that don't warrant halting all parsing

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);
 }
}

It's pretty easy 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. Add another interface to the list that JTReeHandler already implements:

class JTreeHandler implements ContentHandler, ErrorHandler {
Java Tip It's pretty common practice to have one class implement multiple handlers, as shown here.

Next, 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:

public void buildTree(DefaultTreeModel treeModel,
 DefaultMutableTreeNode base, String xmlURI)
 throws IOException, SAXException {
 // Create instances needed for parsing
 XMLReader reader =
 XMLReaderFactory.createXMLReader( );
 JTreeHandler jTreeHandler =
 new JTreeHandler(treeModel, base);
 // Register content handler
 reader.setContentHandler(jTreeHandler);
 // Register error handler
 reader.setErrorHandler(jTreeHandler);
 // Parse
 InputSource inputSource = new InputSource(xmlURI);
 reader.parse(inputSource);
}

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, all related to the DTD and validity of a document. The code in this example writes the warning to a log, and then tries to continue parsing (by not rethrowing a SAXException):

public void warning(SAXParseException exception)
 throws SAXException {
 try {
 FileWriter fw = new FileWriter("error.log");
 BufferedWriter bw = new BufferedWriter(fw);
 bw.write("**Warning**\n");
 bw.write("\tLine: ");
 bw.write(exception.getLineNumber( ));
 bw.write("\n\tURI: ");
 bw.write(exception.getSystemId( ));
 bw.write("\n\tMessage: ");
 bw.write(exception.getMessage( ));
 bw.write("\n\n");
 bw.flush( );
 bw.close( );
 fw.close( );
 } catch (IOException e) {
 throw new SAXException("Could not write to log file", e);
 }
}

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 app, if not so critical as to cause parsing to cease:

public void error(SAXParseException exception)
 throws SAXException {
 try {
 FileWriter fw = new FileWriter("error.log");
 BufferedWriter bw = new BufferedWriter(fw);
 bw.write("**Error**\n");
 bw.write("\tLine: ");
 bw.write(exception.getLineNumber( ));
 bw.write("\n\tURI: ");
 bw.write(exception.getSystemId( ));
 bw.write("\n\tMessage: ");
 bw.write(exception.getMessage( ));
 bw.write("\n\n");
 bw.flush( );
 bw.close( );
 fw.close( );
 } catch (IOException e) {
 throw new SAXException("Could not write to log file", e);
 }
}

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 app administrator when a fatal error occurs; without intervention, these can bring an app to a shuddering halt. For the example, I'll just emulate the behavior of the other two callback methods, but then go on to rethrow a SAXException, which will cause all parsing to halt:

public void fatalError(SAXParseException exception)
 throws SAXException {
 try {
 FileWriter fw = new FileWriter("error.log");
 BufferedWriter bw = new BufferedWriter(fw);
 bw.write("**Fatal Error**\n");
 bw.write("\tLine: ");
 bw.write(exception.getLineNumber( ));
 bw.write("\n\tURI: ");
 bw.write(exception.getSystemId( ));
 bw.write("\n\tMessage: ");
 bw.write(exception.getMessage( ));
 bw.write("\n\n");
 bw.flush( );
 bw.close( );
 fw.close( );
 // Bail out
 throw new SAXException("Fatal Error! Check the log!");
 } catch (IOException e) {
 throw new SAXException("Could not write to log file", e);
 }
}

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.

Breaking the Data

With some error handlers in place, it is worthwhile to generate some problems and see these handlers in action. To generate a sample error, make the following change to the first line of your XML document:

<?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 (parsing halts, obviously):

java javaxml3.SAXTreeViewer as_you.xml org.xml.sax.SAXException: Fatal Error! Check the log!
 at javaxml3.JTreeHandler.fatalError(SAXTreeViewer.java:289)
 at com.sun.org.apache.xerces.internal.util.ErrorHandlerWrapper.
 fatalError(ErrorHandlerWrapper.java:218)
 at com.sun.org.apache.xerces.internal.impl.XMLErrorReporter.
 reportError(XMLErrorReporter.java:386)
 at com.sun.org.apache.xerces.internal.impl.XMLErrorReporter.
 reportError(XMLErrorReporter.java:316)
 at com.sun.org.apache.xerces.internal.impl.XMLScanner.
 reportFatalError(XMLScanner.java:1438)
 at com.sun.org.apache.xerces.internal.impl.XMLScanner.
 scanXMLDeclOrTextDecl(XMLScanner.java:436)
 at com.sun.org.apache.xerces.internal.impl.XMLDocumentFragmentScannerImpl.
 scanXMLDeclOrTextDecl(XMLDocumentFragmentScannerImpl.java:710)
 at com.sun.org.apache.xerces.internal.impl.
 XMLDocumentScannerImpl$XMLDeclDispatcher.dispatch(
 XMLDocumentScannerImpl.java:721)
 at com.sun.org.apache.xerces.internal.impl.
 XMLDocumentFragmentScannerImpl.scanDocument(
 XMLDocumentFragmentScannerImpl.java:368)
 at com.sun.org.apache.xerces.internal.parsers.
 XML11Configuration.parse(XML11Configuration.java:834)
 at com.sun.org.apache.xerces.internal.parsers.
 XML11Configuration.parse(XML11Configuration.java:764)
 at com.sun.org.apache.xerces.internal.parsers.XMLParser.parse(
 XMLParser.java:148)
 at com.sun.org.apache.xerces.internal.parsers.AbstractSAXParser.parse(
 AbstractSAXParser.java:1242)
 at javaxml3.SAXTreeViewer.buildTree(SAXTreeViewer.java:72)
 at javaxml3.SAXTreeViewer.init(SAXTreeViewer.java:47)
 at javaxml3.SAXTreeViewer.main(SAXTreeViewer.java:84)

Check the newly created error.log, and you should see this entry:

**Fatal Error**
 Line: ^A
 URI: file:///Users/bmclaugh/Documents/Oracle/Writing/
 Java%20and%20XML%203rd/subs/code/ch03/as_you.xml
 Message: XML version "1.2" is not supported, only XML 1.0 is supported.

When an XML parser is operating on a document that reports a version of XML greater than that supported by the parser, a fatal error is reported. This tells an app that newer features expected to be used by the document may not be available within the parser and the version that it supports.