Integración de JSF, Spring e Hibernate para crear una Aplicación Web del Mundo Real

La siguiente fase del diseño de una aplicación web es el diseño de la arquitectura de alto nivel. Esto implica subdividir la aplicación en componentes funcionales y particionar estos componentes en capas. El diseño de la arquitectura de alto nivel es neutral a las tecnologías utilizadas.

. Arquitectura Multi-capa

Una arquitectura multicapa particiona todo el sistema en distintas unidades funcionales: cliente, presentación, lógica-de-negocio, integración, y sistema de información empresarial (EIS). Esto asegura una división clara de responsabilidades y hace que el sistema sea más mantenible y extensible. Los sistemas con tres o más capas se han probado como más escalables y flexibles que un sistema cliente-servidor, en el que no existe la capa central de lógica-de-negocios.

La capa del cliente es donde se consumen y presentan los modelos de datos. Para una aplicación Web, la capa cliente normalmente es un navegador web. Los clientes pequeños basados-en-navegador no contienen lógica de presentación; se trata en la capa de presentación.

La capa de presentación expone los servicios de la capa de lógica-de-negocio a los usuarios. Sabe cómo procesar una petición de cliente, cómo interactúar con la capa de lógica-de-negocio, y cómo seleccionar la siguiente vista a mostrar.

La capa de la lógica-de-negocio contiene los objetos y servicios de negocio de la aplicación. Recibe peticiones de la capa de presentación, procesa la lógica de negocio basada en las peticiones, y media en los accesos a los recursos de la capa EIS. Los componenes de la capa de lógica-de-negocio se benefician de la mayoría de lo servicios a nivel de sistema como el control de seguridad, de transaciones y de recursos.

La capa de integración es el puente entre la capa de lógica-de-negocio y la capa EIS. Encapsula la lógica para interactúar con la capa EIS. Algunas veces a la combinación de las capas de integración y de lógica-de-negocio se le conoce como capa central.

Los datos de la aplicación persisten en la capa EIs. Contiene bases de datos relacionales, bases de datos orientadas a objetos, y sistemas antiguos.

. Diseño de la Arquiectura de JCatalog

La siguiente figura muestra el diseño de la arquitectura de alto nivel de JCatalog y cómo se acopla en la arquitectura multi-capa.

La aplicación utiliza una arquitectura multi-capa no-distribuida. La figura anterior muestra el particionamiento de las capas de la aplicación y las tecnologías elegidas para cada capa. También sierve como diagrama de ejemplo de despliegue de la aplicación. Para una arquitectura colocada, las capas de presentación, de lógica-de-negocio y de integración están físicamente localizadas en el mismo contenedor Web. Interfaces bien-definidos aislan las responsabilidades de cada capa. La arquitectura colocada hace que la aplicación sea más simple y escalable.

Para la capa de presentación, la experiencia muestra que la mejor práctica es elegir un marco de trabajo de aplicaciones Web existente y probado en vez de diseñar y construir un marco de trabajo personalizado. Hay varios marcos de trabajo de este tipo entre los que elegir: Struts, WebWork, y JSF. Nosotros utilizaremos JSF para JCatalog.

Se pueden utilizar EJB (Enterprise JavaBeans) o POJO (plain old Java objects) para construir la capa de lógica-de-negocio. EJB con sus interfaces remotos es una mejor elección si la aplicación es distribuida. Como JCatalog es una aplicación web típica que no requiere acceso remoto, se utilizará POJO con la ayuda del marco de trabajo Spring, para esta capa.

La capa de integración maneja la persistencia de los datos con la base de datos relacional. Se pueden utilizar diferentes aproximaciones para implementar la capa de integración:

  • JDBC puro (Java DataBase Connectivity):
    Es la aproximación más flexible; sin embargo es difícil trabajar con JDBC de bajo nivel, y un mal código JDBC no tiene un rendimiento muy bueno.
  • Beans de entidad:
    Un Bean de entidad con persistencia manejada por el contenedor (CMP) es una forma costosa de aislar el código de acceso-a-datos y manejar el mapeo O/R (objecto-relacional) para persistencia de datos. Es una aproximación centrada en el servidor de aplicaciones. Un bean de entidad no ata la aplicación a un tipo particular de base de datos, pero si ata la aplicación al contenedor EJB.
  • Marco de Trabajo de Mapeo O/R:
    Un marco de trabajo de mapeo O/R toma una aproximación centrada en el objeto para implementar la persistencia de datos. Una aplicación de este tipo es fácil de desarrollar y altamente portable. Existen varios marcos de trabajo bajo este dominio: JDO (Java Data Objects), Hibernate, TopLink, y CocoBase son unos pocos ejemplos. En la aplicación de ejemplo se utiliza Hibernate.

Ahora discutiremos los problemas de diseño asociados con cada capa de la aplicación. Como JSF es una tecnología relativamente nueva, aconsejo su utilización.

. La Capa de Presentación y JavaServer Faces

La capa de presentación recoge la entrada del usuario, presenta los datos, controla la navegación por las página y delega la entrada del usuario a la capa de la lógica-de-negocio. La capa de presentación también puede validar la entrada del usuario y mantener el estado de sesión de la aplicación. En las siguientes secciones, discutiremos las consideraciones de diseño y los patrones de la capa de presentación, y la razón porque la que he elegido JSF para implementar JCatalog.

. Model-View-Controller (MVC)

MVC es el patrón de diseño arquitectural recomendado para aplicaciones interactivas Java. MVC separa los conceptos de diseño, y por lo tanto decrementa la duplicación de código, el centralizamiento del control y hace que la aplicación sea más extensible. MVC también ayuda a los desarrolladores con diferentes habiliades a enfocarse en sus habilidades principales y a colaborar a través de interfaces claramente definidos. MVC es el patrón de diseño arquitectural para la capa de presentación.

. JavaServer Faces

JSF es un marco de trabajo de componentes de interface de usuario del lado del servidor para aplicaciones Web basadas en Java. JSF contiene un API para representar componentes UI y manejar sus estados, manejar sus eventos, ña validación del lado del servidor, y la conversión de datos, definir la navegación entre páginas, soportar internacionalización y accesibilidad; y proporcionar extensibilidad para todas estas características. Tambiéne contien dos librerías de etiquetas JSP (JavaServer Pages) personalizadas para expresar componentes UI dentro de una página JSP y para conectar componentes a objetos del lado del servidor.

. JSF y MVC

JSF encaja bien en la arquitectura de la capa de presentación basada en MVC. Ofrece una clara separación entre el comportamiento y la presentación. Une los familiares componentes UI con los conceptos de la capa-Web sin limitarnos a una tecnología de script o lenguaje de marcas particular.

Los beans que hay tras JSF son la capa de modelo (en una sección posterior hablaremos más de estos beans). También contienen acciones, que son una extensión de la capa del controlador y delegan las peticiones del usuario a la capa de la lógica-de-negocio. Observe, que desde la prespectiva de la arquitectura general de la aplicación, también se puede referir a la capa de lógica-de-negocio como la capa del modelo. Las páginas JSP con etiquetas JSF personalizadas son la capa de la vista. El Servlet Faces proporciona la funcionalidad del controlador.

. ¿Por qué JSF?

JSF no es sólo otro marco de trabajo Web. Las siguientes características diferencian a JSF de otros marcos de trabajo:

  • Desarrollo de una Aplicación Web Orientada a Objetos al Estilo Swing:
    El modelo de componentes UI con estado del lado del servidor con oyentes/manejadores de eventos inicia el desarrollo de aplicaciones Web orientadas a objetos.
  • Control de Beans-de-Respaldo:
    Los beans de respaldo son componentes JavaBeans asociados con componentes UI utilizados en la página. El control de beans-de-respaldo separa la definición de los objetos componentes del UI de los objetos que realizan el procesamiento específico de la aplicación y que además contienen los datos. La implementación de JSF almacena y maneja estos ejemplares de beans de respaldo en el ámbito apropiado.
  • Modelo de componentes UI extensible:
    Los componentes UI de JSF son elementos configurables, reutilizables que componen los intefaces de usuario de aplicaciones JSF. Se puede extender un componentes UI estándar y desarrollar componentes más complejos, como barras de menú y árboles.
  • Modelo de Renderizado Flexible:
    Un renderizador separa la vista y la funcionalidad de los componentes UI. Se pueden crear y utilizar varios renderizadores para definir diferentes apariencias del mismo componente para el mismo o diferentes clientes.
  • Modelo de Conversión y Validación Extensible:
    Basados en los convertidores y validadores estándar, se pueden desarrollar convertidores y validadores personalizados, que proporcionan un mejor modelo de protección.

A pesar de su potencia, JSF no esta madura aún. Los componentes, convertidores y validadores que vienen con JSF son básicos. Y el modelo de validación por-componente no puede manejar validaciones muchos-a-muchos entre componentes y validadores. Además, las etiquetas personalizadas de JSF no se pueden integrar con JSTL (JSP Standard Tag Library) simultáneamente.

En la siguientes secciones, describiremos varios aspectos claves y decisiones de diseño que he realizado cuando implementé JCatalog con JSF. Empezaré con una discusión de la definición de beans manejados y beans de respaldo en JSF. Luego presentaré cómo manejar la seguridad, la paginación, el caché, la subida de ficheros, la validación y la personalización de mensajes de error en JSF.

. Bean Manejado, Bean de Respaldo, objeto vista y Modelo de objeto de dominio

JSF presenta dos nuevos términos: managed bean (bean manejado) y backing bean (bean de respaldo). JSF proporciona una fuerte facilidad de bean manejado. Los objetos JavaBean manejados por una implementación JSF se llaman beans manejados. Un bean manejado describe como se crea y se maneja un bean. No tiene nada que ver con las funcionalidades del bean.

El bean de resplado define las propiedades y las lógicas de manejo asociadas con los componentes UI utilizados en la página. Cada propiedad del bean de respaldo está unida a un ejemplar de un componente o a su valor. Un bean de respaldo también define un conjunto de métodos que realizan funciones para el componente, como validar los datos del componente, manejar los eventos que dispara el componente, y realizar el procesamiento asociado con la navegación cuando el componente se activa.

Una típica aplicación JSF acopla un bean de repaldo con cada página de la aplicación. Sin embargo, algunas veces en el mundo real, forzar una relación uno-a-uno entre el bean de respaldo y la página no es la solución ideal. Puede causar problemas como la duplicación de código. En el escenario del mundo real, varias páginas podrían necesitar compartir el mismo bean de respaldo detrás de la escena. Por ejemplo, en JCatalog, las páginas CreateProduct y EditProduct comparten la misma definicion de ProductBean.

Un objeto view (vista) es un objeto modelo utilizado específicamente en la capa de presentación. Contiene los datos que debe mostrar en la capa de la vista y la lógica para validar la entrada del usuario, manejar los eventos, e interactúar con la capa de lógica-de-negocio. El bean de respaldo es el objeto vista en una aplicación basadas en JSF. Bean de respaldo y objeto vista son términos intercambiables en este tutorial.

En comparación con la aproximación ActionForm y Action de Struts, el desarrollo con beans de respaldo de JSF sigue unas mejores prácticas de diseño orientado a objetos. Un bean de respaldo no sólo contiene los datos a ver, también el comportamiento relacionado con esos datos. En Struts, Action y ActionForm contienen los datos y la lógica por separado.

Todos hemos oído hablar del modelo de objeto de dominio. Entonces, ¿cuál es la diferenica en el modelo de objeto de dominio y un objeto vista? En una sencilla aplicación Web, un modelo de objeto de dominio se puede utilizar entre capas, sin embargo, en aplicaciones Web más complejas, se necesita utilizar un modelo de objeto vista separado. Los modelos de objeto de dominio son como objetos de negocio y deberían pertenecer a la capa de lógica-de-negocio. Contiene los datos de negocio y la lógica de negocio asociados con el objeto de negocio específico. Un objeto vista contiene datos específicos de la presentación y el comportamiento. ProductListBean de JCatalog ofrece un buen ejemplo. Contiene los datos y la lógica específica de la capa de presentación; es decir datos y lógica relacionados con la paginación. La desventaja de separar los objetos vista del modelo de objetos de dominio es que debe ocurrir un mapeo de datos entre los dos modelos de objetos. En JCatalog, ProductBeanBuilder y UserBeanBuilder usan las Commons BeanUtils para implementar el mapeo de datos.

. Seguridad

Actualmante, JSF no tiene una característica de seguridad interna. Los requirimientos de seguridad para la aplicación de ejemplo son básicos: sólo se necesita la autentificación basada en nombre de usuario y password para que el usuario entre en la intranet de administración, y no se requiere autorización.

Se han propuesto varias alternativas para manejar la autentificación de usuarios en JSF:

  • Usar un bean de respaldo:
    Esta es una solución simple. Sin embargo, ata los beans de respaldo un árbol de herencia específico.
  • Usar un decorador ViewHandler JSF:
    De esta forma, la lógica está fuertemente unida a una tecnología específica de capa Web.
  • Usar un filtro servlet:
    Una aplicación JSF no es diferente de cualquier otra aplicación Web basada en Java. Hace que un filtro sea el mejor lugar para manejar el chequeo de autentificación. De esta forma, la lógica de autentificación de desacopla de la aplicación web.

En la aplicación de ejemplo, la clase SecurityFilter maneja la autentificación de los usuarios. Actualmente, los recursos protegidos son sólo tres páginas, y sus localizaciones están codificadas dentro de la clase Filter por simplicidad. Se pueden realizar mejoras para externalizar las reglas de seguridad y los recursos protegidos a un fichero de configuración.

. Paginación

La aplicación de un catálogo requiere paginación. La capa de presentación puede manejar la paginación, lo que significa que los datos se debe recuperar y almacenar en esta capa. La paginación también se puede manejar desde la capa de lógica-de-negocio, la capa de integración o incluso desde la capa de EIS. Una de las presunciones de la aplicación JCatalog era que no podía haber más de 500 productos en el catálogo. La información de todos los productos puede caber en la sesión de usuario. La lógica de paginación está en la clase ProductListBean. El parámetro relacionado con la paginación "product per page" se configura mediante la facilidad del bean manejado de JSF.

. Caché

El Caché es una las tecnicas más importantes para mejorar el rendimiento de las aplicaciones web. El caché se puede conseguir en muchas capas dentro de la arquitectura de una aplicación. Es más beneficioso cuando una capa arquitectural puede evitar llamadas a la capa de al lado. La facilidad del bean manejado de JSF hace que el caché sea más sencillo en la capa de presentación. Cambiando el ámbito del bean manejado, los datos que él contiene se pueden cachear dentro de diferentes ámbitos.

La aplicación de ejemplo utiliza un caché de dos niveles. El primer nivel de caché existe dentro de la capa de lógica-de-negocio. La clase CachedCatalogServiceImpl mantiene un caché de lectura/escritura para todos los productos y categorías. Entonces, el caché de primer nivel es un caché de lectura/escritura en el ámbito de la aplicación.

Para simplificar la lógica de paginación y así mejorar la velocidad de la aplicación, los productos también son cacheados dentro de la capa de presentación en el ámbito de la sesión. Cada usuario mantiene su propio ProductListBean dentro de la sesión. Las costes de esta aproximación son la memoria del sistema y datos obsoletos. Durante una sesión de usuario, éste podría ver datos de un catálogo obsoleto si el administrador actualiza el catálogo. Sin embargo, basado en las presunciones, como no existen más de 500 productos y el catálogo no se actualiza de forma frecuente, deberíamos poder asumir estos costes.

. Subida de Ficheros

La implementación de referencia de JSF de Sun no soporta subida de ficheros. Struts tiene buenas capacidades para esto, sin embargo se necesita la librería de integración Struts-Faces para utilizar esta característica. En JCatalog, se asocia una imagen con cada producto. Después de que el usuario crea un nuevo producto, debe subir la imagen asociada con él. La imagen es almacenada dentro del sistema de ficheros del servidor de aplicaciones. El ID del producto es el nombre de la imagen.

La aplicación de ejemplo utiliza el servlet <input type="file">, y el API de subida de ficheros de Jakarta Commons, para implementar una utilidad sencilla. Esta utilidad toma dos parámetros, el directorio de imágenes de producto y la result page de la imagen subida. Los dos son configurables mediante ApplicationBean. Puede ver más detalles en la clase FileUploadServlet.

. Validación

Los validadores estándar que vienen con JSF son básicos y podrían no cumplir con los requerimientos del mundo real. Desarrollar sus propios validadores JSF es sencillo. Yo he desarrollado el validador SelectedItemsRange con una etiqueta personalizada en la aplicación de ejemplo. Valida el número de ítems seleccionados por el componente UI UISelectMany:

<h:selectManyListbox value="#{productBean.selectedCategoryIds}" id="selectedCategoryIds">

   <catalog:validateSelectedItemsRange minNum="1"/>

   <f:selectItems value="#{applicationBean.categorySelectItems}" id="categories"/>

</h:selectManyListbox>

. Personalización de los Mensajes de Error

En JSf, se pueden configurar paquetes de recursos y personalizar los mensajes de error para convertidores y validadores. El paquete de recursos se configura dentro de faces-config.xml:

<message-bundle>catalog.view.bundle.Messages</message-bundle>

Las parejas clave-valor de los mensajes de error se añaden al fichero Message.properties:

#conversion error messages

javax.faces.component.UIInput.CONVERSION=Input data is not in the correct type.


#validation error messages

javax.faces.component.UIInput.REQUIRED=Required value is missing.

. La Capa de Lógica-de-Negocio y el Marco de Trabajo Spring

Los objetos y servicios de negocio existen en la capa de lógica-de-negocio. Un objeto de negocio no sólo contiene datos, también la lógica asociada con ese objeto específico. En la aplicación de ejemplo se han identificado tres objetos de negocio: Product, Category, y User.

Los servicios de negocio interactúan con objetos de negocio y proporcionan una lógica de negocio de más alto nivel. Se debería definir una capa de interface de negocio formal, que contenga los interfaces de servicio que el cliente utilizará directamente. POJO, con la ayuda del marco de trabajo Spring, implementará la capa de lógica-de-negocio de la aplicación JCatalog. Hay dos servicios de negocio: CatalogService contiene la lógica de negocio relacionada con el manejo del catálogo, y UserService contiene la lógica de manejo del usuario.

Spring está basado en el concepto de inversión de control (IoC). Entre las características de Spring utilizadas en la aplicación de ejemplo se incluyen:

  • Manejo de Beans con contexto de aplicación:
    Spring puede organizar de forma efectiva nuestros objetos de la capa central y manejar las conexiones por nosotros. Spring puede eliminar la proliferación de solitarios y facilita unas buenas prácticas de programación orientada a objetos, por ejemplo utilizando interfaces.
  • Manejo de Transaciones Declarativo:
    Spring utiliza AOP (aspect-oriented programming) para ofrecer manejo de transaciones declarativo sin utilizar un contenedor EJB. De esta forma, el control de transaciones se puede aplicar a cualquier POJO. El control de transaciones de Spring no está atado a JTA (Java Transaction API) y puede funcionar con diferentes estrategias de transación. En la aplicación de ejemplo se utiliza el manejo de transación declarativo con Hibernate.
  • Árbol de Excepciones de Acceso a Datos:
    Spring proporciona un magnífico árbol de excepciones en lugar de SQLException. Para poder utilizar este árbol de excepciones, se debe definir un traductor de excepciones de acceso a datos dentro del fichero de configuración de Spring:
    <bean id="jdbcExceptionTranslator" 
        class= "org.springframework.jdbc.support.SQLErrorCodeSQLExceptionTranslator"> 
    
       <property name="dataSource">
    
          <ref bean="dataSource"/>
    
       </property>
    
    </bean>
    

    En la aplicación de ejemplo, si se inserta un producto nuevo con el ID duplicado, se lanza una DataIntegrityViolationException. La excepción es capturada y relanzada como una DuplicateProductIdException. De esta forma, la DuplicateProductIdException se puede manejar de forma diferente a cualquier otra excepción de acceso a datos.

  • Integración con Hibernate:
    Spring no nos fuerza a utilizar su potente característica de abstracción JDBC. Se integra bien con marcos de trabajo de mapeo O/R, especialmente con Hibernate. Spring ofrece un manejo seguro y eficiente de sesiones Hibernate, maneja la configuración de la SessionFactorie de Hibernate y las fuentes de datos JDBC en el contexto de la aplicación, y hace que la aplicación sea más fácil de testear.

. La Capa de Integración e Hibernate

Hibernate es un marco de trabajo de mapeo O/R Open Source que evita la necesidad de utilizar el API JDBC. Hibernate soporta la mayoría de los sistemas de bases de datos SQL. El Hibernate Query Language, diseñado como una extensión mínima, orientada a objetos, de SQL, proporciona un puente elegante entre los mundos objeto y relacional. Hibernate ofrece facilidades para recuperación y actualización de datos, control de transaciones, repositorios de conexiones a bases de datos, consultas programáticas y declarativas, y un control de relaciones de entidades declarativas.

Hibernate es menos invasivo que otros marcos de trabajo de mapeo O/R. Se utilizan Reflection y la generación de bytecodes en tiempo de ejecución, y la generación del SQL ocurre en el momento de la arrancada. Esto nos permite desarrollar objetos persistentes siguiendo el lenguaje común de Java: incluyendo asociación, herencia, polimorfismo, composición y el marco de trabajo Collections de Java. Los objetos de negocio de la aplicación de ejemplo son POJO y no necesitan implementar ningún interface específico de Hibernate.

. Data Access Object (DAO)

En JCatalog se utiliza el patrón DAO. Este patrón abstrae y encapsula todos los accesos a la fuente de datos. La aplicación tiene dos interfaces DAO: CatalogDao y UserDao. Sus clases de implementación, HibernateCatalogDaoImpl y HibernateUserDaoImpl contienen lógica específica de Hibernate para manejar los datos persistentes.

COMPARTE ESTE ARTÍCULO

ENVIAR A UN AMIGO
COMPARTIR EN FACEBOOK
COMPARTIR EN TWITTER
COMPARTIR EN GOOGLE +
HAY 2 COMENTARIOS
  • Anónimo dijo:

    Quiero el código fuente!!! Muchas gracias por este tutorial tan completo.

  • Anónimo dijo:

    Buen tutorial

Conéctate o Regístrate para dejar tu comentario.