Zona HTML Zona Java Zona PHP Zona ASP Zona Bases de datos
-Tutoriales

El API JAXP


Manejar Errores con el Analizador sin Validación

Esta versión del programa Echo utiliza el analizador sin validación. Por eso no puede decir si el documento XML contiene las etiquetas correctas, o si esas etiquetas están en la secuencia correcta. En otras palabras, no puede decirnos si el documento es valido. Sin embargo, si puede decirnos si es o no un documento bien-formateado.

En esta sección del tutorial, modificaremos el fichero slides para generar distintos tipos de errores y veremos como el analizador los maneja. También encontraremos las condiciones de error que son ignoradas por defecto, y veremos cómo manejarlas.

. Introducir un Error

El analizador puede generar uno de estos tipos de errores: error fatal, error, aviso. En este ejercicio, haremos una sencilla modificación en el fichero XML para introducir un error fatal. Luego veremos como es manejado en la aplicación Echo.

Nota:

La estructura XML que crearemos en este ejercicio está en slideSampleBad1.xml. La salida está en Echo05-Bad1.log.

Una forma sencilla de introducir un error fatal es eliminar el "/" de un elemento vacío para crear una etiqueta que no tiene su correspondiente etiqueta final. Esto constituye un error fatal, porque todos los documentos XML deben, por definición, estar bien formateados. Hacemos lo siguiente.

  1. Copiamos slideSample.xml a badSample.xml.

  2. Editamos badSample.xml y eliminamos el caracter mostrado abajo:
     ...
    <!-- OVERVIEW -->
    <slide type="all">
      <title>Overview</title>
      <item>Why <em>WonderWidgets</em> are great</item>
      <item/>
      <item>Who <em>buys</em> WonderWidgets</item>
    </slide>
     ...
    

    para producir.

     ...
    <item>Why <em>WonderWidgets</em> are great</item>
    <item>
    <item>Who <em>buys</em> WonderWidgets</item>   
     ...
    
  3. Ejecutamos el programa Echo sobre el nuevo fichero.

La salida que obtenemos se debe parecer a esto.

...
        ELEMENT: <item>
        CHARS:   The 
            ELEMENT: <em>
            CHARS:   Only
            END_ELM: </em>
        CHARS:    Section
        END_ELM: </item>
    CHARS:   
    END_ELM.
    CHARS:   org.xml.sax.SAXParseException: Expected "</item>" 
         to terminate element starting on line 20.
at com.sun.xml.parser.Parser.fatal(Parser.java:2800)
at com.sun.xml.parser.Parser.fatal(Parser.java:2794)
at com.sun.xml.parser.Parser.maybeElement(Parser.java:1406)
at com.sun.xml.parser.Parser.content(Parser.java:1499)
at com.sun.xml.parser.Parser.maybeElement(Parser.java:1400)
at com.sun.xml.parser.Parser.content(Parser.java:1499)
at com.sun.xml.parser.Parser.maybeElement(Parser.java:1400)
at com.sun.xml.parser.Parser.parseInternal(Parser.java:492)
at com.sun.xml.parser.Parser.parse(Parser.java:284)
at javax.xml.parsers.SAXParser.parse(SAXParser.java:168)
at javax.xml.parsers.SAXParser.parse(SAXParser.java:104)
at javax.xml.parsers.SAXParser.parse(SAXParser.java:131)
at Echo05.main(Echo05.java:59)

Cuando ocurre un error fatal, el analizador no puede continuar. Por eso, si la aplicación no genera una excepción, el manejador de errores-eventos por defecto genera una. El seguimiento de pila es generado por el manejador de la excepción Throwable en nuestro método main.

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

. Manejar una SAXParseException

Cuando se encontró el error, el analizador generó una SAXParseException -- una subclase de SAXException que identifica el fichero y la posición donde ocurrió el error.

Nota:

El código que crearemos en este ejercicio está en Echo06.java. La salida está en Echo06-Bad1.log.

Añadimos el código en negrita de abajo para generar un mejor mensaje de diágnostico cuando ocurra la excepción.

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

Ahora la ejecución del programa genera un mensaje de error que es un poco más útil, de esta forma.

** Parsing error, line 22, uri file:<path>/slideSampleBad1.xml
   Next character must be...

. Manejar una SAXException

Un ejemplar más general de SAXException podría generarse algunas veces por el analizador, pero ocurre más frecuentemente cuando un error se origina en uno de los métodos manejadores de eventos de la aplicación. Por ejemplo, la firma del método startDocument en el interface DocumentHandler está definido para devolver un SAXException.

public void startDocument () throws SAXException

Todos los métodos DocumentHandler (excepto setDocumentLocator) tienen esta declaración de firma.

Una SAXException puede construirse usando un mensaje, otra excepción, o ambos. Por eso, por ejemplo, cuando Echo.startDocument saca un string usando el método emit, cualquier excepción de I/O que ocurra es envuelta en un SAXException y enviada de vuelta al analizador.

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

Si grabamos el objeto Locator cuando se invocó a setDocumentLocator, podríamos usarlo para generar una SAXParseException, que identifique el documento y la localización, en lugar de generar una SAXException.

Cuando el analizador envía la excepción del vuelta al código que lo invocó, tiene sentido usar la excepción original para generar el seguimiento. Añadimos el código en negrita de abajo para hacer esto.

 ...
} catch (SAXParseException err) {
    System.out.println ("** 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 ();
}

Este código prueba a ver si la SAXException envuelve otra excepción. Si es así, genera un seguimiento de pila originado desde donde ocurrió la excepción para hacer más sencillo apuntar al código responsable del error. Si la excepción sólo contiene un mensaje, el código imprime el seguimiento de pila empezando por la localización donde se generó la excepción.

. Mejorar el Manejador de SAXParseException

Como la SAXParseException también puede envolver otra excepción, añadimos el siguiente código en negrita para usarlo en el seguimiento de pila.

  ...
} 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 = spe;
       if (spe.getException() != null)
           x = spe.getException();
       x.printStackTrace();

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

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

El programa ya está listo para manejar cualquier excepción del analizador SAX que vea. Hemos visto que el analizador genera excepciones para errores fatales. Pero para los errores no fatales y los avisos nunca se generan excepciones por el manejador de error por defecto, y no se muestran mensajes. Luego, aprenderemos más sobre los errores y los avisos y veremos como suministrar un manejador de errores para procesarlos.

. Manejar un ParserConfigurationException

Finalmente, recordamos que la clase SAXParserFactory puede lanzar una excepción si no es capaz de crear un analizador. Dicho error podría ocurrir si la factoría no pudiera encontrar la clase necesaria para crear el analizador (class not found error), no se le permitiera el acceso a ella (illegal access exception), o no pudiera ejemplarizarla (instantiation error).

Añadimos el código en negrita para manejar dichos errores.

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

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

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

Este código, como el manejador SAXException, tiene en cuenta que la excepción reportada podría envolver otra excepción.

Nota:

También podría lanzarse una javax.xml.parsers.FactoryConfigurationError si la clase especificada para la factoria por la propiedad del sistema no puede encontrarse o ejemplarizarse. Este es un error no-atrapable, ya que no se espera que el programa no pueda recuperarse.

. Manejar una IOException

Y finalmente, dejemos de interceptar todos los objetos Throwable y capturemos las únicas excepciones que nos quedan, las IOExceptions.

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

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

. Entender los Errores no Fatales

En general, un error no fatal ocurre cuando un documento XML falla en una restricción de validación. Si el analizador encuentra que el documento no es valido (lo que significa que contiene una etiqueta no válida o en una localización no permitida), entonces se genera un evento de error. En general, los errores son generados por el ValidatingParser, dando un DTD que le dice qué etiquetas son válidas.

Nota:

El fichero que crearemos en este ejercicio es slideSampleBad2.xml. La salida está en Echo06-Bad2.log.

La especificación SAX requiere que se genere un evento de error si el documento XML usa una versión de XML que el analizador no puede soportar. Para generar dicho error, hacemos los siguientes cambios para modificar nuestro fichero XML y especificar la version="1.2".

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

Ahora ejecutamos nuestra versión del programa Echo sobre ese fichero. ¿Qué sucede?

Respuesta:¡No sucede nada! Por defecto, el error es ignorado. La salida el programa Echo parece la misma que si se hubiera especificado apropiadamente version="1.0". Para hacer algo más, necesitamos suministrar nuestro propio manejador de error. Haremos esto más adelante.

. Manejar Errores no Fatales

Un tratamiento estándard para errores "no fatales", es tratarlos como si fueran fatales. Después de todo, si ocurre un error de validación en un documento que estamos procesando, probablemente no queremos continuar procesandolo. En este ejercicio haremos exactamente esto.

Nota:

El código del programa que crearemos en este ejercicio está en Echo07.java. La salida está en Echo07-Bad2.log.

Para poder manejar el error, sobreescribimos los métodos HandlerBase que manejan errores fatales, errores no fatales y avisos como parte del interface ErrorHandler. El analizador SAX entrega una SAXParseException a cada uno de estos métodos, por eso generar una excepción cuando ocurre un error es tan simple como lanzarla de vuelta.

Añadimos el código en negrita de abajo para sobreescribir los manejadores de errores.

public void processingInstruction (String target, String data)
throws SAXException
{
  nl();
  emit ("PROCESS: ");
  emit ("<?"+target+" "+data+"?>");
}

// treat validation errors as fatal
public void error (SAXParseException e)
throws SAXParseException
{
  throw e;
}

Ahora cuando ejecutemos nuestra aplicación sobre el fichero con el número de versión errónea, obtendremos una excepción, como la mostrada aquí (pero ligeramente reformateada para mejor lectura).

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:2778)
at com.sun.xml.parser.Parser.readVersion(Parser.java:1052)
at com.sun.xml.parser.Parser.maybeXmlDecl(Parser.java:984)
at com.sun.xml.parser.Parser.parseInternal(Parser.java:478)
at com.sun.xml.parser.Parser.parse(Parser.java:284)
at javax.xml.parsers.SAXParser.parse(SAXParser.java:168)
at javax.xml.parsers.SAXParser.parse(SAXParser.java:104)
at javax.xml.parsers.SAXParser.parse(SAXParser.java:131)
at Echo07.main(Echo07.java:59) 
Nota:

El error realmente ocurre después de que se haya generado el evento startDocument. La cabecera del documento que el programa muestra es una de las creadas en presunción de que todo está bien, en vez de la que está realmente en el fichero.

. Manejar Avisos

Los avisos también son ignorados por defecto. Los avisos son informativos, y requieren un DTD. Por ejemplo, si un elemento está definido dos veces en un DTD, se genera un aviso -- no es ilegal, y no causa problemas, pero es algo que queremos saber ya que podría no haber sido intencionado.

Añadimos el código en negrita de abajo para generar un mensaje cuando ocurre un aviso.

// 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 ());
}
Nota:

Por defecto, HandlerBase lanza una excepción cuando ocurre un error fatal. Podríamos sobreescribir el método fatalError para que lance una excepción diferente, si queremos. Pero si nuestro código no lo hace, la implementación del referencia del analizador SAX lo hará.

 
Patrocinados
 

Copyright © 1999-2010 Programación en castellano. Todos los derechos reservados.
Formulario de Contacto - Datos legales - Publicidad

diseño y desarrollo web por Color Vivo Internet. Un proyecto de los Hermanos Carrero