Siente el pensamiento; piensa el sentimiento
M. Unamuno

Archivos en la categoría Struts

Integrar Struts con Spring - holamundoStruts138Spring

Integrar Struts con Spring - holamundoStruts138SpringVamos al turrón, vamos a implementar lo que hemos visto a nivel teórico.

Creamos un nuevo proyecto web dinámico, holamundoStruts138Spring. Copiamos todo lo que tenemos en holamundoStruts138 sobre este nuevo proyecto para no partir de cero.

Básicamente sobreescribermos el controlador de Struts y utilizaremos la configuración de Spring para inyectar las dependencias a nuestros objetos.

LyckaRequestProcessor

package es.lycka.holamundoStruts138.action;

import java.io.IOException;

import javax.servlet.ServletException;

import javax.servlet.http.HttpServletRequest;

import javax.servlet.http.HttpServletResponse;

import org.apache.struts.action.Action;

import org.apache.struts.action.ActionMapping;

import org.apache.struts.action.ActionServlet;

import org.apache.struts.action.RequestProcessor;

import org.apache.struts.config.ModuleConfig;

import org.apache.struts.util.RequestUtils;

import org.springframework.web.context.WebApplicationContext;

import org.springframework.web.struts.DelegatingActionUtils;

public class LyckaRequestProcessor extends RequestProcessor{

      private WebApplicationContext webApplicationContext;

      public void init(ActionServlet actionServlet, ModuleConfig moduleConfig) throws ServletException {

            super.init(actionServlet, moduleConfig);

            if (actionServlet != null) {

                  this.webApplicationContext = initWebApplicationContext(actionServlet, moduleConfig);

            }

      }

      protected WebApplicationContext initWebApplicationContext(

                  ActionServlet actionServlet, ModuleConfig moduleConfig) throws IllegalStateException {

            return DelegatingActionUtils.findRequiredWebApplicationContext(actionServlet, moduleConfig);

      }

      protected final WebApplicationContext getWebApplicationContext() {

            return webApplicationContext;

      }

      /**

       * Método que sobreescribimos para utilizar beans de Spring.

       */

      protected Action processActionCreate(HttpServletRequest request,

                  HttpServletResponse response, ActionMapping mapping)

                  throws IOException {

            // Acquire the Action instance we will be using (if there is one)

            String className = mapping.getType();

            if (log.isDebugEnabled()) {

                  log.debug(" Looking for Action instance for class " + className);

            }

            // If there were a mapping property indicating whether

            // an Action were a singleton or not ([true]),

            // could we just instantiate and return a new instance here?

            Action instance;

            //Acceso sincronizado al HasMaps de actions

            synchronized (actions) {

                  // Return any existing Action instance of this class

                  instance = (Action) actions.get(className);

                  if (instance != null) {

                        if (log.isTraceEnabled()) {

                             log.trace("  Returning existing Action instance");

                        }

                        return (instance);

                  }

                  // Create and return a new Action instance

                  if (log.isTraceEnabled()) {

                        log.trace("  Creating new Action instance");

                  }

                  try {

                        //String beanName = determineActionBeanName(mapping);

                        String beanName = mapping.getType();

                        if (getWebApplicationContext().containsBean(beanName))

                             instance = (Action)getWebApplicationContext().getBean(beanName, Action.class);                           

                        else

                             instance = (Action) RequestUtils.applicationInstance(className);

                        // Maybe we should propagate this exception

                        // instead of returning null.

                  } catch (Exception e) {

                        log.error(getInternal().getMessage("actionCreate",

                                   mapping.getPath()), e);

                        response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR,

                                   getInternal().getMessage("actionCreate", mapping.getPath()));

                        return (null);

                  }

                  actions.put(className, instance);

            }

            if (instance.getServlet() == null) {

                  instance.setServlet(this.servlet);

            }

            return (instance);

      }

}

Para que funcione correctamente además de las librerías de Struts que ya tenemos importadas, debemos utilizar spring.jar y spring-struts.jar.

Bases, Interfaces e Implementaciones

Ya aprovechamos que vamos a utilizar Spring para desdoblar nuestras clases en una clase Base por cada capa, y para cada clase utilizaremos un interfaz y una implementación.

Para no poner todas las implementaciones, que ya bastante larga va a quedar la entrada, sólo transcribo la implementación de IdentificarBOImpl. Sólo sería necesario el set del bean identificarDAO, pero por comodidad dejo ambas. Las demás implementaciones serían similares.

package es.lycka.holamundoStruts138Spring.negocio.impl;

import es.lycka.holamundoStruts138Spring.model.Usuario;

import es.lycka.holamundoStruts138Spring.negocio.IdentificarBO;

import es.lycka.holamundoStruts138Spring.persistencia.IdentificarDAO;

public class IdentificarBOImpl extends BaseBOImpl implements IdentificarBO  {

      private IdentificarDAO identificarDAO;

      public boolean esUsuarioAutorizado (Usuario usuario) throws Exception {

            boolean esAutorizado               = false;

            try {

                  if (identificarDAO.consultarUsuario (usuario) != null ) {

                        esAutorizado = true;

                  }

            } catch (Exception e) {

                  throw e;

            }

            return esAutorizado;

      }

      public IdentificarDAO getIdentificarDAO() {

            return identificarDAO;

      }

      public void setIdentificarDAO(IdentificarDAO identificarDAO) {

            this.identificarDAO = identificarDAO;

      }

}

web.xml

Vale, ahora debemos modificar el web.xml para utilizar también Spring.

<context-param>

      <param-name>contextConfigLocation</param-name>

      <param-value>

            /WEB-INF/spring/applicationContext-*.xml

      </param-value>

</context-param>

<listener>

      <listener-class>

            org.springframework.web.context.ContextLoaderListener

      </listener-class>

</listener> 

Así, podemos crear varios archivos XML para configurar nuestra aplicación:

  • applicationContext-struts.xml, que contendrá los beans que representan a nuestras Acciones de Struts.
  • applicationContext-negocio.xml, que contendrá los beans que representan a nuestros objetos de la capa de negocio.
  • applicationContext-persistencia.xml, que contendrá los beans que representan a nuestros objetos de la capa de persistencia. Meteremos iBATIS en otro ejemplo.

applicationContext-struts.xml

<?xml version="1.0" encoding="ISO-8859-1"?>

<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"

      "http://www.springframework.org/dtd/spring-beans.dtd">

<beans>

      <bean name="parentAction" class="es.lycka.holamundoStruts138Spring.control.action.AppBaseAction" abstract="true" />

      <bean name="es.lycka.holamundoStruts138Spring.control.action.IdentificarAction" class="es.lycka.holamundoStruts138Spring.control.action.IdentificarAction" singleton="false" parent="parentAction">

            <property name="identificarBO"><ref bean="identificarBO" /></property>

      </bean>

</beans>

applicationContext-negocio.xml

<?xml version="1.0" encoding="ISO-8859-1"?>

<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"

      "http://www.springframework.org/dtd/spring-beans.dtd">

<beans>

      <bean name="baseBO" class="es.lycka.holamundoStruts138Spring.negocio.impl.BaseBOImpl" abstract="true"/>

      <bean id="identificarBO" class="es.lycka.holamundoStruts138Spring.negocio.impl.IdentificarBOImpl"  parent="baseBO">

            <property name="identificarDAO" ref="identificarDAO" />

      </bean>

</beans>

applicationContext-persistencia.xml

<?xml version="1.0" encoding="ISO-8859-1"?>

<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"

      "http://www.springframework.org/dtd/spring-beans.dtd">

<beans>

      <bean id="dao" class="es.lycka.holamundoStruts138Spring.persistencia.impl.BaseDAOImpl" abstract="true" />

      <bean id="identificarDAO" class="es.lycka.holamundoStruts138Spring.persistencia.impl.IdentificarDAOImpl" parent="dao"/>

</beans>

struts-config.xml

Debemos declarar como controlador nuestra nueva clase es.lycka.holamundoStruts138Spring.action.LyckaRequestProcessor

<controller>

      <set-property property="processorClass"

      value="es.lycka.holamundoStruts138Spring.action.LyckaRequestProcessor" />

</controller>

Probamos y … bene !

Integrar Struts con Spring - Teoría

Bueno, ya tenemos un proyecto con Spring - MVC (holamundo) y otro con Struts (holamundoStruts138). Cuál utilizaremos para desarrollar ? Eso lo dejo para otra discusión.

Aquí quiero señalar que existe otra solución, y es integrar Struts con Spring, es decir, utilizar Struts aprovechando sus puntos fuertes como MVC (madurez, documentación, podemos extender el framework con Struts-Validator y Tiles…) y Spring para sus puntos fuertes (como AOP, IoC, DI…).

El framework de Struts realiza una serie de operaciones transparentes para el desarrollador para facilitarle la vida. Entre ellas mantiene un HashMap que contiene referencias a objetos Action ya instanciados. Ante cada petición mira primero este HashMap para ver si contiene la clase Action solicitada. Si es así recupera el objeto a partir de la referencia almacenada en el HashMap; y si no instancia e inicializa la clase Action y guarda su referencia en el HashMap. Al manejar una única instancia de cada Action se ahorra bastante tiempo y el rendimiento de la aplicación mejora notablemente.

Este mecanismo podría ser más eficiente, ya que cada Action invoca a uno o varios objetos de negocio, y con Struts se deben instanciar e inicializar en cada llamada. La integración con Spring perfecciona este mecanismo aprovechando IoC y DI, ya que estos objetos de negocio (utilizados como singletons) ya están instanciados e inicializados en el contendor de Spring.

Si aplicamos IoC y DI también a los Action de Struts conseguimos que las instancias de los Action manejen ya tengan instanciados e inicializados los objetos de negocio, mejorando considerablemente el rendimiento de nuestra aplicación: al levantar la aplicación será un poco lenta pero después irá como un tiro.

Pues manos a la obra. Debemos declarar un bean en Spring que represente nuestro Action para que sea Spring el que le instancie y le inyecte sus dependencias (los objetos de negocio).

Donde gestiona Struts la creación de sus clases Action ? En el método processActionCreate de su controlador, RequestProcessor. Vale, pues sobre escribamos esta clase con nuestro propio controlador, AppRequestProcessor, para que pida la instancia del bean a Spring (la rama true del if) en lugar de crearla Struts (la rama del else). Lo que está en negrita es la clave, sólo un if.

Si queremos utilizar Tiles debemos extender de TilesRequestProcessor.

public class AppRequestProcessor extends RequestProcessor {

. . .

    protected Action processActionCreate(HttpServletRequest request,

            HttpServletResponse response, ActionMapping mapping)

            throws IOException {

            // Acquire the Action instance we will be using (if there is one)

            String className = mapping.getType();

            if (log.isDebugEnabled()) {

                log.debug(" Looking for Action instance for class " + className);

            }

            // If there were a mapping property indicating whether

            // an Action were a singleton or not ([true]),

            // could we just instantiate and return a new instance here?

            Action instance;

            //Acceso sincronizado al HasMaps de actions

            synchronized (actions) {

                // Return any existing Action instance of this class

                instance = (Action) actions.get(className);

                if (instance != null) {

                    if (log.isTraceEnabled()) {

                        log.trace("  Returning existing Action instance");

                    }

                    return (instance);

                }

                // Create and return a new Action instance

                if (log.isTraceEnabled()) {

                    log.trace("  Creating new Action instance");

                }

                try {

                  //String beanName = determineActionBeanName(mapping);

                  String beanName = mapping.getType();

                  if (getWebApplicationContext().containsBean(beanName))

                        instance = (Action)getWebApplicationContext().getBean(beanName, Action.class);   

                  else

                        instance = (Action) RequestUtils.applicationInstance(className);

                    // Maybe we should propagate this exception

                    // instead of returning null.

                } catch (Exception e) {

                    log.error(getInternal().getMessage("actionCreate",

                            mapping.getPath()), e);

                    response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR,

                        getInternal().getMessage("actionCreate", mapping.getPath()));

                    return (null);

                }

                actions.put(className, instance);

            }

            if (instance.getServlet() == null) {

                instance.setServlet(this.servlet);

            }

            return (instance);

        }

. . .

}

Es decir, buscamos si el nombre del bean (beanName) existe en el contexto de Spring, y si es así lo obtenemos de ahí. El nombre lo podemos obtener de la ruta (path) mediante determineActionBeanName(mapping), el método comentado. O bien lo podemos obetener del "type", es decir la clase.

En el contexto de Spring debemos declarar nuestros actions. En el primer caso el nombre del bean será la ruta introducida en el navegador. Ojo, que todos los beans del mismo action deben declarar los mismos BO's porque la instancia que se almacenará será la que se instancia primero, es decir por la primera ruta a la que se acceda:

<bean name="parentAction" class="es.xxx.xxx.control.action.base.AppBaseAction" abstract="true" />

<bean name="/editarDatos" class="es.xxx.xxx.control.action.DatosAction" singleton="false" parent="parentAction">

      <property name="datosBO"><ref bean="datosBO" /></property>

</bean>

<bean name="/guardarDatos" class="es.xxx.xxx.control.action.DatosAction" singleton="false" parent="parentAction">

      <property name="datosBO"><ref bean="datosBO" /></property>

</bean>

<bean name="/validarDatos" class="es.xxx.xxx.control.action.DatosAction" singleton="false" parent="parentAction">

      <property name="datosBO"><ref bean="datosBO" /></property>

</bean>

En el segundo caso el nombre del bean debe ser el "tipo" del action :

<bean name="parentAction" class="es.xxx.xxx.control.action.base.AppBaseAction" abstract="true" />

<bean name="es.xxx.xxx.control.action.DatosAction" class="es.xxx.xxx.control.action.DatosAction" singleton="false" parent="parentAction"> 

      <property name="datosBO"><ref bean="datosBO" /></property>

</bean>

En ambos casos se apoyarían sobre un bean de negocio

<bean name="baseBO" class="es.xxx.xxx.negocio.base.BaseBOImpl" abstract="true"/>

<bean id="datosBO" class="es.xxx.xxx.negocio.impl.DatosBOImpl"  parent="baseBO">

      <property name="datosDAO" ref="datosDAO" />

</bean>

que se apoya sobre otro de persistencia

<!– SqlMap setup for iBATIS Database Layer –>

<bean id="sqlMapClient" class="org.springframework.orm.ibatis.SqlMapClientFactoryBean">

      <property name="configLocation">

<value>classpath:/es/xxx/xxx/persistencia/impl/xml/sql-map-config.xml</value>

      </property>

      <property name="dataSource" ref="dataSource"/>

 </bean>

<!– Generic Dao - can be used when doing standard CRUD –>

<bean id="dao" class="es.xxx.xxx.persistencia.base.BaseDaoImpl" abstract="true">

      <property name="sqlMapClient" ref="sqlMapClient"/>

</bean>

<bean id="datosDAO" class="es.xxx.xxx.persistencia.impl.DatosProyectoDAOImpl" parent="dao"/>

Qué fácil, una vez que se sabe ! :)

Nuestra primera acción con Struts 1.3.8

Bueno, ya sabemos preparar un proyecto web dinámico para utilizarlo con Struts, sabemos descomprimir el struts-blank.war que viene con la distrigución jejeje y utilizarlo como base, nos hemos empollado bien la teoría y sabemos lo que es el patrón MVC, sabemos cómo funciona el Framework de Struts, cómo implementa el patrón MVC y cómo se diseña una aplicación en Struts.

Nuestra primera acción

Primera Acción en Struts 1.3.8Partimos de nuestro anterior proyecto, holamundoStruts138, que dejamos a punto para comenzar a ser implementado con Struts.

Crearemos una acción que recibirá unos datos de entrada introducidos por el cliente (dos cadenas de caracteres, identificador y contraseña), los procesará (comprobará si está autorizado) y decidirá la siguiente acción. Fácil ? Pensemos en cómo implementarlo.

Necesitamos dos jsp's : una jsp de entrada donde introducir los datos (identificar.jsp) y otra para a la que se accederá si se tiene éxito al identificarse (indexIdentificado.jsp).

Necesitamos una acción que recibirá los datos, procesará los datos (los enviará al negocio), y en función del resultado decidirá si dirigir al cliente a la zona que necesita identificación (indexIdentificado.jsp) (si son correctos los datos), o dirigir a la pantalla de identificación (identificar.jsp) (si no son correctos los datos) mostrando un mensaje. Si durante el proceso se detecta un error (una excepción, por ejemplo si la base de datos está caída) también se dirigirá a la pantalla de identificación con un mensaje de error interno.

Sabemos que la acción necesitará un formulario para comunicarse con las jsp's.

Necesitamos una clase de negocio, que en este ejemplo tan simple será muy sencilla. Y necesitamos una clase de persistencia que sería la que accedería a la base de datos para obtener los datos. Con esos datos el negocio decidirá el resultado que pasar a la acción. En este ejemplo tan sencillo no utilizaremos bbdd y los datos estarán a fuego.

Utilizaremos un objeto de modelo (bean Usuario) como apoyo para facilitar y simplificar el código.

Y aunque me gustaría volver a las catacumbas y utilizar únicamente html y scriptles para en otro manual introducir las librerías de etiquetas (taglibs), seamos prácticos y utilicémoslas desde ya.

identificar.jsp

<%@ page language="java" contentType="text/html; charset=ISO-8859-1"

    pageEncoding="ISO-8859-1"%>

<%@ taglib uri="http://struts.apache.org/tags-html" prefix="html" %>

<%@ taglib uri="http://struts.apache.org/tags-bean" prefix="bean" %>

<%@ taglib uri="http://struts.apache.org/tags-logic" prefix="logic" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">

<html>

<head>

      <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">

      <title>Identificación</title>

      <script>

      function enviar (){

            document.identificarForm.submit();

      }

      </script>

</head>

<body>

<div>

Por favor, identifíquese.

</div>

<html:form action="identificar">

<div>

      <span style="text-aling:rigth">Identificador </span>

      <span>&nbsp;&nbsp;&nbsp;<html:text property="usuario.identificador" /></span>

</div>

<div>

      <span style="text-aling:rigth">Contraseña </span>

      <span>&nbsp;&nbsp;&nbsp;<html:password property="usuario.contrasenna" /></span>

</div>

<div>&nbsp;</div>

<div>

      <input type="button" value="Identificar" onclick="javascript:enviar();" />

</div>

</html:form>

<logic:present name="mensaje">

<div>

      <span style="color: red;">

            <fieldset>

                  <bean:write name="mensaje" />

            </fieldset>

      </span>

</div>

</logic:present>

</body>

</html>

indexIdentificado.jsp

<%@ page language="java" contentType="text/html; charset=ISO-8859-1"

    pageEncoding="ISO-8859-1"%>

<%@ taglib uri="/tags/struts-bean" prefix="bean" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">

<html>

<head>

      <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">

      <title>Zona Identificada</title>

</head>

<body>

      Bienvenido a la zona de administración.

      <bean:write scope="session" name="usuario" property="identificador" />

</body>

</html>

es.lycka.holamundoStruts138.model.Usuario

package es.lycka.holamundoStruts138.model;

public class Usuario {

      private String identificador;

      private String contrasenna;

      public Usuario () {

      }

      public Usuario (String identificador, String contrasenna) {

            this.identificador      = identificador;

            this.contrasenna  = contrasenna;

      }

      public String getContrasenna() {

            return contrasenna;

      }

      public void setContrasenna(String contrasenna) {

            this.contrasenna = contrasenna;

      }

      public String getIdentificador() {

            return identificador;

      }

      public void setIdentificador(String identificador) {

            this.identificador = identificador;

      }

}

es.lycka.holamundoStruts138.action.IdentificarAction

package es.lycka.holamundoStruts138.action;

import javax.servlet.http.HttpServletRequest;

import javax.servlet.http.HttpServletResponse;

import org.apache.struts.action.Action;

import org.apache.struts.action.ActionForm;

import org.apache.struts.action.ActionForward;

import org.apache.struts.action.ActionMapping;

import es.lycka.holamundoStruts138.form.IdentificarForm;

import es.lycka.holamundoStruts138.negocio.IdentificarBO;

public class IdentificarAction extends Action {

      public ActionForward execute (ActionMapping mapping, ActionForm form,

                  HttpServletRequest request, HttpServletResponse response) {

            IdentificarForm identificarForm = (IdentificarForm) form;

            String destino                           = "ERROR";

            try {

                  if (IdentificarBO.esUsuarioAutorizado(identificarForm.getUsuario())) {

                        request.getSession().setAttribute("usuario", identificarForm.getUsuario());

                        destino = "SUCCESS";

                  } else {

                        request.setAttribute("mensaje", "El usuario introducido no está autorizado.");

                  }

            } catch (Exception e) {

                  e.printStackTrace();

                  request.setAttribute("mensaje", "Error interno. Por favor, inténtelo otra vez en unos minutos.");

            }

            return mapping.findForward(destino);

      }

}

es.lycka.holamundoStruts138.form.IdentificarForm

package es.lycka.holamundoStruts138.form;

import org.apache.struts.action.ActionForm;

import es.lycka.holamundoStruts138.model.Usuario;

public class IdentificarForm extends ActionForm {

      private Usuario usuario = new Usuario ();

      public Usuario getUsuario() {

            return usuario;

      }

      public void setUsuario(Usuario usuario) {

            this.usuario = usuario;

      }

}

es.lycka.holamundoStruts138.negocio.IdentificarBO

package es.lycka.holamundoStruts138.negocio;

import es.lycka.holamundoStruts138.model.Usuario;

import es.lycka.holamundoStruts138.persistencia.IdentificarDAO;

public class IdentificarBO {

      public static boolean esUsuarioAutorizado (Usuario usuario) throws Exception {

            IdentificarDAO identificarDAO = new IdentificarDAO ();

            boolean esAutorizado               = false;

            try {

                  if (identificarDAO.consultarUsuario (usuario) != null ) {

                        esAutorizado = true;

                  }

            } catch (Exception e) {

                  throw e;

            }

            return esAutorizado;

      }

}

 

es.lycka.holamundoStruts138.persistencia.IdentificarDAO

package es.lycka.holamundoStruts138.persistencia;

import es.lycka.holamundoStruts138.model.Usuario;

public class IdentificarDAO {

      /**

       * Simulamos una consulta a bbdd.

       * Si existiese un usuario en bbdd con el identificador y la contraseña introducidas nos devolvería

       * un objeto usuario. Si no nos devolvería null.

       */

      public Usuario consultarUsuario (Usuario usuario) throws Exception {

            Usuario resultado = null;

            if (usuario != null

            &&  usuario.getIdentificador() != null

            &&  usuario.getContrasenna()   != null

            &&  usuario.getIdentificador().equals("lycka")

            &&  usuario.getContrasenna().equals("lycka")) {

                  resultado = usuario;

            }

            return resultado;

      }

}

struts-config.xml

Me gustaría empezar a hacer las cosas "mejor". Para empezar, las jsp's las agrupamos dentro de un directorio "jsp" debajo de WebContent. Y para seguir, la configuración de struts yo la suelo colgar debajo de un directorio "struts" en WebContent > WEB-INF. Por tanto, debemos indicar en el web.xml la nueva ruta de nuestro struts-config.xml.

<!– Standard Action Servlet Configuration –>

<servlet>

      <servlet-name>action</servlet-name>

      <servlet-class>org.apache.struts.action.ActionServlet</servlet-class>

      <init-param>

            <param-name>config</param-name>

            <param-value>/WEB-INF/struts/struts-config.xml</param-value>

      </init-param>

      <load-on-startup>2</load-on-startup>

</servlet>

 

Ahora muestro el struts-config.xml donde declaramos nuestro formulario y nuestra acción con sus dos posibles salidas (forwards)

<?xml version="1.0" encoding="ISO-8859-1" ?>

<!DOCTYPE struts-config PUBLIC

          "-//Apache Software Foundation//DTD Struts Configuration 1.3//EN"

          "http://struts.apache.org/dtds/struts-config_1_3.dtd">

<struts-config>

<!– === Form Bean Definitions –>

      <form-beans>

            <form-bean name="identificarForm" type="es.lycka.holamundoStruts138.form.IdentificarForm" />

      </form-beans>

    <global-exceptions />

    <global-forwards />

<!– === Action Mapping Definitions –>

    <action-mappings>

            <action path="/identificar" type="es.lycka.holamundoStruts138.action.IdentificarAction"

                  name="identificarForm" scope="request" validate="false">

                  <forward name="SUCCESS" path="/jsp/indexIdentificado.jsp" />

                  <forward name="ERROR" path="/jsp/identificar.jsp" />

            </action>

    </action-mappings>

</struts-config>

Probamos con un login erróneo, con un login correcto, y todo debería de ir bien. Puedes depurar la ejecución para ver por donde pasa el flujo y entenderlo un poco mejor. Bienvenido a Struts !

Como observación, me gusta utilizar sólo caracteres ASCII para nombres de proyectos, paquetes, clases, métodos y variables, para evitar problemas. Por eso, nada de mi querida ñ ni de acentos. En mensajes y trazas no hay problemas.

Podemos cambiar el anterior index.jsp para adecuarlo a la nueva acción:

<%@ page language="java" contentType="text/html; charset=ISO-8859-1"

    pageEncoding="ISO-8859-1"%>   

<%@ taglib uri="http://struts.apache.org/tags-html" prefix="html" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">

<html>

<head>

<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">

<title>Hola Mundo</title>

</head>

<body>

<h1>Lycka te da la bienvenida!</h1>

<p>Bienvenido a nuestra aplicación de Hola Mundo</p>

<p>

Usted puede :

<ul>

<li>Ingresar en la zona privilegiada <html:link href="jsp/identificar.jsp">aquí</html:link>.</li>

</ul>

</p>

</body>

</html>

Ver también

Primeros Pasos con Struts 1.3.8

El Framework de Struts es probablemente el adecuado para que empieces a pelearte con los proyectos web, por su madurez (lleva muchos años siendo la referencia en los proyectos web), su simplicidad, su abundante documentación y la sencillez con la que implementa el patrón Modelo - Vista - Controlador.

Así mismo Struts - Validator y Tiles son complementos perfectos a este framework (para la validación de los datos introducidos por los usuarios web y para modelar y reutilizar los componentes de la Vista).

Por todo esto la versión 1.3.8 (liberada el 10 de Marzo de 2007) es la ideal para comenzar en el mundo del desarrollo web con J2EE.

Herramientas necesarias

Necesitamos nuestras herramientas Java (JDK), un IDE (hoy por hoy Eclipse es el rey y yoxos su profeta) y un servidor de aplicaciones (Tomcat siempre es una buena elección).

Además necesitaremos la distribución 1.3.8 de Struts. La distribución contiene aplicaciones web de ejemplo (empaquetadas en formato .war), entre ellas struts-blank-1.3.8.war, que es una aplicación web vacía preparada para comenzar con Struts, es decir, algo así como el hola mundo que vamos a construir en esta entrada.

holamundoStruts138

Empezamos con el eclipse. Creamos un nuevo proyecto web dinámico, que llamaremos holamundoStruts138. Creamos una página de bienvenida, WebContent > index.jsp.

<%@ page language="java" contentType="text/html; charset=ISO-8859-1"

    pageEncoding="ISO-8859-1"%>

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">

<html>

<head>

<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">

<title>Hola Mundo</title>

</head>

<body>

<h1>Lycka te da la bienvenida!</h1>

<p>Bienvenido a nuestra aplicación de Hola Mundo Struts 1.3.8</p>

</body>

</html>

En el fichero despcriptor de la aplicación web, el WebContent > WEB-INF > web.xml nos aseguramos que esté nuestro fichero en la lista de ficheros de bienvenida. El valor de <display-name> será el que se mostrará en la administración de nuestro servidor de aplicaciones.

<?xml version="1.0" encoding="UTF-8"?>

<web-app id="WebApp_ID" version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">

      <display-name>holamundoStruts138</display-name>

      <welcome-file-list>

            <welcome-file>index.jsp</welcome-file>

      </welcome-file-list>

</web-app>

Ok, ahora desplegamos la aplicación en nuestro servidor de aplicaciones y deberíamos de ver la pantalla de bienvenida que hemos diseñado.

Comenzando con Struts

Necesitamos incorporar las herramientas de Struts a nuestro proyecto, es decir, las librerías. Cojemos las que vienen en el ejemplo de struts-blank-1.3.8.war, que son más de las que necesitamos pero en un futuro nos podrían venir bien :

  • antlr.jar
  • commons-beanutils.jar
  • commons-chain.jar
  • commons-digester.jar
  • commons-logging.jar
  • commons-validator.jar
  • oro.jar
  • struts-core.jar
  • struts-taglib.jar
  • struts-tiles.jar

Ahora debemos declarar la servlet de Struts, e indicar que atenderá las llamadas de nuestras acciones. Típicamente distinguimos las acciones porque su URL termina en la extensión ".do", por ejemplo "altaClientes.do". En el ejemplo utilizaremos la extensión ".lycka".

<!– Standard Action Servlet Configuration –>

<servlet>

  <servlet-name>action</servlet-name>

  <servlet-class>org.apache.struts.action.ActionServlet</servlet-class>

  <init-param>

    <param-name>config</param-name>

    <param-value>/WEB-INF/struts-config.xml</param-value>

  </init-param>

  <load-on-startup>2</load-on-startup>

</servlet>

 

<!– Standard Action Servlet Mapping –>

<servlet-mapping>

  <servlet-name>action</servlet-name>

  <url-pattern>*.lycka</url-pattern>

</servlet-mapping>

Ahora nuestra aplicación está lista para funcionar bajo Struts.

Para continuar…

[Struts] - Extendiendo el Framework

Struts es un framework que ha triunfado por implementar el patrón que a la postre resultó el más adecuado para aplicaciones web, MVC, por su sencillez para desarrollar y mantener aplicaciones, facilidad de aprendizaje y modularidad. Esta modularidad es la que le ha permitido copar el mercado durante estos años después añadir piezas para completar el puzzle, como Struts Validator, Tiles, Log4J, SSLExt… Y a su vez le permitirá sobrevivir como una pieza dentro de otros frameworks más completos como Spring.

Struts no hace magia, cumple junto a sus extensiones con la mayoría de las necesidades de los desarrolladores, pero aún presenta ciertas "lagunas" como la seguridad. La razón es que cada organización es muy particular a la hora de definir e implementar ciertas funciones en general y sus políticas de seguridad en concreto.

extend_large.jpgPara estos casos, Struts permite implementar fácilmente estas nuevas necesidades, típicamente mediante la extensión / sobreescritura de sus clases y sus métodos. Pero OJO! tu aplicación ya no estará utilizando Struts sino tu propio framework, con sus ventajas (se supone que cumple al 100% con tus requisitos) e inconvenientes: sensibilidad de los cambios, mayor dificultad de mantener, de aprender, pruebas, documentación…

Pensar dos veces, actuar una

El hecho de que puedas hacer algo no significa que debas hacerlo o que sea bueno. La experiencia es vital antes de dar el siguiente paso. Implementar tu propio framework puede resolverte la vida o destruir una aplicación en pocas semanas. Antes de cualquier modificación, por mínima que sea, deberías de

  1. Estar seguro al 100% de que el requisito a cumplir es necesario.
  2. Estar seguro al 100% de que el requisito a cumplir seguirá siendo necesario durante el ciclo de vida de la aplicación, que ya conocemos a los clientes y donde dicen digo…
  3. Estar seguro al 100% de que es la mejor forma de implementarlo.
  4. Hacer comprender a tu cliente / jefe de que es un cambio especial. Traducido al lenguaje que pueda entender un cliente / jefe
    1. Has informado por escrito y Necesitas autorización por escrito para continuar.
    2. Necesitas tiempo para realizar un correcto análisis y diseño de esta solución y sus posibles alternativas.
    3. Tardará más tiempo que un desarrollo normal porque son cambios más sensibles (afectan y afectarán a toda la aplicación)
    4. Es altamente recomendable documentar este desarrollo
    5. Es imprescindible realizar pruebas, eso que en España no sabemos lo que son…
  5. Hazlo !!!

Estos pasos previos te llevarán una mañana, y en un futuro pueden ahorrarte semanas de trabajo. 

Para un cambio tan trillado y documentado como sobreescribir el processroles pues no montes tanto follón, o sí que así te das más caché delante del jefe :)

Attached Files:

Comenta