Вы находитесь на странице: 1из 19

Model View Controller - Struts

...
Hasta aquí trabajamos con una metodología que llamamos Modelo 1. Si bien este
modelo es muy simple presenta inconvenientes importantes a medida que el
sistema que estamos desarrollando comienza a crecer.

Con esta metodología una página submitea los datos de su formulario a otra para
que los procese. Nosotros la llamamos “Página de Proceso” y utilizamos el sufijo
“PRO” para identificarla facilmente. La página PRO procesa y forwardea a otra
página “de presentación”, la cual a su vez tiene otro formulario que submitea a
otra página PRO y así sucesivamente.

Es evidente que esta forma de trabajo se pude tornar inmanejable para sistemas
medianos y grantes porque todos los enlaces y vínculos de las diferentes pantallas
y sus correspondientes procesos están distribuídos por toda la aplicación.

Model View Controller - MVC

Por lo anterior, para sistemas medianos y grandes lo más recomendable es aplicar


la metodología llamada Modelo 2. Esta metodología no es más que aplicar el
patrón de diseño MVC “Model View Controller”.

Analicemos el siguiente gráfico.


Podemos ver en el gráfico como las páginas JSP submitean sus datos (formularios,
links, etc) a un servlet llamado ActionServlet. Este servlet consulta una tabla de
configuración en la que se define una clase Java para procesar cada JSP, y
diferentes destinos (forwards) según el resultado que se indique en el proceso
Java.

Viendo los valores de la tabla podemos deducir que: A X1.jsp lo procesa


X1Action.java. Si este dice “bien” entonces la próxima página a mostrar será
X2.jsp. Pero si X1Action.java dice “mal” entonces la próxima página a visualizar
será X1Err.jsp.

El patrón de diseño MVC permite separar la capa de presentación (view) de la


capa de negocios (model) y controlar (controller) el paso de datos entre una capa
y la otra.

Un gráfico completo de MVC es el siguiente:

En este gráfico podemos ver una página login.jsp que submitea los datos (usuario
y password) de su formulario al ActionServlet que a su vez los envía a
LoginAction.java. Esta clase invoca el método login del Facade (objeto que
centraliza la lógica de la aplicación) y obtiene como retorno un objeto de
transferencia de datos (DTO - "Data Transfer Object") el cual lo reenvia a la
próxima página JSP (app.jsp).

Podemos diferenciar entonces las tres partes involucradas en MVC: el view (las
páginas JSP), el model (desde el Facade para atrás) y el controller (los Action
que hacen de intermediarios entre el view y el model).
Struts

Struts es un framework que implementa MVC. En esta implementación la tabla de


vínculos es un archivo XML llamado struts-config.xml.

Para desarrollar un proyecto con Struts dentro de MyEclipse debemos crear un


proyecto web y (clickeando con el botón derecho sobre el nombre del proyecto)
agregarle MyEclipse --> “Struts Capabilities”.

En la próxima pantalla veremos lo siguiente:


Seleccionamos la opción "Struts 1.2" y modificamos el "Base package for new
classes" poniendo "test.struts". Con esto, cada vez que el wizard tenga que crear
una nueva clase la creará (por default) en este paquete.

El wizard incluirá varios archivos .jar en el /lib de la aplicación web. Todas las
clases que utilizaremos en este curso están dentro del archivo struts.jar.
También modifirará el descriptor (web.xml) de la aplicación (ver más abajo) y
creará el archivo struts-config.xml dentro del directorio /WEB-INF.

Primer Ejemplo

Se trata de una pantalla de "Login". El usuario ingresa username y password. Si


el password es correcto (en el ejemplo lo harcodeamos a “pepito”) entonces se
mostrará la pantalla de ok. Si no coincide se mostrará la pantalla de error.

struts-config.xml (vista gráfica de MyEclipse)


El gráfico muestra que tenemos una página inicial: login.jsp. Esta página tendrá
un formulario HTML cuyo submit enviará la información a una clase java:
LoginAction.java. Esta clase debe procesar la información que recibe de
login.jsp y determinar si está “bien” o “mal”. Si está bien entonces la próxima
página que se visualizará será okLogin.jsp. Si está mal entonces se visualizará
errLogin.jsp.

Decimos que bien y mal son forwards del action /login. Y que login.jsp es el
input de dicho action.

Nota: como vimos más arriba, la responsabilidad de LoginAction.java no es


procesar los datos. Su responsabilidad es invocar en el facade el método que
corresponda pasándole los datos que recibió de la página JSP. En este ejemplo,
para simplificar, el proceso de verificar si el password es correcto o no lo
haremos en el action, pero esta no es la forma correcta de usar MVC.

El gráfico anterior es una representación del archivo struts-config.xml. MyEclipse


permite verlo en forma gráfica o bien ver directamente el código XML. Para
comenzar (creo yo) la vista gráfica es más entendible pero a medida que uno
comienza a dominar el framework es más simple trabajar directamente con XML.

struts-config.xml
1:
2:<?xml version="1.0" encoding="UTF-8"?>
3:<!DOCTYPE struts-config PUBLIC "-//Apache Software
4:Foundation//DTD Struts Configuration 1.2//EN"
5:"http://struts.apache.org/dtds/struts-config_1_2.dtd">
6:
7:<struts-config>
8: <data-sources />
9: <form-beans />
10: <global-exceptions />
11: <global-forwards />
12:
13: <action-mappings>
14:
15: <action
16: path="/login"
17: type="test.struts.action.LoginAction"
18: input="/login.jsp">
19: <forward name="bien" path="/okLogin.jsp" />
20: <forward name="mal" path="/errLogin.jsp" />
21: </action>
22:
23: </action-mappings>
24:
25: <message-resources
26: parameter="test.struts.ApplicationResources"
27: />
28:</struts-config>
29:

En el archivo struts-config.xml tendremos varias ocurrencias del TAG action.


Justamente cada ocurrencia de este TAG representa una acción que vamos a
procesar.

Notemos que en los forward relacionan un nombre lógico (“bien”, “mal”) con un
recurso físico (okLogin.jsp, errLogin.jsp). También podemos ver que type indica
la clase que procesará la información e input la página desde donde se envía la
información a LoginAction.java.

Veamos el código de cada una de las otras partes involucradas en este gráfico:
login.jsp, LoginAction.java, okLogin.jsp, errLogin.jsp y (obviamente) web.xml
(sin el cual nada de esto sería posible)

login.jsp
1:
2:<html>
3:<body>
4: <form action="login.do">
5: Usuario <input type="text" name="usuario"><br>
6: Clave <input type="password" name="clave"><br>
7: <input type="submit" value="Login">
8: </form>
9:</body>
10:</html>
11:

Notemos que el formulario de login.jsp submitea a "login.do". Veremos luego que


en el archivo web.xml el wizard de struts de MyEclipse agregó un url-pattern que
vincula *.do con el ActionServlet. Esto significa que cualquier request que llegue
a la aplicación pidiendo algún recurso que termine con .do será enviado
automaticamente al ActionServlet.

Por ejemplo: http://localhost:8080/HMAPP/login.do

Este request llegará al ActionServlet. El ActionServlet va a eliminar el .do y con


el /login buscará dentro de los action-mappings definidos en struts-config.xml
para saber que clase tiene que instanciar para procesar el request.

Al submitear en el formulario a login.do estamos enviando la información del


form HTML al ActionServlet y este (leyendo el struts-config.xml) la enviará a
LoginAction.java.

web.xml
1:
2:<?xml version="1.0" encoding="UTF-8"?>
3:<web-app xmlns="http://java.sun.com/xml/ns/j2ee"
4:xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
5:version="2.4"
6:xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee
7:http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
8:
9: <servlet>
10: <!-- define un servlet bajo el nombre action -->
11: <!-- cuya clase es ActionServlet -->
12: <servlet-name>action</servlet-name>
13: <servlet-class>
14: org.apache.struts.action.ActionServlet
15: </servlet-class>
16:
17: <init-param>
18: <param-name>config</param-name>
19: <param-value>
20: /WEB-INF/struts-config.xml
21: </param-value>
22: </init-param>
23: <init-param>
24: <param-name>debug</param-name>
25: <param-value>3</param-value>
26: </init-param>
27: <init-param>
28: <param-name>detail</param-name>
29: <param-value>3</param-value>
30: </init-param>
31: <load-on-startup>0</load-on-startup>
32: </servlet>
33:
34: <!-- Vincula *.do con el servlet action -->
35: <servlet-mapping>
36: <servlet-name>action</servlet-name>
37: <url-pattern>*.do</url-pattern>
38: </servlet-mapping>
39:</web-app>
40:

Veamos la clase LoginAction.java. Esta clase debe heredar de la clase base


Action y sobreescribir el método execute. El ActionServlet va a invocar a este
método para que nuestra clase procese la información.

loginAction.java
1:
2:package test.struts.action;
3:import javax.servlet.http.*;
4:import org.apache.struts.action.*;
5:
6:// esta clase hereda Action (de Struts)
7:public class LoginAction extends Action
8:{
9: // escribir un action basicamente es escribir una
10: // clase que herede de Action y que sobrescriba el
11: // metodo execute(), que al retornar una instancia
12: // de ActionForward le indica al ActionServlet
13: // cual es la proxima pagina que debe mostrar
14: public ActionForward execute(
15: ActionMapping mapping
16: , ActionForm form
17: , HttpServletRequest request
18: , HttpServletResponse response)
19: {
20: String usuario=request.getParameter("usuario");
21: String clave=request.getParameter("clave");
22:
23: // si el password es "pepito" entonces esta ok
24: if( clave.equals("pepito") )
25: {
26: return mapping.findForward("bien");
27: }
28: else
29: {
30: return mapping.findForward("mal");
31: }
32: }
33:}
34:

El método execute (heredado de Action) en LoginAction.java debe retornar un


ActionForward. Este objeto lo obtenemos a partir del objeto mapping (que
recibimos como parámetro). El método findForward de este objeto retorna un
ActionForward direccionado al destino físico especificado en struts-config.xml.

Por último, veamos okLogin.jsp y errLogin.jsp.

okLogin.jsp
1:
2:<html>
3: <body>
4: :O)
5: </body>
6:</html>
7:

errLogin.jsp
1:
2:<html>
3: <body>
4: :O(
5: </body>
6:</html>
7:
Formularios y Custom Tags

Struts permite validar la información que submitea la página JSP. Esta validación
se puede realizar del lado del server o del lado del cliente. En este caso
estudiaremos la validación del lado del server.

Para validar la información que submitea la página podemos agregar un punto


intermedio entre la página JSP y el Action que la procesa: el formulario.

El formulario no es más que una clase java que extienda a la clase ActionForm
(de Struts), con tantos atributos como campos tenga el form HTML de la página,
sus setters y getters, y un método (sobreescrito) para validar la información que
contienen los atributos.

El ActionServlet envia primero al formulario la información submiteada por la


página. Si este da el ok entonces la información será enviada al Action. Pero si el
form no la acepta entonces el ActionServlet forwardeará a la página que originó
la entrada de la información: (identificada por el parámetro input en el struts-
config.xml).

Volviendo al ejemplo, necesitaremos crear una clase LoginForm.java.

Si lo hacemos a través del wizard de MyEclipse, haciendo "botón derecho" sobre


la vista gráfica del struts-config.xml podremos ver la opción "New Form".
Luego veremos la siguiente pantalla.

En esta pantalla ingresamos el "Use case" (login) y los demás campos se


completarán solos. Esto nos va a crear una clase LoginForm.java en el paquete
test.struts.form.

Con el botón Add podemos agregar los atributos que queremos que el tenga el
formulario.

La clase generada es la siguiente:


LoginForm.java
1:
2:package test.struts.form;
3:
4:import javax.servlet.http.HttpServletRequest;
5:import org.apache.struts.action.*;
6:
7:public class LoginForm extends ActionForm
8:{
9:
10: private String usuario;
11: private String clave;
12:
13: public ActionErrors validate(
14: ActionMapping mapping,
15: HttpServletRequest request)
16: {
17: ActionErrors errores=new ActionErrors();
18: if( usuario.trim().length()<=0 )
19: {
20:
21: errores.add("usuario"
22: ,new ActionMessage(
23: "login.usr.noCompleta"));
24: }
25: if( clave.trim().length()<=0 )
26: {
27: errores.add("clave"
28: ,new ActionMessage(
29: "login.pwd.noCompleta"));
30: }
31: return errores;
32: }
33:
34: public void reset(ActionMapping mapping
35: , HttpServletRequest request)
36: {
37: clave="";
38: }
39:
40: public String getUsuario()
41: {
42: return usuario;
43: }
44: public void setUsuario(String usuario)
45: {
46: this.usuario = usuario;
47: }
48: public String getClave()
49: {
50: return clave;
51: }
52: public void setClave(String clave)
53: {
54: this.clave = clave;
55: }
56:}
57:

Vemos que el form (LoginForm.java) extiende a ActionForm (de Struts). Tiene


un atributo para cada uno de los campos del formulario HTML, sus stters y
getters.
La clase también sobreescribe dos métodos: reset y validate. El primero
simplemente permite resetear los valores de los atributos. Debemos notar que
solo reseteamos el atributo clave. Esto se debe a que, si el login no es correcto,
no queremos que el usuario tenga que volver a tipear su username. Esta
característica es muy util cuando tenemos que manejar formularios grandes
(como veremos en el siguiente ejemplo).

En el método validate tenemos que retornar una instancia de ActionErrors (de


Struts) para indicarle al ActionServlet si existen errores o no. ActionErrors es una
especie de Hashtable que contiene instancias de ActionMessage (de Struts)
relacionadas con una key. La key es el nombre del campo en el formulario HTML,
y para inicializar el mensaje de error que queremos mostrar se utiliza el archivo
ApplicationResources.properties que debemos completar como se muestra a
continuación:

ApplicationResources.properties
1:
2:# con "#" podemos escribir comentarios
3:# formato:
4:# nomVar=valor de la variable (sin comillas)
5:
6:login.usr.noCompleta=<br>Ingrese un nombre de usuario
7:login.pwd.noCompleta=<br>Debe ingresar su password
8:

En este archivo definimos los mensajes que queremos mostrar en las páginas JSP.
Los mensajes están asociados a una key. Esta key es la que usamos para
instanciar el ActionMessage que usamos en el método validate de
LoginForm.java.

El form (LoginForm.java) está relacionado con LoginAction.java. Esta relación


se representa en el struts-config.xml que (como creamos el form con el wizard)
ya debe estar modificado.

Veamos las modificaciones:

struts-config.xml (define y relaciona el formulario loginForm)


1:
2:<?xml version="1.0" encoding="UTF-8"?>
3:<!DOCTYPE struts-config PUBLIC "-//Apache Software
4:Foundation//DTD Struts Configuration 1.2//EN"
5:"http://struts.apache.org/dtds/struts-config_1_2.dtd">
6:
7:<struts-config>
8: <data-sources />
9:
10: <!-- en esta seccion se definen los forms -->
11: <form-beans>
12: <form-bean name="loginForm"
13: type="test.struts.form.LoginForm" />
14: </form-beans>
15:
16: <global-exceptions />
17: <global-forwards />
18:
19: <action-mappings>
20:
21: <!-- notemos que ahora tenemos el atributo -->
22: <!-- name que relaciona el form (loginForm) -->
23: <!-- con este action mapping -->
24: <action path="/login"
25: type="test.struts.action.LoginAction"
26: input="/login.jsp"
27: name="loginForm">
28: <forward name="bien" path="/okLogin.jsp" />
29: <forward name="mal" path="/errLogin.jsp" />
30: </action>
31:
32: </action-mappings>
33:
34: <message-resources
35: parameter="test.struts.ApplicationResources" />
36:</struts-config>
37:

Como vemos, el action está relacionado con un formulario. En este caso es


loginForm.

Ahora modificaremos la página JSP para mostrar (cuando corresponda) los


mensajes de error enviados por el formulario. Para esto tenemos que utilizar una
librería de custom tags provista por struts.

login.jsp (usa custom tags de Struts)


1:
2:<%@ taglib uri="/WEB-INF/struts-html.tld"
3: prefix="html"%>
4:
5:<html:html>
6:<html:errors/>
7:<body>
8: <html:form action="/login.do">
9: Usuario <html:text property="usuario" /> <br>
10: Clave <html:password property="clave" /> <br>
11: <html:submit value="Login" />
12: </html:form>
13:</body>
14:</html:html>
15:

El resultado es:
Ahora, para finalizar vamos a mejorar la página login.jsp para que se vea de esta
forma:

La única diferencia es que utilizamos una tabla para alinear mejor los campos del
formulario y separamos los mensajes de error poniendo cada uno debajo del
campo que corresponde. El código es el siguiente:

login.jsp (separa los mensajes según el campo)


1:
2:<%@ taglib uri="/WEB-INF/struts-html.tld"
3: prefix="html"%>
4:
5:<html:html>
6:
7:
8:<body>
9: <html:form action="/login.do">
10: <table>
11: <tr>
12: <td>
13: Usuario
14: </td>
15: <td>
16: <html:text property="usuario" />
17: <font color="red">
18: <html:errors property="usuario" />
19: </font>
20: </td>
21: </tr>
22: <tr>
23: <td>
24: Clave
25: </td>
26: <td>
27: <html:password property="clave" />
28: <font color="red">
29: <html:errors property="clave" />
30: </font>
31: </td>
32: </tr>
33: <tr>
34: <td colspan="2">
35:
36: <html:submit value="Login" />
37:
38: </td>
39: </tr>
40: </table>
41: </html:form>
42:</body>
43:</html:html>
44:

Model View Controller - Struts

...
Resumen

Struts nos permite mantener bien separas las vistas (páginas JSP) del modelo
(Facade y todo lo que está por detrás). Pero también provee herramientas para
facilitar la programación de la funcionalidad de las páginas web. Por ejemplo los
formularios.

Es decir que para que una página JSP contenga un formulario tendremos que
realizar una serie de pasos. En este nuevo ejemplo analizaremos una página para
registrar nuevos usuarios. Se llamará nuevoUsuario.jsp.

Entonces los pasos serán los siguientes:

1 - Crear una entrada dentro del TAG action-mapping en el archivo struts-


config.xml con los siguientes parámetros:

1:
2: <action
3: path="/nuevoUsuario"
4: type="test.struts.action.NuevoUsuarioAction"
5: input="/nuevoUsuario.jsp"
6: name="nuevoUsuarioForm">
7: </action>
8:

2 - Definir una entrada dentro del TAG form-beans también en el archivo struts-
config.xml definiendo el form para nuestra página.
1:
2:<form-beans>
3: <form-bean
4: name="nuevoUsuarioForm"
5: type="test.struts.form.NuevoUsuarioForm"/>
6:</form-beans>
7:

3 - Crear la clase NuevoUsuarioAction.java que debe extender de Action y


sobreescribir el método execute.

4 - Crear la clase NuevoUsuarioForm.java que debe extender de ActionForm y


sobreescribir el método validate. Además debe tener un atributo por cada campo
del formulario HTML.

Veamos la página y luego el código.

nuevoUsuario.jsp
1:
2:<%@ taglib uri="/WEB-INF/struts-html.tld"
3: prefix="html"%>
4:
5:<html:html>
6:<body>
7: <html:form action="/nuevoUsuario.do">
8: <table>
9: <tr><td>Nombre de Usuario</td>
10: <td>
11: <html:text property="usuario" />
12: <font color="red">
13: <html:errors property="usuario" />
14: </font>
15: </td>
16: </tr>
17: <tr><td>Clave</td>
18: <td>
19: <html:password property="clave1" />
20: <font color="red">
21: <html:errors property="clave1" />
22: </font>
23: </td>
24: </tr>
25: <tr><td>Reingrese la Clave</td>
26: <td>
27: <html:password property="clave2" />
28: <font color="red">
29: <html:errors property="clave2" />
30: </font>
31: </td>
32: </tr>
33: <tr><td>Dirección de Email</td>
34: <td>
35: <html:text property="email" />
36: <font color="red">
37: <html:errors property="email" />
38: </font>
39: </td>
40: </tr>
41: <tr>
42: <td colspan="2">
43: <html:submit value="Enviar"/>
44: <html:reset/></td>
45: </td>
46: </table>
47: </html:form>
48:</body>
49:</html:html>
50:

NuevoUsuarioForm.java
1:
2:package test.struts.form;
3:import javax.servlet.http.HttpServletRequest;
4:import org.apache.struts.action.ActionErrors;
5:import org.apache.struts.action.ActionForm;
6:import org.apache.struts.action.ActionMapping;
7:import org.apache.struts.action.ActionMessage;
8:
9:public class NuevoUsuarioForm extends ActionForm
10:{
11: private String usuario;
12: private String clave1;
13: private String clave2;
14: private String email;
15:
16: public ActionErrors validate(
17: ActionMapping arg0
18: , HttpServletRequest arg1)
19: {
20: ActionErrors errores=new ActionErrors();
21:
22: // usuario no puede estar vacio
23: if( usuario.trim().length()<=0 )
24: {
25: errores.add(
26: "usuario"
27: ,new ActionMessage("nuevoUsr.usr.vacio"));
28: }
29:
30: // Por ejemplo, 5 caracteres como minimo
31: if( clave1.trim().length()<5 )
32: {
33: errores.add(
34: "clave1"
35: ,new ActionMessage(
36: "nuevoUsr.clave.corta"));
37: }
38:
39: // la clave 2 debe ser igual a la primera
40: if( !clave2.equals(clave1) )
41: {
42: errores.add("clave2"
43: ,new ActionMessage(
44: "nuevoUsr.clave.noCoincide"));
45: }
46:
47: // el mail debe tener al menos una "@"
48: if( email.indexOf('@')<0 )
49: {
50: errores.add("email"
51: ,new ActionMessage(
52: "nuevoUsr.mail.malFormato"));
53: }
54:
55: return errores;
56: }
57:
58: public String getClave1()
59: {
60: return clave1;
61: }
62:
63: public void setClave1(String clave1)
64: {
65: this.clave1 = clave1;
66: }
67:
68: public String getClave2()
69: {
70: return clave2;
71: }
72:
73: public void setClave2(String clave2)
74: {
75: this.clave2 = clave2;
76: }
77:
78: public String getEmail()
79: {
80: return email;
81: }
82:
83: public void setEmail(String email)
84: {
85: this.email = email;
86: }
87:
88: public String getUsuario()
89: {
90: return usuario;
91: }
92:
93: public void setUsuario(String usuario)
94: {
95: this.usuario = usuario;
96: }
97:}
98:

struts-config.xml
1:
2:<?xml version="1.0" encoding="UTF-8"?>
3:<!DOCTYPE struts-config PUBLIC "-//Apache Software
4:Foundation//DTD Struts Configuration 1.2//EN"
5:"http://struts.apache.org/dtds/struts-config_1_2.dtd">
6:
7:<struts-config>
8: <data-sources />
9: <form-beans>
10: <form-bean
11: name="nuevoUsuarioForm"
12: type="test.struts.form.NuevoUsuarioForm" />
13: </form-beans>
14: <global-exceptions />
15: <global-forwards />
16: <action-mappings>
17: <action
18: path="/nuevoUsuario"
19: type="test.struts.action.NuevoUsuarioAction"
20: input="/nuevoUsuario.jsp"
21: name="nuevoUsuarioForm">
22: </action>
23:
24: </action-mappings>
25: <message-resources
26: parameter="test.struts.ApplicationResources" />
27:</struts-config>
28:

NuevoUsuarioAction.java
1:
2:package test.struts.action;
3:
4:import javax.servlet.http.HttpServletRequest;
5:import javax.servlet.http.HttpServletResponse;
6:
7:import org.apache.struts.action.Action;
8:import org.apache.struts.action.ActionForm;
9:import org.apache.struts.action.ActionForward;
10:import org.apache.struts.action.ActionMapping;
11:
12:public class NuevoUsuarioAction extends Action
13:{
14: public ActionForward execute(
15: ActionMapping mapping
16: , ActionForm form
17: , HttpServletRequest request
18: , HttpServletResponse response)
19: throws Exception
20: {
21: return mapping.findForward("ok");
22: }
23:}
24:

Publicado por PabloSZ

Вам также может понравиться