Proyecto de Integración
 

Aplicación web con Struts

Introducción

En esta sesión de integración vamos a convertir la aplicación web a MVC con Struts. Los objetivos básicos son:

  • Convertir los servlets de listar libros, alta, baja y modificación en acciones de Struts
  • Validar datos de entrada: uso de actionforms y tags de formularios de Struts
  • Internacionalizar la aplicación

Configuración de Struts en el proyecto

Antes de empezar a refactorizar el código, hay que incluir las dependencias de Struts en el POM y crear los ficheros de configuración. Seguid estos pasos:

  1. Incluir en el pom.xml las dependencias de Struts: struts-core, struts-taglibs y struts-el.
    <dependency>
    	<groupId>org.apache.struts</groupId>
      	<artifactId>struts-core</artifactId>
       	<version>1.3.10</version>
      	<type>jar</type>
       	<scope>compile</scope>
    </dependency>
      
    <dependency>
       	<groupId>org.apache.struts</groupId>
       	<artifactId>struts-el</artifactId>
       	<version>1.3.10</version>
       	<type>jar</type>
      	<scope>compile</scope>
    </dependency>
    
    <dependency>
       	<groupId>org.apache.struts</groupId>
       	<artifactId>struts-taglib</artifactId>
       	<version>1.3.10</version>
       	<type>jar</type>
       	<scope>compile</scope>
    </dependency>
    
  2. Cread el struts-config.xml en la carpeta WEB-INF. Podéis usar por ejemplo este texto como plantilla:
    <?xml version="1.0" encoding="ISO-8859-1" ?>
    <!DOCTYPE struts-config PUBLIC
              "-//Apache Software Foundation//DTD Struts Configuration 1.2//EN"
              "http://jakarta.apache.org/struts/dtds/struts-config_1_2.dtd">
    <struts-config>
        <form-beans>
        </form-beans>
    
        <global-exceptions>
        </global-exceptions>
    
        <global-forwards>
        </global-forwards>
    
        <action-mappings>	
        </action-mappings>
    
    </struts-config>
    
  3. Modificad el web.xml para usar el servlet de Struts Ahora todas las peticiones acabadas en .do deben llegar al servlet de Struts. Por ello necesitáis añadir un <servlet>. y un <servlet-mapping> para hacer el mapeo
    <servlet>
       	<servlet-name>controlador</servlet-name>
       	<servlet-class>org.apache.struts.action.ActionServlet</servlet-class>
       	<init-param>
         		<param-name>config</param-name>
         		<param-value>/WEB-INF/struts-config.xml</param-value>
       	</init-param>
       	<load-on-startup>2</load-on-startup>
    </servlet>
    <servlet-mapping>
       	<servlet-name>controlador</servlet-name>
       	<url-pattern>*.do</url-pattern>
    </servlet-mapping>
    
  4. Comprobar que Struts funciona correctamente. Provisionalmente, podéis crear una acción de prueba que simplemente se redirija a un JSP. En la sección de "action-mappings" del struts-config.xml se puede definir la acción
     <action path="/test" forward="/index.jsp"/>
     

    y crear una página index.jsp provisional que contenga simplemente un "Hola Struts". Ahora podéis desplegar el proyecto y acceder a la URL http://localhost:8080/proy-int-web/test.do. Tras autentificarnos debe aparecer el index.jsp recién creado. Si no lo hace, revisad los pasos anteriores.

Cambios en la seguridad declarativa

Actualmente, todas las URL del proyecto están restringidas mediante autentificación declarativa. Esto es adecuado en la versión actual, pero sería problemático a partir de esta sesión, ya que va a haber operaciones, como la elección de idioma, que no pueden estar protegidas.

Vamos a cambiar la restricción a todas las URL (/*) por una restricción a un nuevo directorio, que vamos a llamar auth. Redirigiremos al usuario a una URL dentro de este directorio para forzar la autentificación. Por tanto, en el web.xml hay que cambiar el url-pattern /* por /auth/*

En el web.xml también hay que modificar la etiqueta <welcome-file>, ya que apunta al servlet de login y nosotros queremos que apunte a una URL protegida. Cambiaremos dicha etiqueta para que apunte a una nueva página "auth/auth.jsp".

Crearemos por tanto el JSP auth.jsp dentro de una nueva carpeta auth en src/main/webapp. Al acceder a esta página forzamos a que "salte" la seguridad declarativa, y se muestre la página de login. Una vez logueados se accederá a auth.jsp. Como queremos que se siga ejecutando automáticamente SeleccionarLibroServlet, en auth.jsp vamos a hacer una redirección a dicho servlet.

(fichero "/auth/auth.jsp")

<% response.sendRedirect("SeleccionarLibroServlet"); %>
Forward vs. Redirect
Es importante diferenciar entre redirect y forward. Este último no nos sirve, ya que se salta la seguridad. Recordemos que los servlets hacen forward a los JSP en /jsp y en teoría estos no tienen acceso permitido a ningún rol.

Comprobad que al ejecutar el proyecto se sigue mostrando la pantalla de login y este funciona.

Refactorización de un servlet en acción de Struts

La primera acción que vamos a implementar es la correspondiente al servlet. SeleccionarLibroServlet. Esta os puede servir como ejemplo para todos los servlets que no requieran validación de datos de entrada

Uso de constantes

Debemos evitar en la medida de lo posible el uso de literales de tipo cadena en nuestro código, sustituyéndolos por constantes. De esta forma evitaremos posibles inconsistencias y errores de tecleo. En Struts, cada vez que referenciemos un forward o una clave en un .properties, vamos a hacerlo a través de una constante y no poniendo la cadena literalmente en el código.

Para almacenar estas constantes, definiremos un interface Tokens, dentro del nuevo paquete org.especialistajee.jbib.struts. Por el momento, en él definiremos algunas constantes para indicar resultado correcto y erróneo de una acción:

package org.especialistajee.jbib.struts;

public interface Tokens {
	// Forwards comunes
	String FOR_OK = "OK";
	String FOR_ERROR = "error";
	//Por si nos interesa distinguir el tipo de usuario
	String FOR_OK_BIBLIO = "OK_BIBLIO";
	String FOR_OK_PROFE = "OK_PROFE";
	String FOR_OK_ALU = "OK_ALU";
}

Conversión del servlet en acción

Las acciones de libros las meteremos en el nuevo paquete org.especialistajee.jbib.struts.acciones.libro. La acción correspondiente al servlet SeleccionarLibroServlet será la clase LibroListTodosAccion. Haremos que dicha clase herede de la clase Action de Struts y sobreescribiremos el método execute

Cambio de nombre
Aunque el servlet de listar libros se llamaba SeleccionarLibroServlet por "razones históricas", a partir de ahora vamos a cambiar el nombre de la acción para indicar que es un listado. Reservaremos el prefijo "Sel" para indicar que tratamos con un único objeto (libro, en este caso).

Del servlet básicamente nos interesa la variable static logger y casi todo el contenido del método doGet, salvo todo lo referente al RequestDispatcher, que en Struts implementaremos con forwards. A continuación se muestra parte del código de la nueva acción (faltan los imports). Destacamos en negrita la parte que se ha tomado del servlet:

public class LibroListTodosAccion extends Action {
	static Log logger = LogFactory.getLog(LibroListTodosAccion.class);

	@Override
	public ActionForward execute(ActionMapping mapping, ActionForm form,
			HttpServletRequest request, HttpServletResponse response)
			throws Exception {
		ActionForward result = null;
		
		try {
		   ILibroDao ild = FactoriaDaos.getInstance().getLibroDao();
		   List<LibroDomain> lista = ild.getLibros();
		   request.setAttribute("listaLibros", lista);

		   if (request.isUserInRole(TipoUsuario.BIBLIOTECARIO.name())) { 
		      result = mapping.findForward(Tokens.FOR_OK_BIBLIO);
		      } else if (request.isUserInRole(TipoUsuario.PROFESOR.name())
				|| request.isUserInRole(TipoUsuario.ALUMNO.name())) {
			      result = mapping.findForward(Tokens.FOR_OK);
		   }
		} catch (BibliotecaException ex) {
			request.setAttribute("error",
			      "Error obteniendo el listado de libros. " + ex);
			logger.error("Error obteniendo el listado de libros. " + ex);
			result = mapping.findForward(Tokens.FOR_ERROR);
		}
		return result;
	}
}

No nos interesa controlar si el usuario tiene un rol distinto a bibliotecario, alumno o profesor ya que solo dejaremos acceso a la acción a estos tres roles, usando seguridad de Struts.

En la acción todavía nos quedan dos literales de tipo cadena: "listaLibros" y "error". Sería recomendable cambiarlos por constantes dentro de Tokens. Para sistematizar el nombre de la constante

  • Daremos el nombre RES_XXX (de respuesta) a un atributo en el que vayamos a almacenar algo
  • Daremos el nombre MSJ_XXX a un mensaje de error. Recordemos que en Struts esto será una clave en un .properties, de la que nos ocuparemos más adelante.

Tokens quedará:

package org.especialistajee.jbib.struts;

public interface Tokens {
	// Forwards comunes
	String FOR_OK = "OK";
	String FOR_ERROR = "error";
	//Por si nos interesa distinguir el tipo de usuario
	String FOR_OK_BIBLIO = "OK_BIBLIO";
	String FOR_OK_PROFE = "OK_PROFE";
	String FOR_OK_ALU = "OK_ALU";
	
	//Atributos
	String RES_LIBROS = "listaLibros";
	//Mensajes
	String MSJ_LIBRO_LIST_ERROR = "msj.libro.list.error";	

}

Configuración en el struts-config

Hay que definir la acción de listar libros con sus forwards. El de error lo vamos a hacer global para que se pueda compartir con más acciones.

Mostramos en negrita los cambios introducidos

<global-forwards>
   <forward name="error" path="/jsp/error.jsp" />
</global-forwards>

<action-mappings>
   <action path="/LibroListTodos"
	  type="org.especialistajee.jbib.struts.acciones.libro.LibroListTodosAccion">
	<forward name="OK_BIBLIO" path="/jsp/biblio/listadoLibros.jsp" />
	<forward name="OK" path="/jsp/usuario/listadoLibros.jsp" />
   </action>
</action-mappings>

Como hemos cambiado el nombre de SeleccionarLibroServlet, tenemos que acordarnos de cambiarlo en la página auth/auth.jsp, para que ahora salte a la nueva acción (URL LibroListTodos.do)

<% response.sendRedirect("LibroListTodos.do"); %>

Ahora podemos intentar hacer login de nuevo y comprobar que todo sigue funcionando correctamente, con la nueva acción de Struts en lugar del servlet.

Seguridad declarativa de Struts

Podemos controlar declarativamente la seguridad en el struts-config mediante el atributo roles de la acción, para dejar únicamente acceso al rol o roles autorizados.

...
<action path="/LibroListTodos" 
           type="org.especialistajee.jbib.struts.acciones.libro.LibroListTodosAccion"
           roles="BIBLIOTECARIO,PROFESOR,ALUMNO">
		   ...

Si se intenta acceder con un rol no autorizado se lanzará una excepción. Para evitar que el usuario final vea esta excepción, podemos indicarle a Struts que en su lugar muestre una página de error, en la sección "global-exceptions" del struts-config

<global-exceptions>
	<exception key="msj.noautorizado.error" 
		type="org.apache.struts.chain.commands.UnauthorizedActionException"
		path="/jsp/noAutorizado.jsp"/>
</global-exceptions>

El código anterior indica que cuando se produzca un intento de ejecutar una acción por un rol no autorizado (en Struts, una excepción UnauthorizedActionException) se salte a una página JSP. En dicha página podemos mostrar el error con las tags de Struts <html:messages> o <html:errors>. El texto del error se tomará de un fichero .properties, por lo que debemos hacer varias cosas: El texto del error debe crearse en el fichero resources/mensajes.properties de mensajes bajo la clave .

  1. Crear una nueva carpeta de código fuente en src/main/resources (botón derecho sobre "Java Resources" y seleccionar "New > Source folder"). Dentro de ella crear un fichero mensajes.properties
  2. En dicho fichero crear el mensaje de error para acceso no autorizado, bajo la clave msj.noautorizado.error
    msj.noautorizado.error=No estás autorizado para realizar esta operación
    
  3. Referenciar el .properties desde el struts-config.xml. Tras la etiqueta de cierre de action-mappings (casi al final del archivo), colocar el message-resources:
       ...
       </action-mappings>
       <message-resources parameter="mensajes"/>
    </struts-config>
     
Probar la seguridad declarativa
Como a la única acción creada por el momento tienen acceso todos los roles, la forma más sencilla de probar por ahora la seguridad declarativa sería quitar uno de ellos temporalmente del atributo "roles" de la acción y comprobar que al entrar con ese rol salta el mensaje de error.

Cambios en los JSP

Los enlaces al antiguo servlet en los JSP deben cambiarse por enlaces a la nueva acción. En concreto, en este caso debemos cambiar los que aparecen en /jsp/biblio/menu.jspf y /jsp/usuario/menu.jspf. Es recomendable usar la etiqueta link de Struts, que nos permite especificar el nombre lógico de la acción en lugar de la URL física. Además eliminamos la necesidad de tener en cuenta el request.contextPath. Por ejemplo, así quedaría el menú de bibliotecario:


<%@ taglib uri="http://struts.apache.org/tags-html" prefix="html" %>
<div id="sidebar">
<h2>Libros</h2>
<html:link action="/libroListTodos">Listar</html:link>
<a href="${pageContext.request.contextPath}/LibroPreAddServlet">Alta</a><br/>
</div>

Recuerda que también debes cambiar el enlace en el menú de profesor o alumno.

Gestión de mensajes de error

Conviene usar el soporte de Struts para gestión de mensajes de error, ya que ofrece ventajas como la internacionalización. Por ello vamos a reemplazar el atributo de request llamado "error" que usábamos hasta el momento por código de Struts. En la acción, cambiamos la parte en la que se captura la excepción:

} catch (BibliotecaException ex) {
  ActionMessages am = new ActionMessages();
  am.add(ActionMessages.GLOBAL_MESSAGE, 
     		new ActionMessage(Tokens.MSJ_LIBRO_LIST_ERROR, ex));
  saveErrors(request, am);
  logger.error("Error obteniendo el listado de libros. " + ex);
  return mapping.findForward(Tokens.FOR_ERROR);
}

Nótese que el mensaje de error tiene un parámetro, que es la excepción producida. En el fichero mensajes.properties definiremos el nuevo mensaje:

msj.noautorizado.error=No estás autorizado para realizar esta operación
msj.libro.list.error=Error en el listado de libros: {0}

Nos falta cambiar el JSP de error, usando las tags de struts para mostrar el mensaje. Cambiaremos /jsp/error.jsp

<%@ taglib uri="http://struts.apache.org/tags-html" prefix="html" %>
...
<h2>Error</h2>
<p>
   <html:errors/> <!-- sustituye al ${requestScope.error} de antes -->
</p>
...
Aclaración
Al poner html:errors estamos mostrando todos los mensajes de error juntos, lo que nos impide formatear con HTML cada mensaje de error por separado. En este caso solo hay un mensaje, así que no importa. Recordemos que si usamos html:messages vamos iterando por cada mensaje y podemos meter HTML por enmedio, pero la etiqueta es más tediosa de usar.

Comprobad que el mensaje de error se muestra adecuadamente. Tal y como está la aplicación es difícil probarlo, ya que el error únicamente se podría producir por una excepción en el getLibros del DAO IOperacionDAO. Aunque sea un poco extraño, colocad un throws new BibliotecaException después de la llamada al DAO, de manera provisional,para verificar la gestión de errores.

Una vez hechos todos los cambios, el servlet SeleccionarLibroServlet ya no es necesario y podemos eliminar tanto su código como su mapeo en el web.xml.

Por supuesto, tenéis que convertir el resto de servlets en acciones de Struts, siguiendo el modelo anterior. El caso de alta de libro es un caso "especial", ya que hay que validar datos de entrada, como veréis a continuación.

Recapitulando lo que hemos hecho, para convertir un servlet en acción hay que:

  1. Crear la clase con la acción y colocar en su método execute la parte que podamos aprovechar del servlet
  2. Mapear la acción en el struts-config.xml, poniéndole además los roles autorizados
  3. Cambiar los JSP que enlazaban al antiguo servlet para que ahora enlacen a la acción, usando tags de Struts para que quede más "limpio"
  4. Gestionar los posibles mensajes de error
  5. Eliminar el mapeo al antiguo servlet en el web.xml

Alta de libros

El alta de libro se dividirá en dos acciones, como los dos servlets que tenéis hasta ahora:

  • LibroPreAddAccion es la acción que lleva al formulario de alta. Simplemente devolverá un forward para saltar a él. Opcionalmente, podéis meter en ella código para verificar envíos duplicados.
  • LibroAddAccion es la acción que se llama desde el formulario de alta. Necesitaremos validar los datos de entrada y por tanto deberíamos usar un ActionForm.

Configuración del plugin "validator"

Vamos a usar el plugin validator para validar los datos de entrada. Aseguráos de que la aplicación está preparada para validator, es decir que en WEB-INF existen los ficheros validation.xml y validator-rules.xml. Para este último podéis usar el incluido en la distribución estándar de struts, mientras que el primero debe contener únicamente

<?xml version="1.0" encoding="ISO-8859-1" ?>
<!DOCTYPE form-validation PUBLIC
   "-//Apache Software Foundation//DTD Commons Validator 
   Rules Configuration 1.0//EN"
   "http://jakarta.apache.org/commons/dtds/validator_1_0.dtd">
<form-validation>
</form-validation>
 

Además en el struts-config se debe cargar el plugin validator, esto se consigue con la etiqueta <plug-in> que debemos colocar hacia el final del archivo de configuración.

	...
    <plug-in className="org.apache.struts.validator.ValidatorPlugIn">
        <set-property property="pathnames" 
            value="/WEB-INF/validator-rules.xml,/WEB-INF/validation.xml"/>
    </plug-in>
</struts-config>

Uso de ActionForm

El uso de ActionForms implica una serie de cambios con respecto a las acciones que no los necesitan:

  • Hay que crear el propio actionform: las clases se deben crear en el paquete org.especialistajee.jbib.struts.actionforms. Por el momento, únicamente habrá una clase LibroForm para los datos del libro. Recordemos que las propiedades deben ser todas de tipo String, para que puedan acomodar cualquier valor que introduzca el usuario, incluso datos de tipo incorrecto.
  • Hay que referenciar el actionform en el struts-config, con la etiqueta form-bean. Dadle el name LibroForm
  • Hay que configurar en el struts-config.xml la accion en que se va a usar (atributos name, validate, scope e input).
    • Para scope será suficiente con poner el valor request ya que el ActionForm solo es necesario en la petición que desencadena el alta de libro, pero no necesitamos conservarlo más tiempo.
    • En input pondremos que vaya de nuevo al formulario de alta del libro si hay errores.
  • Hay que definir la validación en el validation.xml: al dar de alta un libro, el isbn debe tener como mínimo 10 caracteres, y el número de páginas debe ser un entero positivo. Son obligatorios los campos isbn, título y autor.
  • En el código de la acción, hay que copiar los datos del actionform al objeto LibroDomain. Para ello nos ayudaremos de la librería beanutils, cuyo uso se describe en el apartado siguiente

La librería beanutils

En lugar de copiar los datos manualmente campo a campo del actionform al LibroDomain podemos usar una librería de jakarta denominada beanutils. Esta librería contiene métodos de utilidad para el trabajo con beans. Entre ellos ofrece un método que permite copiar automáticamente los campos de un objeto en otro, si tienen el mismo nombre. Beanutils usa reflection para hacer la copia y puede convertir automáticamente los tipos.

Para usar BeanUtils necesitamos incluir la siguiente dependencia en el pom.xml

<dependency>
   <groupId>commons-beanutils</groupId>
   <artifactId>commons-beanutils</artifactId>
   <version>1.8.3</version>
   <type>jar</type>
   <scope>compile</scope>
</dependency>

Una vez hecho esto podemos copiar la información desde el actionform al objeto de dominio introduciendo en la acción LibroAddAccion un código como sigue:

...
LibroDomain libro = new LibroDomain();
try {
	LibroForm libroForm = (LibroForm) form;
	libro.setIsbn(libroForm.getIsbn());
	//BeanUtils.copyProperties(destino, origen)
	BeanUtils.copyProperties(libro, libroForm);
	libro.setFechaAlta(new Date());
	...

Nótese que inicializamos el isbn del libro manualmente, porque el objeto puede dar problemas con beanutils si no tiene inicialmente un isbn (en concreto el método hashCode generaría un null pointer exception).

Actualización de libros

La actualización de libros es similar al alta. También usaremos un actionform, pero en este caso para copiar los datos desde el propio actionform al formulario HTML. Por tanto, su sitio es la acción de "PreUpdate", la que nos lleva a ver el formulario HTML:

<action path="/LibroPreUpdate"
   type="org.especialistajee.jbib.struts.acciones.libro.LibroPreUpdateAccion"
   roles="BIBLIOTECARIO" name="LibroForm" scope="request" validate="false">
   <forward name="OK" path="/jsp/biblio/modificaLibro.jsp" />			
</action>

Obsérvese que el actionform tiene validate=false porque no es necesario validar los datos, ya que proceden de la base de datos. La acción de "PreUpdate" los obtiene gracias al DAO y los coloca en el actionform, copiándolos del entity libro gracias a BeanUtils. Se muestra a continuación el fragmento del execute de la acción que realiza la tarea descrita:

String isbn = request.getParameter("isbn");

try {
   if (isbn == null) {
      throw new BibliotecaException("Se debe especificar un ISBN.");
   }

   // Obtener el libro de la BD
   ILibroDao lDao = FactoriaDaos.getInstance().getLibroDao();
   LibroDomain libro = lDao.getLibro(isbn);
   if (libro == null) {
      throw new BibliotecaException("No se puede obtener el libro indicado.");
   }
   //form es el parámetro ActionForm del método execute
   LibroForm libroForm = (LibroForm) form;
   // copiar el libro en el actionform
   BeanUtils.copyProperties(libroForm, libro);
   result = mapping.findForward(Tokens.FOR_OK);	
} catch (BibliotecaException ex) {
	...

Al copiar los datos en el ActionForm que recibe el execute como parámetro, conseguimos que Struts los pase a los campos del formulario de modificación de libro (siempre que usemos las tags HTML de Struts en dicho formulario).

Modificación de las páginas JSP

Se deberían usar las taglibs de Struts en todas las páginas JSP. De este modo aprovechamos las posibilidades del framework y además facilitamos la internacionalización (apartado siguiente).

Se usarán taglibs de Struts para:

  • Los formularios
  • Los enlaces a las acciones. Si necesitamos un enlace con parámetros podemos hacerlo con los atributos paramId, paramName y paramProperty. Por ejemplo en la página listadoLibros.jsp, pondríamos el enlace para modificar el libro como sigue:
    <html:link action="/LibroPreUpdate" paramId="isbn" paramName="libro"
      paramProperty="isbn">[Editar]</html:link>
    

    Donde paramName es el bean del que se saca la información (en este caso "libro"), paramProperty es la propiedad del bean que queremos usar en el enlace y paramId es el nombre del parámetro HTTP a generar.

  • Los textos que queramos internacionalizar (ver apartado siguiente).

Internacionalización

La aplicación debe estar internacionalizada al menos para castellano e inglés, y para todas las opciones del bibliotecario (por el momento no es necesario para profesores y alumnos). Se deben traducir tanto las pantallas como los mensajes de error.

Requisitos de la implementación:

  • La acción para cambiar el idioma se implementará en la clase CambiaIdiomaAccion en el paquete es.ua.jtech.proyint.struts.acciones. Al menos en la página de login se incluirá un enlace a dicha acción, pasándole como parámetro el código de idioma. La acción, tras ejecutarse correctamente hará un forward a la página de login, que también debe estar internacionalizada.
  • Los mensajes internacionalizados se colocarán en el correspondiente .properties de la manera más sistemática posible, para que sean sencillos de localizar. Intentad seguir lo más fielmente posible una estructura como la del siguiente ejemplo:
    # opciones de menu
    menu.usuario.List = Listar todos los usuarios
    menu.libro.List = Listar todos los libros 
    menu.libro.Sel = Ver los datos de un libro
    menu.libro.Add = Dar de alta un libro
    menu.titulo = Menú principal
    
    #index
    index.biblioteca = Biblioteca JTECH
    #cabecera
    cabecera.cerrarSesion = Cerrar sesion
    #pie
    
    #datos de un libro
    libro.ISBN = ISBN
    libro.titulo = Título
    libro.autor = Autor
    libro.numPaginas = Nº págs
    libro.fechaAlta = Fecha de Alta
    
    #pantallas sobre libros
    libro.crearLibro = Crear libro
    libro.datosLibro = Datos del libro
    

Resumen

Proyecto a entregar

En esta sesión se deberá entregar el proyecto de integración actualizando el proyecto jbib-web con los siguientes puntos:

  • Añadir las dependencias de Struts y BeanUtils en el pom.xml
  • Cambiar las URL protegidas en el web.xml (de /* a /auth/*).
  • Crear el fichero de configuración de Struts, WEB-INF/struts-config.xml
  • Crear el interfaz Tokens, donde se definirán constantes para las acciones.
  • Convertir los servlets para listar todos los libros, dar de alta, baja y modificar libros en acciones de Struts. Añadir validación al alta y la modificación de libros. Crear el actionform LibroForm y la validación de datos en el validation.xml
  • Crear una nueva cabecera y pie para la parte del bibliotecario que use las taglib de Struts. Cambiar los JSP del bibliotecario para que también hagan uso de estas taglibs
  • Internacionalizar la parte del bibliotecario, en español e inglés. Añadir una acción para cambiar el idioma, accesible al menos desde la página de login. Crear también los ficheros de mensajes traducidos en la carpeta resources