★ wanayoo — archive 1999 http://java.sun.com/xml/docs/tutorial/sax/3_error.htmlNouvelle recherche | Portail wanayoo
Previous | Next | Index | TOC | Top | Top Contents Index Glossary


Handling Errors with the Nonvalidating Parser

Link Summary
Exercises

API Links

Glossary Terms

DTD, error, fatal error, valid, warning, well-formed

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.log.

One easy way to introduce a fatal error is to remove the final "/" from the empty slide 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 highlighted below:
  3.  ...
    <!-- 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>   
     ...
  4. Run the Echo program on the new file.

The output you get now looks like this:

...
        ELEMENT: <item>
        CHARS:   The 
            ELEMENT: <em>
            CHARS:   Only
            END_ELM: </em>
        CHARS:    Section
        END_ELM: </item>
    CHARS:   
    END_ELM: 
CHARS:   org.xml.sax.SAXParseException: Next character 
         must be ">" terminating element "slide".
at com.sun.xml.parser.Parser.fatal(Parser.java:2797)
at com.sun.xml.parser.Parser.fatal(Parser.java:2791)
at com.sun.xml.parser.Parser.nextChar(Parser.java:2715)
at com.sun.xml.parser.Parser.maybeElement(Parser.java:1410)
at com.sun.xml.parser.Parser.content(Parser.java:1498)
at com.sun.xml.parser.Parser.maybeElement(Parser.java:1399)
at com.sun.xml.parser.Parser.parseInternal(Parser.java:491)
at com.sun.xml.parser.Parser.parse(Parser.java:283)
at Echo05.main(Echo05.java:73)

<BUG> The diagnostic message should indicate that a terminator was not found for the item element. If it identifies any character at all, it should be looking for "<", rather than ">". This error should be fixed shortly. </BUG>

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.log.

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

  ...
} catch (SAXParseException err) {
     System.out.println ("** Parsing error" 
        + ", line " + err.getLineNumber ()
        + ", uri " + err.getSystemId ());
     System.out.println("   " + err.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 18, uri file:<path>/slideSampleBad1.xml
   Next character must be...

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 DocumentHandler interface is defined as returning a SAXException:

public void startDocument () throws SAXException

All of the DocumentHandler 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 ("** Parsing error" 
        + ", line " + err.getLineNumber ()
        + ", uri " + err.getSystemId ());
    System.out.println("   " + err.getMessage ());

} catch (SAXException e) {
    Exception x = e;
    if (e.getException () != null)
        x = e.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 it for the stack trace:

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

     // Unpack the delivered exception to get the exception it contains
     Exception x = err;
     if (err.getException () != null) 
         x = err.getException ();
     x.printStackTrace ();

} catch (SAXException e) {
    Exception	x = e;
    if (e.getException () != null)
        x = e.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. Next, you'll learn more about errors and warnings and find out how to supply an error handler to process them.

Understanding NonFatal Errors

In general, a nonfatal error occurs when an XML document fails a validity constraint. If the parser finds that the document is not valid (which means that it contains an invalid tag or a tag in location that is disallowed), then an error event is generated. In general, then, errors are generated by the ValidatingParser, given a DTD that tells it which tags are valid. There is one kind of error, though, that is generated by the nonvalidating parser you have been working with so far. You'll experiment with that error next.

Note: The file you'll create in this exercise is slideSampleBad2.xml. The output is in Echo06-Bad2.log.

The SAX specification requires an error event to be generated if the XML document uses a version of XML that the parser does not support. To generate such an error, make the changes shown below to alter your XML file so it specifies version="1.2".

<?xml version='1.02' encoding='us-ascii'?>

Now run your version of the Echo program on that file. What happens? (See below for the answer.)

Answer: Nothing happens! By default, the error is ignored. The output from the Echo program looks the same as if version="1.0" had beenproperly specified. To do something else, you need to supply your own error handler. You'll do that next.

Handling Nonfatal Errors

A standard treatment for "nonfatal" errors is to treat them as if they were fatal. After all, if a validation error occurs in a document you are processing, you probably don't want to continue processing it. In this exercise, you'll do exactly that.

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

To take over error handling, you supply the SAX parser with an ErrorHandler, which defines methods for handling fatal errors, nonfatal errors, and warnings. Add the code highlighted below to your Echo application to define an inner class for an error handler:

    ...
    System.exit (0);
}

  
static class MyErrorHandler extends HandlerBase
{
    public void error (SAXParseException e)
    throws SAXParseException
    {
        throw e;
    }

}

static private Writer out;
...

Once again, the class extends HandlerBase, which provides default implementations for the three error-event handlers that the ErrorHandler interface calls for. 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.

Next, add the code highlighted below to set up the parser so it uses an instance of your new error handler:

// Get an instance of the non-validating parser.
Parser parser;
parser = ParserFactory.makeParser ("com.sun.xml.parser.Parser");
parser.setDocumentHandler ( new Echo() );

          
// Set the new error handler
parser.setErrorHandler ( new MyErrorHandler () );

// Parse the input
parser.parse (input);

Now when you run your app on the file with the faulty version number, you get an exception, as shown here (but slightly reformatted for readability):

START DOCUMENT
<?xml version='1.0' encoding='UTF-8'?>
** Parsing error, line 1, uri file:/<path>/slideSampleBad2.xml XML version "1.0" is recognized, but not "1.2". org.xml.sax.SAXParseException: XML version "1.0" is recognized, but not "1.2". at com.sun.xml.parser.Parser.error(Parser.java:2775) at com.sun.xml.parser.Parser.readVersion(Parser.java:1051) at com.sun.xml.parser.Parser.maybeXmlDecl(Parser.java:983) at com.sun.xml.parser.Parser.parseInternal(Parser.java:477) at com.sun.xml.parser.Parser.parse(Parser.java:283) at Echo.main(Echo.java:76)

Note: The error actually occurs after the startDocument event has been generated, so the document header that the program "echoes" is the one it expects, rather than the one that is actually in the file.

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:

static class MyErrorHandler extends HandlerBase
{
    public void error (SAXParseException e)
    throws SAXParseException
    {
        throw e;
    }

    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, you won't be seeing any just yet. But when one does occur, you're ready!

Note: By default, HandlerBase throws an exception when a fatal error occurs. You could override the fatalError method to throw a different exception, if you like. But if your code doesn't, the Java XML SAX parser will.


Previous | Next | Index | TOC | Top | Top Contents Index Glossary