75
4.4 Tutorial de JSTL y Jakarta Struts

4.4 Tutorial de JSTL y Jakarta Struts - tic.udc.esfbellas/teaching/is-2003-2004/Tema4Apart... · n Ejemplos n ${user.firstName}= user.getFirstName() n ${user.address.city}= user.getAddress().getCity()

  • Upload
    haxuyen

  • View
    215

  • Download
    0

Embed Size (px)

Citation preview

4.4 Tutorial de JSTL y Jakarta Struts

¿ Qué es JSTL ?

n Existen numerosas librerías de tags JSP que permitenn Iterar sobre coleccionesn Imprimir valores de propiedades de JavaBeans de forma

seguran Internacionalización de mensajes, números, fechas, etc.n Generación de URLs aplicando URL rewritingn Acceso a documentos XMLn Etc.

n Recientemente, se ha estandarizado JSTL (JSP Standard Tag Library)n Librería de tags estándar y lenguaje de expresiones

Tags en JSTL (1)

n Coren Control de flujon Soporte para URLs

n I18n (internacionalización)n Soporte para internacionalización: establecimiento del Locale, generación de mensajes, formateo de números, cantidades monetarias, fechas, etc.

n XMLn Parsing de un documento XMLn Flujo de control para recorrer un documento XML

(alternativa a XSL para casos sencillos)n Tags para lanzar transformaciones XSL

Tags en JSTL (y 2)

n Acceso a BDsn Permiten lanzar sentencias SQL a BDs relacionalesn Sólo deberían usarse para prototipado rápido o

aplicaciones muy simples

n En este apartado y en los dos siguientes se ilustran parte de los tags de los grupos Core e I18n

n Existen dos versiones de la librería de tags (“twin libraries”)n JSTL-RT: no soporta el lenguaje de expresionesn JSTL-EL: soporta el lenguaje de expresiones

n Es la que usaremos

Lenguaje de expresiones en JSTL (1)

n Actualmente en JSP si se desea dar valor a un atributo de un tag, es preciso usar una expresión <%= ... %>n Ejemplo<jsp:useBean id="shoppingCart" scope="session"

class="org.acme.ShoppingCart"/><xxx:if test="<%= shoppingCart.getNumberOfProducts() > 0 %>">

...</xxx:if>

n JSTL proporciona un lenguaje de expresiones para facilitar su construcciónn Ejemplo<xxx:if test="${sessionScope.shoppingCart.numberOfProducts > 0}">

...</xxx:if>

n JSP 2.0 incluirá este lenguaje de expresionesn Estará disponible para cualquier librería de tags

Lenguaje de expresiones en JSTL (2)

n Expresiones y literalesn Las expresiones tienen que ir rodeadas por ${ y }.n Cualquier valor que no empiece por ${, se considera un

literaln Los literales que incluyen el símbolo ${, han de escaparlo

rodeándolo de ${' y '}

n Ejemplo:<xxx:aTag att="This literal includes ${'${'} character"/>

n Acceso a atributos de objetos Java en expresionesn Se puede acceder a las propiedades de un JavaBean, y a

objetos de un Map, List o vector

Lenguaje de expresiones en JSTL (3)

n Acceso a atributos de objetos Java en expresiones (cont)n Ejemplos

n ${user.firstName} = user.getFirstName()n ${user.address.city} = user.getAddress().getCity()n ${user.preferencesMap["shipping"]} =

user.getPreferencesMap().get("shipping")n ${user.preferencesList[0]} = user.getPreferencesList().get(0)

n Unifica el tratamiento de los operadores . y []n ${user.firstName} es equivalente a ${user["firstName"]}

n ${user.preferencesMap["shipping"]} es equivalente a ${user.preferencesMap.shipping}

n Para determinados casos, es preciso usar el operador []n ${user.preferencesMap["book.fiction"]} es equivalente a

user.getPreferencesMap().get("book.fiction")

n ${user.preferencesMap[product.category]} es equivalente a user.getPreferencesMap().get(product.getCategory())

Lenguaje de expresiones en JSTL (4)

n Objetos implícitosn Entre otros

n pageScope (Map)n requestScope (Map)n sessionScope (Map)n applicationScope (Map)n param (Map que mapea nombres de parámetros univaluados a String)

n paramValues (Map que mapea nombres de parámetros multivaluados a String[])

n Cuando se usa un objeto sin especificar su ámbito (el objeto implícito en el que está contenido), se busca en los ámbitos page, request, session y application (en este orden)n Ejemplo<xxx:if test="${shoppingCart.numberOfProducts > 0}">

...</xxx:if>

Lenguaje de expresiones en JSTL (y 5)

n Literalesn Boolean (true y false)n Numéricosn Cadenas de caracteres (entre comillas simples o dobles)n null

n Operadoresn Aritméticos: +,-, *, /, div, %, modn Lógicos: &&, and, ||, or, !, notn Relacionales: ==, eq, !=, ne, <, lt, >, gt, <=, le, >=, gen empty: permite comprobar si un valor es nulln Ejemplos

<c:if test="${!empty requestScope.previous}">

...

</c:if>

<c:if test="${sessionScope.shoppingCart.numberOfProducts > 0}">

...

</c:if>

n Se pueden usar paréntesis

¿ Qué es Struts ?

n Framework OpenSource para implementar aplicaciones web con servlets y JSP según el patrón arquitectónico Model-View-Controller

n Subproyecto de Jakartan Autor principal: Craig R. McClanahan

n Funciona sobre cualquier servidor de aplicaciones web que implemente las APIs de servlets y JSP

n Ha ganado gran relevancia en el mundo de las aplicaciones web Javan Versión 1.0 estable en Julio 2001n Posteriormente, surgieron muchos otros framework MVCn Actualmente se está estandarizando JavaServer Faces

(http://java.sun.com/j2ee/javaserverfaces), que formará parte de J2EEn Struts 1.2 estará parcialmente integrado con JSTL y JavaServer

Faces, y la integración será completa en Struts 2.0

¿ Qué proporciona Struts ?

n Un servlet Front Controller y clases relacionadasn Una librería de tags JSP muy completa

El patrón Front Controller en Struts (1)

javax.servlet.http.HttpServlet

org.apache.struts.action.Action

+ execute

ActionN

0..n

Action1...

org.apache.struts.action.ActionServlet

# doGet# doPost

org.apache.struts.action.ActionForm

+ reset+ validate

<<instantiate>>

ActionFormNActionForm1... <<use>>

<<use>>

El patrón Front Controller en Struts (2)

n ActionServletn Servlet Front Controllern En web.xml se especifica que todas las URLs que impliquen

procesamiento (por GET o POST) vayan a este servletn Ej.: las URLs que termine en .do

n Clases ActionFormn Si el programador lo desea, puede acceder a los parámetros de la

request a través de un JavaBean que extiende ActionFormn Especialmente útil en formularios

n Clase Action => método executen Accede a los parámetros de la request, directamente o vía el

ActionForm correspondienten Realiza la operación invocando un método de un Session

Facade del modelo o una fachada del controladorn Deja el resultado devuelto por el método en la request o en la

sesiónn Devuelve un objeto ActionForward, que representa la URL que

hay que visualizar a continuación (sendRedirect o forward)

El patrón Front Controller en Struts (3)

n Fichero de configuraciónn Clases ActionForm que usa nuestra aplicación

n Nombre lógico (ej.: loginForm)

n Nombre completo de la clase (ej.: es.udc.fbellas.j2ee.strutstutorial.portal3.http.view.actionforms.LoginForm)

n URLs que implican procesamienton URL de tipo path relativo a contexto (ej.: /Login)

n No llevan el .do final

n Nombre completo de la clase Action (ej.: es.udc.fbellas.j2ee.strutstutorial.portal3.http.controller.actions.LoginAction)

n Nombre lógico de la clase ActionForm asociada

El patrón Front Controller en Struts (y 4)

n Fichero de configuración (cont)n Definiciones de nombres lógicos de URLs

n Nombre que usan las acciones cuando devuelven un ActionForward (ej.: ShowMainPage)

n sendRedirect o forwardn URL a invocar (ej.: /MainPage.jsp)

n Cuando el servlet ActionServlet arranca (init), lee el fichero de configuración

n Crea una única instancia de cada clase Actionn No se crea una instancia de una clase Action por cada

petición que se reciben Tienen que ser thread-safen Misma situación que cuando se trabaja con servlets

La librería de tags de Struts (1)

n Beann Imprimir el valor de las propiedades de JavaBeans de

manera seguran Soporte para internacionalización de mensajesn No los usaremos, dado que JSTL ofrece una alternativa

estándar

n HTMLn Generación de HTML básico

n Campos de entrada en formulariosn Enlaces (con URL rewriting)

n Logicn Control de flujon No los usaremos, dado que JSTL ofrece una alternativa

estándar

La librería de tags de Struts (y 2)

n Templaten Caso particular del patrón Composite View (plantillas de

páginas JSP)n Lo veremos con MiniBank y MiniPortal

n Tilesn Una versión mejorada de Templaten Se incorporó en la versión 1.1

Arquitectura MVC con Struts

n Modelon Clases independientes de la vista y el controlador

n Controladorn Conjunto de clases Actionn Interactúan con el modelo y seleccionan la siguiente vista

(dejándole los datos en uno de los cuatro posibles ámbitos, normalmente request o session)

n Vistan Conjunto de clases ActionFormn Conjunto de páginas JSP

n No contienen código Javan Sólo visualizan datosn Usan acciones JSP para recuperar los valores a mostrar y

formatearlos

Demo Portal-3 (1)

Lanzar el navegador

Acceder a Portal-3 main page

Demo Portal-3 (2)

Clic Login

Demo Portal-3 (y 3)

Portal-3 main page(Welcome to Portal-3)

Clic en LogoutTerminar y lanzar el navegador dos días más tarde

Acceder a Portal-3 main page

Estructura de paqueteses.udc.fbellas.j2ee.util.struts.action

es.udc.fbellas.j2ee.strutstutorial.portal3

http

controller

actions

model

userfacade

delegate

exceptions

view

actionforms

messages

jar tvf StrutsTutorial.war (1)

MainPage.jspInternalError.jspLogin.jspIndex.jspWEB-INF/Struts/struts-html.tldWEB-INF/Struts/struts-config.xmlWEB-INF/StdTagLibs/fmt.tldWEB-INF/StdTagLibs/c.tldWEB-INF/lib/standard.jarWEB-INF/lib/jstl.jarWEB-INF/lib/jaxen-full.jarWEB-INF/lib/saxpath.jarWEB-INF/lib/struts.jarWEB-INF/lib/commons-*.jarWEB-INF/lib/StandardUtil.jarWEB-INF/lib/WebUtil.jar

jar tvf StrutsTutorial.war (y 2)

WEB-INF/classes/es/udc/fbellas/j2ee/strutstutorial/portal3/model/userfacade/delegate/UserFacadeDelegate.class

WEB-INF/classes/es/udc/fbellas/j2ee/strutstutorial/portal3/model/userfacade/exceptions/IncorrectPasswordException.class

WEB-INF/classes/es/udc/fbellas/j2ee/strutstutorial/portal3/http/controller/actions/LoginManager.class

WEB-INF/classes/es/udc/fbellas/j2ee/strutstutorial/portal3/http/controller/actions/LogoutAction.class

WEB-INF/classes/es/udc/fbellas/j2ee/strutstutorial/portal3/http/controller/actions/LoginAction.class

WEB-INF/classes/es/udc/fbellas/j2ee/strutstutorial/portal3/http/controller/actions/MainPageAction.class

WEB-INF/classes/es/udc/fbellas/j2ee/strutstutorial/portal3/http/view/actionforms/LoginForm.class

WEB-INF/classes/es/udc/fbellas/j2ee/strutstutorial/portal3/http/view/messages/Messages.properties

WEB-INF/web.xml

Comentariosn WEB-INF/Struts

n Ficheros *.tld: descriptores de las librerías de las acciones proporcionadas por Struts

n struts-config.xml: configuración de Struts para la aplicación del tutorial

n WEB-INF/StdTagLibsn Ficheros *.tld: descriptores de las librerías de las acciones

proporcionadas por JSTLn WEB-INF/lib

n struts.jar, commons-*.jar: Strutsn standard.jar, jstl.jar, jaxen-full.jar, saxpath.jar:

Jakarta Standard TagLibs (implementación OpenSource de JSTL)n StandardUtil.jar y WebUtil.jar: subsistema Util de J2EE-

Examplesn WEB-INF/classes/es/.../Messages.properties

n Internacionalización de mensajes

WEB-INF/web.xml (1)<?xml version="1.0" encoding="ISO-8859-1"?>

<!DOCTYPE web-appPUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN“"http://java.sun.com/j2ee/dtds/web-app_2_3.dtd">

<web-app>

<distributable/>

<!-- =============== Standard TagLibs configuration ============= -->

<context-param><param-name>javax.servlet.jsp.jstl.fmt.

localizationContext</param-name><param-value>es.udc.fbellas.j2ee.strutstutorial.portal3.http.

view.messages.Messages</param-value></context-param>

WEB-INF/web.xml (2)<!-- ================ Front controller configuration ============ -->

<servlet><servlet-name>action</servlet-name><servlet-class>org.apache.struts.action.ActionServlet

</servlet-class><init-param><param-name>config</param-name><param-value>/WEB-INF/Struts/struts-config.xml</param-value>

</init-param><init-param><param-name>debug</param-name><param-value>2</param-value>

</init-param><init-param><param-name>detail</param-name><param-value>2</param-value>

</init-param><load-on-startup>2</load-on-startup>

</servlet>

WEB-INF/web.xml (3)<!-- =================== Servlet mapping ======================== -->

<servlet-mapping><servlet-name>action</servlet-name><url-pattern>*.do</url-pattern>

</servlet-mapping>

<!-- ========================= Session ========================== -->

<session-config><session-timeout>30</session-timeout>

</session-config>

<!-- ======================= Welcome page ======================= -->

<welcome-file-list><welcome-file>Index.jsp</welcome-file>

</welcome-file-list>

WEB-INF/web.xml (y 4)<!-- ================= Standard TagLibs descriptors ============ -->

<taglib><taglib-uri>http://java.sun.com/jstl/core</taglib-uri><taglib-location>/WEB-INF/StdTagLibs/c.tld</taglib-location>

</taglib>

<taglib><taglib-uri>http://java.sun.com/jstl/fmt</taglib-uri><taglib-location>/WEB-INF/StdTagLibs/fmt.tld</taglib-location>

</taglib>

<!-- =============== Struts Tag Library descriptors ============= -->

<taglib><taglib-uri>/struts-html.tld</taglib-uri><taglib-location>/WEB-INF/Struts/struts-html.tld</taglib-location>

</taglib>

</web-app>

Comentarios (1)

n context-paramn Permite definir un parámetro de configuración global a toda

la aplicación webn Accesible vía Servlet.getServletConfig().getServletContext().getInitParameter()

n En el ejemplo se utiliza para dar valor al parámetro de configuración javax.servlet.jsp.jstl.fmt.localizationContext

n Lo usan los tags de internacionalización de mensajes de JSTLn Nombre del fichero de mensajes (sin sufijo .properties)n Debe estar debajo de WEB-INF/classes y usar un nombre

consistente con su ubicación (como si de una clase se tratase)

Comentarios (2)

n Servlet org.apache.struts.actions.ActionServletn Aparecen dos tags que no hemos usado hasta ahora

n init-param

n Permite definir un parámetro de configuración específico al servlet y su valor

n Accesible vía Servlet.getServletConfig().getInitParameter()

n load-on-startup

n Indica que el servlet se debería cargar cuando el servidor arranque la aplicación web

n El valor (opcional) indica el orden relativo de carga con respecto a otros servlets (cuanto menor sea el valor, antes se carga)

Comentarios (3)

n Servlet org.apache.struts.actions.ActionServletn Parámetros de inicialización

n config

n Path de tipo relativo a contexto del fichero de configuración de Struts

n detail

n Nivel de detalle en los mensajes de depuración durante el parsingde los ficheros de configuración

n debugn Nivel de detalle en los mensajes de depuración de ActionServlet

n Más información en JavaDoc de org.apache.struts.actions.ActionServlet

Comentarios (y 4)

n Librerías de tags proporcionadas por JSTL y Strutsn Aparece un tag que no hemos usado hasta ahora, taglib,

que contiene los elementosn taglib-uri: URI a usar en la directiva taglib de JSP (para

importar la librería)n taglib-location: path relativo a contexto del fichero .tld

(Tag Library Descriptor) de la librería de tagsn c.tld: Tags Core de JSTLn fmt.tld: Tags de internacionalización de JSTLn struts-html.tld: Tags HTML de Struts

WEB-INF/Struts/struts-config.xml (1)

<?xml version="1.0" encoding="ISO-8859-1" ?>

<!DOCTYPE struts-config PUBLIC"-//Apache Software Foundation//DTD Struts Configuration 1.1//EN""http://jakarta.apache.org/struts/dtds/struts-config_1_1.dtd">

<struts-config>

<!-- ============= Form Bean Definitions ========================== -->

<form-beans>

<form-bean name="loginForm“type="es.udc.fbellas.j2ee.strutstutorial.portal3.http.view.actionforms.LoginForm"/>

</form-beans>

WEB-INF/Struts/struts-config.xml (2)<!-- ============ Global Forward Definitions ====================== -->

<global-forwards><forward name="MainPage" path="/MainPage.do" redirect="true"/><forward name="InternalError" path="/InternalError.jsp“

redirect="true"/></global-forwards>

<!-- ============ Action Mapping Definitions ====================== -->

<action-mappings>

<action path="/MainPage"type="es.udc.fbellas.j2ee.strutstutorial.portal3.http.controller.actions.MainPageAction">

<forward name="ShowMainPage" path="/MainPage.jsp"/></action>

<action path="/Login"type="es.udc.fbellas.j2ee.strutstutorial.portal3.http.controller.actions.LoginAction"

name="loginForm"scope="request"input="/Login.jsp"validate="true"/>

WEB-INF/Struts/struts-config.xml (y 3)<action path="/Logout"

type="es.udc.fbellas.j2ee.strutstutorial.portal3.http.controller.actions.LogoutAction"/>

<!-- =================================================================The standard administrative actions available with Struts. Thesemust be either omitted or protected by security in a realapplication deployment.

================================================================ -->

<action path="/admin/addFormBean"type="org.apache.struts.actions.AddFormBeanAction"/>

...

<!-- ========== Message Resources Definitions ================== -->

<message-resourcesparameter="es.udc.fbellas.j2ee.strutstutorial.portal3.http.view.messages.Messages"/>

</action-mappings>

</struts-config>

Comentarios (1)

n Definiciones de nombres lógicos de URLsn Se usan en la implementación de las acciones para devolver

un ActionForward y en algunas acciones JSPn Se especifican con forward

n Atributos documentados en JavaDoc de org.apache.struts.action.ActionForward

n name: Nombre lógicon path: path relativo a contexto de la URL a la que se invocarán redirect: true (sendRedirect) o false (forward)

n false por defecto

n Pueden ser globales (global-forwards) o particulares a una acción (action)

Comentarios (2)

n actionn Atributos documentados en JavaDoc de org.apache.struts.config.ActionConfig

n type: nombre completo de la clase Actionn path: URL (path relativo a contexto) que provocará la

invocación de la acciónn ¡ Se especifican sin el sufijo .do !

n name: nombre del ActionForm (definido por form-bean) que captura los parámetros de la invocación

n scope: ámbito (request o session) del ActionFormn En general, request

n input: path relativo a contexto del formulario de entradan validate: true si el Front Controller tiene que llamar al

método validate del ActionFormn En general, true

Comentarios (y 3)

n message-resourcesn Especifica la ubicación del fichero de mensajesn Actualmente Struts no está integrado con JSTL

WEB-INF/classes/es/.../Messages.properties (1)

Buttons.login=Login

ErrorMessages.loginName.notFound=Login name not foundErrorMessages.mandatoryField=Mandatory fieldErrorMessages.password.incorrect=Incorrect passwordErrorMessages.retry=Please, check if the operation has been performed, \

and retry if necessary

errors.footer=</b></font>errors.header=<font color="red"><b>

InternalError.title=Internal error

WEB-INF/classes/es/.../Messages.properties (y 2)

Login.loginName=Login nameLogin.password=PasswordLogin.rememberMyPassword=Remember my password (cookies must be enabled)Login.title=Portal-3 login form

MainPage.hello=HelloMainPage.login=LoginMainPage.logout=LogoutMainPage.title=Portal-3 main pageMainPage.welcome=Welcome to Portal-3

Comentarios (1)

n Asocia pares <identificadorMensaje, mensaje>n Convenios de nombrado para los identificadores de mensajesn Ordenados alfabéticamenten errors.footer y errors.header son dos identificadores

especiales que entiende la acción html:errors de Strutsn Messages.properties

n Mensajes en el lenguaje por defecto del servidorn Messages_xx.properties

n Mensajes en el lenguaje cuyo código ISO es xxn Códigos ISO en

http://www.ics.uci.edu/pub/ietf/http/related/iso639.txtn en: Inglésn es: Españoln gl: Gallego

n Etc.

Comentarios (y 2)

n Messages.properties sólo resuelve un aspecto particular de la internacionalización de aplicaciones: impresión de mensajes en distintos idiomasn En una aplicación más compleja puede ser necesario tener

trozos de páginas en distintos idiomas (con gran cantidad de texto estático) y seleccionarlas o incluirlas dinámicamente desde otras en función del idioma

n Otros aspectos en internacionalizaciónn Formatear y tratar fechas, horas, números, cantidades

monetariasn JSTL proporciona tags para ellon También paquetes java.text y java.util

n Puede requerir tablas para almacenar contenido en distintos idiomasn Ej.: un servicio de noticias

es.udc.fbellas.j2ee.strutstutorial.portal3.model.userfacade.delegate

n Simula la fachada del modelo que proporciona las operaciones relativas a la interacción del usuario con el portal

UserFacadeDelegate

+ UserFacadeDelegate()

+ login(loginName : String, password : String, passwordIsEncrypted : boolean) : void

es.udc.fbellas.j2ee.util.struts.action

Action

(from action)

DefaultAct ion

+ execute(actionMapping, actionForm, request, response) : ActionForward# doExecute(actionMapping, actionForm, request, response) : ActionForward# doOnInternalError(actionMapping, actionForm, request, response, internalErrorException) : ActionForw...

PropertyValidator

Comentarios

n DefaultActionn Problema

n En general, las clases Action invocarán una operación sobre un Business Delegate del modelo o una fachada del controlador, que puede lanzar InternalErrorException

n Es necesario (1) capturarla, (2) imprimirla en un log (para depuración) e (3) ir a una página que indique error interno

n Las clases Action derivarán de DefaultActionn Implementa execute (Template Method) en términos de doExecute y doOnInternalError

n Las clases hijas implementan doExecute, que tiene la misma signatura que execute, pero puede lanzar adicionalmente InternalErrorException

n PropertyValidatorn Clase utilidad para validar campos de entrada comunes

(double, long, String, etc.)

es.udc.fbellas.j2ee.util.struts.action.DefaultAction (1)

package es.udc.fbellas.j2ee.util.struts.action;

import java.io.IOException;import javax.servlet.ServletContext;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import javax.servlet.ServletException;import org.apache.struts.action.Action;import org.apache.struts.action.ActionMapping;import org.apache.struts.action.ActionForm;import org.apache.struts.action.ActionForward;import org.apache.struts.action.ActionError;import org.apache.struts.action.ActionErrors;

import es.udc.fbellas.j2ee.util.exceptions.InternalErrorException;

es.udc.fbellas.j2ee.util.struts.action.DefaultAction (2)

public ActionForward execute(ActionMapping mapping,ActionForm form, HttpServletRequest request,HttpServletResponse response)throws IOException, ServletException {

try { return doExecute(mapping, form, request, response);

} catch (InternalErrorException e) { return doOnInternalErrorException(mapping, form, request,

response, e);}

}

protected abstract ActionForward doExecute(ActionMapping mapping,ActionForm form, HttpServletRequest request,HttpServletResponse response)throws IOException, ServletException, InternalErrorException;

es.udc.fbellas.j2ee.util.struts.action.DefaultAction (y 3)

protected ActionForward doOnInternalErrorException(ActionMapping mapping, ActionForm form,HttpServletRequest request, HttpServletResponse response, InternalErrorException internalErrorException)throws IOException, ServletException {

/* * Log error, even with debug level <= 0, because it is a* severe error. */

ServletContext servletContext = servlet.getServletConfig().getServletContext();

servletContext.log(internalErrorException.getMessage(), internalErrorException);

/* Redirect to input page. */return mapping.findForward("InternalError");

}

}

es.udc.fbellas.j2ee.util.struts.action.PropertyValidator (1)

public final class PropertyValidator {

private final static String INCORRECT_VALUE = "ErrorMessages.incorrectValue";

private final static String MANDATORY_FIELD = "ErrorMessages.mandatoryField";

private PropertyValidator() {}

public final static long validateLong(ActionErrors errors,String propertyName, String propertyValue, boolean mandatory,long lowerValidLimit, long upperValidLimit) {

long propertyValueAsLong = 0;

if (validateMandatory(errors, propertyName, propertyValue,mandatory)) {

boolean propertyValueIsCorrect = true;

es.udc.fbellas.j2ee.util.struts.action.PropertyValidator (2)

try {propertyValueAsLong =

new Long(propertyValue).longValue();if ( (propertyValueAsLong < lowerValidLimit) ||

(propertyValueAsLong > upperValidLimit) ) {propertyValueIsCorrect = false;

}} catch (NumberFormatException e) {

propertyValueIsCorrect = false;}

if (!propertyValueIsCorrect) {errors.add(propertyName,

new ActionError(INCORRECT_VALUE));}

}

return propertyValueAsLong;

}

es.udc.fbellas.j2ee.util.struts.action.PropertyValidator (y 3)

public final static boolean validateMandatory(ActionErrors errors,String propertyName, String propertyValue) {

if ((propertyValue == null) || (propertyValue.length() == 0)) {errors.add(propertyName, new ActionError(MANDATORY_FIELD));return false;

} else {return true;

} }

private final static boolean validateMandatory(ActionErrors errors,String propertyName, String propertyValue, boolean mandatory) {

if (mandatory) {return validateMandatory(errors, propertyName,

propertyValue);} else {

return true;}

}

// Resto de métodos validateXXX => Análogos ...}

Comentarios

n org.apache.struts.action.ActionMappingn Permite acceder a los valores configurados en struts-config.xml para la acción en ejecución (inclusive a los forwards globales)

n org.apache.struts.action.ActionErrorsn Juega el papel del mapa de errores que hemos empleado en

apartados anterioresn El mensaje de error es un org.apache.struts.action.ActionError, que dispone de un constructor que permite especificar el identificador del mensaje de error (en Messages.properties)

n org.apache.struts.action.ActionForwardn Representa la siguiente URL a la que hay que irn El Front Controller lo invocará después de llamar al método execute sobre la acción

es.udc.fbellas.j2ee.strutstutorial.portal3.http.view.actionforms.LoginForm (1)

public class LoginForm extends ActionForm {

private String loginName;private String password;private boolean rememberMyPassword;

public LoginForm() {reset();

}

public String getLoginName() {return loginName;

}

public void setLoginName(String loginName) {this.loginName = loginName.trim();

}

es.udc.fbellas.j2ee.strutstutorial.portal3.http.view.actionforms.LoginForm (2)

public String getPassword() {return password;

}

public void setPassword(String password) {this.password = password;

}

public boolean getRememberMyPassword() {return rememberMyPassword;

}

public void setRememberMyPassword(boolean rememberMyPassword) {this.rememberMyPassword = rememberMyPassword;

}

es.udc.fbellas.j2ee.strutstutorial.portal3.http.view.actionforms.LoginForm (y 3)

public void reset(ActionMapping mapping, HttpServletRequest request) {reset();

}

public ActionErrors validate(ActionMapping mapping,HttpServletRequest request) {

ActionErrors errors = new ActionErrors();

PropertyValidator.validateMandatory(errors, "loginName", loginName);PropertyValidator.validateMandatory(errors, "password", password);

return errors;

}

private void reset() {loginName = null;password = null;rememberMyPassword = false;

}

}

Comentarios

n LoginFormn Juega el mismo papel que la clase vista en el apartado 4.2n Hereda de org.apache.struts.action.ActionForm

n Generalmente interesa redefinir reset y validaten reset

n El Front Controller lo llama antes de dar valor a las propiedadesn validate

n Permite validar las propiedades después de que el Front Controller les haya dado valores

n Sólo se invoca si se ha especificado validate="true" para la acción correspondiente en struts-config.xml

n Si devuelve un ActionErrors no vacío, el Front Controller (1) no invocará el método execute sobre la acción correspondiente, (2) insertará un atributo con los errores en larequest, y (3) hará un forward a la URL que especifica el atributo input del action correspondiente en struts-config.xml (formulario de entrada)

es.udc.fbellas.j2ee.strutstutorial.portal3.http.controller.actions

DefaultAction

(from action)

LoginAction LogoutActionMainPageAction

LoginManager

UserFacadeDelegate

(from delegate)

<<use>><<use>>

<<use>>

<<use>>

es.udc.fbellas.j2ee.strutstutorial.portal3.http.controller.actions.LoginAction (1)

public class LoginAction extends DefaultAction {

public ActionForward doExecute(ActionMapping mapping,ActionForm form, HttpServletRequest request,HttpServletResponse response)throws IOException, ServletException, InternalErrorException {

/* Get data. */LoginForm loginForm = (LoginForm) form;String loginName = loginForm.getLoginName();String password = loginForm.getPassword();boolean rememberMyPassword = loginForm.getRememberMyPassword();

/* Do login. */ActionErrors errors = new ActionErrors();

es.udc.fbellas.j2ee.strutstutorial.portal3.http.controller.actions.LoginAction (y 2)

try {

LoginManager.login(request, response, loginName, password,rememberMyPassword);

} catch (InstanceNotFoundException e) {errors.add("loginName", new ActionError(

"ErrorMessages.loginName.notFound"));} catch (IncorrectPasswordException e) {

errors.add("password", new ActionError("ErrorMessages.password.incorrect"));

}

/* Return ActionForward. */if (errors.isEmpty()) {

return mapping.findForward("MainPage");} else {

saveErrors(request, errors); return new ActionForward(mapping.getInput());

}

}

}

es.udc.fbellas.j2ee.strutstutorial.portal3.http.controller.actions.LogoutAction

public class LogoutAction extends DefaultAction {

public ActionForward doExcute(ActionMapping mapping,ActionForm form, HttpServletRequest request,HttpServletResponse response)throws IOException, ServletException, InternalErrorException {

/* Do logout. */LoginManager.logout(request, response);

/* Return ActionForward. */ return mapping.findForward("MainPage");

}

}

Comentarios

n Las acciones utilizan el método saveErrors(heredado de org.struts.apache.action.Action) para insertar un atributo con los errores en la request

MainPage.jsp (1)

<%@ taglib prefix="c" uri="http://java.sun.com/jstl/core" %><%@ taglib prefix="fmt" uri="http://java.sun.com/jstl/fmt" %><%@ taglib prefix="html" uri="/struts-html.tld" %>

<html><head> <title><fmt:message key="MainPage.title"/></title></head>

<body text="#000000" bgcolor="#ffffff" link="#000ee0" vlink="#551a8b" alink="#000ee0">

MainPage.jsp (2)<%-- Welcome --%>

<h1>

<c:choose><c:when test="${empty sessionScope.loginName}">

<fmt:message key="MainPage.welcome"/></c:when><c:otherwise>

<fmt:message key="MainPage.hello"/><c:out value="${sessionScope.loginName}"/>

</c:otherwise></c:choose>

</h1>

<%-- Some <br>'s --%>

<br><br><br>

MainPage.jsp (y 3)<%-- Links to Login or Logout --%>

<c:choose><c:when test="${empty sessionScope.loginName}">

<html:link href="Login.jsp"><fmt:message key="MainPage.login"/>

</html:link></c:when><c:otherwise>

<html:link href="Logout.do"><fmt:message key="MainPage.logout"/>

</html:link></c:otherwise>

</c:choose>

</body>

</html>

Login.jsp (1)<%@ taglib prefix="fmt" uri="http://java.sun.com/jstl/fmt" %><%@ taglib prefix="html" uri="/struts-html.tld" %>

<html><head> <title><fmt:message key="Login.title"/></title></head>

<body text="#000000" bgcolor="#ffffff">

<html:form action="Login.do">

<table width="100%" border="0" align="center" cellspacing="12">

<%-- Login name --%>

<tr> <th align="right" width="50%">

<fmt:message key="Login.loginName"/></th><td align="left">

<html:text property="loginName" size="16" maxlength="16"/><html:errors property="loginName"/>

</td></tr>

Login.jsp (2)<%-- Password --%>

<tr> <th align="right" width="50%">

<fmt:message key="Login.password"/></th><td align="left">

<html:password property="password" size="16“maxlength="16"/>

<html:errors property="password"/></td>

</tr>

<%-- Remember my password --%>

<tr> <th align="right" width="50%">

<fmt:message key="Login.rememberMyPassword"/></th><td align="left">

<html:checkbox property="rememberMyPassword"/></td>

</tr>

Login.jsp (y 3)<%-- Login button --%>

<tr> <td width="50%"></td><td align="left" width="50%">

<html:submit><fmt:message key="Buttons.login"/>

</html:submit></td>

</tr>

</table>

</html:form>

</body>

</html>

Comentarios

n html:text, html:password y html:checkboxrecuperan el valor de la propiedad asociada a través del método getXXX (property="XXX") sobre la instancia de LoginForm enganchada a la requestn Saben que el ActionForm asociado es de tipo LoginForm,

dado que el atributo action de html:form es igual a Login.do

n struts-config.xml especifica LoginForm como la clase ActionForm para la URL /Login.do

n html:errorsn Imprime el mensaje de error asociado a la propiedad

especificada si figura en el ActionErrors enganchado a la request

n El mensaje vendrá flanqueado por errors.header y errors.footer (Messages.properties)

InternalError.jsp<%@ taglib prefix="fmt" uri="http://java.sun.com/jstl/fmt" %>

<html><head> <title><fmt:message key="InternalError.title"/></title></head>

<body text="#000000" bgcolor="#ffffff">

<h3><div align="center"><font color="red"><fmt:message key="InternalError.title"/>.<fmt:message key="ErrorMessages.retry"/></font></div></h3>

</body>

</html>

Un pequeño problema

n Situaciónn Imaginemos que la página de bienvenida fuese MainPage.jspn En realidad es Index.jsp

n Un usuario hace el login y selecciona “Remember my password”

n Termina la sesiónn Accede dos días después tecleando la URL de la aplicación

en su navegador (ej.: http://www.acme.org/MiniPortal)n La sesión no contendrá el atributo loginName, dado que no

se ha ejecutado LoginManager.getLoginNamen Se le redirige a la página de login

Una solución

n El navegador nunca invocará a /MainPage.jspdirectamente, sino a /Index.jsp o /MainPage.don Index.jsp

n Página de bienvenidan Hace un forward a /MainPage.do => se ejecuta MainPageAction

n MainPageActionn LoginManager.getLoginName y forward a

/MainPage.jsp

n Cuando se haga un sendRedirect a la página principal se hará siempre con la URL /MainPage.do y nunca con /MainPage.jspn /MainPage.jsp nunca aparecerá en la caja de diálogo del

navegador, de manera que el usuario nunca hará un bookmark a esa página, sino a /MainPage.do o /Index.jsp

n En MiniPortal volveremos a discutir este problema

es.udc.fbellas.j2ee.strutstutorial.portal3.http.controller.actions.MainPageAction

public class MainPageAction extends DefaultAction {

public ActionForward doExecute(ActionMapping mapping,ActionForm form, HttpServletRequest request,HttpServletResponse response)throws IOException, ServletException, InternalErrorException {

/* * "LoginManager.getLoginName" creates an appropriate session* if the session had expired, or the user had not logged in, * but he/she had selected "remember my password" in the last* login. */

LoginManager.getLoginName(request);

/* Return ActionForward. */ return mapping.findForward("ShowMainPage");

}

}

Index.jsp

<jsp:forward page="MainPage.do"/>

Action forms dinámicos y Validator

n Action forms dinámicos (Struts 1.1)n Se especifican (en XML) en el fichero de configuraciónn Evitan la necesidad de programar action forms de tipo Java

Bean

n Validator (Struts 1.1)n Permite definir reglas de validación (en XML) de las

propiedades de los action forms (sean dinámicos o no), evitando tener que codificar éstas en Java