Download 4.4 Tutorial de JSP 2.0, JSTL y Jakarta Struts

Document related concepts
no text concepts found
Transcript
4.4 Tutorial de JSP 2.0, JSTL y Jakarta
Struts
JSP 2.0 (1)
„
¿Qué añade JSP 2.0 frente a JSP 1.x?
„
Lenguaje de expresiones
„
„
“Documentos JSP”
„
„
„
Anteriormente sólo estaba disponible en JSTL
Páginas JSP en sintaxis XML
JSP 1.2 también permitía escribir documentos JSP, pero de una
manera más incómoda
Implementar tags a medida utilizando la propia tecnología
JSP
„
„
„
Se implementan en páginas JSP
Más sencillo que el API de extensión de tags
(javax.servlet.jsp.tagext), pero menos potente
Útil para tags orientados a presentación, para tags que hagan
uso de librerías existentes y para desarrolladores de páginas
JSP que no dispongan de conocimientos de Java
JSP 2.0 (y 2)
„
Compatibilidad con JSP 1.x
„
„
„
Un contenedor de JSP 2.0 tiene que poder ejecutar
aplicaciones con sintaxis JSP 1.x
Es posible migrar una aplicación JSP 1.x a sintaxis JSP 2.0
página a página
En este apartado, y en los dos siguientes, se ilustran
la sintaxis de los documentos JSP y el lenguaje de
expresiones
Lenguaje de expresiones (1)
„
En JSP 1.x si se desea dar valor a un atributo de un
tag, es preciso usar una expresión <%= ... %>
„
Ejemplo
<jsp:useBean id="shoppingCart" scope="session"
class="org.acme.ShoppingCart"/>
<xxx:if test="<%= shoppingCart.getNumberOfProducts() > 0 %>">
...
</xxx:if>
„
JSP 2.0 proporciona un lenguaje de expresiones para
facilitar su construcción
„
Ejemplo
<xxx:if test="${sessionScope.shoppingCart.numberOfProducts > 0}">
...
</xxx:if>
Lenguaje de expresiones (2)
„
Expresiones y literales
„
„
Las expresiones tienen que ir rodeadas por ${ y }.
Cualquier valor que no empiece por ${, se considera un
literal
„
„
Los literales que incluyen el símbolo ${, han de escaparlo
rodeándolo de ${' y '}
Ejemplo:
<xxx:aTag att="This literal includes ${'${'} character"/>
„
Acceso a atributos de objetos Java en expresiones
„
Se puede acceder a las propiedades de un JavaBean, y a
objetos de un Map, List o vector
Lenguaje de expresiones (3)
„
Acceso a atributos de objetos Java en expresiones
(cont)
„
Ejemplos
„
„
„
„
„
${user.firstName} = user.getFirstName()
${user.address.city} = user.getAddress().getCity()
${user.preferencesMap["shipping"]} =
user.getPreferencesMap().get("shipping")
${user.preferencesList[0]} = user.getPreferencesList().get(0)
Unifica el tratamiento de los operadores . y []
„
„
${user.firstName} es
equivalente a ${user["firstName"]}
${user.preferencesMap["shipping"]} es equivalente a
${user.preferencesMap.shipping}
„
Para determinados casos, es preciso usar el operador []
„
„
${user.preferencesMap["book.fiction"]} es equivalente
user.getPreferencesMap().get("book.fiction")
a
${user.preferencesMap[product.category]} es equivalente
user.getPreferencesMap().get(product.getCategory())
a
Lenguaje de expresiones (4)
„
Objetos implícitos
„
Entre otros
„
„
„
„
„
„
„
pageScope (Map)
requestScope (Map)
sessionScope (Map)
applicationScope (Map)
param (Map que mapea nombres de parámetros univaluados a
String)
paramValues (Map que mapea nombres de parámetros
multivaluados a String[])
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)
„
Ejemplo
<xxx:if test="${shoppingCart.numberOfProducts > 0}">
...
</xxx:if>
Lenguaje de expresiones (y 5)
„
Literales
„
„
„
„
„
Boolean (true y false)
Numéricos
Cadenas de caracteres (entre comillas simples o dobles)
null
Operadores
„
Aritméticos: +,-, *, /, div, %, mod
Lógicos: &&, and, ||, or, !, not
Relacionales: ==, eq, !=, ne, <, lt, >, gt, <=, le, >=, ge
empty: permite comprobar si un valor es null
„
Ejemplos
„
„
„
<xxx:if test="${!empty requestScope.previous}">
...
</xxx:if>
<xxx:if test="${sessionScope.shoppingCart.numberOfProducts > 0}">
...
</xxx:if>
„
Se pueden usar paréntesis
JSTL (1)
„
En el pasado existían numerosas librerías de tags JSP
que permitían
„
„
„
„
„
„
„
Iterar sobre colecciones
Imprimir valores de propiedades de JavaBeans de forma
segura
Internacionalización de mensajes, números, fechas, etc.
Generación de URLs aplicando URL rewriting
Acceso a documentos XML
Etc
Por ello, se decidió estandarizar una librería general
de tags, llamada JSTL (JSP Standard Tag Library)
JSTL (2)
„
Tags en JSTL
„
Core
„
„
„
I18n (internacionalización)
„
„
Control de flujo
Soporte para URLs
Soporte para internacionalización: establecimiento del Locale,
generación de mensajes, formateo de números, cantidades
monetarias, fechas, etc.
XML
„
„
„
Parsing de un documento XML
Flujo de control para recorrer un documento XML (alternativa a
XSL para casos sencillos)
Tags para lanzar transformaciones XSL
JSTL (y 3)
„
Tags en JSTL (cont)
„
Acceso a BDs
„
„
„
Funciones
„
„
JSP 2.0 define un mecanismo para añadir funciones al
lenguaje de expresiones
JSTL 1.1 define un conjunto de funciones estándar
„
„
Permiten lanzar sentencias SQL a BDs relacionales
Sólo deberían usarse para prototipado rápido o
aplicaciones muy simples
length (aplicable a Collection y String), toLowerCase,
toUpperCase, substring, contains, etc
En este apartado y en los dos siguientes se ilustran
parte de los tags de los grupos Core e I18n
¿ Qué es Struts ?
„
„
Framework OpenSource para implementar
aplicaciones web con servlets y JSP según el patrón
arquitectónico Model-View-Controller
Subproyecto de Jakarta
„
„
„
Autor original: Craig R. McClanahan
Funciona sobre cualquier servidor de aplicaciones
web que implemente las APIs de servlets y JSP
Ha ganado gran relevancia en el mundo de las
aplicaciones web Java
„
„
Versión 1.0 estable en Julio 2001
Posteriormente, surgieron otros framework MVC
¿ Qué proporciona Struts ?
„
Un framework que da soporte para implementar las
capas controlador y vista de una aplicación web
„
„
„
„
Servlet Front Controller y clases relacionadas
Sistema de plantillas
Validación de parámetros
Una librería de tags JSP muy completa
El patrón Front Controller en Struts (1)
javax.servlet.http.HttpServlet
org.apache.struts.action.ActionServlet
0..n
# doGet
# doPost
org.apache.struts.action.Action
+ execute
<<use>> <<instantiate>>
org.apache.struts.action.ActionForm
+ reset
+ validate
ActionForm1
Action1
...
...
ActionFormN
<<use>>
ActionN
El patrón Front Controller en Struts (2)
„
ActionServlet
„
„
Servlet Front Controller
En web.xml se especifica que todas las URLs que impliquen
procesamiento (por GET o POST) vayan a este servlet
„
„
Clases ActionForm
„
Si el programador lo desea, puede acceder a los parámetros de la
request a través de un JavaBean que extiende ActionForm
„
„
Ej.: las URLs que termine en .do
Especialmente útil en formularios
Clase Action => método execute
„
„
„
„
Accede a los parámetros de la request, directamente o vía el
ActionForm correspondiente
Realiza la operación invocando un método de un Session
Facade del modelo o una fachada del controlador
Deja el resultado devuelto por el método en la request o en la
sesión
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)
„
Fichero de configuración
„
Clases ActionForm que usa nuestra aplicación
„
„
„
Nombre lógico (ej.: loginForm)
Nombre completo de la clase (ej.:
es.udc.fbellas.j2ee.strutstutorial.portal3.http
.view.actionforms.LoginForm)
URLs que implican procesamiento
„
URL de tipo path relativo a contexto (ej.: /Login)
„
„
„
No llevan el .do final
Nombre completo de la clase Action (ej.:
es.udc.fbellas.j2ee.strutstutorial.portal3.http
.controller.actions.LoginAction)
Nombre lógico de la clase ActionForm asociada
El patrón Front Controller en Struts (y 4)
„
Fichero de configuración (cont)
„
Definiciones de nombres lógicos de URLs
„
„
„
„
„
Nombre que usan las acciones cuando devuelven un
ActionForward (ej.: ShowMainPage)
sendRedirect o forward
URL a invocar (ej.: /MainPage.jsp)
Cuando el servlet ActionServlet arranca (init), lee el
fichero de configuración
Crea una única instancia de cada clase Action
„
„
„
No se crea una instancia de una clase Action por cada
petición que se recibe
Tienen que ser thread-safe
Misma situación que cuando se trabaja con servlets
La librería de tags de Struts (1)
„
Bean
„
„
„
„
Logic
„
„
„
Imprimir el valor de las propiedades de JavaBeans de
manera segura
Soporte para internacionalización de mensajes
No los usaremos, dado que JSTL ofrece una alternativa
estándar
Control de flujo
No los usaremos, dado que JSTL ofrece una alternativa
estándar
HTML
„
Generación de HTML básico
„
„
Campos de entrada en formularios
Enlaces (con URL rewriting)
La librería de tags de Struts (y 2)
„
Tiles
„
Caso particular del patrón Composite View
„
„
Sistema de plantillas para páginas JSP
Reemplaza a Template
„
El sistema de plantillas que se usaba con Struts 1.0
Arquitectura MVC con Struts
„
Modelo
„
„
Controlador
„
„
„
Clases independientes de la vista y el controlador
Conjunto de clases Action
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)
Vista
„
Conjunto de clases ActionForm
„
Conjunto de páginas JSP
„
„
„
No contienen código Java
Sólo visualizan datos
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 en Login
Clic en el botón “Login”
Demo Portal-3 (3)
Clic en Logout
Terminar y lanzar el navegador dos días más tarde
Acceder a Portal-3 main page
Portal-3 main page
(Welcome to Portal-3)
Demo Portal-3 (4)
„
„
Este ejemplo, al igual que los siguientes, usa XHTML
1.0 Estricto y CSS 2.0
XHTML
„
„
Versión XML de HTML (ej.: todos los tags tienen que
cerrarse, los valores de los atributos tienen que
entrecomillarse, tags en minúsculas, etc)
CSS
„
„
„
El XHTML generado sólo contiene contenido estructurado
El formato (fuentes, colores, posicionamiento, etc) se
especifica en una hoja (fichero) de estilos CSS
El aspecto gráfico de la aplicación puede cambiarse
modificando la hoja CSS
Demo Portal-3 (y 5)
„
CSS (cont)
„
También puede ser interesante tener un conjunto de hojas
CSS con distintos formatos para una misma aplicación web
„
„
„
„
„
Visualización en ordenador de sobremesa
Visualización en PDA (ej.: no muestra o resume cabecera,
sidebar y pié de página)
“Printer-friendly pages”
Etc
Se procura huir del uso de tablas, excepto para la
presentación de datos que siempre han de
visualizarse de manera tabular (ej.: las cuentas de un
usuario en una aplicación bancaria)
„
CSS tiene sus propios mecanismos de posicionamiento
Estructura de paquetes
es.udc.fbellas.j2ee.util.struts.action
es.udc.fbellas.j2ee.strutstutorial.portal3
http
controller
actions
view
actionforms
messages
model
userfacade
delegate
exceptions
jar tvf StrutsTutorial.war (1)
Index.jspx
InternalError.jspx
Login.jspx
MainPage.jspx
css/styles.css
WEB-INF/Struts/struts-config.xml
WEB-INF/lib/jstl.jar
WEB-INF/lib/standard.jar
WEB-INF/lib/antlr.jar
WEB-INF/lib/commons-*.jar
WEB-INF/lib/jakarta-oro.jar
WEB-INF/lib/struts.jar
WEB-INF/lib/StandardUtil.jar
WEB-INF/lib/WebUtil.jar
jar tvf StrutsTutorial.war (y 2)
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/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/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/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/view/
messages/Messages.properties
WEB-INF/web.xml
Comentarios (1)
„
Documentos JSP
„
„
„
Existen varios métodos para especificar que una página JSP
es un documento JSP
Quizás la manera más natural consiste en usar un descriptor
de la aplicación web conforme a Servlet 2.4 (como ya
hicimos en anteriores apartados) y usar jspx como
extensión de las páginas JSP que sean documentos JSP
WEB-INF/Struts
„
struts-config.xml: configuración de Struts para la
aplicación del tutorial
Comentarios (y 2)
„
WEB-INF/lib
„
struts.jar, commons-*.jar: Struts
„ standard.jar, jstl.jar, jakarta-oro.jar,
antlr.jar: Jakarta Standard TagLibs (implementación
OpenSource de JSTL)
„ StandardUtil.jar y WebUtil.jar: subsistema Util de
J2EE-Examples
WEB-INF/classes/es/.../Messages.properties
„
„
Internacionalización de mensajes
WEB-INF/web.xml (1)
<?xml version="1.0" encoding="ISO-8859-1"?>
<web-app xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee
http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"
version="2.4">
<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 (y 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.jspx</welcome-file>
</welcome-file-list>
</web-app>
Comentarios (1)
„
context-param
„
„
„
Permite definir un parámetro de configuración global a toda
la aplicación web
Accesible vía
Servlet.getServletConfig().getServletContext
().getInitParameter()
En el ejemplo se utiliza para dar valor al parámetro de
configuración
javax.servlet.jsp.jstl.fmt.localizationContext
„
„
„
Lo usan los tags de internacionalización de mensajes de JSTL
Nombre del fichero de mensajes (sin sufijo .properties)
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)
„
Servlet org.apache.struts.actions.ActionServlet
„
Aparecen dos tags que no hemos usado hasta ahora
„
init-param
„
„
„
Permite definir un parámetro de configuración específico al servlet
y su valor
Accesible vía
Servlet.getServletConfig().getInitParameter()
load-on-startup
„
„
Indica que el servlet se debería cargar cuando el servidor
arranque la aplicación web
El valor (opcional) indica el orden relativo de carga con respecto a
otros servlets (cuanto menor sea el valor, antes se carga)
Comentarios (y 3)
„
Servlet org.apache.struts.actions.ActionServlet
„
Parámetros de inicialización
„
config
„
„
detail
„
„
Path de tipo relativo a contexto del fichero de configuración de
Struts
Nivel de detalle en los mensajes de depuración durante el parsing
de los ficheros de configuración
debug
„
Nivel de detalle en los mensajes de depuración de
ActionServlet
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.2//EN"
"http://jakarta.apache.org/struts/dtds/struts-config_1_2.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.jspx"
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.jspx"/>
</action>
<action path="/Login"
type="es.udc.fbellas.j2ee.strutstutorial.portal3.http.
controller.actions.LoginAction"
name="loginForm" scope="request" input="/Login.jspx"
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.
These must be either omitted or protected by security in a real
application deployment.
================================================================ -->
<action path="/admin/addFormBean"
type="org.apache.struts.actions.AddFormBeanAction"/>
...
</action-mappings>
<!-- ============ Message Resources Definitions =================== -->
<message-resources parameter="es.udc.fbellas.j2ee.strutstutorial.
portal3.http.view.messages.Messages"/>
</struts-config>
Comentarios (1)
„
En el fichero struts-config.xml se usa
<!DOCTYPE struts-config PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 1.2//EN"
"http://jakarta.apache.org/struts/dtds/struts-config_1_2.dtd">
„
„
El especificador PUBLIC permite especificar un identificador
y una URI para la ubicación del DTD
El procesador del documento XML (en este caso, Struts)
puede usar el identificador para recuperar un DTD
localmente almacenado, y en consecuencia, no usar la URI
especificada
„
struts.jar incluye el DTD
Comentarios (2)
„
Definiciones de nombres lógicos de URLs
„
„
Se usan en la implementación de las acciones para devolver
un ActionForward y en algunas acciones JSP
Se especifican con forward
„
„
„
„
Atributos documentados en JavaDoc de
org.apache.struts.action.ActionForward
name: nombre lógico
path: path relativo a contexto de la URL a la que se invocará
redirect: true (sendRedirect) o false (forward)
„
„
false por defecto
Pueden ser globales (global-forwards) o particulares a
una acción (action)
Comentarios (3)
„
action
„
„
„
Atributos documentados en JavaDoc de
org.apache.struts.config.ActionConfig
type: nombre completo de la clase Action
path: URL (path relativo a contexto) que provocará la
invocación de la acción
„
„
„
name: nombre del ActionForm (definido por form-bean)
que captura los parámetros de la invocación
scope: ámbito (request o session) del ActionForm
„
„
„
¡ Se especifican sin el sufijo .do !
En general, request
input: path relativo a contexto del formulario de entrada
validate: true si el Front Controller tiene que llamar al
método validate del ActionForm
„
En general, true
Comentarios (y 4)
„
message-resources
„
„
Especifica la ubicación del fichero de mensajes
Actualmente Struts no está integrado con JSTL
WEB-INF/classes/es/.../Messages.properties (1)
Buttons.login=Login
ErrorMessages.loginName.notFound=Login name not found
ErrorMessages.mandatoryField=Mandatory field
ErrorMessages.password.incorrect=Incorrect password
ErrorMessages.retry=Please, check if the operation has been performed, \
and retry if necessary
errors.footer=</span>
errors.header=<span class="errorMessage">
InternalError.title=Internal error
WEB-INF/classes/es/.../Messages.properties (y 2)
Login.loginName=Login name
Login.password=Password
Login.rememberMyPassword=Remember my password (cookies must be enabled)
Login.title=Portal-3 login form
MainPage.hello=Hello
MainPage.login=Login
MainPage.logout=Logout
MainPage.title=Portal-3 main page
MainPage.welcome=Welcome to Portal-3
Comentarios (1)
„
Asocia pares <identificadorMensaje, mensaje>
„
„
„
„
Messages.properties
„
„
Convenios de nombrado para los identificadores de mensajes
Ordenados alfabéticamente
errors.footer y errors.header son dos identificadores
especiales que entiende la acción html:errors de Struts
Mensajes en el lenguaje por defecto del servidor
Messages_xx.properties
„
„
Mensajes en el lenguaje cuyo código ISO es xx
Códigos ISO en
http://www.ics.uci.edu/pub/ietf/http/related/iso639.txt
„
en: Inglés
es: Español
gl: Gallego
„
Etc
„
„
Comentarios (y 2)
„
Messages.properties sólo resuelve un aspecto
particular de la internacionalización de aplicaciones:
impresión de mensajes en distintos idiomas
„
„
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 seleccionarlos o incluirlos dinámicamente
en función del idioma
Otros aspectos en internacionalización
„
Formatear y tratar fechas, horas, números, cantidades
monetarias
„
„
„
JSTL proporciona tags para ello
También paquetes java.text y java.util
Puede requerir tablas para almacenar contenido en distintos
idiomas
„
Ej.: un servicio de noticias
es.udc.fbellas.j2ee.strutstutorial.portal3.model.userfacade.delegate
UserFac adeDelegate
+ UserFacadeDelegate()
+ login(l oginNam e : S tring, password : S t ri ng, pas swordIsE ncrypte d : boo lean) : void
„
Simula la fachada del modelo que proporciona las
operaciones relativas a la interacción del usuario con
el portal
es.udc.fbellas.j2ee.util.struts.action
A ction
(from action)
Def aul tA ct io n
+ execute(actionM apping, actionForm , request, response) : A ctionForward
# doE xecute(actionMapping, actionForm , request, response) : A ctionForward
# doOnInternalE rror(actionM apping, actionForm , request, response, internalE rrorE xception) : A ctionForw...
P rope rt yV alidat or
Comentarios
„
DefaultAction
„
Problema
„
„
„
Es necesario (1) capturarla, (2) imprimirla en un log (para
depuración) e (3) ir a una página que indique error interno
Las clases Action derivarán de DefaultAction
„
„
„
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
Implementa execute (Template Method) en términos de
doExecute y doOnInternalError
Las clases hijas implementan doExecute, que tiene la misma
signatura que execute, pero puede lanzar adicionalmente
InternalErrorException
PropertyValidator
„
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
import
import
import
import
import
import
import
import
java.io.IOException;
javax.servlet.ServletContext;
javax.servlet.http.HttpServletRequest;
javax.servlet.http.HttpServletResponse;
javax.servlet.ServletException;
org.apache.struts.action.Action;
org.apache.struts.action.ActionMapping;
org.apache.struts.action.ActionForm;
org.apache.struts.action.ActionForward;
import es.udc.fbellas.j2ee.util.exceptions.InternalErrorException;
es.udc.fbellas.j2ee.util.struts.action.DefaultAction (2)
public abstract class DefaultAction extends Action {
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 ActionMessage(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 ActionMessage(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
„
org.apache.struts.action.ActionMapping
„
„
Permite acceder a los valores configurados en strutsconfig.xml para la acción en ejecución (inclusive a los forwards
globales)
org.apache.struts.action.ActionErrors
„
„
Juega el papel del mapa de errores que hemos empleado en
apartados anteriores
El mensaje de error es un
org.apache.struts.action.ActionMessage, que dispone
de un constructor que permite especificar el identificador del
mensaje de error (en Messages.properties)
„
„
ActionMessage se introdujo en Struts 1.2, y reemplaza a
ActionError
org.apache.struts.action.ActionForward
„
„
Representa la siguiente URL a la que hay que ir
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
„
LoginForm
„
„
„
Juega el mismo papel que la clase vista en el apartado 4.2
Hereda de org.apache.struts.action.ActionForm
Generalmente interesa redefinir reset y validate
„
reset
„
„
El Front Controller lo llama antes de dar valor a las propiedades
validate
„
„
„
Permite validar las propiedades después de que el Front
Controller les haya dado valores
Sólo se invoca si se ha especificado validate="true" para
la acción correspondiente en struts-config.xml
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 la
request, y (3) hará un forward a la URL que especifica el
atributo input del action correspondiente en strutsconfig.xml (formulario de entrada)
es.udc.fbellas.j2ee.strutstutorial.portal3.http.controller.actions
Defaul tAc tion
(from ac tion)
M ai nP ageA c t ion
< < us e> >
LoginA c t ion
< <us e>>
LogoutA c tion
< < use> >
LoginM anager
<< us e>>
Us erFacadeDelegate
(from delegate)
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. */
ActionMessages errors = new ActionMessages();
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 ActionMessage(
"ErrorMessages.loginName.notFound"));
} catch (IncorrectPasswordException e) {
errors.add("password", new ActionMessage(
"ErrorMessages.password.incorrect"));
}
/* Return ActionForward. */
if (errors.isEmpty()) {
return mapping.findForward("MainPage");
} else {
saveErrors(request, errors);
return new ActionForward(mapping.getInput());
}
}
}
Comentarios
„
„
Las acciones utilizan el método saveErrors
(heredado de org.struts.apache.action.Action)
para insertar un atributo con los errores en la
request
org.apache.struts.action.ActionMessages
„
„
„
Se introdujo en Struts 1.2
Similar a ActionErrors
Existen dos versiones del método saveErrors, una que
acepta ActionErrors (“deprecated”) y otra que acepta
ActionMessages (el usado en el ejemplo)
es.udc.fbellas.j2ee.strutstutorial.portal3.http.controller.actions.LogoutAction
public class LogoutAction extends DefaultAction {
public ActionForward doExecute(ActionMapping mapping,
ActionForm form, HttpServletRequest request,
HttpServletResponse response)
throws IOException, ServletException, InternalErrorException {
/* Do logout. */
LoginManager.logout(request, response);
/* Return ActionForward. */
return mapping.findForward("MainPage");
}
}
MainPage.jspx (1)
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:jsp="http://java.sun.com/JSP/Page"
xmlns:fmt="http://java.sun.com/jsp/jstl/fmt"
xmlns:html="http://struts.apache.org/tags-html"
xmlns:c="http://java.sun.com/jsp/jstl/core">
<jsp:output doctype-root-element="html"
doctype-public="-//W3C//DTD XHTML 1.0 Strict//EN"
doctype-system="http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"
omit-xml-declaration="true" />
<jsp:directive.page contentType="text/html; charset=iso-8859-1" />
<head>
<title><fmt:message key="MainPage.title" /></title>
<c:url var="stylesURL" value="/css/styles.css" />
<link rel="StyleSheet" href="${stylesURL}"
type="text/css" media="all" />
<meta http-equiv="Content-Type"
content="text/html; charset=iso-8859-1" />
</head>
<body>
MainPage.jspx (2)
<!-- Welcome -->
<div id="header">
<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>
</div>
MainPage.jspx (y 3)
<!-- Links to Login or Logout -->
<p>
<c:choose>
<c:when test="${empty sessionScope.loginName}">
<c:url var="loginURL" value="Login.jspx" />
<a href="${loginURL}">
<fmt:message key="MainPage.login" />
</a>
</c:when>
<c:otherwise>
<html:link action="Logout.do">
<fmt:message key="MainPage.logout" />
</html:link>
</c:otherwise>
</c:choose>
</p>
</body>
</html>
Comentarios (1)
„
„
La página JSP es un documento XML bien formado
Importación de librerías
„
„
„
En el tag raíz aprovechamos para importar las librerías de
tags que se precisan, especificando sus espacios de nombres
El espacio de nombres por defecto es el correspondiente a
los tags de XHTML (http://www.w3.org/1999/xhtml)
Librerías
„
http://java.sun.com/JSP/Page (jsp)
„
„
http://java.sun.com/jsp/jstl/fmt (fmt)
„
„
Tags I18n de JSTL
http://struts.apache.org/tags-html (html)
„
„
Tags estándar de JSP (proporcionados por el contenedor)
Tags HTML de Struts
http://java.sun.com/jsp/jstl/core (c)
„
Tags Core de JSTL
Comentarios (2)
„
Importación de librerías (cont)
„
Cuando se importa una librería, el contenedor busca
automáticamente su descriptor (fichero .tld) en
„
„
WEB-INF (y sus subdirectorios)
En los ficheros .jar que usa la aplicación
„
„
Dentro del fichero .jar busca debajo de META-INF (y sus
subdirectorios)
El descriptor especifica, entre otras cosas,
„
„
„
La URI del espacio de nombres (que es lo que utiliza el
contenedor para saber que éste es el descriptor de la librería)
Los nombres de los tags que proporciona la librería y los
nombres de las clases que los implementan
En el servlet generado por el contenedor, por cada aparición de
un tag de una librería, se crea una instancia la clase
correspondiente y se invocan los métodos necesarios a través
de un interfaz estándar
Comentarios (3)
„
Importación de librerías (cont)
„
Cuando el contenedor encuentra un tag no JSP (ej.: html),
que importa librerías JSP (ej.: xlmns:fmt="... "), en la
respuesta generada no incluye los xlmns:xxx
correspondientes
„
Ej.: Para ...
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:jsp="http://java.sun.com/JSP/Page"
xmlns:fmt="http://java.sun.com/jsp/jstl/fmt"
xmlns:html="http://struts.apache.org/tags-html"
xmlns:c="http://java.sun.com/jsp/jstl/core">
„
... genera ...
<html xmlns="http://www.w3.org/1999/xhtml">
Comentarios (4)
„
En MainPage.jspx se utiliza
<jsp:output doctype-root-element="html"
doctype-public="-//W3C//DTD XHTML 1.0 Strict//EN"
doctype-system="http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"
omit-xml-declaration="true" />
„
... lo que genera ...
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
„
„
... y se genera en el lugar adecuado, es decir, antes de que
se genere el tag raíz html
omit-xml-declaration="true" provoca que no se
genere la declaración XML
„
„
Por defecto, el contenedor añade la declaración XML al
principio del documento generado por un documento JSP
Lo lógico sería generar la declaración XML (dado que todo
documento XML debería tenerla), sin embargo causa
problemas en algunos navegadores
Comentarios (5)
„
Tags jsp:directive.XXX
„
„
Equivalentes a las directivas <%@ XXX ... %>, con sus
mismos atributos
En MainPage.jspx se utiliza
<jsp:directive.page contentType="text/html; charset=iso-8859-1" />
„
„
„
Permite especificar el contentType de la respuesta HTTP
En un documento JSP, el contentType por defecto es
text/xml, lo que provoca que algunos navegadores (ej.:
Internet Explorer) visualicen la respuesta como un documento
XML (y no como una página HTML)
En una página JSP (que no sea un documento JSP), el
contentType por defecto es text/html, y por eso nunca lo
hemos tenido que especificar en los ejemplos anteriores
Comentarios (6)
En el ejemplo se usa
„
<c:url var="stylesURL" value="/css/styles.css" />
<link rel="StyleSheet" href="${stylesURL}"
type="text/css" media="all" />
Alternativamente se podría haber usado
„
<link rel="StyleSheet" href="css/styles.css"
type="text/css" media="all" />
„
Pero esto resultaría tedioso y propenso a errores en una
aplicación web grande con páginas JSP en directorios con
cierto nivel de anidamiento (ej.:
href="../../../css/styles.css")
Usar
„
<link rel="StyleSheet" href="/css/styles.css"
type="text/css" media="all" />
„
No funcionaría, dado que la URL /css/styles.css no
existe
Comentarios (7)
Usar
„
<link rel="StyleSheet" href="/StrutsTutorial/css/styles.css"
type="text/css" media="all" />
„
„
Sería una mala idea, dado que el administrador del servidor
de aplicaciones web podría querer instalar la aplicación web
con otro nombre
El ejemplo usa el tag c:url
„
„
Aplica URL rewriting si el navegador no acepta cookies
(aunque en este caso no es útil)
Si la URL es de tipo path relativo a contexto (ej.:
/css/styles.css), le antepone el nombre de la
aplicación web, de manera que la URL resultante es de tipo
path absoluto (ej.:.
/StrutsTutorial/css/styles.css)
Comentarios (y 8)
„
html:link
„
„
Genera el enlace HTML (<a href= ... </a>)
Cuando se utiliza el atributo action, aplica URL rewriting si
el navegador no acepta cookies
„
„
El atributo action tiene que especificar la URL de una
acción de Struts
NOTA: también dispone (alternativamente) del atributo
href
„
„
La URL puede apuntar a cualquier sitio
No se aplica URL rewriting
Login.jspx (1)
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:jsp="http://java.sun.com/JSP/Page"
xmlns:fmt="http://java.sun.com/jsp/jstl/fmt"
xmlns:html="http://struts.apache.org/tags-html"
xmlns:c="http://java.sun.com/jsp/jstl/core">
<jsp:output doctype-root-element="html"
doctype-public="-//W3C//DTD XHTML 1.0 Strict//EN"
doctype-system="http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"
omit-xml-declaration="true" />
<jsp:directive.page contentType="text/html; charset=iso-8859-1" />
<head>
<title><fmt:message key="Login.title" /></title>
<c:url var="stylesURL" value="/css/styles.css" />
<link rel="StyleSheet" href="${stylesURL}"
type="text/css" media="all" />
<meta http-equiv="Content-Type"
content="text/html; charset=iso-8859-1" />
</head>
<body>
Login.jspx (2)
<!-- Struts tags must render XHML -->
<html:xhtml/>
<!-- Print login form -->
<html:form action="Login.do">
<!-- Login name -->
<div class="field">
<span class="label">
<fmt:message key="Login.loginName" />
</span>
<span class="entry">
<html:text property="loginName" size="16" maxlength="16" />
<html:errors property="loginName" />
</span>
</div>
Login.jspx (3)
<!-- Password -->
<div class="field">
<span class="label">
<fmt:message key="Login.password" />
</span>
<span class="entry">
<html:password property="password" size="16"
maxlength="16" />
<html:errors property="password" />
</span>
</div>
<!-- Remember my password -->
<div class="field">
<span class="label">
<fmt:message key="Login.rememberMyPassword" />
</span>
<span class="entry">
<html:checkbox property="rememberMyPassword" />
</span>
</div>
Login.jspx (y 4)
<!-- Login button -->
<div class="button">
<html:submit><fmt:message key="Buttons.login" /></html:submit>
</div>
</html:form>
</body>
</html>
Comentarios (1)
„
html:xhtml
„
„
Causa que los tags de Struts de la librería HTML que se usen
en esa página generen XHTML en vez de HTML (por defecto,
algunos tags, como html:text, html:password o
html:checkbox generan los tags sin cerrarlos, mientras
que otros sí los cierran, como por ejemplo, html:link)
html:text, html:password y html:checkbox
recuperan el valor de la propiedad asociada a través
del método getXXX (property="XXX") sobre la
instancia de LoginForm enganchada a la request
(con nombre loginForm)
„
„
Saben que el ActionForm asociado se llama loginForm,
dado que el atributo action de html:form es igual a
Login.do
struts-config.xml especifica loginForm como el
nombre del ActionForm para la URL /Login.do
Comentarios (y 2)
„
html:errors
„
„
Imprime el mensaje de error asociado a la propiedad
especificada si figura en el ActionErrors/
ActionMessages enganchado a la request
El mensaje vendrá flanqueado por errors.header y
errors.footer (Messages.properties)
InternalError.jspx (1)
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:jsp="http://java.sun.com/JSP/Page"
xmlns:fmt="http://java.sun.com/jsp/jstl/fmt"
xmlns:c="http://java.sun.com/jsp/jstl/core">
<jsp:output doctype-root-element="html"
doctype-public="-//W3C//DTD XHTML 1.0 Strict//EN"
doctype-system="http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"
omit-xml-declaration="true" />
<jsp:directive.page contentType="text/html; charset=iso-8859-1" />
<head>
<title><fmt:message key="InternalError.title" /></title>
<c:url var="stylesURL" value="/css/styles.css" />
<link rel="StyleSheet" href="${stylesURL}"
type="text/css" media="all" />
<meta http-equiv="Content-Type"
content="text/html; charset=iso-8859-1" />
</head>
<body>
InternalError.jspx (y 2)
<p>
<fmt:message key="InternalError.title" />.
<fmt:message key="ErrorMessages.retry" />
</p>
</body>
</html>
Un pequeño problema
„
Situación
„
Imaginemos que la página de bienvenida fuese
MainPage.jspx
„
„
„
„
Un usuario se autentica seleccionando “Remember my
password”
Termina la sesión
Accede dos días después tecleando la URL de la aplicación
en su navegador (ej.: http://www.acme.org/StrutsTutorial)
„
„
En realidad es Index.jspx
Se ejecuta MainPage.jspx
La sesión no contendrá el atributo loginName, dado que no
se ha ejecutado LoginManager.getLoginName
Una solución
„
El navegador nunca invocará a /MainPage.jspx
directamente
„
Index.jspx
„
„
„
MainPageAction
„
„
LoginManager.getLoginName y forward a
/MainPage.jspx
Cuando se haga un sendRedirect a la página principal se
hará siempre con la URL /MainPage.do y nunca con
/MainPage.jspx
„
„
Página de bienvenida
Hace un forward a /MainPage.do => se ejecuta
MainPageAction
/MainPage.jspx 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
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.jspx
<jsp:forward xmlns:jsp="http://java.sun.com/JSP/Page"
page="MainPage.do" />