Aplicación paso a paso con Struts
En numerosas ocasiones me escriben usuarios del Web consultándome como
comenzar a construir aplicaciones Web con Java. Hoy vamos a ver un posible
modo…. y, como siempre, vamos a tratar cambiar la perspectiva típica ….
Os advierto que en este caso no va a ser un tutorial muy básico….
Análisis del Problema
No debemos confundir el medio y el fin. Nuestro objetivo es construir una
aplicación y struts es unos más de los componentes técnicos que vamos a utilizar.
Lo más importante es definir de un modo inequívoco que es lo que queremos
hacer, es decir, tomar los requisitos:
Queremos construir un subsistema para el Web www.adictosaltrabajo.com que nos permita:
|
Los objetivos vemos que nos son excesivamente ambiciosos pero nos puede
valer. Los requisitos son independientes de la tecnología….
Ahora vamos a, antes de tirar una sola línea de código, profundizar en los
problemas para anticipar posibles riesgos ligados a la indeterminación
de los requisitos o la falta de análisis de potenciales riesgos.
Análisis del problema
Debemos hacernos algunas preguntas
- ¿Cuales son los datos que queremos que nos introduzcan (directa e
indirectamente a través de su navegación)? - ¿Que rechazo puede producir introducir esos datos por parte del usuario?
- ¿Como puede afectar esta información al tratamiento de datos que hacemos
en el Web asociados a la LOPD (Ley Orgánica de Protección de Datos)? - ¿Cual va a ser el volumen de datos a manejar?
- ¿Que potenciales riesgos de seguridad puede tener la aplicación para
nuestro sistema?
Nos respondemos
|
También nos debemos preguntar ¿estas son todas las preguntas que nos tenemos
que hacer? ¿estamos planteando bien la toma de requisitos?
Ya os adelanto que no lo estamos
haciendo demasiado bien aunque esto es otra guerra que ya lucharemos algún día
(conceptualización y acotamiento de un problema, valoración rápida de esfuerzo
y coste, identificación de riesgos típicos y particulares, etc. )
Si no somos capaces de hacer una buena definición del problema utilizando buenas técnicas de análisis, los problemas en la fase de desarrollo serán mucho más grandes,
Además se nos juntarán muchos problemas más:
|
Diseño de la solución
Primer problema
Realizamos una descomposición del trabajo a realizar (WBS Work
Breakdown Structure)
- Definir la tabla de países (y llenarla). Estudiar como luego podríamos
situar en un plano los resultados - Definir la tabla de visitantes (con el campo para observaciones)
- Definir el componente que sea capaz de interaccionar con estas tablas
(acceso a datos) - Definir las reglas para validar si los campos que introduce el usuario son
válidos- Numero de inserciones introducidas en un día (para que no nos boqueen el
sistema) - Rango de valores (no nos podemos fiar de las peticiones que recibimos)
- Numero de inserciones introducidas en un día (para que no nos boqueen el
- Diseñar el interfaz de usuario y los posibles mensajes de error.
E incluso nos planteamos una posible solución (aplicando nuestro conocimiento
sobre patrones de asignación de responsabilidad [GRASP])
Si diseñamos, podemos cuestionar el diseño y hacernos preguntas
del estilo.
¿Qué es más conveniente a la hora de validar los parámetros?
¿Que el gestor de visitas valide el país (el componente de
negocio GestorVisitas)?¿Que el objeto Visita sea quien valide que los campos con los
que se inicializa sean correctos (Visita en el constructor)?¿Que el interfaz (struts) valide los parámetros antes de continuar?
Pues no es tan sencillo y dependerá de distintas cosas:
¿Es posible que cambiemos de FrameWork?
¿Es posible que creemos otros interfaces (consola Windows o
proceso nocturno) a la misma
aplicación?¿Cual es el potencial del utilización simultanea de uso de
validaciones? ¿Y de reutilización en distintas aplicaciones de la misma
validación?¿Qué facilidades nos proporciona el FrameWork?
Con estas preguntas podríamos sacar unas conclusiones que
se podrían transportar al resto de componentes de mi aplicación (podemos
prototipar para determinar si las conclusiones son correctas antes de haber
tirado demasiado código en paralelo).
Nosotros (sin entrar en más detalles) vamos a tomar las
siguientes decisiones (que pueden ser más o menos acertadas):
La comprobación del país la realizamos en GestorVisitas
(configurando una consulta sobre la clase Validador)
Porque así no ligamos la capa de negocio a la de presentación
y podríamos reaprovechar la validación si cambiamos la interfaz y decidimos
que no sea Web.No la ponemos en la clase Visita porque en algún otro
subsistema nos puede interesar volver a hacer la comprobación sin estar ligada
la validación con la clase VisitaLa eliminación de palabras conflictivas la hacemos en la capa de
presentación
Porque puede ser general para todos los parámetros de nuestra
aplicación Web (esto es un potencial problema de seguridad) y el FrameWork nos
lo facilita (aunque no vamos a profundizar en la resolución del problema)Porque en otros subsistemas (como en procesos batch nocturnos) podríamos
requerir llamar a esta función de negocio y no es necesario realizar las
comprobaciones de seguridad por cada llamada (el entorno no debería ser
tan hostil)
Otras consideraciones de diseño
La obtención de una conexión a la base de datos (en sistemas
concurrentes) es un potencial riesgo en las aplicaciones Web (cuello de botella) y vamos a tratar de
anticipar el problema. Suerte que ya hicimos otro tutorial donde analizamos como
configurar un pool de conexiones a la base de datos con Struts.
Aún así tenemos que hacernos más preguntas:
- ¿La capa de negocio y presentación van a estar en la misma máquina? ¿Y en
el fututo?- Si es así, es muy posible que queramos utilizar las facilidades del
servidor Web de configuración y administración de pilas de conexiones. - Sino es así, es posible que mi aplicación sea más compleja y/o que tenga
que obtener las conexiones de otros sitios (un servidor de aplicaciones o mi
propio gestor)
- Si es así, es muy posible que queramos utilizar las facilidades del
Bueno, se ponga como se ponga, parece que hace falta que nuestras funciones
de negocio no sean las encargadas de gestionar las conexiones a la base de datos.
Si ellas no las gestionan, significa que les debe llegar algo que les permita
acceder a la conexión a la base de datos cuando sea necesario (esto es una
posible aplicación del patrón de diseño de inversión de responsabilidad). Además este algo
debe ser independiente del tipo de aplicación que estemos construyendo (del tipo
de interfaz de usuario).
De momento hemos pintado unos cuantos elementos que es posible que no
entendamos:
- Nuestro GestorVisitas tendrá siempre una variable llamada
fuenteDatos (inicialmente nula) que nos permitirá acceder a un conexión a
la base de datos. - El GestorVisitas implementa un interfaz marcador (sin
métodos) que nos permitirá determinar en ejecución que esta clase requiere
acceder a la base de datos - La clase Visita implementa el interfaz VO (Value Object) que nos
será útil cuando queramos que clases que representen valores sean tratadas de
un modo homogéneo (por ejemplo caches y paginaciones)
(No estaría mal repasar un poquito los patrones de
diseño generales y los
J2EE)
Conclusiones del diseño
Todavía no hemos tirado una línea y ya tenemos claras bastantes cosas.
¿SORPRENDIDOS? Es que
para esto vale DISEÑAR
Pensar así (aunque el diseño no sea muy avanzado) requiere formación y
entrenamiento. Nno os pongáis nerviosos que todo llega (yo todos los días aprendo
algo nuevo).
Construcción
Jamás debemos anticiparnos a la hora de empezar a escribir código.
Cuanto mejor esté definido lo que tenemos que hacer, menos nos
costará hacerlo y menos frustrados nos encontraremos por retocar
constantemente nuestro desarrollo.
Instalación del entorno
Lo primero que hacemos es buscar una herramienta cómoda para construir. A mi
me gusta NetBeans y aunque ya hay una primera versión 4, vamos a dejarla un poco
que se estabilice antes de usarla (estar siempre a la última es asumir demasiado
riesgo)
Construimos un nuevo proyecto (da igual la versión 3.5 o 3.6)
Lo creamos
Le asignamos nombre
Nos descargamos la última versión de Struts (en este caso si utilizo la
última versión porque me interesa comprobar su estado)
Descomprimimos el fichero y localizamos la aplicación Web ejemplo en blanco
Situamos el fichero en nuestra instalación del TOMCAT
Arrancamos TOMCAT y automáticamente se descomprime el directorio
y está ya lista la aplicación Web para ser utilizada.
Montamos en NetBeans el directorio descomprimido
Y ya tenemos todo lo necesario para empezar a construir nuestra
aplicación
Arrancamos la aplicación para verificar que no hay ningún problema de
compatibilidad
Decisión del punto donde empezar a construir
Ahora tenemos que empezar por algún sitio (esto es normalmente lo más difícil
de decidir).
- Si empezamos por la parte de presentación, es muy posible que construyamos
una aplicación demasiado vinculada al FrameWork y al problema particular (a
corto plazo) por
lo que tendremos poca capacidad de reutilización. - Si empezamos por la capa de acceso a negocio, es muy posible que
despistemos que algunas cosas que dependen de contexto, como por ejemplo como
obtener una conexión a la base de datos.
Si utilizamos siempre el mismo FrameWork (sea el que sea), esta pregunta solo
nos la tenemos que hacer solo una vez y aplicarla en la fase de diseño. Si
estamos constantemente cambiando el modo de hacer las cosas, será difícil
anticipar conclusiones. Si en una organización grande se deja esta decisión a
criterio de cada equipo, sacar conclusiones globales es difícil y más aún
extrapolar soluciones de problemas encontrados en producción.
Os recomiendo que empecéis siempre por la base de datos…. esto es realmente
lo importante. Si en el futuro se pusiera de moda otro lenguaje y tenemos bien
modelada la base de datos, la sustitución será menos traumática.
Diseño de la base de datos
Creamos una nueva base de datos con el panel de control de MySQL. Podéis ver
como
instalar MySQL y su consola en este otro tutorial.
Le asignamos un nombre a la base de datos
Creamos las tablas
Definimos los campos de la primera tabla
Le asignamos el nombre a la tabla
Y comprobamos el resultado
Y repetimos la operación con la tabla visitas (ojo que el país ahora no debe
ser clave única)
Insertamos los primeros valores de prueba con lo que conseguimos descubrir
las sentencias SQL que necesitaríamos en nuestros programas. Podéis ver en otro
de nuestro tutoriales como
generar el código Java para acceder a estas tablas sin saber demasiado.
Introducimos también algún valor en la tabla de países
Construcción de la aplicación
Lo primero que hacemos es construir el formulario de entrada de datos.
En un formulario pueden ocurrir distintos problemas:
- Que el usuario por equivocación haga un doble click e introduzca dos veces
los datos - Que inserte valores incorrectos y necesitemos volver a mostrar los campos
y destacar de algún modo el campo incorrecto.
Utilizaremos un JSP con las etiquetas particulares que proporciona
Struts para resolver los problemas anteriormente mencionados (aunque con el
primero hay que hacer algo más).
Tenemos que entender como funciona un poquito el FrameWork Struts
- Un usuario hace una petición
- Cada petición está asociada a una acción que contiene la regla de negocio
particular - La acción suele necesitar un conjunto de datos que se encapsulan en un
objeto llamado Form (este Form puede ser una clase física o un elemento
general cuyo comportamiento se define en el fichero de configuración, también
llamado DynaForm) - Los datos se pueden validar y si falla la validación se puede volver a
mostrar el formulario de entrada. - Una vez ejecutada la acción, puede que el resultado sea favorable o
desfavorable por lo que se delega sobre distintos elementos de presentación en
cada caso. - Todo mensaje mostrado puede sacarse a un fichero de recursos para
favorecer su mantenimiento y la internacionalización de las aplicaciones
(aunque ahora no nos haga falta). - Cada elemento se construye por separado y se relacionan en el fichero
struts-config.xml
Ya se que no es fácil la primera vez (y menos aún identificar los errores
según aparecen). Vamos a construir únicamente el código que es particular del
interfaz que hemos elegido y marcaremos en el código los puntos en los que hace
falta acceder a las reglas de negocio y los datos (que eso ya es Java puro y
duro)
La estructura
Aunque la aplicación no es plenamente funcional con esta estructura de
ficheros seréis capaz de reproducir el ejemplo. En rojo hemos marcado los únicos
ficheros que hemos cambiado o creado. Debéis tener precaución con los ficheros
en el directorio lib (ver
el tutorial sobre como configura el pool de conexiones a la base de datos con
struts). No os preocupéis de momento en la posición de los ficheros
creados (eso es un problema menor a estas alturas aunque en el futuro si nos
deberá preocupar)
* index.jsp * resultadocorrecto.jsp * visitas.jsp * ****META-INF * context.xml * MANIFEST.MF * ****pages * Welcome.jsp * ****WEB-INF * struts-bean.tld * struts-config.xml * struts-html.tld * struts-logic.tld * struts-nested.tld * struts-tiles.tld * tiles-defs.xml * validation.xml * validator-rules.xml * web.xml * ****classes * * MessageResources.properties * * * ****resources * * MessageResources.properties * * * ****webstruts * InsertaVisitasAction.class * InsertaVisitasAction.java * ****lib * commons-beanutils.jar * commons-collections.jar * commons-dbcp-1.1.jar * commons-digester.jar * commons-fileupload.jar * commons-lang.jar * commons-logging.jar * commons-pool-1.1.jar * commons-validator.jar * jakarta-oro.jar * jdbc2_0-stdext.jar * jstl.jar * mysql-connector-java-3.0.8-stable-bin.jar * standard.jar * struts-legacy.jar * struts.jar * ****src * build.xml * ****java ****resources application.properties |
A mi me gusta tocar los ficheros de configuración a mano, sobre todo al
principio, pero no olvidéis que hay herramientas gráficas (consolas
para struts) para poder hacerlo (incluso se integran fácilmente con nuestro
editor)
El fichero de configuración * struts-config.xml
<?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> <!-- ============================================ Data Source Configuration --> <data-sources> <data-source type="org.apache.commons.dbcp.BasicDataSource"> <set-property property="driverClassName" value="com.mysql.jdbc.Driver" /> <set-property property="url" value="jdbc:mysql://localhost/visitasadictos" /> <set-property property="username" value="root" /> <set-property property="password" value="" /> <set-property property="maxActive" value="10" /> <set-property property="maxWait" value="5000" /> <set-property property="defaultAutoCommit" value="false" /> <set-property property="defaultReadOnly" value="false" /> <set-property property="validationQuery" value="SELECT COUNT(*) FROM paises" /> </data-source> </data-sources> <!-- ================================================ Form Bean Definitions --> <form-beans> <form-bean name="VisitasForm" type="org.apache.struts.action.DynaActionForm"> <form-property name="pais" type="java.lang.Integer"/> <form-property name="observaciones" type="java.lang.String"/> </form-bean> </form-beans> <!-- ========================================= Global Exception Definitions --> <global-exceptions> <!-- sample exception handler <exception key="expired.password" type="app.ExpiredPasswordException" path="/changePassword.jsp"/> end sample --> </global-exceptions> <!-- =========================================== Global Forward Definitions --> <global-forwards> <!-- Default forward to "Welcome" action --> <!-- Demonstrates using index.jsp to forward --> <forward name="welcome" path="/Welcome.do"/> <forward name="correcto" path="/resultadocorrecto.jsp"/> </global-forwards> <!-- =========================================== Action Mapping Definitions --> <action-mappings> <!-- Default "Welcome" action --> <!-- Forwards to Welcome.jsp --> <action path="/Welcome" forward="/pages/Welcome.jsp"/> <action path="/InsertaVisistas" type="webstruts.InsertaVisitasAction" name="VisitasForm" scope="request" validate="true" input="/visitas.jsp"/> </action-mappings> <!-- ============================================= Controller Configuration --> <controller processorClass="org.apache.struts.tiles.TilesRequestProcessor"/> <!-- ======================================== Message Resources Definitions --> <message-resources parameter="MessageResources" /> <!-- =============================================== Plug Ins Configuration --> <plug-in className="org.apache.struts.tiles.TilesPlugin" > <!-- Path to XML definition file --> <set-property property="definitions-config" value="/WEB-INF/tiles-defs.xml" /> <!-- Set Module-awareness to true --> <set-property property="moduleAware" value="true" /> </plug-in> <plug-in className="webstruts.InsertaVisitasAction" > </plug-in> <!-- =================================================== Validator plugin --> <plug-in className="org.apache.struts.validator.ValidatorPlugIn"> <set-property property="pathnames" value="/WEB-INF/validator-rules.xml,/WEB-INF/validation.xml"/> </plug-in> </struts-config> |
La acción * InsertaVisitasAction.java
/* * InsertaVisitasAction.java * * Created on October 31, 2004, 12:27 PM * * Roberto Canales Mora */ package webstruts; import org.apache.struts.action.*; import javax.servlet.http.*; import java.util.*; public class InsertaVisitasAction extends Action implements org.apache.struts.action.PlugIn { public ActionForward execute(ActionMapping actionMapping,ActionForm actionForm, HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception { ActionForward retValue = null; try // ejecutamos la funcion de negocio { // Obtenemos acceso al formulario DynaActionForm formulario = (DynaActionForm) actionForm; // aqui va la inserción de los datos System.out.println("El pais es: " + formulario.get("pais")); System.out.println("Observaciones: " + formulario.get("observaciones")); // redirigimos a la presentación retValue = actionMapping.findForward("correcto"); } catch(Exception e) // en caso de problemas retornar a la página origen { // recuperamos acceso al JSP deorigen retValue = actionMapping.getInputForward(); // damos de alta los errores ActionErrors errores = new ActionErrors(); errores.add(errores.GLOBAL_ERROR, new ActionError("error.logica")); errores.add("pais", new ActionError("error.enpais")); this.addErrors(httpServletRequest, errores); } return retValue; // redirigimos a la presentacion } /* * Los métodos destroy e init son obligatorios al implementar el interfaz PlugIn */ public void destroy() { } public void init(org.apache.struts.action.ActionServlet actionServlet, org.apache.struts.config.ModuleConfig moduleConfig) throws javax.servlet.ServletException { Hashtable vPaises = new Hashtable(); // aqui hay que poner el código que nos proporcione los datos de la base de datos vPaises.put("1","España"); // este codigo hay que quitarlo vPaises.put("2","Chile"); // mostramos un mensaje para asegurarnos que los datos están en memoria System.out.println("Tenemos el Vector"); // ponemos la tabla en el ámbito de la aplicación actionServlet.getServletContext().setAttribute("tablapaises",vPaises); } } |
Esta acción tiene una peculiaridad y es que implementa en interfaz
plugIn (métodos destroy e init) que nos permite que se ejecute código nada mas
inicializarse el componente. La tabla de países no creo que cambie demasiado a
menudo por lo que nos puede interesar recuperarla solo una vez para todos los
usuarios del sistema.
El JSP de entrada de datos * visitas.jsp
<%@ taglib uri="/tags/struts-bean" prefix="bean" %> <%@ taglib uri="/tags/struts-html" prefix="html" %> <html> <head> <title><bean:message key="index.titulo"/></title> </head> <body> <center> <h2><bean:message key="index.cuentanostuorigen"/></h2> <hr width='60%'> <html:form action='/InsertaVisistas'> <table border="0"> <tr> <td>Pais</td><td> <html:select property="pais" > <html:options collection="tablapaises" property="key" labelProperty="value" /> </html:select> </td><td><html:errors property="pais" /> </td> </tr> <tr> <td><!-- podría ser una clave en el fichero de recursos --> Observaciones</td><td> <html:textarea property="observaciones" cols="50" rows="3" /> </td><td><html:errors property="observaciones" /></td> </tr> <tr><td> <html:submit value="Enviar"/></td> </tr> </table> </html:form> <br/> <font color="#FF0000"> <html:errors/> </font> </center> </body> </html> |
Analizamos este código con detenimiento porque tiene muchas más cosas de las
que puede parecer en un principio.
¿De donde salen los valores del formulario? De los datos que ha puesto el plugIn en la aplicación <html:select property=»pais» > |
¿De donde salen los textos de la página que no están directamente escritos? Del fichero de recursos <bean:message key=»index.titulo»/>
|
¿Para que vale esta línea? Para mostrar errores cuando se produzcan problemas en validaciones <html:errors property=»pais» /> Debemos buscar en la acción este código para entenderlo errores.add(«pais», new ActionError(«error.enpais»));
|
JSP resultante * resultadocorrecto.jsp
<%@page contentType="text/html"%> <html> <head><title>JSP Page</title></head> <body> <h2>La acción se ejecutó satisfactoriamente</h2> </body> </html> |
Añadir el código de acceso a datos
Ahora que ya tenemos el código particular de Struts y nos queda el código
de acceso a la base de datos. Esto es Java puro y duro.
Creamos el resto de las clases (aunque el código
no es definitivo ya que el tratamiento de errores de momento es pésimo)
Visita.java
package webstruts; /* * Visita.java * Created on October 31, 2004, 6:17 PM * @author Roberto Canales */ public class Visita { private int id; private int pais; private String observaciones; private String ip; /** Creates a new instance of Visita */ public Visita() {} /** Creates a new instance of Visita */ public Visita(int pais, String observaciones,String ip) { this.pais = pais; this.observaciones = observaciones; this.ip = ip; } /** Getter for property id. * @return Value of property id. */ public int getId() {return id;} /** Setter for property id. * @param id New value of property id. */ public void setId(int id) {this.id = id;} /** Getter for property observaciones. * @return Value of property observaciones. */ public java.lang.String getObservaciones() {return observaciones;} /** Setter for property observaciones. * @param observaciones New value of property observaciones. */ public void setObservaciones(java.lang.String observaciones) { this.observaciones = observaciones;} /** Getter for property pais. * @return Value of property pais. */ public int getPais() {return pais;} /** Setter for property pais. * @param pais New value of property pais. */ public void setPais(int pais) {this.pais = pais;} /** Getter for property ip. * @return Value of property ip. */ public java.lang.String getIp() {return ip;} /** Setter for property ip. * @param ip New value of property ip. */ public void setIp(java.lang.String ip) {this.ip = ip;} } |
GestorVisitas.java
/* * GestorVisitas.java * * Created on October 31, 2004, 6:21 PM */ package webstruts; import javax.sql.*; import java.sql.*; /** * * @author Roberto Canales Mora */ public class GestorVisitas extends ClaseNegocio { /** Creates a new instance of GestorVisitas */ public GestorVisitas() { } void depura(String mensaje) { System.out.println("GestorVisitas - " + mensaje); } public boolean insertaVisita(Visita pVisita) { try { Connection con = this.dameConexion(); PreparedStatement pstmt = con.prepareStatement("insert into visitas (pais,observaciones,ip) values (?,?,?)"); // establecemos los valores variables pstmt.setInt(1,pVisita.getPais()); // establecemos el entero pstmt.setString(2,pVisita.getObservaciones()); // establecemos el entero pstmt.setString(3,pVisita.getIp()); // establecemos el entero // ejecutamos la consulta int resultado = pstmt.executeUpdate(); depura("El número de elementos afectados es " + resultado); // retornamos la conexion al pool con.close(); return true; } catch (SQLException e) { depura("Error al insertar " + e.getMessage()); return false; } } } |
ClaseNegocio.java
/* * ClaseNegocio.java * Created on October 31, 2004, 6:20 PM */ package webstruts; import javax.sql.*; import java.sql.*; /** * * @author Roberto Canales Mora */ public class ClaseNegocio { private javax.sql.DataSource fuenteDatos = null; /** Creates a new instance of ClaseNegocio */ public ClaseNegocio() { } public boolean inicializaFuenteDatos(javax.sql.DataSource pFuenteDatos) { fuenteDatos = pFuenteDatos; return true; } public java.sql.Connection dameConexion() { try { return fuenteDatos.getConnection(); } catch(Exception e) { } return null; } } |
InsertaVisistasAction.java (todavía hay que recuperar los países de la base
de datos pero no queremos mezcla demasiadas cosas)
/* * InsertaVisitasAction.java * * Created on October 31, 2004, 12:27 PM * * Roberto Canales Mora */ package webstruts; import org.apache.struts.action.*; import javax.servlet.http.*; import java.util.*; public class InsertaVisitasAction extends Action implements org.apache.struts.action.PlugIn { public ActionForward execute(ActionMapping actionMapping, ActionForm actionForm,HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception { ActionForward retValue = null; try // ejecutamos la funcion de negocio { // Obtenemos acceso al formulario DynaActionForm formulario = (DynaActionForm) actionForm; // Construimos el objeto de negocio GestorVisitas gestorVisitas = new GestorVisitas(); // le damos acceso a la fuente de datos gestorVisitas.inicializaFuenteDatos(this.getDataSource(httpServletRequest)); // recuperamos el pais y observaciones Integer pais = (Integer)formulario.get("pais"); String observaciones = formulario.get("observaciones").toString(); // llamamos al metodo de negocio boolean resultado = gestorVisitas.insertaVisita(new Visita(pais.intValue(), observaciones, httpServletRequest.getRemoteAddr())); // redirigimos a la presentación if(resultado == true) retValue = actionMapping.findForward("correcto"); else retValue = actionMapping.findForward("incorrecto"); } catch(Exception e) // en caso de problemas retornar a la página origen { // recuperamos acceso al JSP deorigen retValue = actionMapping.getInputForward(); // damos de alta los errores ActionErrors errores = new ActionErrors(); errores.add(errores.GLOBAL_ERROR, new ActionError("error.logica")); errores.add("pais", new ActionError("error.enpais")); this.addErrors(httpServletRequest, errores); } return retValue; // redirigimos a la presentacion } /* * Los métodos destroy e init son obligatorios al implementar el interfaz PlugIn */ public void destroy() { } public void init(org.apache.struts.action.ActionServlet actionServlet, org.apache.struts.config.ModuleConfig moduleConfig) throws javax.servlet.ServletException { Hashtable vPaises = new Hashtable(); // aqui hay que poner el código que nos proporcione los datos de la base de datos vPaises.put("1","España"); // este codigo hay que quitarlo vPaises.put("2","Chile"); // mostramos un mensaje para asegurarnos que los datos están en memoria System.out.println("Tenemos el Vector"); // ponemos la tabla en el ámbito de la aplicación actionServlet.getServletContext().setAttribute("tablapaises",vPaises); } } |
Obtención de los datos
Para que el ejemplo sea completamente utilizable necesitamos las tablas.
Vamos a recuperar la estructura de la tabla valiéndonos de phpMyAdmin (recordamos
otro de nuestro tutoriales)
Elegimos las tablas y el modo
de extracción.
Y nos genera algo tal que esto
# # Table structure for table `paises` # DROP TABLE IF EXISTS `paises`; CREATE TABLE `paises` ( `id` int(4) NOT NULL auto_increment,`pais` varchar(50) NOT NULL default '', `x` int(4) default '0',`y` int(4) default '0', PRIMARY KEY (`id`),UNIQUE KEY `paisesindex` (`pais`) ) TYPE=MyISAM AUTO_INCREMENT=5 ; # # Dumping data for table `paises` # INSERT INTO `paises` VALUES (1, 'España', 0, 0); INSERT INTO `paises` VALUES (2, 'Chile', 0, 0); INSERT INTO `paises` VALUES (3, 'Guatemala', 0, 0); INSERT INTO `paises` VALUES (4, 'Francia', 10, 10); # -------------------------------------------------------- # # Table structure for table `visitas` # DROP TABLE IF EXISTS `visitas`; CREATE TABLE `visitas` ( `id` int(11) NOT NULL auto_increment, `pais` int(4) unsigned zerofill NOT NULL default '0000', `observaciones` varchar(100) NOT NULL default '12', `ip` varchar(255) NOT NULL default '', PRIMARY KEY (`id`), KEY `indexpais` (`pais`) ) TYPE=MyISAM AUTO_INCREMENT=5 ; # # Dumping data for table `visitas` # INSERT INTO `visitas` VALUES (1, 0002, 'comentario1', '127.0.0.1'); INSERT INTO `visitas` VALUES (2, 0001, 'comentario2', '127.0.0.1'); INSERT INTO `visitas` VALUES (3, 0001, 'comentario3', '127.0.0.1'); INSERT INTO `visitas` VALUES (4, 0002, 'comentario4', '127.0.0.1'); |
Con esto ya tenemos todo lo necesario para orientar el programa.
Conclusiones
Si tienes las ideas medianamente claras y eres capaz de descomponer un
problema grande en problemas pequeñitos, no es tan difícil construir una
aplicación.
Parece que hemos invertido demasiado esfuerzo para algo aparentemente trivial
y que podríamos haber resuelto prácticamente con un par de JSPs o Servlets pero
no nos engañemos: Todas las aplicaciones tiende a complicarse y si ha hemos
constuido así será fácil ampliarla por cualquier sitio.
Aunque parezca contradictorio con el párrafo anterior, francamente creo que
tendemos a tecnificar demasiado los problemas y nos dejamos llevar por las
modas; No en todos los casos es necesario solucionar los problemas de un modo
tan flexible (esto implica un coste inicial muy alto que puede hacer inviable un
plan de negocio).
Struts es un buen posible punto de partida pero tampoco nos tenemos que
equivocar:
- Es solo un modo más de plantear el Front-End (interfaz con el cliente) de
una aplicación y hay 200 modos más. - Requiere un entrenamiento y aprendizaje que se justificará si lo
utilizamos habitualmente o queremos construir sistemas altamente robustos. - Muchas empresas crean su propios FrameWorks y puedes llevarte sorpresas si
te acostumbras solo a utilizar Struts - Algunas empresas tienen desaconsejado el uso de Struts (normalmente porque
han tenido malas experiencias con consultoras que han construido soluciones
muy acopladas por entender mal el producto) - El mantenimiento es enrevesado y puede despistar a equipos poco
cualificados - Es necesario ampliarlo para resolver problemas en entornos profesionales
No tengo palabras para agradecerle el haber creado este \\\»paso a paso\\\» de la creación de aplicaciones mediantes struts. Después de haberlo leido detenidamente, he comprendido casi en su totalidad el funcionamiento de dicho framework.
Muchas gracias