Tenéis que estar orgullosos de vuestro enemigo
F. Nietzsche

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 !

12 Comentarios hasta el momento »

  1. alfonzo dijo

    7 de September del 2009 a las 4:06 pm

    Saludos, ante todo quiero felicitarte por el excelente trabajo que realizas al dedicar importante parte de su tiempo en la publicacion de este tipo de post, que les son de gran ayuda a programadores como yo que se inician en estos framework.

    Por otro lado, queria plantearte una duda que tengo, y es la siguiente, entiendo cual es la estructura de la clase IdentificarDAOImpl en base a la que tu has publica en este post (IdentificarBOImpl), pero no se cuales serian los cambios a nivel de las clases IdentificarBO, IdentificarDAO y cual es la estructura de las nuevas clases BaseBOImpl y BaseDAOImpl.

    Te agradezco toda la ayuda que puedas prestarme,

    Me seria de gran ayuda saber ademas la estructura de la clase AppBaseAction. Si no es molestia y si todavia tienen las fuentes a la mano, me podrian hacer llegar dichas fuentes…

    De ante mano muchas gracias…

  2. yoyoooyoy dijo

    8 de September del 2009 a las 11:24 pm

    Buenas alfonzo,

    no entiendo muy bien tu duda. Creo que te refieres a que ahora a las clases BO se les inyecta sus respectivos DAOs desde Spring. Por tanto se declaran los DAOs con sus respectivos Getters y Setters, y declaramos los beans

    y luego los inyectamos al BO

    Para la estructura, echa un ojo a esto:
    http://www.hachisvertas.net/blog/01/wp-content/uploads/2008/07/holamundostruts138spring.bmp

    Por razones de diseño (buenas prácticas), todas las clases de negocio deberían de extender de una Base, las de persistencia de otra, etc

    Espero haberte ayudado. Si no es así, vuelve a preguntarme, y disculpa por la tardanza, realmente toy muy liado

    Saludos

  3. alfonzo dijo

    10 de September del 2009 a las 4:55 pm

    Gracias por responderme,

    Fijate que la clase IdentificarBOImpl que colocaste en este post implementa IdentificarBO, pero ella es una clase (en el holamundoStruts138) no una interface, de alli que asumo que ese es unos de los cambios que hay que hacerle al menos a esta clase y a la IdentificarDAO.

    Por otro lado, las clases BaseBOImpl y BaseDAOImpl son nuevas y me gustaria saber si deben tener una estructura especifica o si simplemente como tu digiste “todas las clases de negocio deberían de extender de una Base”.

    Si entiendo bien la clase AppBaseAction, debe hacer lo siguiente (corrigeme si no es asi): La clase AppBaseAction debera extender de LyckaRequestProcessor y mis clases action ahora extenderan de AppBaseAction, y de alli continuo con los cambios.

    Saludos.

  4. yoyoooyoy dijo

    14 de September del 2009 a las 12:38 pm

    Nada hombre, cdo saco tiempo respondo. Intenté contactar a tu mail pero me devolvió el correo.

    Vale, ahora entiendo tu duda. En el ejemplo holamundoStruts138 tanto el negocio como la persistencia están implementadas con una única clase. En este ejemplo, cada una está implementada con un interfaz y una clase que lo implementa.

    Cuál es mejor ? Yo con Struts 1.3.8 trabajé de la forma que puse en el ejemplo, una única clase. Sin embargo con Spring resulta fácil trabajar con interfaces y es lo recomendable desde el punto de vista de las buenas prácticas en el diseño. Así le puedes inyectar una implementación u otra en función de las necesidades.

    Yo la verdad es que no lo veo tan útil salvo en casos concretos. La persistencia, cuando se piensa en que se puede cambiar de base de datos (cuántas veces ocurre esto). El negocio, para casos muy particulares. Por ejemplo, si tienes diferentes proveedores y les quieres aplicar distintos descuentos, puedes implementar un interfaz común a todo ellos e implementar una clase particular para cada proveedor que implemente sus respectivos descuentos o mecanismos particulares.

    Respecto a lo de AppBaseAction, BaseBO y BaseDAO, tb es aplicacble a Struts 1.3.8, pero no lo puse por vago y para simplificar el ejemplo. Tiene que ver con otra buena práctica, la del desacoplo :
    Framework > Arquitectura > Capa App > Clase
    Los BaseBO y BaseDAO corresponden a Capa App, y contendrían las funcionalidades comunes a la capa y particulares para esa aplicación. No sé si me explico, esto da para otro post con más tranquilidad.

    Vale, AppBaseAction no está suficientemente detallado en el post, a ver si saco tiempo para actualizarlo. AppBaseAction es lo mismo que BaseBO. Los actions de tu aplicación deberían de extender de AppBaseAction, y este debería de extender de la clase Action de tu Arquitectura (olvidémosla ahora), y esa de Arquitectura (o directamente este AppBaseAction) de la clase Action de Struts 1.3.8

    Así estás aislando la aplicación para no tener que cambiar el código el día que se pase a Struts 2.0 : sólo habría que cambiar la Arq y AppBaseAction.

  5. alfonzo dijo

    16 de September del 2009 a las 2:41 pm

    Saludos,

    Primero quiero agradecerte por responderme a las preguntas que te he hecho, y no te preocupes por el tiempo de demora, que entendemos que eres una persona con responsabilidades y ocupaciones como lo somos todos. Algo muy importante es que de tu valioso tiempo tomas un poco para ayudar a las demás personas, lo cual te lo agradecemos.

    Ahora bien, se me ha presentado el siguiente error cuando intento correr la aplicación:

    GRAVE: No action instance for path /identificar could be created
    org.springframework.beans.factory.BeanNotOfRequiredTypeException: Bean named ‘com.prueba.integrarstrutspringhibernate.action.IdentificarAction’ must be of type [org.apache.struts.action.Action], but was actually of type [com.prueba.integrarstrutspringhibernate.action.IdentificarAction]
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:322)

    No he podido encontrar la solución a este problema, espero me puedas ayudar.

    NOTA: Cambie para un nuevo correo para que si quieres me contactes por alli.

    Gracias por tu ayuda….

  6. alfonzo dijo

    16 de September del 2009 a las 3:06 pm

    Saludos,

    Ya encontre la SOLUCION ;) al ERRROR que te plantee anteriormente, se me prendio el bombillo luego que te escribi el post (me falto prestale un poco mas de atención al error), el problema era que la clase IdentificarAction la estaba extendiendo de AppBaseAction y esta a la vez extendia de LyckaRequestProcessor, lo que hice fue que la clase base en vez de extender de la clase LyckaRequestProcessor que extendiera de la clase Action de Struts y el problema desaparecio.

    Si lo que hice es incorrecto te agradeceria mucho me corrigieras, porque tengo la duda con respecto a si la clase base debe de extender de la clase Action, por la cuestion que sobreescribimos el controlador de Struts.

    Gracias y saludos….

  7. yoyoooyoy dijo

    16 de September del 2009 a las 10:26 pm

    Uf, gracias alfonzo, no veas lo agradable que es que leer palabras de agradecimiento de desconocidos cuando mis conocidos me escriben lo que me escriben…

    Mi tiempo no es tan valioso, no más que el de cualquiera, simplemente cada día me apetece menos estar delante de un ordenador

    Efectivamente, el error que me comentas se soluciona así, AppBaseAction debe de extender la clase Action de Struts, y nuestro controlador (LyckaRequestProcessor) debe extender del controlador de Struts (RequestProcessor)

    Y si aprendes de tus errores, todo error nos acerca más a la verdad :)

  8. Carolina dijo

    19 de November del 2009 a las 1:47 am

    Hola, muchas felicidades por el tutorial, lo vengo siguiendo desde los primeros pasos con struts, y gracias por resolver mi duda en primeras acciones con struts, ahora me queda la duda de como queda todo el proyecto, ya que por lo que puedo ver se hacen varias implementaciones.

    Te lo agradeceria mucho. Saludos.

  9. yoyoooyoy dijo

    19 de November del 2009 a las 8:47 pm

    Hola Carolina,

    no sé si te he entendido bien, y no sé si me explicaré bien, estoy todavía enfermo y con los efectos de los antibióticos :S

    hay varios tutoriales en la bitácora, dos de Struts1, uno de Struts2, este para integrarlo con Spring gracias al magnífico Chema… además llevo tiempo queriendo completarlos con otros tutoriales de iBATIS, Hibernate, Maven… pero no saco tiempo.

    En este ejemplo concreto se utilizan una interfaz que puede ser implementada de varias formas. Por ejemplo, el acceso a base de datos, puedes construir diferentes implementaciones (una para bases de datos de MySQL, otra para SQL Server, otra para Oracle…). Luego cuando desplieges tu proyecto en un entorno concreto, utilizas una única implementación a dicho interfaz (por ejemplo, si en tu entorno de local utilizas MySQL y en preproducción y producción utilizas Oracle, en local implementarías el interfaz con la implementación de MySQL y en el resto con la de Oracle).

    Para tu proyecto, simplemente elige el framework. Estos tutoriales son una base, y luego ya vas implementando el resto de tu proyecto.

    Espero haberme explicado, si no es así dímelo :)

  10. Carolina dijo

    19 de November del 2009 a las 11:26 pm

    Muchisimas gracias por responderme y espero que te mejores pronto, solo una pregunta más, ¿cuál es la clase AppBaseAction?. Saludos.

  11. yoyoooyoy dijo

    21 de November del 2009 a las 9:00 pm

    Nada, un placer. Respecto a la clase AppBaseAction, echa un ojo a los comentarios de este mismo post. Si sigues teniendo dudas, a ver si como digo en los comentarios hago un post a parte sobre esta clase.

  12. Luis Guerrero de la Rosa dijo

    4 de December del 2009 a las 10:02 pm

    Que tal me encuentro desarrollando una aplicacion en struts 1.3.8 ,pero para evitar la creacion de pantallas se me paso un proyecto cascaron hecho en spring
    y ese framework nunca me toco utilizarlo hasta este momento por lo tal estoy en jaque ,
    me gustaria si me puedes asesorar mas especificamente

    saludos y muchas gracias

    atte.-Luis Guerrero

Comentarios RSS · TrackBack URI

Dejanos tu Comentario

Nombre: (Requerido)

E-Mail: (Requerido)

Sitio WEB:

Comentario:

Comenta