1. Introducción
2. El módulo de almacenamiento
2.1. La base de datos
2.2. Gestión de la base de datos: clase FAQBD
2.3. Excepciones que se tratan: clases FAQBDException y
FAQDesconocidoException
3. El módulo de administración
3.1. El servlet de administración: clase FAQCentralServlet
3.2. Seguridad en las transacciones: clase FAQComandoToken
3.3. Páginas JSP
3.4. Comandos posibles
4.1. Servlet de acceso Web: clase FAQServlet
4.2. Páginas JSP para acceso a las FAQs
5. Configuración de la aplicación
5.1. Fichero descriptor (web.xml)
5.2. Fichero de usuarios (tomcat-users.xml)
La estructura de ficheros y directorios de la aplicación es la siguiente:
/ (aquí se deben colocar las páginas JSP del módulo de acceso web) /jsp (aquí se colocarán las páginas JSP del módulo de administración) /WEB-INF/tomcat-users.xml /WEB-INF/web.xml /WEB-INF/classes/faqs/FAQBean.java /WEB-INF/classes/faqs/bd/FAQBD.java /WEB-INF/classes/faqs/bd/FAQBDException.java /WEB-INF/classes/faqs/bd/FAQDesconocidoException.java /WEB-INF/classes/faqs/comandos/FAQComando.java /WEB-INF/classes/faqs/comandos/FAQComandoAbortar.java /WEB-INF/classes/faqs/comandos/FAQComandoDelete.java /WEB-INF/classes/faqs/comandos/FAQComandoException.java /WEB-INF/classes/faqs/comandos/FAQComandoGet.java /WEB-INF/classes/faqs/comandos/FAQComandoGetAll.java /WEB-INF/classes/faqs/comandos/FAQComandoInsert.java /WEB-INF/classes/faqs/comandos/FAQComandoNull.java /WEB-INF/classes/faqs/comandos/FAQComandoToken.java /WEB-INF/classes/faqs/comandos/FAQComandoUpdate.java /WEB-INF/classes/faqs/servlets/FAQCentralServlet.java /WEB-INF/classes/faqs/servlets/FAQServlet.java /WEB-INF/lib/mm.mysql-2.0.4-bin.jar
Como ejercicio se va a desarrollar un sistema que permite gestionar (crear, manipular y visualizar) una lista de FAQs (Frequently Asked Questions), manteniendo las mismas en una base de datos, y haciéndolas accesibles vía web. El sistema de FAQs permitirá añadir, actualizar y eliminar entradas de la lista de preguntas, y también podremos consultar las preguntas que hay en cada momento en la base de datos.
La aplicación deberá tener las siguientes características:
Se permitirá que los usuarios de la intranet del sitio web puedan manipular las FAQs, y que los usuarios externos puedan obtener los listados de las mismas.
Para desarrollar la aplicación se va a dividir en 3 módulos que pueden desarrollarse individual e independientemente.
Todas las clases Java están dentro del paquete faqs (cada una en el subpaquete que corresponda). Las páginas JSP de administración se encuentran en el directorio jsp, y las páginas Web visibles desde fuera (las del módulo de acceso Web) quedan en el directorio raíz.
Para poder intercambiar información entre módulos, se tiene creada una clase que representa cada FAQ. Es la clase FAQBean (dentro del paquete faqs):
package faqs; import java.util.Date; public class FAQBean { int id; // Identificador del FAQ String pregunta; // Texto de la pregunta String respuesta; // Texto de la respuesta Date fechaModif; // Fecha última modificacion public FAQBean() { id = 0; pregunta = ""; respuesta = ""; fechaModif = new Date(); } // === METODOS GET Y SET PARA OBTENER CADA CAMPO === public int getID() { return id; } public void setID(int pID) { id = pID; } public String getPregunta() { return pregunta; } public void setPregunta(String pPregunta) { pregunta = pPregunta; fechaModif = new Date(); } public String getRespuesta() { return respuesta; } public void setRespuesta(String pRespuesta) { respuesta = pRespuesta; fechaModif = new Date(); } public Date getFechaModif() { return fechaModif; } public void setFechaModif(Date pFecha) { fechaModif = pFecha; } public String toString() { return "[" + id + "] " + "Pregunta: " + pregunta + "; Respuesta: " + respuesta + "\n"; } }
Los métodos para modificar el ID y la fecha no se utilizan realmente (se modifican internamente). También notar que al modificar el texto de la pregunta o la respuesta, se actualiza la fecha de última modificación.
Vemos que para cada FAQ se almacenan 4 datos:
Este módulo controla el acceso a la base de datos donde se almacenan las FAQs. Así se separa la administración y acceso web de la manipulación de la base de datos, y se proporciona una abstracción que permite modificar este módulo sin tener que alterar el resto de la aplicación (el acceso a base de datos es transparente, e incluso podría considerarse utilizar otro sistema de almacenamiento (ficheros de texto, etc)).
Las clases Java utilizadas en este módulo se encuentran en el paquete faqs.bd.
La base de datos se llama faqs, y tiene una sola tabla, faqs, con los siguientes campos:
Notamos que es una estructura idéntica a los campos de la clase FAQBean.
Tenemos aquí un fichero ZIP con la base de datos. Para instalarla:
Para ello, si tenéis un usuario creado con login root y password mysql, seguid los pasos siguientes (si no, cambiad dicho login y password por el que tengáis, a la hora de seguir los pasos):mysql -uroot -pmysql < faqs.sql
mysql -uroot -pmysql
GRANT ALL PRIVILEGES ON faqs.* to 'root'@'localhost.localdomain' identified by 'mysql'; FLUSH PRIVILEGES;
La clase FAQBD se encarga de gestionar todo el acceso a base de datos (selección, inserción, eliminación,...). La clase FAQBean se emplea como "intermediaria" entre la base de datos y la aplicación.
Detalles sobre la implementación
Se capturan todas las excepciones (SQLException y otras) para tratarlas como FAQBDException, un tipo propio de excepción. Como subtipo se tiene FAQDesconocidoException, que se lanza cuando no se puede localizar una FAQ.
Detalles sobre la implementación
Este módulo es una herramienta que permite al personal encargado crear y mantener la base de datos de FAQs. Se proporciona una interfaz basada en páginas JSP que permite añadir, borrar y modificar FAQs en la base de datos. Esta herramienta será para uso interno, y no visible desde el exterior.
El módulo de administración está compuesto de una serie de pantallas interconectadas, donde en cada pantalla se da al usuario la opción de volver al menú principal o realizar la acción asociada a la página. La estructura general del módulo viene en el siguiente esquema:
donde se tienen 3 operaciones:
Todas las pantallas serán páginas JSP. Más adelante veremos qué pantallas componen cada una de las operaciones. Todas ellas se gestionan a través de un servlet central FAQCentralServlet que veremos también a continuación.
El servlet FAQCentralServlet se encarga de controlar la lógica de la aplicación y redirigir cada petición a la página apropiada, según el estado de la aplicación y los datos de la petición. Las páginas serán páginas JSP que harán uso de los datos que les proporcione el servlet. De esta forma, las páginas JSP se utilizarán sólo para mostrar y presentar la información.
Detalles sobre la implementación
En el servlet central FAQCentralServlet se utiliza una clase FAQComandoToken, para mantener la integridad de las transacciones que se realicen en la base de datos.
Algunas acciones en una aplicación son vulnerables si se re-ejecutan accidentalmente. Por ejemplo, cuando insertemos un FAQ, podemos correr el riesgo de realizar operaciones incorrectas si recargamos la página (podría intentar re-insertar de nuevo el FAQ).
Para estos casos, necesitamos capturar la acción a realizar e impedir que pueda causar problemas. Hay que advertir al servlet de que esa acción ya se ha realizado. Para eso aplicamos una técnica de tokens, mediante la clase FAQComandoToken.
Detalles sobre la implementación
Las páginas JSP serán las encargadas de mostrar las opciones del menú de administración y permitir que el usuario introduzca los datos de las operaciones.
Todas las páginas tienen en común las siguientes características:
cmd
indicando la operación. Hay que tener en cuenta que este servlet está
mapeado con la URL faqcentral
.token
,
que toman de la petición.Deben desarrollarse las siguientes páginas:
cmd=abortar
), insertar (cmd=exe-insert
).pregunta
(pregunta
del FAQ), respuesta
(respuesta del FAQ).token
.cmd=abortar
), borrar (cmd=delete
).id
del FAQ a borrar.faqs
, un array de objetos FAQBean.cmd=abortar
), borrar FAQ (cmd=exe-delete
)id
del FAQ a borrar.faq
, un objeto FAQBean con el FAQ
a borrar.token
cmd=abortar
), actualizar FAQ (cmd=update
)id
del FAQ a actualizar.faqs
, un array de objetos FAQBean.cmd=abortar
), actualizar FAQ (cmd=exe-update
)id
del FAQ a actualizar.faq
, un objeto FAQBean con el FAQ
a actualizar.token
Utilizamos nombres de comandos para indicar qué tareas realizar en el administrador (añadir un FAQ, borrarla, etc). Cada actividad se representa por un comando específico, implementado por una clase, que utiliza la interfaz genérica FAQComando. Todos los comandos y clases en general se encuentran en el paquete faqs.comandos.
Detalles sobre la implementación
Este módulo permite obtener las FAQs almacenadas y mostrarlas en páginas web. Distinguiremos dos posibles modos de acceso:
Para proporcionar estos dos posibles modos, se tiene un servlet que facilita la información solicitada (todas las FAQs en el primer caso, y una FAQ con un id determinado en el segundo), y luego tendremos una serie de páginas JSP para mostrar la información.
Se tiene el servlet FAQServlet en el paquete faqs.servlets que se encarga de devolver bien un listado con todas las FAQs de la base de datos, bien los datos de una FAQ en concreto.
En función de si encuentra o no un parámetro id, se buscará la FAQ del id indicado, o todas las FAQs, utilizando para ello los comandos FAQComandoGet y FAQComandoGetAll vistos antes, respectivamente.
Detalles sobre la implementación
Todas las páginas JSP de este módulo deben colocarse en el directorio raíz de la aplicación. Las páginas tienen tres características comunes:
faqs
.pagina
para saber qué
pagina debe mostrar los resultados de la operación.Hay que implementar las siguientes páginas:
pagina=faqs.jsp
). id
, identificador de la FAQ a ver, pagina=faq.jsp
faqs
, un array de objetos FAQBean
con todos los FAQs.faq
, el objeto FAQBean a visualizar.Nos falta, por último, ver los ficheros de configuración de la aplicación Web:
El fichero descriptor web.xml tiene el siguiente contenido:
<!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" "http://java.sun.com/j2ee/dtds/web-app_2_3.dtd"> <web-app> <display-name>Aplicacion de FAQs</display-name> <description> Aplicacion de FAQs con JSP </description> <servlet> <servlet-name>faqcentral</servlet-name> <servlet-class>faqs.servlets.FAQCentralServlet</servlet-class> </servlet> <servlet> <servlet-name>faqs</servlet-name> <servlet-class>faqs.servlets.FAQServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>faqcentral</servlet-name> <url-pattern>/faqcentral</url-pattern> </servlet-mapping> <servlet-mapping> <servlet-name>faqs</servlet-name> <url-pattern>/faqs</url-pattern> </servlet-mapping> <security-constraint> <web-resource-collection> <web-resource-name>FAQS</web-resource-name> <url-pattern>/jsp/*</url-pattern> </web-resource-collection> <auth-constraint> <role-name>administrador</role-name> </auth-constraint> </security-constraint> <login-config> <auth-method>BASIC</auth-method> <realm-name>FAQS</realm-name> </login-config> </web-app>
Se mapea el servlet FAQCentralServlet con la URL /faqcentral, y el servlet FAQServlet con la URL /faqs. Luego, se protege el directorio /jsp/ de la aplicación, para que sólo puedan acceder usuarios administradores. Se utiliza para ello autentificación BASIC.
El fichero con los usuarios registrados tomcat-users.xml tiene un usuario usuario1 con password password1, con un rol de administrador. Sólo este usuario podría acceder al sitio Web.
Si tenemos algún fichero de usarios ya establecido en nuestro servidor, tendríamos que añadir este usuario en él. Si no, podemos copiar el fichero entero.
<?xml version="1.0" encoding="ISO-8859-1" ?> <tomcat-users> <user name="usuario1" password="password1" roles="administrador"/> </tomcat-users>