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.
- Copiamos slideSample.xml a badSample.xml.
- 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> ...
- 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�. |