4.3 Problemas con servlets y páginas
JSP. Patrones
Situación a la que queremos llegar
Poder usar directamente herramientas de diseño de
páginas web para implementar la vista
Las actualizaciones al aspecto gráfico no deben
provocar un re-arranque del servidor
Separación de roles
Informáticos
Modelo, controlador y partes de la vista no relacionados con el aspecto gráfico
Diseñadores gráficos o similares
Implementación del aspecto gráfico de la aplicación
Esta separación en muchos casos puede ser sólo ideal (depende de la cultura de la empresa), pero la idea es tender hacia ella
¿ Son los Servlets la solución ?
¿ Podemos usar directamente herramientas de diseño
de páginas web ?
No
¿ Se pueden hacer actualizaciones al aspecto gráfico
sin re-arrancar el servidor ?
No
¿ Es posible una separación de roles ?
Las personas que desarrollan el modelo no tienen porque ser las mismas que las que hacen los servlets
Sin embargo, las personas que desarrollan los servlets necesitan conocimientos de programación
¿ Es JSP la solución ?
¿ Podemos usar directamente herramientas de diseño
de páginas web ?
Sí
¿ Se pueden hacer actualizaciones al aspecto gráfico
sin re-arrancar el servidor ?
Sí
¿ Es posible una separación de roles ?
Las personas que desarrollan el modelo no tienen porque ser las mismas que las que hacen las páginas JSP
Sin embargo, las personas que desarrollan las páginas JSP necesitan conocimientos de programación para incrustar el código Java (scriptlets y expresiones)
¿ Cuál es la solución ?
Una que permita construir páginas JSP sin código
Java (o de otro lenguaje de programación)
¿ Cómo podemos conseguirlo ?
Con un buen diseño Model-View-Controller
Usando el mecanismo de extensión de tags de JSP
Permite implementar “acciones a medida” (custom actions)
Paquete javax.servlet.jsp.tagext
Podríamos implementar un buen número de acciones para
eliminar una parte importante de los scriptlets y expresiones de las páginas JSP
Un diseñador gráfico podría aprender a usarlas
Al fin y al cabo, tienen el aspecto de los tags HTML (un nombre y atributos)
Problema 1: URL rewriting
Para que una aplicación que use sesiones sea
robusta, necesita aplicar URL rewriting a todas las
URLs que genera o hace un sendRedirect
Ej.: Extracto de Portal2/MainPage.jsp
<a href="<%= response.encodeURL("../Index.jsp") %>"> Servlet and JSP tutorial main page</a>
<br>
<a href="<%= response.encodeURL("ProcessLogout.jsp") %>">Logout</a>
Sería más sencillo disponer de una acción que lo
hiciese automáticamente
<html:link page="../Index.jsp">Servlet and JSP tutorial main page
</html:link>
<br>
<html:link page="ProcessLogout.jsp">Logout</html:link>
Problema 2: Mostrar formularios (1)
Repasar Portal2/ShowLogin.jsp
Necesidad de un scriptlet muy grande para recuperar posibles errores
Necesidad de usar expresiones para insertar los posibles mensajes de errores
Necesidad de aplicar URL rewriting en el atributo action del formulario
Sería más sencillo disponer de acciones que
ocultasen todo este código
Problema 2: Mostrar formularios (2)
<html>
<head>
<title>Portal-2 login form</title>
</head>
<body text="#000000" bgcolor="#ffffff">
<html:form action="ProcessLogin.jsp">
<table width="100%" border="0" align="center" cellspacing="12">
<%-- Login name --%>
<tr>
<th align="right" width="50%">Login name</th>
<td align="left">
<html:text property="loginName" size="16" maxlength="16"/>
<html:errors property="loginName"/>
</td>
</tr>
Problema 2: Mostrar formularios (3)
<%-- Password --%>
<tr>
<th align="right" width="50%">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%">
Remember my password (cookies must be enabled)
</th>
<td align="left">
<html:checkbox property="rememberMyPassword"/>
</td>
</tr>
Problema 2: Mostrar formularios (y 4)
<tr>
<td width="50%"></td>
<td align="left" width="50%">
<input type="submit" value="Login">
</td>
</tr>
</table>
</html:form>
</body>
</html>
Patrón View Helper
Este tipo de acciones JSP a medida que estamos
viendo (problemas 1 y 2), y que nos ayudan a
generar la vista, corresponden a la aplicación del
patrón View Helper ( Core J2EE Patterns )
Facilitan el acceso a objetos Java (en cualquiera de
los cuatro ámbitos)
Generan HTML, WML, etc. sin aspecto gráfico
Ej.: Campos de entrada en formularios, URLs (para hacer más transparente el paso de parámetros, URL rewriting, etc.)
Nunca deben generar HTML, WML, etc.
centrado en aspecto gráfico
Ejemplo de mala idea: tener una acción que imprime un objeto Java en una tabla
Precisamente lo que queremos evitar es código Java mezclado con HTML, WML, etc.
Problema 3: Procesar peticiones (1)
Las páginas JSP de procesamiento suelen tener el
siguiente esquema
(1.1) Recuperar los valores de los parámetros
Directamente de la request
Usando JavaBeans
(1.2) Validar los parámetros
En caso de tratarse del procesamiento de un formulario, si hay errores, se hace un forward al formulario con los errores
insertados en la request
En caso de que ocurra algún error “inesperado” =>
sendRedirect a una página que indique “error interno”
(1.3) Invocar una operación sobre un Business Delegate del modelo o una fachada del controlador
Si se produce una excepción, mismo tratamiento que en la validación de parámetros
Problema 3: Procesar peticiones (2)
(1.4) Hacer un sendRedirect a otra página o
generar una respuesta
(1.4.1) sendRedirect
Ej.: En el procesamiento de un formulario de login
(1.4.2) Generar una respuesta
Ej.: En el procesamiento de una búsqueda
Variantes
En muchas situaciones reales el paso 1.3 se implementa sin hacer uso de Business Delegates y lanzando queries
directamente contra la BD vía JDBC
¡ Modelo mezclado con la vista !
Quizás sería mejor decir: ¡ modelo inexistente !
Difícil de mantener en aplicaciones de tamaño medio y grande
Problema 3: Procesar peticiones (3)
Análisis
Páginas de procesamiento que son del tipo 1.4.1
No generan aspecto gráfico
Sólo contienen un scriptlet grande
Páginas de procesamiento que son del tipo 1.4.2
Contiene un scriptlet grande al principio
La última parte de la página genera la respuesta HTML, WML, etc.
Suelen hacer uso de scriptlets para iterar sobre los resultados devueltos por la operación del modelo y darles formato HTML, WML, etc.
Ej.: Imprimir los resultados de una búsqueda en una tabla
Problema 3: Procesar peticiones (4)
¿ Cómo podemos eliminar el código Java en las
páginas de tipo 1.4.1 ?
Implementándolas como servlets (controlador) y no como páginas JSP
¿ Cómo podemos eliminar el código Java en las
páginas de tipo 1.4.2 ?
Implementándolas como un servlet (controlador) y una página JSP (vista)
Servlet
Ejecuta el código que antes iba en el scriptlet
En caso de errores => forward a la página JSP que muestra el formulario (con errores insertados en la request) o
sendRedirect a una página de error interno
En otro caso, deja el valor de retorno de la operación del Business Delegate en la request como atributo y hace un forward a la página JSP de visualización de resultados
Problema 3: Procesar peticiones (5)
¿ Cómo podemos eliminar el código Java en las
páginas de tipo 1.4.2 ? (cont)
Página JSP de visualización de resultados
Recupera los resultados de la request con acciones JSP
Hace uso de acciones JSP para iterar sobre los resultados de tipo Collection o similar
Problema 3: Procesar peticiones (6)
El enfoque anterior requiere un servlet por cada
petición que haya que procesar
Si deseamos saber cuál es la siguiente URL a la que se salta tras invocar una petición de procesamiento, hay que
examinar el servlet en cuestión
Flujo de control difícil de seguir
Si se necesita aplicar políticas globales a todos los servlets, es necesario programarlas en cada uno
Ej.: Hacer log de cada petición que se recibe, imprimiendo la URL invocada, los nombres de los parámetros y sus valores
Problema 3: Procesar peticiones (7)
Patrón Front Controller ( Core J2EE Patterns )
Emplea un solo servlet
javax.servlet.http.HttpServlet
Action + process
ActionN
0..n
Action1
...
FrontController
# doGet
# doPost
doGet llama a doPost (o alrevés)
Problema 3: Procesar peticiones (8)
Patrón Front Controller (cont)
Configuramos el servidor de aplicaciones para que las peticiones a URLs que terminen en .do (por ejemplo) se redirijan al servlet FrontController
En WEB-INF/web.xml
<servlet>
<servlet-name>FrontController</servlet-name>
<servlet-class>FrontController</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>FrontController</servlet-name>
<url-pattern>*.do</url-pattern>
</servlet-mapping>
Problema 3: Procesar peticiones (9)
Patrón Front Controller (cont)
Ej.: Portal-2
Acciones: ProcessLoginAction y ProcessLogoutAction
Funcionalmente equivalentes a Portal2/ProcessLogin.jsp y Portal2/ProcessLogout.jsp
Las peticiones a las URLs Portal2/ProcessLogin.do y Portal2/ProcessLogout.do llegan al FrontController
Cuando el FrontController arranca (init)
Lee un fichero de configuración que especifica
Portal2/ProcessLogin.do => Action =
ProcessLoginAction, Success = /Portal2/MainPage.jsp, Input = /Portal2/ShowLogin.jsp
Portal2/ProcessLogout.do => Action = ProcessLogoutAction, Success = /Index.jsp
InternalError = /Portal2/InternalError.jsp
Crea una instancia por cada acción existente
Problema 3: Procesar peticiones (10)
Patrón Front Controller (cont)
Cuando al FrontController le llega una petición, la pasa a la acción correspondiente
El método process de la acción (1) recupera los valores de los parámetros, (2) invoca una operación sobre un Business Delegate o una fachada del controlador, (3) deja los
resultados en la request, y (4) hace un forward o
sendRedirect a Success, Input o InternalError
La clase base Action (u otra en un framework más complejo) le facilita el hacer el forward/sendRedirect
Usa nombre lógicos (Success, Input o InternalError) y no los nombres de las URLs reales
El sendRedirect aplica URL rewriting automáticamente
El fichero de configuración deja claro el flujo de control
Problema 3: Procesar peticiones (y 11)
Si quisiésemos aplicar una política global (ej.: trazar
peticiones en un log para depuración) a todas las
acciones o un subconjunto de ellas
doGet/doPost (FrontController) pueden hacerlo antes de llamar a la acción
También puede hacerlo la clase base Action
O aún mejor, podemos aplicar Chain Of Responsability en doGet/doPost (o en la clase base Action) antes y/o después de ejecutar la acción
Cadena de filtros de pre-procesamiento
Cadena de filtros de post-procesamiento
Core J2EE Patterns llama Intercepting Filter a este patrón
Otros problemas
Hay que tener cuidado cuando se escribe el valor de
un atributo de un objeto Java en el HTML generado
Podría contener caracteres especiales para HTML: <, >, ', " y &
Es preciso convertirlos a referencias internas: <, >,
', " y &
En los ejemplos del tutorial de servlet no se trata este caso
Ej.: Elegir <hola> como nombre de login y observar el valor que imprime MainPage.jsp
¿ Y si queremos internacionalizar la aplicación ?
Ej.: que el usuario pueda elegir el idioma: español, gallego, inglés, etc.
Problema: en los ejemplos que hemos visto los mensajes están dentro de las páginas JSP
Y más problemas, y más problemas, y más
problemas ...
Conclusión (1)
Necesitamos un framework que nos proporcione
Un servlet FrontController y clases relacionadas
Un buen número de acciones JSP a medida (custom tags) que actúen como View Helpers para
Facilitar la construcción de las páginas que muestran formularios (campos de entrada y errores)
Imprimir URLs (con URL rewriting automático)
Acceso a objetos Java en cualquiera de los posibles ámbitos (request, session, page y application)
Imprimir el valor de atributos de objetos Java
Iterar sobre objetos de tipo Collection o similar
Soporte para internacionalización
Etc.
Conclusión (2)
Con este framework una aplicación web tendrá el
siguiente aspecto
Modelo
Conjunto de clases que implementan la lógica de negocio
Independiente de la vista
Controlador
Servlet FrontController y clases acción
Desacopla la vista del modelo
Vista
Páginas JSP sin código Java
Usan muchas acciones JSP a medida (la mayor parte proporcionados por el framework)
Conclusión (y 3)
JSTL (JSP Standard Tag Library)
Librería estándar de tags (acciones) JSP
Apache Struts
Framework MVC
Librería de tags JSP
JSTL es una alternativa estándar para algunos de los tags de Struts, pero no para otros (ej.: soporte para formularios)