Plantear una aplicación Web y Struts

1
65171

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:

  1. Proporcionar a los usuarios la capacidad de decir de donde nos
    escriben e introducir algunos datos que nos permitan obtener
    estadísticas.
  2. Posteriormente querríamos mostrar en un mapa visual los datos
    adquiridos (esto lo dejamos para otro tutorial)

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

  • Para favorecer que a los usuarios no le de mucho apuro rellenar la
    información, vamos a solicitar solamente el país  y darles la
    oportunidad de hacer algún comentario del Web.
  • Como no les pedimos datos personales (vinculados a un usuario en
    concreto) no afecta al tratamiento personal de datos.
  • El volumen de datos se plantea que sea pequeño pero debemos
    establecer mecanismos de seguridad para que algún usuario maliciosos no
    nos saturase el Web con la entrada masiva de información (siempre hay
    alguien que nos complica la vida )

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:

  • La introducción de nueva funcionalidad (el tiempo pasa y las
    necesidades evolucionan)
  • Las correcciones de errores conceptuales
  • Las prisas por no haber llevado un ritmo de trabajo estable y
    habernos confiado en un principio
  • La complejidad de ciertas funciones que en principio parecían mucho
    mas sencillas.

 

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)
  • 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?

  1. ¿Que el gestor de visitas valide el país (el componente de
    negocio GestorVisitas)?

  2. ¿Que el objeto Visita sea quien valide que los campos con los
    que se inicializa sean correctos (Visita en el constructor)?

  3. ¿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 Visita

La 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)

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:

  1. Que el usuario por equivocación haga un doble click e introduzca dos veces
    los datos
  2. 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

  1. Un usuario hace una petición
  2. Cada petición está asociada a una acción que contiene la regla de negocio
    particular
  3. 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)
  4. Los datos se pueden validar y si falla la validación se puede volver a
    mostrar el formulario de entrada.
  5. 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.
  6. 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).
  7. 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» >

<html:options collection=»tablapaises» property=»key» labelProperty=»value» />
</html:select>

¿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
correctamente

  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

1 COMENTARIO

  1. 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

DEJA UNA RESPUESTA

Por favor ingrese su comentario!

He leído y acepto la política de privacidad

Por favor ingrese su nombre aquí

Información básica acerca de la protección de datos

  • Responsable:
  • Finalidad:
  • Legitimación:
  • Destinatarios:
  • Derechos:
  • Más información: Puedes ampliar información acerca de la protección de datos en el siguiente enlace:política de privacidad