
Al igual que en una aplicación Java, una excepción es un objeto que es un ejemplar de java.lang.Throwable o de una de sus subclases. Throwable tiene dos subclases estándards -java.lang.Exception, que describe excepciones, y java.lang.Error, que describe errores.
Los errores son diferentes de las excepciones. Los errores normalmente indican problemas de enlaces o de la máquina virtual de los que nuestra aplicación Web podría no recuperarse, como errores de memoria. Sin embargo, las excepciones son condiciones que pueden capturarse y recuperarse de ellas. Estas excepciones podrían ser, por ejemplo, un NullPointerException o un ClassCastException, que nos dicen que se ha pasado un valor nulo o un dato del tipo erróneo a nuestra aplicación mientras se estaba ejecutando.
Las excepciones en tiempo de ejecución son fáciles de menejar en una aplicación JSP, porque estan almacenadas una cada vez en el objeto implícito llamado exception. Podemos usar el objeto exception en un tipo especial de página JSP llamado página de error, donde mostramos el nombre de la clase exception, su seguimiento de pila, y un mensaje informativo para el usuario.
Las excepciones en tiempo de ejecución son lanzadas por el fichero JSP compilado, el fichero class Java que contiene la versión traducida de nuestra página JSP. Esto significa que nuestra aplicación ha sido compilada y traducida correctamente. (Las excepciones que ocurren mientras un fichero está siendo compilado o traducido no son almacenadas en el objetoexception y tienen sus mensajes mostrados en la ventana de comandos, en vez de en la página de error. Estas no son el tipo de excepciones descritas en este tutorial.)
Este tutorial describe cómo crear una sencilla aplicación JSP con varias páginas, un componente JavaBean y una página de error que ofrece mensajes informativos al usuario. En este ejemplo, el Bean sigue la pista sobre la página en la que estaba trabajando el usuario cuando se lanzó la excepción, que nos da a nosotros, el desarrollador, información útil para que podamos mostrar un mensaje informativo. Este es un simple mecanismo de seguimiento de error.
Cuando añadimos una entrada al fichero map, introducimos tanto un nombre (la clave) como una dirección email (el valor). Podemos buscar o borrar una dirección email introduciendo sólo un nombre. El nombre no puede se null porque es una clave. Si un usuario intenta introducir un nombre null, la aplicación lanza una excepción y muestra una página de error.
Si estas viendo este documento en la pantalla, veras que algunos nodos son rojos y otros on negros.
El árbol rojo-negro tiene nodos que pueden ser ramas u hojas. Los nodos hojas son los nodos que hay al final de una línea, mientras que los nodos ramas son los nodos más grandes que conectan con dos o más líneas. Los nodos se almacenan en una estructura compensada en el árbol, usando las siguientes condiciones:
La ventaja de un árbol, para nosotros, los desarrolladores Web, es que podemos crear un fichero map que almacena datos en orden ascendente (ordenados por claves) y que tiene tiempos de búsqueda rápidos.
<%@ include file="copyright.html" %>
<%@ page isThreadSafe="false" import="java.util.*, email.Map"
errorPage="error.jsp" %>
<jsp:useBean id="mymap" scope="session" class="email.Map" />
<jsp:setProperty name="mymap" property="name" param="name" />
<jsp:setProperty name="mymap" property="email" param="email" />
<% mymap.setAction( "add" ); %>
<html>
<head><title>Email Finder</title></head>
<body bgcolor="#ffffff" background="background.gif" link="#000099">
<!-- the form table -->
<form method="get">
<table border="0" cellspacing="0" cellpadding="5">
<tr>
<td width="120"> </td>
<td align="right"> <h1>Email Finder</h1> </td>
</tr>
<tr>
<td width="120" align="right"><b>Name</b></td>
<td align="left"><input type="text" name="name" size="35"></td>
</tr>
<tr>
<td width="120" align="right"><b>Email Address</b></td>
<td align="left"><input type="text" name="email" size="35"></td>
</tr>
<tr>
<td width="120"> </td>
<td align="right">
Please enter a name and an email address.
</td>
</tr>
<tr>
<td width="120"> </td>
<td align="right">
<input type="submit" value="Add">
</td>
</tr>
<!-- here we call the put method to add the
name and email address to the map file -->
<%
String rname = request.getParameter( "name" );
String remail = request.getParameter( "email" );
if ( rname != null) {
mymap.put( rname, remail );
}
%>
<tr>
<td width="120"> </td>
<td align="right">
The map file has <font color="blue"><%= mymap.size() %>
</font> entries.
</font>
</td>
</tr>
<tr>
<td width="120"> </td>
<td align="right">
<a href="lookup.jsp">Lookup</a> |
<a href="delete.jsp">Delete</a>
</td>
</tr>
</table>
</form>
</body>
</html>
<%@ include file="copyright.html" %>
<%@ page isThreadSafe="false" import="java.util.*, email.Map"
errorPage="error.jsp" %>
<jsp:useBean id="mymap" scope="session" class="email.Map" />
<jsp:setProperty name="mymap" property="name" param="name" />
<% mymap.setAction( "lookup" ); %>
<html>
<head><title> Email Finder </title></head>
<body bgcolor="#ffffff" background="background.gif" link="#000099">
<form method="get">
<table border="0" cellspacing="0" cellpadding="5">
<tr>
<td width="120"> </td>
<td align="right"> <h1>Email Finder</h1> </td>
</tr>
<tr>
<td width="120" align="right"><b>Name</b></td>
<td align="left"><input type="text" name="name" size="35"></td>
</tr>
<tr>
<td width="120"> </td>
<td align="right">
Please enter a name for which
<br>
you'd like an email address.
</td>
</tr>
<tr>
<td width="120"> </td>
<td align="right">
The map file has <font color="blue"> <%= mymap.size() %></font>
entries.
</td>
</tr>
<tr>
<td width="120"> </td>
<td align="right"> <input type="submit" value="Lookup"> </td>
</tr>
<% if ( request.getParameter( "name" ) != null ) { %>
<%@ include file="lookupresponse.jsp" %>
<% } %>
<tr>
<td width="120"> </td>
<td align="right">
<a href="email.jsp">Add</a> |
<a href="delete.jsp">Delete</a>
</td>
</tr>
</table>
</body>
</html>
<%@ page import="java.util.*, email.Map" %> <tr> <td width="120"> </td> <td align="right"> <b> Success! </b> </td> </tr> <tr> <td width="120"> </td> <td align="right"> <jsp:getProperty name="mymap" property="name" /> <br> <jsp:getProperty name="mymap" property="email" /> </td> </tr>
<%@ include file="copyright.html" %>
<%@ page isThreadSafe="false" import="java.util.*, email.Map"
errorPage="error.jsp" %>
<jsp:useBean id="mymap" scope="session" class="email.Map" />
<jsp:setProperty name="mymap" property="name" param="name" />
<!-- tags the JSP page so that we can display
the right exception message later -->
<% mymap.setAction( "delete" ); %>
<html>
<head><title> Email Finder </title></head>
<body bgcolor="#ffffff" background="background.gif" link="#000099">
<form method="get">
<table border="0" cellspacing="0" cellpadding="5">
<tr>
<td width="120"> </td>
<td align="right"> <h1>Email Finder</h1> </td>
</tr>
<tr>
<td width="120" align="right"><b>Name</b></td>
<td align="left"> <input type="text" name="name" size="35"> </td>
</tr>
<tr>
<td width="120"> </td>
<td align="right">
Please enter a name you would like to delete.
</td>
</tr>
<tr>
<td width="120"> </td>
<td align="right">
The map file has <font color="blue"> <%= mymap.size() %></font>
entries.
</td>
</tr>
<tr>
<td width="120"> </td>
<td align="right"> <input type="submit" value="Delete"> </td>
</tr>
<!-- display the name and email address, then
delete them from the map file -->
<% if ( request.getParameter( "name" ) != null ) { %>
<%@ include file="deleteresponse.jsp" %>
<%
mymap.remove( request.getParameter("name") ) ;
}
%>
<tr>
<td width="120"> </td>
<td align="right">
<a href="email.jsp">Add</a> |
<a href="lookup.jsp">Lookup</a>
</td>
</tr>
</table>
</body>
</html>
<%@ page import="java.util.*, email.Map" %> <tr> <td width="120"> </td> <td align="right"> <b>Success!</b> </td> </tr> <tr> <td width="120"> </td> <td align="right"> <jsp:getProperty name="mymap" property="name" /> <br> <jsp:getProperty name="mymap" property="email" /> <br><p> has been deleted from the map file. </td> </tr>
<%@ include file="copyright.html" %>
<%@ page isErrorPage="true" import="java.util.*, email.Map" %>
<jsp:useBean id="mymap" scope="session" class="email.Map" />
<html>
<head><title>Email Finder</title></head>
<body bgcolor="#ffffff" background="background.gif" link="#000099">
<table border="0" cellspacing="0" cellpadding="5">
<tr>
<td width="150" align="right"> </td>
<td align="right" valign="bottom"> <h1> Email Finder </h1> </td>
</tr>
<tr>
<td width="150" align="right"> </td>
<td align="right"> <b>Oops! an exception occurred.</b> </td>
</tr>
<tr>
<td width="150" align="right"> </td>
<td align="right">The name of the exception is
<%= exception.toString() %>.
</td>
</tr>
<tr>
<td width="150" align="right"> </td>
<td align="right"> </td>
</tr>
<% if (mymap.getAction() == "delete" ) { %>
<tr>
<td width=150 align=right> </td>
<td align=right>
<b>This means that ...</b>
<p>The entry you were trying to
<font color="blue">delete</font> is not in the map file <br>
<b><i>or</i></b>
<br>
you did not enter a name to delete.
<p>
Want to try <a href="delete.jsp">again</a>?
</td>
</tr>
<% }
else if (mymap.getAction() == "lookup" ) { %>
<tr>
<td width="150" align="right"> </td>
<td align="right">
<b><i>This means that ...</b></i>
<p>the entry you were trying to
<font color="blue">look up</font>
is not in the map file, <b><i>or</i></b>
<br>
you did not enter a name to look up.
<p>
Want to try <a href="lookup.jsp">again</a>?
</td>
</tr>
<% }
else if (mymap.getAction() == "add" ) { %>
<tr>
<td width="150" align="right"> </td>
<td align="right">
<b><i>This means that ...</b></i>
<p>You were trying to <font color="blue">add</font>
an entry with a name of null.
<br>
The map file doesn't allow this.
<p>
Want to try <a href="email.jsp">again</a>?
</td>
</tr>
<% } %>
</table>
package email;
import java.util.*;
public class Map extends TreeMap {
// In this treemap, name is the key and email is the value
private String name, email, action;
private int count = 0;
public Map() { }
public void setName( String formName ) {
if ( formName != "" ) {
name = formName;
}
}
public String getName()
return name;
}
public void setEmail( String formEmail ) {
if ( formEmail != "" ) {
email = formEmail;
System.out.println( name ); // for debugging only
System.out.println( email ); // for debugging only
}
}
public String getEmail() {
email = get(name).toString();
return email;
}
public void setAction( String pageAction ) {
action = pageAction;
}
public String getAction() {
return action;
}
}
Los métodos que hemos usado de TreeMap son estos con sus excepciones:
La clase TreeMap lanza una ClassCastException cuando el usuario trata de introducir un dato del tipo erróneo en unf ichero map, por ejemplo, un int donde el fichero map está esperando un String. Tengamos en cuenta que la clase TreeMap también se usa en aplicaciones cliente Java. En nuestra aplicación JSP, esta aplicación no ocurrirá, porque el usuario introduce un nombre y una dirección email en un formulario HTML, que siempre pasa los datos al Bean como strings. Incluso si el usuario teclea 6 como un nombre, el valor envíado es un String.
Sin embargo, los métodos get, put, y remove lanzan una NullPointerException si el usuario no introduce nada o se pasa un valor null al Bean. Esta la excepción más comun que necesita manejar la aplicación email. Esta excepción podría ocurrir siempre que el usuario intente añadir, buscar o eliminar una entrada del fichero map. Recuerda que la clave, (en este caso el nombre) no peude ser null.
El código que maneja la adicción de valores null está en los métodos setName y setEmail de Map.java y en un scriptlet en email.jsp:
Map.java:
public void setName( String formName ) {
if ( formName != "" ) {
name = formName;
}
}
public void setEmail( String formEmail ) {
if ( formEmail != "" ) {
email = formEmail;
System.out.println( name ); // for debugging only
System.out.println( email ); // for debugging only
}
}
email.jsp:
<%
String rname = request.getParameter( "name" );
String remail = request.getParameter( "email" );
if ( rname != null) {
mymap.put( rname, remail );
}
%>
Tanto setName como setEmail chequean su el usuario ha introducido un valor en el formulario antes de seleccionar sus respectivas propiedades. Si el formulario es un valor null, el Bean no selecciona ninguna propiedad, el método put no añade nada al fichero map, y no se lanza ninguna excepción.
lookup.jsp:
<% if ( request.getParameter( "name" ) != null ) { %>
<%@ include file="lookupresponse.jsp" %>
<% } %>
lookupresponse.jsp:
<tr>
<td width="120"> </td>
<td align="right">
<font face="helvetica" size="-2">
<jsp:getProperty name="mymap" property="name" />
<br>
<jsp:getProperty name="mymap" property="email" />
</font>
</td>
</tr>
Este ejemplo tiene dos piezas de código que trabajan juntas. La página lookup.jsp, donde introducimos un nombre por el que queremos buscar en el fichero map, tiene un scriptlet que chequea si el usuario ha introducido un nombre en el formulario o no. Si el usuario no ha introducido un nombre o introduce uno que no existe en el fichero map, el Bean lanza una NullPointerException y la aplicación muestra una página de error -- que es el comportamiento deseado! En este caso, podemos estar felices porque se muestra la página de error.
Podríamos haber observado que las líneas del fichero lookupresponse.jsp usan la etiqueta <jsp:getProperty> para recuperar el nombre y la dirección email desde el Bean. También podríamos intentar recuperar la dirección email usando expresiones, algo como esto:
Si usamos estas líneas, el comportamiento de la aplicación sería un poco diferente. En vez de lanzar una NullPointerException y mostrar una página de error, mostraría el nombre que introdujo el usuario, con la palabra null debajo en la página JSP. En la implementación JSP de Sun, la etiqueta <jsp:getProperty> maneja intencionadamente las valores null de forma diferente que los scriptlets o las expresiones. La forma de manejar los valores Null depende del motor JSP utilizado.<%= request.getParameter( "name" ) %> <br> <%= mymap.get( request.getParameter( "name" ) ) %>
delete.jsp:
<% if ( request.getParameter( "name" ) != null ) { %>
<%@ include file="deleteresponse.jsp" %>
<%
mymap.remove( request.getParameter("name") ) ;
}
%>
deleteresponse.jsp:
<tr>
<td width="120"> </td>
<td align="right">
<font face="helvetica" size="-2">
<jsp:getProperty name="mymap" property="name" />
<br>
<jsp:getProperty name="mymap" property="email" />
<br><p>
has been deleted from the map file.
</font>
</td>
</tr>
En los ejemplos de código, los ficheros que usan esta directiva son email.jsp, lookup.jsp, y delete.jsp. Sólo podemos especificar un página de error por cada página JSP.<%@ page isThreadSafe="false" import="java.util.*, email.Map" errorPage="error.jsp" %>
Esto significa que podemos diseñar una aplicación JSP para que cada página JSP llame a una página de error diferente, o que varias páginas JSP llamen a un misma página de error. En la aplicación email, varias páginas JSP llaman a un página de error, a sí simplificamos el número de ficheros que necesitamos para mantener una aplicación.
Deberíamos usar al menos una página de error en una aplicación JSP. Si no especificamos una página de error, los mensajes de excepción y el seguimiento de pila se mostrarán en la ventana de comandos desde la que se arrancó el motor JSP, mientras que el navegador Web mostrará un mensaje de error HTTP no informativo, por ejemplo, un mensaje 404 o 501. Esta definitivamente no es una manera adecuada de manejar excepciones.
Primero, veamos un ejemplo de la directiva page de una página de error:
Una vez que hemos seleccionado isErrorPage a true, podemos usar el objeto exception. exception es del tipo java.lang.Throwable, por eso podemos usar cualquier método definido en Throwable con exception en un scriptlet o una expresión, por ejemplo:<%@ page isErrorPage="true" import="java.util.*, email.Map" %>
La expresión exception.toString() muestra el nombre de la clase de la excepción, por ejemplo, java.lang.NullPointerException, mientras que exception.printStackTrace() muestra el seguimiento de pila de la excepción. El nombre de la clase y el seguimiento de pila son probablemente muy útiles para nuestro usuario. Para evitar esto, podríamos querer escribir algún tipo de mecanismo de seguimiento para proporcionar información que nos ayude a darle un mensaje informativo a nuestro usuario.
private String action;
public void setAction( String pageAction ) {
action = pageAction;
}
public String getAction() {
return action;
}
Cada una de las páginas email.jsp, lookup.jsp, y delete.jsp seleccionan el valor de action con una línea como esta (que viene desde email.jsp):
Si ocurre una excepción, error.jsp chequea el valor de action e incluye el mensaje apropiado para cada valor, usando líneas como estas:<% mymap.setAction( "add" ); %>
<% if (mymap.getAction() == "delete" ) { %>
.. text message here ..
else if (mymap.getAction() == "lookup" ) { %>
.. text message here ..
else if (mymap.getAction() == "add" ) { %>
.. text message here ..
<% } %>
Por supuesto, esta es una forma sencilla de implementar seguimiento. Si nos movemos dentro del desarrollo de aplicaciones J2EE con beans enterprise, podemos escribir aplicaciones que graben el estado.
Los paths dados aquí son para un sistema UNIX, si estás usando Windows, deberás usar los mismos paths pero con el separador de directorios invertido: