179
Catálogo de Patrones de Diseño J2EE. I.- Capa de Presentación Autor: Sun Traductor: Juan Antonio Palos (Ozito) Puedes encontrar la versión original de este tutorial en Inglés en: http://java.sun.com/blueprints/corej2eepatterns/Patterns/index.html Introducción a los Patrones o ¿Qué es un Patrón? o Abstracción de Patrones o Identificar un Patrón o Patrones contra Estrategias o El Catálogo de Patrones J2EE (Core J2EE Patterns) Plantilla de Patrón Intercepting Filter o Contexto o Problema: o Causas o Solución Estructura Participantes y Responsabilidades Estrategias o Consecuencias o Patrones Relacionados Front Controller o Contexto o Problema o Causas o Solución Estructura Participantes y Responsabilidades Estrategias o Consecuencias o Patrones Relacionados View Helper o Contexto o Problema o Causas o Solución Estructura Participantes y Responsabilidades Estrategias o Consecuencias o Patrones Relacionados Composite View o Contexto o Problema o Causas o Solución Estructura Participantes y Responsabilidades Estrategias o Consecuencias o Código de Ejemplo o Patrones Relacionados Service to Worker o Contexto o Problema o Causas o Solución Estructura Participantes y Responsabilidades

Catálogo de Patrones de Diseño J2EE

Embed Size (px)

Citation preview

Page 1: Catálogo de Patrones de Diseño J2EE

Catálogo de Patrones de Diseño J2EE. I.- Capa de Presentación Autor: Sun

Traductor: Juan Antonio Palos (Ozito)

Puedes encontrar la versión original de este tutorial en Inglés en: http://java.sun.com/blueprints/corej2eepatterns/Patterns/index.html

• Introducción a los Patrones o ¿Qué es un Patrón? o Abstracción de Patrones o Identificar un Patrón o Patrones contra Estrategias o El Catálogo de Patrones J2EE (Core J2EE Patterns)

• Plantilla de Patrón • Intercepting Filter

o Contexto o Problema: o Causas o Solución

� Estructura � Participantes y Responsabilidades � Estrategias

o Consecuencias o Patrones Relacionados

• Front Controller o Contexto o Problema o Causas o Solución

� Estructura � Participantes y Responsabilidades � Estrategias

o Consecuencias o Patrones Relacionados

• View Helper o Contexto o Problema o Causas o Solución

� Estructura � Participantes y Responsabilidades � Estrategias

o Consecuencias o Patrones Relacionados

• Composite View o Contexto o Problema o Causas o Solución

� Estructura � Participantes y Responsabilidades � Estrategias

o Consecuencias o Código de Ejemplo o Patrones Relacionados

• Service to Worker o Contexto o Problema o Causas o Solución

� Estructura � Participantes y Responsabilidades

Page 2: Catálogo de Patrones de Diseño J2EE

� Estrategias o Consecuencias o Código de Ejemplo o Patrones Relacionados

• Dispatcher View o Contexto o Problema o Causas o Solución

� Estructura � Participantes y Responsabilidades � Estrategias

o Consecuencias o Código de Ejemplo o Patrones Relacionados

Page 3: Catálogo de Patrones de Diseño J2EE

Catálogo de Patrones de Diseño J2EE. I.- Capa de Presentación Autor: Sun

Traductor: Juan Antonio Palos (Ozito)

• Introducción a los Patrones o ¿Qué es un Patrón? o Abstracción de Patrones o Identificar un Patrón o Patrones contra Estrategias o El Catálogo de Patrones J2EE (Core J2EE Patterns)

Introducción a los Patrones

¿Qué es un Patrón?

Algunas personas definen un patrón como una solución recurrente para un problema en un contexto. Estos términos -- contexto, problema y solución -- merecen una pequeña explicación. Primero, ¿qué es un contexto? Un contexto es el entorno, situación, o condiciones interrelacionadas dentro de las cuales existe algo. Segúndo, ¿qué es un problema? Un problema es una cuestión insatisfecha, algo que se necesita investigar y resolver. Un problema se puede especificar mediante un conjunto de causas y efectos. Normalmenete un problema está restringido al contexto en el que ocurre. Finalmente, la solución se refiere a la respuesta al problema dentro de un contexto que ayuda a resolver las dificultades.

Entonces, si tenemos una solución a un problema en un contexto, ¿es un patrón? No necesariamente. También necesitamos asociar la característica de recurrencia con la definición de un patrón. ¿Eso es todo? Quizás no. Los patrones deberían comunicar soluciones de diseño a los desarrolladores y arquitectos que los leen y los utilizan. Como puedes ver, aunque el concepto de patrón es bastante simple, definir realmente el término es muy complejo.

Hemos señalado sólo las referencias para que puedas indagar en más profundidad en la historia de los patrones y aprender sobre ellos en otras áreas. Sin embargo, deberías tener en mente que la definición de patrón que hemos adoptado funciona. En nuestro catálogo, se describe un patrón de acuerdo a sus principales características: contexto, problema y solucion, junto con otros aspectos importantes, como causas y consecuencias. La página que describe la plantilla de patrones explica estas características en más detalle.

Abstracción de Patrones

Un patrón describe, con algún nivel de abtrascción, una solución experta a un problema. Normalmente, un patrón está documentado en forma de una plantilla. Aunque es una práctica estándar documentar los patrones en un formato de plantilla especializado, esto no significa que sea la única forma de hacerlo. Además, hay tantos formatos de plantillas como autores de patrones, esto permite la creatividad en la documentación de patrones.

Los patrones solucionan problemas que existen en muchos niveles de abstracción. Hay patrones que describen soluciones para todo, desde el análisis hasta el diseño y desde la arquitectura hasta la implementación. Además, los patrones existen en diversaa áreas de interés y tecnologías. Por ejemplo, hay un patrón que describe como trabajar con un lenguaje de programación específico o un segmento de la industria específico, como la sanidad.

Identificar un Patrón

Se han manejado muchos proyectos J2EE en Sun Java Center, y, con el tiempo, se ha notado que ocurren problemas similares en todos estos proyectos. También se ha visto que han emergido soluciones similares para todos estos problemas. Aunque las estrategias de implementación variaran, las soluciones generales eran bastante similares.

Page 4: Catálogo de Patrones de Diseño J2EE

Cuando se ha visto un problema y una solución recurrentes, se ha intentado identificar y documentar sus características usando la plantilla de patrón. Los candidatos a patrones no se han añadido al catálogo de patrones hasta que no se ha podido observar y documentar su utilización varias veces en diferentes proyectos. También se emprendió el procesamiento de significado de los patrones buscando patrones en soluciones ya implementadas.

Luego, hay una "Regla de Tres", como se la conoce en la comunidad de los patrones. Esta regla es una guiá para ascender un patrón candidato al catálogo de patrones. De acuerdo a esta regla, una solución permanece como un patrón candidato hasta que se ha verificado en al menos tres sistemas diferentes. Ciertamente que hay mucho espacio para la interpretación en reglas como ésta, pero ayudan a proporcionar un contexto para la identificación de patrones.

Muchas veces, soluciones similares podrían representar un sólo patrón. Cuando se decide cómo formar un patrón, es importante considerar cual es la mejor forma de comunicar la solución. Algunas veces, un nombre indenpendiente mejora la comunicación entre los desarrolladores. Si es así, es mejor considerar la documentación de dos soluciones similares como dos patrones diferentes. Por otro lado, podría ser mejor comunicar la solución destilando las ideas similares en una combinación de patrón/estrategia.

Patrones contra Estrategias

Cuando se empezó a documentar los patrones J2EE, se tomó la decisión de documentarlos con un nivel de abstracción relativamente alto. Al mismo tiempo, cada patrón incluye varias estrategias que proporcionan detalles de su implementación a bajo nivel. A través de las estrategias, cada patrón documenta una solución con varios niveles de abstracción. Se ha reconocido que se podría haber documentado algunas de estas estrategias como patrones por sí mismas. Sin embargo, se ha creído que la estructura de plantilla actual comunica más claramente la relación de las estrategias con la estructura de patrón de alto nivel en que se ha incluido.

Se han anotado algunos de los problemas con respecto a la relación entre las estrategias y los patrones:

• Los patrones existen a un nivel de abstracción más alto que las estrategias. • Los patrones incluyen implementaciones más comunes o más recomendadas que las

estrategias. • Las estrategias proporcionan un punto de extensibilidad para cada patrón. Los desarrolladores

descubren e inventan nuevas formas de implementar patrones, produciendo nuevas estrategias para patrones bien-conocidos.

• Las estrategias promueven una mejor comunicación, proporcionando nombres para aspectos de bajo nivel de una solución particular.

El Catálogo de Patrones J2EE (Core J2EE Patterns)

Abajo puedes ver una representación gráfica del Catálogo de Patrones Principales de J2EE (Core J2EE Patterns):

Page 5: Catálogo de Patrones de Diseño J2EE
Page 6: Catálogo de Patrones de Diseño J2EE

Catálogo de Patrones de Diseño J2EE. I.- Capa de Presentación Autor: Sun

Traductor: Juan Antonio Palos (Ozito)

• Plantilla de Patrón

Plantilla de Patrón

Los patrones J2EE se han estructurado de acuerdo a una plantilla de patrón definida. Cada sección de la plantilla de patrón contribuye a entender el patrón particular. También observarás que se ha intentado dar un nombre de patrón descriptivo a cada patrón J2EE. Aunque es dificil acompasar un patrón singular con su nombre, se ha intentado que los nombres de patrón proporcionen suficiente información sobre la función del patrón.

Se ha adoptado una plantilla de patrón que consta de las siguientes secciones:

• Contexto: Conjunto de entornos bajo los cuales existe el patrón.

• Problema: Describe los problemas de diseño que se ha encontrado el desarrollador.

• Causas: Lista los razones y motivos que afectan al problema y la solución. La lista de causas ilumina las razones por las que uno podría haber elegido utilizar el patrón y proporciona una justificación de su uso.

• Solución: Describe brevemente la solución y sus elementos en más detalle. La sección de la solución contiene dos sub-secciones:

o Estructura: Utiliza diagramas de clases UML para mostrar la estructura básica de la solución. Los diagramas de secuencias UML de esta sección presentan los mecanismos dinámicos de la solución. Hay una explicación detalladas de los participantes y las colaboraciones.

o Estrategias: Describe diferentes formas en las que se podría implementar el patrón.

• Consecuencias: Aquí se describen las compensaciones del patrón, esta sección se enfoca en los resultados de la utilización de un patrón en particular o de su estrategia, y anota los pros y los contras que podrían resultar de la aplicación del patrón.

• Patrones Relacionados: Esta sección lista otros patrones relevantes en el catálogo de patrones J2EE u otros recursos externos, como los patrones de diseño GoF. Por cada patrón relacionado, hay una breve descripción de su relación con el patrón que se está describiendo.

Page 7: Catálogo de Patrones de Diseño J2EE

Catálogo de Patrones de Diseño J2EE. I.- Capa de Presentación Autor: Sun

Traductor: Juan Antonio Palos (Ozito)

• Intercepting Filter o Contexto o Problema: o Causas o Solución

� Estructura � Participantes y Responsabilidades � Estrategias

o Consecuencias o Patrones Relacionados

Intercepting Filter

Contexto

El mecanismo de manejo de peticiones de la capa de presentación recibe muchos tipos diferentes de peticiones, cada uno de los cuales requiere varios tipos de procesamiento. Algunas peticiones simplemente requieren su reenvio al componente manejador apropiado, mientras que otras peticiones deben ser modificadas, auditadas, o descomprimidas antes de su procesamiento posterior.

Problema:

Se requiere un pre-procesamiento y un post-procesamiento de unas peticiones o respuestas de un cliente Web.

Cuando una petición entra a una aplicación Web, normalmente debe pasar varios test de entrada antes del estado de procesamiento principal. Por ejemplo,

• ¿Se ha autentificado el cliente? • ¿Tiene el cliente una sesión válida? • ¿La dirección IP del cliente es de una red conocida? • ¿Viola alguna restricción el path de la petición? • ¿Qué codificación usa el cliente para enviar los datos? • ¿Soportamos el tipo de navegador del cliente?

Algunos de estos chequeos son tests, que resultan en una respuesta de si o no que determina si continuará el procesamiento. Otros chequeos manipulan el stream de datos entrantes a una forma aceptable para el procesamiento.

La solución básica consiste en un serie de chequeos condicionales, si cualquiera de ellos falla la petición se aborta. Las sentencias if/else anidadas son una estrategia estándar, pero esta solución tiene fragilidad de código y un estilo de programación de copiar-y-pegar, porque el flujo del filtrado y la acción de los filtros se compila dentro de la aplicación.

La clave para solventar este problema de una forma flexible y no obstrusiva es tener un mecanismo simple para añadir y eliminar componentes de procesamiento, en el que cada componente completa una acción de filtrado específica.

Causas

• Procesamiento común, como un chequeo del esquema de codificación de datos o la información de login de cada petición, completo por cada petición.

Page 8: Catálogo de Patrones de Diseño J2EE

• Se desea la centralización de la lógica común. • Se debería facilitar la adición o eliminación de sevicios sin afectar a los componentes existentes,

para que se puedan utilizar en gran variedad de combinaciones, como o Logging y autentificación. o Depuración y transformación de la salida para un cliente específico o Descomprensión y conversión del esquema de codificación de la entrada.

Solución

Crear filtros conectables para procesar servicios comunes de una forma estándar sin requerir cambios en el código principal del procesamiento de la petición. Los filtros interceptan las peticiones entrantes y las respuestas salientes, permitiendo un pre y post-procesamiento. Podemos añadir y eliminar estos filtros a discrección, sin necesitar cambios en nuestro código existente.

Podemos, en efecto, decorar nuestro procesamiento principal con una veriedad de servicios comunes, como la seguridad, el logging, el depurado, etc. Estos filtros son componentes independientes del código de la aplicación principal, y pueden añadirse o eliminarse de forma declarativa. Por ejemplo, se podría modificar un fichero de configuración de despliegue para configurar una cadena de filtros. Cuando un cliente pide un recurso que corresponde con este mapeo de URL configurado, se procesa cada filtro de la cadena antes de poder invocar el recurso objetivo.

Estructura

La siguiente figura representa el diagrama de clases del patrón Intercepting Filter.

Participantes y Responsabilidades

La siguiente figura representa el diagrama de la secuencia del patrón Intercepting Filter.

Page 9: Catálogo de Patrones de Diseño J2EE

FilterManager

El FilterManager maneja el procesamiento de filtros. Crea el FilterChain con los filtros apropiados, en el orden correcto e inicia el procesamiento.

FilterChain

El FilterChain es una collection ordenada de filtros indenpendientes.

FilterOne, FilterTwo, FilterThree

Estos son los filtros individuales que son mapeados a un objetivo. El FilterChain coordina su procesamiento.

Target

El Target es el recurso que el cliente ha solicitado.

Estrategias

Custom Filter

El filtro se implementa mediante una estrategia personalizada definida por el desarrollador. Esto es menos flexible y menos poderoso que la preferida Estrategia de Filtro Estándar que veremos en la siguiente sección y que sólo está disponible en contenedores que soporten la especificación servlet 2.3. La estrategia de filtro personalizado es menos poderosa porque no puede proporcionar una envoltura para los objetos request y response de una forma estándar y portable. Además, el objeto request no se puede modificar, y se debe introducir alguna suerte de mecanismo de buffer si los filtros son para controlar los streams de salida. Para implementar esta estrategia, el desarrollador podría utilizar el patrón Decorator

Page 10: Catálogo de Patrones de Diseño J2EE

[GoF] para envolver los filtros alrededor de la lógica principal del procesamiento de la petición. Por ejemplo, podría haber un filtro de depuración que envuelva un filtro de autentificación. Los siguientes fragmentos de código muestran como se podrían crear estos mecanismos de forma programátia:

DebuggingFilter:

public class DebuggingFilter implements Processor { private Processor target; public DebuggingFilter(Processor myTarget) { target = myTarget; } public void execute(ServletRequest req, ServletResponse res) throws IOException, ServletException { //Do some filter processing here, such as // displaying request parameters target.execute(req, res); } }

CoreProcessor:

public class CoreProcessor implements Processor { private Processor target; public CoreProcessor() { this(null); } public CoreProcessor(Processor myTarget) { target = myTarget; } public void execute(ServletRequest req, ServletResponse res) throws IOException, ServletException { //Do core processing here } }

En el controlador servlet, hemos delegado en un método llamado processRequest para manejar las peticiones entrantes:

public void processRequest(ServletRequest req, ServletResponse res) throws IOException, ServletException { Processor processors = new DebuggingFilter( new AuthenticationFilter(new CoreProcessor())); processors.execute(req, res); //Then dispatch to next resource, which is probably // the View to display dispatcher.dispatch(req, res); }

Sólo para propósitos de ejemplo, imagina que cada componente de procesamiento escribe en la salida estándar cuando se ejecuta. El siguiente ejemplo muestra la posible salida de la ejecución:

Debugging filter preprocessing completed... Authentication filter processing completed... Core processing completed... Debugging filter post-processing completed...

Se ejecuta una cadena de procesadores en orden. Cada procesador, excepto el último de la cadena, se considera un filtro. En el componente procesador final es donde se encapsula el procesamiento principal que queremos completar para cada petición. Con este diseño, necesitaremos cambiar el código de la clase CoreProcessor, así como cualquier clase de filtro, cuando querramos modificar la forma de manejar las peticiones.

La siguiente figura muestra un diagrama de secuencia que describe el flujo de control cuando se utiliza el código de filtro de los ejemplos anteriores:

Page 11: Catálogo de Patrones de Diseño J2EE

Observa que cuando usamos una implementación de Decorator, cada filtro invoca directamente al siguiente filtro, aunque usando un interface genérico. De forma alternativa, esta estrategia se puede implementar utilizando un FilterManager y un FilterChain. En este caso, estos componentes manejan el procesamiento de los filtros y los filtros individuales no se comunican con ningún otro filtro directamente. Este diseño se aproxima al de una implementación compatible con Servlet 2.3, aunque aún así es una estrategia personalizada. En el primero de los siguientes listado veremos la clase FilterManager que crea un objeto FilterChain.

public class FilterManager { public void processFilter(Filter target, javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response) throws javax.servlet.ServletException, java.io.IOException { FilterChain filterChain = new FilterChain(); // The filter manager builds the filter chain here // if necessary // Pipe request through Filter Chain filterChain.processFilter(request, response); //process target resource target.execute(request, response); } }

El FilterChain añade filtros a la cadena en el orden apropiado (por brevedad lo hemos hecho en el constructor, pero normalmente se haría en el lugar del comentario), procesa los filtros, y finalmente procesa el recurso objetivo:

public class FilterChain { // filter chain private Vector myFilters = new Vector(); // Creates new FilterChain public FilterChain() { // plug-in default filter services for example // only. This would typically be done in the // FilterManager, but is done here for example // purposes addFilter(new DebugFilter()); addFilter(new LoginFilter()); addFilter(new AuditFilter()); } public void processFilter( javax.servlet.http.HttpServletRequest request,

Page 12: Catálogo de Patrones de Diseño J2EE

javax.servlet.http.HttpServletResponse response) throws javax.servlet.ServletException, java.io.IOException { Filter filter; // apply filters Iterator filters = myFilters.iterator(); while (filters.hasNext()) { filter = (Filter)filters.next(); // pass request & response through various // filters filter.execute(request, response); } } public void addFilter(Filter filter) { myFilters.add(filter); } }

En la siguiente figura podemos ver el diagrama de secuencia de este código:

Esta estrategia no nos permite crear filtros que sean tan flexibles y poderosos como nos gustaría. Por una cosa, los filtros se añade y eliminan programáticamente. Aunque podríamos escribir un mecanismo propietario para manejar la adición y eliminación de filtros mediante un fichero de configuración, aún no tendríamos ninguna forma de envolver los objetos request y response. Además, sin un mecanismo de buffer sofisticado, esta estrategia no proporcionará post-procesamiento flexible.

La Estrategia de Filtro Estándar proporciona soluciones para estos problemas, utilizando características de la especificación Servlet 2.3, que proporciona una solución estándar al dilema de los filtros.

Standard Filter

Los filtros se controlan de forma declarativa utilizando un descriptor de despliegue, según se describe en la especificación Servlet 2.3. Esta especificación incluye un mecanismo estándar para construir cadenas de filtos y poder añadir y eliminar filtros de esa cadena de forma transparente. Los filtros se contruyen sobre interfaces, y se añaden o eliminan de una forma declarativa modificando el descriptor de despliegue de una aplicación Web.

Nuestro ejemplo para esta estrategia será crear un filtro que preprocese peticiones de cualquier tipo de codificación como las que podríamos manejar en el código principal de manejo de peticiones. ¿Por qué podría ser esto necesario? Los formularios HTML que incluyen un upload de ficheros utilizan un tipo de codificación diferente a la mayoría de formularios. Así, los datos del formulario que acompañan al upload

Page 13: Catálogo de Patrones de Diseño J2EE

no están disponibles mediante simples llamadas a getParameter(). Por eso, creamos dos filtros que preprocesen las peticiones, traduciendo todos los tipos de codificación en un sólo formato consistente. El formato que elegimos es hacer que todos los datos del formulario estén disponibles como atributos de la petición.

Un fitlro maneja la forma de codificación estándar para el tipo application/x-www-form-urlencoded y el otro maneja el tipo de codificación menos común multipart/form-data, que se utiliza en los formularios que incluyen uploads. Los filtros traducen todos los datos del formulario en atributos de la petición, para que el mecanismo principal de manejo de peticiones pueda trabajar con todas las peticiones de la misma manera. en lugar de hacerlo con los casos especiales de las diferentes codificaciones.

El siguiente ejemplo muestra un filtro que traduce las peticiones utilizando el esquema de codificación de formularios estándar:

public class StandardEncodeFilter extends BaseEncodeFilter { // Creates new StandardEncodeFilter public StandardEncodeFilter() { } public void doFilter(javax.servlet.ServletRequest servletRequest,javax.servlet.ServletResponse servletResponse,javax.servlet.FilterChain filterChain) throws java.io.IOException, javax.servlet.ServletException { String contentType = servletRequest.getContentType(); if ((contentType == null) || contentType.equalsIgnoreCase( "application/x-www-form-urlencoded")) { translateParamsToAttributes(servletRequest, servletResponse); } filterChain.doFilter(servletRequest, servletResponse); } private void translateParamsToAttributes( ServletRequest request, ServletResponse response) { Enumeration paramNames = request.getParameterNames(); while (paramNames.hasMoreElements()) { String paramName = (String) paramNames.nextElement(); String [] values; values = request.getParameterValues(paramName); System.err.println("paramName = " + paramName); if (values.length == 1) request.setAttribute(paramName, values[0]); else request.setAttribute(paramName, values); } } }

El ejemplo siguiente muestra el filtro que maneja las traduciones de las peticiones que utilizan el esquema de codificación multipart.:

public class MultipartEncodeFilter extends BaseEncodeFilter { public MultipartEncodeFilter() { } public void doFilter(javax.servlet.ServletRequest servletRequest, javax.servlet.ServletResponse servletResponse,javax.servlet.FilterChain filterChain) throws java.io.IOException, javax.servlet.ServletException { String contentType = servletRequest.getContentType(); // Only filter this request if it is multipart // encoding if (contentType.startsWith( "multipart/form-data")){

Page 14: Catálogo de Patrones de Diseño J2EE

try { String uploadFolder = getFilterConfig().getInitParameter( "UploadFolder"); if (uploadFolder == null) uploadFolder = "."; /** The MultipartRequest class is: * Copyright (C) 2001 by Jason Hunter * <[email protected]>. All rights reserved. **/ MultipartRequest multi = new MultipartRequest(servletRequest, uploadFolder, 1 * 1024 * 1024 ); Enumeration params = multi.getParameterNames(); while (params.hasMoreElements()) { String name = (String)params.nextElement(); String value = multi.getParameter(name); servletRequest.setAttribute(name, value); } Enumeration files = multi.getFileNames(); while (files.hasMoreElements()) { String name = (String)files.nextElement(); String filename = multi.getFilesystemName(name); String type = multi.getContentType(name); File f = multi.getFile(name); // At this point, do something with the // file, as necessary } } catch (IOException e) { LogManager.logMessage( "error reading or saving file"+ e); } } // end if filterChain.doFilter(servletRequest, servletResponse); } // end method doFilter() }

El código de estos filtros se basa en la especificación servlet 2.3. También se usa un filtro base, desde el que descienden estos dos filtros. El filtro base mostrado en el siguiente ejemplo proporciona el comportamiento por defecto para los métodos de retrollamada del filtro estándar:

public class BaseEncodeFilter implements javax.servlet.Filter { private javax.servlet.FilterConfig myFilterConfig; public BaseEncodeFilter() { } public void doFilter( javax.servlet.ServletRequest servletRequest, javax.servlet.ServletResponse servletResponse, javax.servlet.FilterChain filterChain) throws java.io.IOException, javax.servlet.ServletException { filterChain.doFilter(servletRequest, servletResponse); } public javax.servlet.FilterConfig getFilterConfig() { return myFilterConfig; } public void setFilterConfig( javax.servlet.FilterConfig filterConfig) { myFilterConfig = filterConfig; } }

Abajo tenemos un extracto del descriptor de despliegue de la aplicación Web que contiene este ejemplo. Muestra cómo se registran estos dos filtros y luego los mapea a un recurso, en este caso un sencillo servlet de prueba.

. . .

Page 15: Catálogo de Patrones de Diseño J2EE

<filter> <filter-name>StandardEncodeFilter</filter-name> <display-name>StandardEncodeFilter</display-name> <description></description> <filter-class> corepatterns.filters.encodefilter. StandardEncodeFilter</filter-class> </filter> <filter> <filter-name>MultipartEncodeFilter</filter-name> <display-name>MultipartEncodeFilter</display-name> <description></description> <filter-class>corepatterns.filters.encodefilter. MultipartEncodeFilter</filter-class> <init-param> <param-name>UploadFolder</param-name> <param-value>/home/files</param-value> </init-param> </filter> . . . <filter-mapping> <filter-name>StandardEncodeFilter</filter-name> <url-pattern>/EncodeTestServlet</url-pattern> </filter-mapping> <filter-mapping> <filter-name>MultipartEncodeFilter</filter-name> <url-pattern>/EncodeTestServlet</url-pattern> </filter-mapping> . . .

En la siguiente figura podemos ver el diagrama de secuencia de este ejemplo:

Los filtros StandardEncodeFilter y MultiPartEncodeFilter interceptan el control cuando un cliente hace una petición al servlet controlador. El contenedor acepta el rol de manejador de filtros y conduce el control a estos filtros llamando a sus métodos doFilter. Después de completar su procesamiento, cada filtro pasa el control al FilterChain que lo contiene, que está instruido para ejecutar el siguiente filtro. Una vez que el control ha pasado por los dos filtros, el siguiente componente en recibir el control es el recurso objetivo real, en este caso el servlet controlador.

Los filtros, según lo soporta la especificación Servlet 2.3, también soportan la envoltura de los objetos request y response. Esta característica proporciona un mecanismo mucho más podereos que el que se puede construir utilizando la implementación personlizada de la sección anterior. Por supuesto, también podríamos construir una aproximación híbrida combinando las dos estrategias.

Base Filter

Page 16: Catálogo de Patrones de Diseño J2EE

Un filtro base sirve como una superclase común para todos los filtros. Las características comunes se pueden encapsular en el filtro base y se pueden compartir entre todos los filtros. Por ejemplo, un filtro base es un buen lugar para incluir el comportamiento por defecto de los métodos de retrollamada del contenedor, como hemos visto en la sección anterior. El siguiente fragmento de código nos muestra cómo hacer esto:

public class BaseEncodeFilter implements javax.servlet.Filter { private javax.servlet.FilterConfig myFilterConfig; public BaseEncodeFilter() { } public void doFilter(javax.servlet.ServletRequest servletRequest,javax.servlet.ServletResponse servletResponse, javax.servlet.FilterChain filterChain) throws java.io.IOException, javax.servlet.ServletException { filterChain.doFilter(servletRequest, servletResponse); } public javax.servlet.FilterConfig getFilterConfig() { return myFilterConfig; } public void setFilterConfig(javax.servlet.FilterConfig filterConfig) { myFilterConfig = filterConfig; } }

Template Filter

Usar un filtro base del que descienden todos los demás permite a la clase base proporcionar la funcionalidad de Plantilla de Métodos [GoF]. En este caso, el filtro base se utiliza para dictar los pasos generales que debe completar cada filtro, aunque deja las especifidades de cómo completar esos pasos en la subclase de cada filtro. Normalmente, esto se definiría de forma burda, métodos básicos que simplemente imponen una estructura limitada a cada plantilla. Esta estrategia también se puede combinar con cualquier otra estrategia de filtros. Los siguientes listados muestran como utilizar esta estrategia con la Estrategia de Filtro Declarado:

public abstract class TemplateFilter implements javax.servlet.Filter { private FilterConfig filterConfig; public void setFilterConfig(FilterConfig fc) { filterConfig=fc; } public FilterConfig getFilterConfig() { return filterConfig; } public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { // Common processing for all filters can go here doPreProcessing(request, response, chain); // Common processing for all filters can go here doMainProcessing(request, response, chain); // Common processing for all filters can go here doPostProcessing(request, response, chain); // Common processing for all filters can go here // Pass control to the next filter in the chain or // to the target resource chain.doFilter(request, response); } public void doPreProcessing(ServletRequest request, ServletResponse response, FilterChain chain) { } public void doPostProcessing(ServletRequest request, ServletResponse response, FilterChain chain) { }

Page 17: Catálogo de Patrones de Diseño J2EE

public abstract void doMainProcessing(ServletRequest request, ServletResponse response, FilterChain chain); }

Dando esta definición de clase para TemplateFilter, cada filtro se implementa como una subclase que sólo debe implementar el método doMainProcessing. Estas subclases tienen la opción de implementar los otros tres métodos si lo desean. Abajo tenemos un ejemplo de una subclase que implementa el método obligatorio (dictado por nuestra plantilla de filtro) y el método de preprocesamiento opcional.

public class DebuggingFilter extends TemplateFilter { public void doPreProcessing(ServletRequest req, ServletResponse res, FilterChain chain) { //do some preprocessing here } public void doMainProcessing(ServletRequest req, ServletResponse res, FilterChain chain) { //do the main processing; } }

En la siguiente figura podemos ver el diagrama de secuencia de esta estrategia:

En el diagrama de secuencia, las subclases, como DebuggingFilter, definen el procesamiento sobreescribiendo el método abstracto doMainProcessing, y, opcionalmente, doPreProcessing y doPostProcessing. Así, la plantilla de filtro impone una estructura para el procesamiento de todos los filtros, así como proporciona un lugar para encapsular el código que es común para cada filtro.

Consecuencias

• Centraliza el Control con Controladores de Acoplamiento Ligero Los filtros proporcionan un lugar centralaizado para controlar el procesamiento a través de múltiples peticiones, como lo hace el controlador. Los filtros están mejor preparados para manejar las peticiones y respuestas para el control último por un recurso objetivo, como un controlador. Además, un controlador frecuentemente junta el control de numerosos sevicios comunes y no relacionados, como la autentificación, el login, la encriptación, etc., mientras que los filtros nos permiten controladores de acoplamiento más ligero, que se pueden combinar.

• Mejora la Reutilización Los filtros promueven la limpieza del particionamiento de la aplicación y aconsejan su reutilización. Estos interceptores conectables se añaden y eliminan al código existente de forma

Page 18: Catálogo de Patrones de Diseño J2EE

transparente y debido a su interface estándar, funcionan en cualquier combinación y son reutilizables por varias presentaciones.

• Configuración Declarativa y Flexible Se pueden combinar numerosos servicios en varias permutaciones sin tener que recompilar ni una sola vez el código fuente.

• La Compartición Información es Ineficiente Compartir información entre filtros puede ser ineficiente, porque por definición todo filtro tiene acoplamiento ligero. Si se deben compartir grandes cantidades de información entre los filtros, esta aproximación podría ser muy costosa.

Patrones Relacionados

• Front Controller El controlador resuelve algunos problemas similares, pero está mejor diseñado para manejar el procesamiento principal.

• Decorator [GoF] El patrón Intercepting Filter está relacionado con el patrón Decorator, que proporciona envolturas conectables dinámicamente.

• Template Method [GoF] El patrón de Plantillas de Métodos se utiliza para implementar la Estrategia de Plantilla de Filtros.

• Interceptor [POSA2] El patrón Intercepting Filter está relacionado con el patron Interceptor, que permite que se pueden añadir servicios de forma transparente y dispararlos automáticamente.

• Pipes and Filters [POSA1] El patrón Intercepting Filter está relacionado con el patrón Pipes and Filters.

Page 19: Catálogo de Patrones de Diseño J2EE

Catálogo de Patrones de Diseño J2EE. I.- Capa de Presentación Autor: Sun

Traductor: Juan Antonio Palos (Ozito)

• Front Controller o Contexto o Problema o Causas o Solución

� Estructura � Participantes y Responsabilidades � Estrategias

o Consecuencias o Patrones Relacionados

Front Controller

Contexto

El mecanismo de manejo de peticiones de la capa de presentación debe controlar y coordinar el procesamiento de todos los usuarios a través de varias peticiones. Dichos mecanismos de control se pueden manejar de una forma centralizada o descentralizada.

Problema

El sistema requiere un punto de acceso centralizado para que el manejo de peticiones de la capa de presentación soporte la integración de los servicios del sistema, recuperación de contenidos, control de vistas, y navegación. Cuando el usuario accede a la vista directamente sin pasar un mecanismo centralizado, podrían ocurrir dos problemas:

• Se requiere que cada vista proporcione sus propios servicios del sistema, lo que normalmente resulta en duplicación de código.

• La vista de navegación se deja a los visores. Esto podría resultar en una mezcla de contenidos y navegación.

Además, el control distribuido es más difícil de mantener, ya que los cambios se tienen que realizar en numerosos lugares.

Causas

• El procesamiento de servicios del sistema comunes se completa por cada petición. Por ejemplo, el servicio de seguridad completa los chequeos de autentificación y autorización.

• La lógica se maneja mejor en una localización central en lugar de estar replicada dentro de varias vistas.

• Existen puntos de decisión con respecto a la recuperación y manipulación de los datos. • Se utilizan varias vistas para responder a peticiones de negocio similares. • Puede ser muy útil un punto de contacto centralizado para manejar una petición, por ejemplo,

para controlar y grabar el camino de un usuario por la site. • Los servicios del sistema y la lógica de control de vistas son relativamente sofisticados.

Solución

Usar un controlador como el punto inicial de contacto para manejar las peticiones. El controlador maneja el control de peticiones, incluyendo la invocación de los servicios de seguridad como la autentificación y autorización, delegar el procesamiento de negocio, controlar la elección de una

Page 20: Catálogo de Patrones de Diseño J2EE

vista apropiada, el manejo de errores, y el control de la selección de estrategias de creación de contenido.

El controlador proporciona un punto de entrada centralizado que controla y maneja las peticiones Web. Centralizando los puntos de decisión y control, el controlador también ayuda a reducir la cantidad de código Java, llamadas a scriptles, embebidos en la página JavaServer Pages (JSP).

Centralizar el control en el controlador y reduciendo la lógica de negocios en la vista permite reutilizar el código entre peticiones. Es una aproximación preferible a la alternativa de embeber código en varias vistas porque esta aproximación trata con entornos más propensos a errores, y de reutilización del tipo copiar-y-pegar.

Típicamente, un controlador se coordina con un componente dispatcher. Los dispatchers son responsable del control de la vista y de la navegación. Así, un dispatcher elige la siguiente vista por el usuario y dirige el control al recurso. Los dispatchers podrían encapsularse directametne dentro del controlador o se puede extraer en un componente separado.

Aunque el patrón Front Controller sugiere la centralización del manejo de peticiones, no limita el número de manejadores en el sistema, como lo hace Singleton. Una aplicación podría utilizar varios controladores en un sistema, cada uno mapeado a un conjunto de servicios distintos.

Estructura

La siguiente figura representa el diagrama de clases del patrón Front Controller.

Participantes y Responsabilidades

La siguiente figura representa el diagrama de la secuencia del patrón Front Controller. Muestra como el controlador maneja una petición:

Page 21: Catálogo de Patrones de Diseño J2EE

Controller

El controlador es el punto de contacto inicial para manejar todas las peticiones en el sistema. El controlador podría delegar a un helper para completar la autentificación y la autorización de un usuario o para iniciar la recuperación de un contacto.

Dispatcher

Un dispatcher es el responsable del manejo de la vista y de la navegación, controlando la elección de la siguiente vista que se le presentará al usuario, y proporcionando el mecanismo para dirigir el control a ese recurso.

Un dispatcher se puede encapsular dentro de un controlador o se puede separar en otro componente que trabaja de forma coordinada. El dispatcher puede proporcionar un re-envío estático de la vista o un mecanismo de re-envío más sofisticado.

El dispatcher utiliza un objeto RequestDispatcher (soportado en la especificación Servlet) y encapsula algún procesamiento adicional.

Helper

Un helper es el responsable de ayudar a la vista o al controlador a completar su procesamiento. Así, los helpers tienen muchas responsabilidades, incluyendo la recopilación de los datos requeridos por la vista y el almacenamiento en el modelo intermedio, en cuyo caso algunas veces nos podemos referir al helper como un bean de valor. Además, los helpers pueden adaptar este modelo de datos para usarlo en la vista.

Una vista podría trabajar con cualquier número de helpers, que normalmente son componentes JavaBeans (JSP 1.0+) y etiquetas personalizadas (JSP 1.1+). Además, un helper podría representar un objeto Command o un Transformador XSL, que se utiliza en combinación con una hoja de estilos para adaptar y convertir el modelo a la forma apropiada.

Page 22: Catálogo de Patrones de Diseño J2EE

View

Una Vista representa y muestra información al cliente. La vista recupera información desde el modelo. Los helpers soportan las diferentes vistas encapsulando y adaptanto el modelo de datos subyacente para usarlo en el display.

Estrategias

Hay varias estrategias para implementar un controlador.

Servlet Front

La estrategia de Servlet Frontal sugiere la implementación del controlador como un servlet. Aunque semánticamente equivalente, es mejor que la Estrategia de JSP Frontal. El controlador maneja los aspectos del manejo de peticiones que están relacionados con el procesamiento de negocio y el control de flujo. Estas responsabilidades están relacionadas con, pero son lógicamente independientes, del formateo de display, y es más apropiado encapsularlas en un servlet en lugar de en una página JSP.

Esta estrategia tiene algunos potenciales inconvenientes. En particular, no permite utilizar algunas de las utilidadess del entorno de ejecución JSP, como es el relleno automático de parámetros de la peticion. Afortunadamente, este inconveniente es mínimo porque es relativamente fácil crear u obtener utilidades similares para su uso general. Abajo podemos ver un ejemplo de la Estrategia Servlet Front:

public class EmployeeController extends HttpServlet { // Initializes the servlet. public void init(ServletConfig config) throws ServletException { super.init(config); } // Destroys the servlet. public void destroy() { } /** Processes requests for both HTTP * <code>GET</code> and <code>POST</code> methods. * @param request servlet request * @param response servlet response */ protected void processRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, java.io.IOException { String page; /**ApplicationResources provides a simple API * for retrieving constants and other * preconfigured values**/ ApplicationResources resource = ApplicationResources.getInstance(); try { // Use a helper object to gather parameter // specific information. RequestHelper helper = new RequestHelper(request); Command cmdHelper= helper.getCommand(); // Command helper perform custom operation page = cmdHelper.execute(request, response); } catch (Exception e) { LogManager.logMessage( "EmployeeController:exception : " + e.getMessage()); request.setAttribute(resource.getMessageAttr(), "Exception occurred : " + e.getMessage()); page = resource.getErrorPage(e); } // dispatch control to view dispatch(request, response, page); } /** Handles the HTTP <code>GET</code> method.

Page 23: Catálogo de Patrones de Diseño J2EE

* @param request servlet request * @param response servlet response */ protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, java.io.IOException { processRequest(request, response); } /** Handles the HTTP <code>POST</code> method. * @param request servlet request * @param response servlet response */ protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, java.io.IOException { processRequest(request, response); } /** Returns a short description of the servlet */ public String getServletInfo() { return "Front Controller Pattern" + " Servlet Front Strategy Example"; } protected void dispatch(HttpServletRequest request, HttpServletResponse response, String page) throws javax.servlet.ServletException, java.io.IOException { RequestDispatcher dispatcher = getServletContext().getRequestDispatcher(page); dispatcher.forward(request, response); } }

JSP Front

La estrategia de JSP Frontal sugiere la implementación del controlador como una página JSP. Aunque semánticamente equivalente, es mejor utilizar la Estrategia de Servlet Frontal. Como el controlador maneja el procesamiento que no está especificamente relacionado con el formateo de la salida, no tiene sentido implementar este componente como una página JSP.

La implementación del controlador como una página JSP tiene otra razón para no ser la preferida: Requiere que un desarrollador de software trabaje con una página de etiquetas para poder modificar la lógica del control de peticiones. El siguiente listado es un ejemplo de esta estrategia:

<%@page contentType="text/html"%> <%@ page import="corepatterns.util.*" %> <html> <head><title>JSP Front Controller</title></head> <body> <h3><center> Employee Profile </h3> <% /**Control logic goes here... At some point in this code block we retrieve employee information, encapsulate it within a value object and place this bean in request scope with the key "employee". This code has been omitted. We either dispatch to another JSP at this point or simply allow the remaining portions of scriptlet code to execute**/ %> <jsp:useBean id="employee" scope="request" class="corepatterns.util.EmployeeTO"/> <FORM method=POST > <table width="60%"> <tr> <td> First Name : </td> <td> <input type="text" name="<%=Constants.FLD_FIRSTNAME%>" value="<jsp:getProperty name="employee" property="firstName"/>"> </td> </tr> <tr> <td> Last Name : </td> <td> <input type="text"

Page 24: Catálogo de Patrones de Diseño J2EE

name="<%=Constants.FLD_LASTNAME%>" value="<jsp:getProperty name="employee" property="lastName"/>"></td> </tr> <tr> <td> Employee ID : </td> <td> <input type="text" name="<%=Constants.FLD_EMPID%>" value="<jsp:getProperty name="employee" property="id"/>"> </td> </tr> <tr> <td> <input type="submit" name="employee_profile"> </td> <td> </td> </tr> </table> </FORM> </body> </html>

Command and Controller

Basada en el patrón Commando de [GoF], la estrategia de Commando y Controlador sugiere proporcionar un interface genérico para los componentes helper en los que el controlador podría delegar responsabilidades, minimizando el acoplamiento entre estos componentes. Añadir o modificar el trabajo que necesitan realizar estos helpers no requiere ningún cambio en el interface entre el controlador y ellos, sino en el tipo y/o contenido de los comandos. Esto proporciona un mecanismo flexible y fácilmente extensible para que los desarrolladores puedan añadir comportamientos al manejo de peticiones.

Finalmente, como el procesamiento de comandos no está acoplado a la invocación de comandos, el mecanismo de procesamiento de comandos se podría reutilizar para varios tipos de clientes, no sólo con los navegadores Web. Esta estrategia también facilita la creación de comandos compuestos. Aquí tenemos un ejemplo de esta estrategia:

/** This processRequest method is invoked from both * the servlet doGet and doPost methods **/ protected void processRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, java.io.IOException { String resultPage; try { RequestHelper helper = new RequestHelper(request); /** the getCommand() method internally uses a factory to retrieve command objects as follows: Command command = CommandFactory.create( request.getParameter("op")); **/ Command command = helper.getCommand(); // delegate request to a command object helper resultPage = command.execute(request, response); } catch (Exception e) { LogManager.logMessage("EmployeeController", e.getMessage() ); resultPage = ApplicationResources.getInstance(). getErrorPage(e); } dispatch(request, response, resultPage); }

En la siguiente figura podemos ver el diagrama de secuencia de la estrategia Command and Controller.

Page 25: Catálogo de Patrones de Diseño J2EE

Phisical Resource Mappping

En la estrategia de Mapeo Físico de Recursos todas las peticiones se hacen sobre nombres de recursos físicos especificos en lugar de sobre nombres lógicos. Un ejemplo es la siguiente URL: http://some.server.com/resource1.jsp. En el caso de un controlador, un ejemplo de URL podría ser: http://some.server.com/servlet/Controller. Es preferible utilizar la estrategia de Mapeo Lógico de Recursos porque proporciona una mayor flexibilidad, porque nos permite mapear los recuros de una forma declarativa.

Logical Resource Mapping

En la estrategia de Mapeo de Recurso Lógico todas las peticiones se hacen sobre nombres de recursos lógicos en vez de sobre nombres físicos específicos. Los recursos físicos a los que se refieren estos nombres lógicos podrían modificarse de una forma declarativa.

Por ejemplo, la URL http://some.server.com/process podría mapearse como:

process=resource1.jsp

o como:

process=resource2.jsp

o como:

process=servletController

Multiplexed Resource Mapping

La estrategia de Mapeo Multiplexado de Recursos realmente es una estrategia de Mapeo Lógico de Recursos. Esta estrategia no mapea sólo a un nombre lógico, sino a un conjunto completo de nombres lógicos, para un sólo recurso físico. Por ejemplo, un mapeo de comodín podría mapear todas las peticiones que terminen en .ctrl a un controlador específico, como podermos ver en la siguiente tabla:

Page 26: Catálogo de Patrones de Diseño J2EE

Petición> Mapeo http://some.server.com/action.ctrl *.ctrl = servletController

De hecho, es la estragegia que utilizan los motores de páginas JSP para asegurarse de que las peticiones de recursos de páginas JSP (es decir, recursos cuyos nombres terminan en .jsp) las procesa el manejador apropiado.

Se pueden añadir información adicional a una peticion, proporcionando mayores detalles para este mapeo lógico, como se puede ver en la siguiente tabla:

Petición> Mapeo http://some.server.com/profile.ctrl?usecase= create *.ctrl = servletController

Un beneficio importante de la utilización de esta estrategia es que proporciona una enorme flexibilidad cuando diseñanos nuestros componentes de manejo de peticiones. Cuando se combina con las otras estrategias, como con la estrategia Command and Controller, se pueden crear un potente marco de trabajo para manejar peticiones.

Consideremos un controlador que maneja todas las peticiones que terminan en .ctrl, como hemos visto antes. Consideremos también la parte izquierda del nombre de recursos delimitado por puntos (profile en el ejemplo de arriba) para que sea una parte del nombre de un ejemplo. Ahora combinamos este nombre con el valor de parámetro de la consulta (creado en el ejemplo de arriba). Estamos diciendo a nuestos manejador de peticiones que queremos procesar un ejemplo llamado crear perfil. Nuestro mapeo de recursos multiplexado envía la petición a nuestro servletController, que es parte del mapeo mostrado en la tabla anterior. Nuestro controlador crea el objeto command apropiado, según describimos en la Estrategia Command and Controller. ¿Cómo sabe el controlador en qué objeto command debería delegar? Utilizando la información adicional de la URI de la petición, el controlador delega en el objeto command que maneja la creación de perfiles. Esté podría ser un objeto ProfileCommand que sirve peticiones para la creación y modificación de perfiles, o podría ser un objeto más específico como ProfileCreationCommand.

Dispatcher in Controller

Cuando la funcionalidad del dispatcher es mínima, se puede plegar junto al controlador, como se muestra en la siguiente figura:

Page 27: Catálogo de Patrones de Diseño J2EE

Base Front

La estrategia Base Front utilizada en combinación con la estrategia Servlet Front, sugiere la implementación de una clase base controladora, cuya implementación deben extender otros controladoes. La clase Base Front podría contener implementaciones comunes y por defecto, aunque las subclases puedan sobreescribir estas implementaciones. El inconviente de esta estrategia es el hecho de que un superclase compartida, aunque promueve la reutilización y la compartición, tiene el problema de crear un herencia frágil, donde los cambios necesarios para una subclase afectan a todas las demás subclases.

Filter Controller

Los filtros proporcionan un soporte similar para la centralización de manejo de peticiones. Así, algunos aspectos de un controlador se pueden implementar de forma razonable como un filtro. Al mismo tiempo, los filtros se enfocan principalmente en la intercepción y decoración de peticiones, no en el procesamiento y en la generación de respuestas. Aunque hay un solapamietno de responsabilidades, como en el manejo del log y la depuración, cada componente complementa a los otros cuando se utilizan de la forma apropiada.

Consecuencias

• Centraliza el Control Un controlador proporciona un lugar central para manejar los servicios del sistema y la lógica de negocios entre varias peticiones. Un controlador maneja el procesamiento de la lógica de negocio y el manejo de peticiones. El acceso centralizado a una aplicación significa que las peticiones se pueden seguir y guardar muy fácilmente. Debemos tener en mente, que como controles centralizados, es posible presentar un sólo punto de fallo. En la práctica, esto ráramente es un problema, ya que típicamente existe múltiples controladores, bien dentro de un sólo servidor o en un cluster.

• Mejora la Manejabilidad de la Seguridad Un controlador centraliza el control, propocionando un punto de choque para intentos de accesos ilícitos en la aplicación Web. Además, auditar una sola entrada en la aplicación requiere menos recursos que distribuir los chequeos de seguridad entre todas las páginas.

• Mejora la Reutilizabilidad Un controlador promueve el particionamiento limpio de la aplicación y aconseja la reutilización, ya que el código que es común entre los componentes se mueve dentro de un controlador o es manejado por un controlador.

Patrones Relacionados

• View Helper El patrón Front Controller, en conjunción con el patrón View Helper, describe la creación de lógica de negocio de la vista y proporciona un punto central de control y reenvio. El flujo lógico se construye dentro del controlador y el código de manejo de datos se mueve hacia los helpers.

• Intercepting Filter Tanto Intercepting Filter como Front Controller describen formas de centralizar el control de ciertos tipos de procesamiento de peticiones, sugiriendo diferentes aproximaciones a este problema.

• Dispatcher View y Service to Worker Los patrones Dispatcher View y Service to Worker son otras forma de nombrar la combinación entre el patrón View Helper con un dispatcher, y un patrón Front Controller. Dispatcher View y Service to Worker, aunque estructuralmente son iguales, describen diferentes divisiones de labores entre los componentes.

Page 28: Catálogo de Patrones de Diseño J2EE

Catálogo de Patrones de Diseño J2EE. I.- Capa de Presentación Autor: Sun

Traductor: Juan Antonio Palos (Ozito)

• View Helper o Contexto o Problema o Causas o Solución

� Estructura � Participantes y Responsabilidades � Estrategias

o Consecuencias o Patrones Relacionados

View Helper

Contexto

El sistema crea el contenido de la presentación, lo que requiere el procesamiento de datos de negocio dinámicos.

Problema

Los cambios en la capa de presentación ocurren muy frecuentemente y son difíciles de desarrollar y mantener cuando la lógica de acceso a los datos de negocio y la lógica del formateo de la presentación se mezclan. Esto hace el sistema menos flexible, menos reutilizable, y generalmente menos adaptable a los cambios.

Mezclar la lógica de negocio y de sistema con el procesamiento de la vista reduce la modularidad y también proporciona una pobre separación de los roles entre los equipos de producción Web y de desarrollo de software.

Causas

• Los requerimientos de asimilación de datos de negocio no son triviales. o Embeber la lógica de negocio en la vista promueve un tipo de reutilización del tipo

copiar-y-pegar. Esto causa problemas de mantenimiento y errores porque una pieza de lógica se reutiliza en la misma vista o en otra diferente simplemente duplicándola en la nueva localización.

o Es deseable promover una separación limpia de labores teniendo diferentes individuos cumpliendo los roles de miembros de un equipo de desarrollo de software y de producción Web.

o Una vista comúnmente se utiliza para responder a una petición de negocios particular.

Solución

Una vista contiene código de formateo, delegando sus responsabilidades de procesamiento en sus clases de ayuda, implementadas como JavaBeans o etiquetas personalizadas. Las clases de ayuda o helpers también almacenan el modelo de datos intermedio de la vista y sirven como adaptadores de datos de negocio.

Hay varias estrategias para implementar el componente de la vista. La estrategia JSP View siguiere utilizar una JSP como el componente vista. Esta es la estrategia preferida, y es la que se utiliza más comunmente. La otra estrategia principal es la estrategia Servlet View, que utiliza un servlet como la vista.

Page 29: Catálogo de Patrones de Diseño J2EE

Encapsular la lógica de negocio en un helper en lugar de hacerlo en la vista hace que nuestra aplicación sea más modular y facilita la reutilización de componentes. Varios clientes, como controladores y vistas, podrían utilizar el mismo helper para recuperar y adaptar estados del modelo similares para su presentación en varias vistas. La única forma de reutilizar la lógica embebida en una vista es copiando y pegando en cualquier lugar. Además, la duplicación mediante copiar-y-pegar hace que un sistema sea difícil de mantener, ya que necesitamos corregir en muchos sitios el mismo error potencial.

Una señal de que podríamos necesitar aplicar este patrón al código existente es cuando el código scriptlet domina en la vista JSP. Ya que el objetivo general cuando se aplica este patron, es el particionamiento de la lógica de negocio fuera de la vista. Mientras alguna lógica está mejor cuando se encapsula dentro de objetos helper, otra lógica está mejor situándola en un componente centralizado que se sitúa delante de las vistas y los helpers -- esta podría la lógica que sea común entre varias peticiones, como los chequeos de autentificación o servicios de logs, por ejemplo.

Si no se emplea un controlador separado en la arquitectura, o si no se utiliza para manejar todas las peticiones, entonces el componente vista se convierte en el punto de contacto inicial para manejar algunas peticiones. Para ciertas peticiones, particularmente aquellas que implican un procesamiento mínimo, este escenario funcionará bien. Típicamente, esta situación ocurre cuando páginas que están basadas en información estática, como la primera de un conjunto de páginas que se servirán a un usuario para obtener alguna información. .

El patrón View Helper se enfoca en recomendar formas de particionar las responsabilidades de nuestras aplicaciones.

Estructura

Abajo podemos observar el diagrama de clases que representa el patrón View Helper.

Participantes y Responsabilidades

La siguiente figura muestra el diagrama de secuencia que represetna el patrón View Helper. Normalmente un controlador media entre el cliente y la vista. Aunque en algunos casos, no se utiliza un controlador y la vista se convierte en el punto de contacto inicial para manejar peticiones.

Page 30: Catálogo de Patrones de Diseño J2EE

Como se ha podido observar en el diagrama de clases, podrían no haber helpers asociados con una vista. En este caso simple, la página podría ser completamente estática o incluir una muy pequeña cantidad de scriptles. Este escenario se describe en el siguiente diagrama de secuencia:

View

Una vista representa y muestra información al cliente. La información que se utiliza en un display dinámico se recupera de un modelo. Los helpers soportan vistas encapsulando y adaptando un modelo para utilizarlo en un display.

Helper

Un helper es el responsable de ayudar a la vista o al controlador a completar su procesamiento. Así los helpers tienen numerosas responsabilidades, incluyendo la obtención de los datos requeridos por la vista y su almacenamiento en el modelo intermedio, en cuyo caso algunas veces nos podemos referir al helper como un Bean de Valor. Además, los helpers podrían adaptar este modelo de datos para que los utilizara la vista. Los helpers pueden servir peticiones de datos desde la vista simplemente proporcionando acceso a los datos o fomateando los datos como contenido Web.

Una vista podría trabajar con cualquier número de helpers, que normalmente están implementados como JavaBeans (JSP 1.0+) y etiquetas personalizadas (JSP 1.1+). Además, un helper podría representar un objeto Command o un Tranformador XSL, que se utiliza en combinación con una hoja de estilo para adaptar y convertir el modelo en el formato apropiado.

ValueBean

Un Bean de Valor es un otro nombre para un helper que es responsable de contener el estado del modelo intermedio para que lo utilice una vista. Un caso típico, es el que tiene un servicio de negocio que devuelve un bean de valor en respuesta a esta petición. Este caso, el Bean de valor cumple el rol de un objeto Transfer.

BusinessService

El servicio de negocio es un rol que cumple el servicio al que está accediendo el cliente. Típicamente, se accede al servicio de negocio mediante un Delegado de Negocio. El rol del delegado de negocio es proporcionar control y protección para el servicio de negocio (podremos ver el patrón Business Delegate más adelante).

Estrategias

Page 31: Catálogo de Patrones de Diseño J2EE

JSP View

La estrategia de Vista JSP sugiere la utilización de una JSP como el componente vista. Aunque semánticamente equivalente a la estrategia de Vista Servlet, es una solución más elegante y más utilizada. Las vistas son el dominio de los diseñadores Web, que prefieren un lenguaje de marcas al código Java. El siguiente ejemplo muestra un ejemplo de código para esta estrategia. El fragmento es de un fichero fuente llamado welcome.jsp, al que nos reenvia un controlador servlet después de situar el JavaBean WelcomeHelper en el ámbito de la peticion:

<jsp:useBean id="welcomeHelper" scope="request" class="corepatterns.util.WelcomeHelper" /> <HTML> <BODY bgcolor="FFFFFF"> <% if (welcomeHelper.nameExists()) { %> <center><H3> Welcome <destacar> <jsp:getProperty name="welcomeHelper" property="name" /> </destacar><br><br> </H3></center> <% } %> <H4><center>Glad you are visiting our site!</center></H4> </BODY> </HTML>

La estrategia alternativa de Vista Servlet normalmente se implementa embebiendo marcas HTML directamente dentro del código Java. Mezclar código Java y etiquetas de marcas crea una pobre separación de los roles de usuario dentro de un proyecto e incrementa las dependencias de los mismos recursos entre varios miembros de diferentes equipos. Cuando un individuo trabaja en una plantilla que contiene código o etiquetas no familiares, incrementa el riesgo de que un cambio accidental introduzca problemas en el sistema. También hay una reducción de la eficiencia del entorno de trabajo (demasiada gente compartiendo los mismos recursos físicos) y un incremento en el manejo de control de fuentes.

Estos problemas ocurren más frecuentemente en grandes entornos empresariales que tienen requerimientos de sistemas más complicados y que utilizan equipos de desarrolladores. Tiene menos probabilidades de ocurrir en pequeños sistemas que tienen sencillos requerimientos y utilizan unos pocos desarrolladores porque el mismo individuo cumple los roles mencionados arriba. Sin embargo, debemos tener en mente que los proyectos suelen empezar pequeños -- con requerimiento sencillos y unos cuantos desarrolladores -- pero finalmente evolucionan y se convierten en suficientemente sofisticados como para beneficiarse de estas sugerencias.

Servlet View

La estrategia de Vista Servlet utiliza un servlet como la vista. Es semánticametne equivalente al estrategia preferida de Vista JSP. Sin embargo, la estrategia de Vista Servlet, como vemos en el ejemplo siguiente, es más engorrosa para los equipos de desarrollo de software y de producción Web porque embebe etiquetas de marcas dentro del código Java. Cuando las etiquetas se mezclan con el código Jaba, la plantilla de la vista es más difícil de actualizar y modificar.

public class Controller extends HttpServlet { public void init(ServletConfig config) throws ServletException { super.init(config); } public void destroy() { } /** Processes requests for both HTTP * <code>GET</code> and <code>POST</code> methods. * @param request servlet request * @param response servlet response */ protected void processRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, java.io.IOException {

Page 32: Catálogo de Patrones de Diseño J2EE

String title = "Servlet View Strategy"; try { response.setContentType("text/html"); java.io.PrintWriter out = response.getWriter(); out.println("<html><title>"+title+"</title>"); out.println("<body>"); out.println("<h2><center>Employees List</h2>"); EmployeeDelegate delegate = new EmployeeDelegate(); /** ApplicationResources provides a simple API * for retrieving constants and other * preconfigured values**/ Iterator employees = delegate.getEmployees( ApplicationResources.getInstance(). getAllDepartments()); out.println("<table border=2>"); out.println("<tr><th>First Name</th>" + "<th>Last Name</th>" + "<th>Designation</th><th>Id</th></tr>"); while (employees.hasNext()) { out.println("<tr>"); EmployeeTO emp = (EmployeeTO)employees.next(); out.println("<td>"+emp.getFirstName()+ "</td>"); out.println("<td>"+emp.getLastName()+ "</td>"); out.println("<td>"+emp.getDesignation()+ "</td>"); out.println("<td>"+emp.getId()+"</td>"); out.println("</tr>"); } out.println("</table>"); out.println("<br><br>"); out.println("</body>"); out.println("</html>"); out.close(); } catch (Exception e) { LogManager.logMessage("Handle this exception", e.getMessage() ); } } /** Handles the HTTP <code>GET</code> method. * @param request servlet request * @param response servlet response */ protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, java.io.IOException { processRequest(request, response); } /** Handles the HTTP <code>POST</code> method. * @param request servlet request * @param response servlet response */ protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, java.io.IOException { processRequest(request, response); } /** Returns a short description of the servlet. */ public String getServletInfo() { return "Example of Servlet View. " + "JSP View is preferable."; } /** dispatcher method **/ protected void dispatch(HttpServletRequest request, HttpServletResponse response, String page) throws javax.servlet.ServletException, java.io.IOException { RequestDispatcher dispatcher = getServletContext().getRequestDispatcher(page); dispatcher.forward(request, response); } }

JavaBean Helper

Page 33: Catálogo de Patrones de Diseño J2EE

El helper se implementa como un JavaBean. La utilización de helpers resulta en una separación limpia de la vista y el procesamiento de negocio en una aplicación, ya que la lógica de negocio está construida fuera de la vista y dentro del componente helper. En este caso, la lógica de negocio se encapsula en un JavaBean, que ayuda en la recuperación de contenido y adapta y almacena el modelo para usarlo en la vista.

La utilización de la estrategia JavaBean Helper requiere menos trabajo que la estrategia Custom Tag Helper, ya que los JavaBeans se construyen e integran más facilmente en un entorno JSP. Además, incluso los desarrolaldores novatos entienden los JavaBeans. En el siguiente código podemos ver un ejemplo de esta estrategia:

<jsp:useBean id="welcomeHelper" scope="request" class="corepatterns.util.WelcomeHelper" /> <HTML> <BODY bgcolor="FFFFFF"> <% if (welcomeHelper.nameExists()) { %> <center><H3> Welcome <destacar> <jsp:getProperty name="welcomeHelper" property="name" /> </destacar><br><br> </H3></center> <% } %> <H4><center>Glad you are visiting our site!</center></H4> </BODY> </HTML>

Custom Tag Helper

El helper se implementa como una etiqueta personalizada (sólo JSP 1.1+). La utilización de helpers resulta en una separación limpia de la vista y el procesamiento de negocio en una aplicación, ya que la lógica de negocio está construida fuera de la vista y dentro del componente helper. En este caso, la lógica de negocio se encapsula en un componente de etiqueta personalizada, que podría ayudar en la recuperación de contenido y adaptar el modelo para su utilización en la vista.

Usar esta estrategia requier mucho más trabajo que hacerlo con la estrategia JavaBean Helper, ya que el desarrollo de etiquetas personalizadas es moderadamente complicado en relación al desarrollo de JavaBeans. No solo es más complejo el proceso de desarrollo, sino que hay mucha complejidad con respecto a la integración y el manejo de las etiquetas completadas. Para utilizar esta estrategia, debemos configurar el entorno con numerosos artefactos generados, incluyendo la propia etiqueta, un descriptor de librería de etiquetas, y los ficheros de configuración. Abajo podemos ver un fragmento de una vista JSP que utiliza esta estrategia:

<%@ taglib uri="/web-INF/corepatternstaglibrary.tld" prefix="corepatterns" %> <html> <head><title>Employee List</title></head> <body> <div align="center"> <h3> List of employees in <corepatterns:department attribute="id"/> department - Using Custom Tag Helper Strategy. </h3> <table border="1" > <tr> <th> First Name </th> <th> Last Name </th> <th> Designation </th> <th> Employee Id </th> <th> Tax Deductibles </th> <th> Performance Remarks </th> <th> Yearly Salary</th> </tr> <corepatterns:employeelist id="employeelist_key"> <tr> <td><corepatterns:employee attribute="FirstName"/> </td> <td><corepatterns:employee

Page 34: Catálogo de Patrones de Diseño J2EE

attribute= "LastName"/></td> <td><corepatterns:employee attribute= "Designation"/> </td> <td><corepatterns:employee attribute= "Id"/></td> <td><corepatterns:employee attribute="NoOfDeductibles"/></td> <td><corepatterns:employee attribute="PerformanceRemarks"/></td> <td><corepatterns:employee attribute="YearlySalary"/></td> <td> </tr> </corepatterns:employeelist> </table> </div> </body> </html>

Business Delegate as Helper

Los componentes helper normalmente hacen llamadas distribuidas a la capa de negocio. Sugerimos la utilización de un delegado de negocio para poder ocultar los detalles de la implementación subyacente, así como dicho helper simplemente invoca un servicio de negocio sin conocer los detalles sobre su implementación física y su distribución.

Tanto el helper como el delegado de negocio deben implementarse como JavaBeans. Así, se podría combinar la noción del componente helper y del delegado de negocio e implementar el delegado de negocio como un tipo de helper especializado. Hay una mayor distinción entre un helper y un delegado de negocio: Un desarrollador que trabaja en la capa de presentación escribe un componente helper, mientras que el delegado normalmente lo escribe un desarrollador que trabaja en los servicios de la capa de negocio. (Nota: El delegado también se podría proporcionar como una parte del marco de trabajo). Así, está estrategia trato mucho más sobre quién escribe realmente el delegado que sobre la propia implementación. Si hay algún solapamiento en los roles de desarrollo, entonces deberiamos considerar la utilización de esta estrategia.

/**A servlet delegates to a command object helper, as shown in the following excerpt:**/ String resultPage = command.execute(request, response); /**The command object helper uses the business delegate, which is simply implemented as another JavaBean helper, as shown in the following excerpt:**/ AccountDelegate accountDelegate = new AccountDelegate();

Una nota sobre los Helpers:

Los helpers JavaBean se utilizan para ayudar en la recuperación de contenido y en el almacenamiento y adaptación del modelo para la vista. Los helpers JavaBean también se utilizan frecuentemente como objetos Command.

Al igual que los helpers JavaBean, los helpers de etiquetas personalizadas podrían cumplir cualquiera de estos roles, excepto el de actuar como un objeto command. Al contrario que los helpers JavaBean, los helpers de etiquetas personalizadas están bien diseñados para el control de flujo y la interacción con la vista. Los helpers de etiquetas personalizadas utilizados de esta forma encapsulan lógica que de otro modo podría embeberse directamente dentro de la página JSP como código scriptlet. Otra área donde es preferible utilizar helpers de etiquetas personalizadas es en el formato de datos para display. Una etiqueta personalizada puede iterar sobre una colección de resultados, formatear esos resultados en un tabla HTML, y embeber la tabla dentro de la

Page 35: Catálogo de Patrones de Diseño J2EE

vista JSP sin requerir ningún scriptlet Java.

Consideremos un ejemplo en el que le pedimos a un cliente Web alguna información de la cuenta del sistema, como se ve en la siguiente figura.

Podemos ver cinco helpers en este diagrama. Los cuatro helpers JavaBean son el objeto AccountCommand, el objeto Account, el objeto AccountDAO, y AccountDetails. El único helper de etiqueta personalizada es el objeto TableFormatter.

El controlador maneja la petición. Crea o busca el objeto command apropiado, que está implementado como un helper JavaBean. En este caso, es el objeto command que procesa las peticiones de información de la cuenta. El controlador invoca al objeto Command, que le pide la información sobre la cuenta a un objeto Account. El objeto Account invoca al servicio de negocio, pidíendole estos detalles, que se devuelven en forma de un objeto Transfer implementado como un JavaBean.

¿Entonces, como accede el objeto Account a los servicios de negocio? Examinemos dos casos, uno sencillo y otro un poco más complicado. En el caso sencillo, imaginemos que un proyecto está en fase de aproximación, enfasando Enterprise JavaBeans (EJB) dentro de la capa de negocio en el tiempo. Asumamos que estámos en el momento en que se está accediendo a la base de datos mediante llamadas JDBC desde la capa de presentación. En ese caso, el objeto Account utiliza un objeto Data Access (en páginas posteriores veremos este patrón), ocultando los detalles de implementación para acceder a la base de datos. El objeto Data Access sabe qué consultas SQL son las necesarias para recuperar la información. Estos detalles están ocultos del resto de la aplicación, reduciendo el acoplamiento y haciendo que todos los componentes sean más modulares y reutilizables. Este es el caso descrito en el diagrama de secuencia anterior.

Cuando la arquitectura se vuelve más sofisticada, se introduce un EJB en la capa de negocio, entonces el objeto Data Access se reemplaza con un Business Delegate (más adelante veremos este patrón), que normalmente está escrito por

Page 36: Catálogo de Patrones de Diseño J2EE

los desarrolladores de servicios de negocio. El delegado oculta los detalles de implementación de la búsqueda EJB, de la invocación y del manejo de excepciones. También podría mejorar el rendimiento proporcionando servicio de caché. De nuevo, el objeto reduce el acoplamiento entre las capas, mejorando la reutilización y la modularidad de varios componentes. Sin importar la implementación específica de este objeto, su interface podría permanecer invariable durante esta transición. La siguiente figura describe este escenario después de la transición al delegado de negocio.

Ahora el objeto command tiene que manejar el objeto AccountDetails, el cual almacena antes de devolver el control al controlador. El Controller lo reenvia a la vista apropiada, llamada AccountView.jsp. Entonces la vista obtiene una combinación de datos en bruto y de datos formateados de los helpers AccountDetails y TableFormatter, respectivamente. TableFormatter está implementado como una etiqueta personalizada que pasa a través de los datos en bruto y los formatea en una tabla HTML para mostrarla. Como vimos, esta conversión no requiere escribir nigún scriptlet en la vista, lo que sí sería necesario para realizar la misma funcionalidad con un helper JavaBean.

Además, el objeto Account o el helper AccountDetails podrían proporcionar métodos convenientes para adaptar los datos en bruto a otros formatos. Aunque dichos métodos no introducirían etiquetas HTML en los datos, podrían proporcionar diferentes combinaciones de datos. Un ejemplo es entregar el nombre completo del usuario en varios formatos, como "Lastname, Firstname" o "Firstname Lastname", etc.

Transformer Helper

El helper se implementa como un eXtensible Stylesheet Language Transformer. Esto es particularmente útil con modelos que existen como marcas estructuradas, como el lenguaje eXtensible Markup Language (XML), bien nativamente dentro de sistemas legales o mediante alguna forma de conversión. Utilizar esta estrategia puede ayudarnos a forzar la separación entre el modelo y la vista, ya que la mayoría de las marcas de la vista se tienen que crear en una hoja de estilos separada. La siguiente figura describe una potencial implementación de esta estrategia:

Page 37: Catálogo de Patrones de Diseño J2EE

El controlador maneja la petición e invoca al objeto Command, implementado como un helper JavaBean. El objeto Command inicia la recuperación de los datos de la cuenta. El objeto Account invoca al servicio de negocio, que devuelve los datos en forma de un objeto Transfer, implementado como un JavaBean.

Se completa la recuperación de contenido y el control se pasa al AccountView, que utiliza su etiqueta personalizada transformer para manipular el estado del modelo. El transformer trata con un hoja de estilo, que describe cómo transformar el modelo, normalmente describiendo cómo formatearlo con etiquetas para mostrarlo en el cliente. La hoja de estilo normalmente se recupera como un fichero estático, aunque se podría generar dinámicamente. Aquí tenemos un ejemplo de lo que debería ser la etiqueta personalizada:

<xsl:transform model="accounthelper" stylesheet="/transform/styles/basicaccount.xsl"/>

Consecuencias

• Mejora el Particionamiento de la Aplicación, la Reutilización y el Mantenimiento Utilizar helpers resulta en una clara separación de la vista del procesamiento de negocio en una aplicación. Los helpers, en forma de JavaBeans (JSP 1.0+) y etiquetas personalizadas (JSP 1.1+), proporcionan un lugar externo para que la vista encapsule la lógica de negocio. Por el contrario, el código en scriptlets dentro de las páginas JSP, emborrona ampliamente la situación, especialmente en gandes proyectos.

Además, la lógica de negocio que se construye fuera de las JSPs y dentro de los JavaBeans y las etiquetas personalizadas se reutiliza, reduciendo la duplicación y facilitando el mantenimiento.

• Mejora la Separación de Roles Separar la lógica del formateo de la lógica de negocio de la aplicación reduce las dependencias podrían tener los individuos que juegan los diferentes roles en los mismos recursos. Por ejemplo, un desarrollador de software podría poseer código que está embebido dentro de marcas HTML, mientras que un miembro del equipo de producción Web podría necesitar modificar la distribución de la página y diseñar componentes que están mezclados con la lógia de negocios. Ningún individuo que cumpla estos roles podría estar familiarizado con las implementaciones específicas del trabajo del otro individuo, y asi se evita el riesgo de introducir bugs mediante modificaciones accidentales del sistema.

Patrones Relacionados

Page 38: Catálogo de Patrones de Diseño J2EE

• Business Delegate Los componentes helper necesitan métodos de acceso al API de servicios de negocio. También es importante reducir el acoplamiento entre helpers en la capa de presentación y entre servicios de negocio en la capa de negocio. Se recomienda que se utilice un delegate porque estas capas podrían estar distribuidas físicamente por la red. El delegado le oculta al clinte los detalles subyacentes de la búsqueda y acceso a los servicios de negocio, y también podría proporcionar un caché intermedio para reducir el tráfico de la red.

• Dispatcher View y Service to Worker Cuando sea deseable el control centralizado para manejar problemas como la seguridad, el control del carga de trabajo, la recuperación de contenidos y la navegación, debemos considerar la utilización de los patrones Dispatcher View o Service to Worker.

• Front Controller Este patrón está emparejado con el patrón View Helper para crear los patrones Dispatcher View o Service to Worker.

Page 39: Catálogo de Patrones de Diseño J2EE

Catálogo de Patrones de Diseño J2EE. I.- Capa de Presentación Autor: Sun

Traductor: Juan Antonio Palos (Ozito)

• Composite View o Contexto o Problema o Causas o Solución

� Estructura � Participantes y Responsabilidades � Estrategias

o Consecuencias o Código de Ejemplo o Patrones Relacionados

Composite View

Contexto

Las páginas Web sofisticadas presentan contenido de varias fuentes de datos, utilizando varias subvistas que completan una sóla página. Además, varios individuos con diferentes habilidades contribuyen al desarrollo y mantenimiento de esas páginas Web.

Problema

En lugar de proporcionar un mecanismo para combinar modularmente, en el que porciones atómicas de una vista componen un página completa, las páginas se construyen embebiendo código de formateo directamente dentro de cada vista.

La modificación de la distribución de múltiples vistas es difícil y propensa a errores, debido a la duplicación de código.

Causas

• Las porciones atómicas del contenido de la vista cambian con frecuencia. • Varias vistas compuestas utilizan subvistas similares, como un tabla de inventario de clientes.

Estas porciones atómicas se decoran con una plantilla de texto alrededor diferente o aparecen en diferentes localizaciones dentro de la página.

• Los cambios de distribución son más difíciles de manejar y el código más difícil de mantener cuando las subvistas se embeben directamente y se duplican en varias vistas.

• Frecuentemente, embeber las porciones cambiantes de la plantilla de texto directamente dentro de las vistas también afecta potencialmente a la disponibilidad y administración del sistema. El servidor podría necesitar reinicializarse antes de que los cliente vieran las modificaciones o actualizaciones de estas plantillas de componentes.

Solución

Utilizar vistas compuestas que se componen de varias subvistas atómicas. Cada componente de la plantilla se podría incluir dinámicamente dentro del total y la distribución de la página se maneja independientemente del contenido.

Esta solución promueve la creación de una vista compuesta basada en la inclusión y sustitución de fragmentos de plantilla modulares tanto estáticos como dinámicos. También promueve la reutilización de porciones atómicas de la vista asegurando un diseño modular. Es apropiado utilizar este patrón para generar páginas que muestran componentes que podrían combinarse en una gran variedad de formas. Este escenario ocurre, por ejemplo, con sites de portal que incluyen numerosas subvistas independientes,

Page 40: Catálogo de Patrones de Diseño J2EE

como noticias, información del tiempo, y valores de stocks en una sóla página. La distribución de la página se maneja y modifica de forma independiente al contenido de las subvistas.

Otro beneficio de este patrón es que los diseñadores Web pueden hacer un prototipo de la distribución de la site, conectando contenido estático en todas las regiones de la plantilla. Según va progresando el desarrollo de la site, ese contenido estático se puede sustituir por el contenido dinámico.

La siguiente figura muestra una captura de pantalla de la página inicial de Sun, java.sun.com. Se identifican cuatro regiones: Navegación, Búsqueda, Contenido y Cabeceras. Aunque el contenido de cada una de estas subvistas podría estar originado desde diferentes fuentes de datos, se unen para crear un sóla página compuesta.

Esta patrón también tiene inconvenientes. Provoca una sobrecarga en el entorno de ejecución, un precio que hay que pagar por el incremento de flexibilidad que proporciona. La utilización de un mecanismo de distribución más sofisticado también trae consigo algunos problemas de manejabilidad y desarrollo, ya que hay más artefactos que mantener y un cierto nivel indirecto de implementación que entender.

Estructura

La siguiente figura representa el diagrama de clases del patrón Composite View.

Page 41: Catálogo de Patrones de Diseño J2EE

Participantes y Responsabilidades

La siguiente figura representa el diagrama de secuencia del patrón Composite View.

Composite View

Una vista compuesta es una vista a la que se le han agregado varias subvistas.

View Manager

El Controlador de Vista maneja la inclusión de porciones de fragmentos de plantilla en la vista compuesta. El Controlador de Vista podría ser parte de un motor de ejecución estándar de páginas JSP, en la forma de la etiqueta <jsp:include> de JSP, o podría encapsularse dentro de un helper JavaBean (JSP 1.0+) o una etiqueta personalizada (JSP 1.1+) para proporcionar una funcionalidad más robusta.

Un beneficio adicional de utilizar un mecanismo distinto a la etiqueta include estándar es que éstos facilitan la inclusión condicional. Por ejemplo, ciertos fragmentos de plantilla se podrían incluir sólo si el usuario cumple un rol particular o si se cumplen ciertas condiciones del sistema. Además, utilizar un componente helper como View Manager nos permite un control más sofisticado de toda la estructura de la página, lo que es útil para crear distribuciones de páginas reutilizables.

Included View

Page 42: Catálogo de Patrones de Diseño J2EE

Una Vista Incluida es una subvista que es una pieza atómica de un vista completa mayor. Esta vista incluida potencialmente también podría ser una vista compuesta, incluyeno ella misma varias subvistas.

Estrategias

JSP page View

Ver la estrategia "JSP page View" en el patrón View Helper.

Servlet View

Ver la estrategia "Servlet View" en el patrón View Helper.

JavaBean View Management

El control de la vista se implementa utilizando componentes JavaBeans, como se ve en el siguiente ejemplo. La vista delega en el JavaBean, que implementa la lógica personalizada para controlar la distribución y composición de la vista. Las decisiones sobre la distribución de la págína se deben basar en roles de usuario o políticas de seguridad, haciéndolo mucho más poderoso que la etiqueta include de una página JSP. Aunque es semánticamente equivalente a la Estrategia de Control de Vista por Etiqueta Personalizada, no es tan elengante porque introduce código scriptlet en la vista.

Utilizar esta estrategia requiere menos trabajo que utilizar la estrategia preferida de Control de Vista por Etiqueta Personalziada, porque es más fácil construir componentes JavaBeans e integrarlos en un entorno de páginas JSP. Además, incluso los desarrolladores más novatos pueden entender los componentes JavaBeans. Esta estrategia también es fácil desde el punto de vista de la manejabilidad, porque los componentes JavaBeans son los únicos artefactos que hay que manejar y configurar.

<%@page import="corepatterns.compositeview.beanhelper.ContentHelper" %> <% ContentHelper personalizer = new ContentHelper(request); %> <table valign="top" cellpadding="30%" width="100%"> <% if (personalizer.hasWorldNewsInterest() ) { %> <tr> <td><jsp:getProperty name="feeder" property="worldNews"/></td> </tr> <% } if ( personalizer.hasCountryNewsInterest() ) { %> <tr> <td><jsp:getProperty name="feeder" property="countryNews"/></td> </tr> <% } if ( personalizer.hasCustomNewsInterest() ) { %> <tr> <td><jsp:getProperty name="feeder" property="customNews"/></td> </tr> <% } if ( personalizer.hasAstronomyInterest() ) { %> <tr> <td><jsp:getProperty name="feeder" property="astronomyNews"/></td> </tr> <% } %> </table>

Page 43: Catálogo de Patrones de Diseño J2EE

Standard Tag View Management

El control de la vista se implementa utilizando etiquetas JSP estándar, como <jsp:include>. Utilizar etiquetas estándar para manejar la distribución y composición de las vistas es una estrategia fácil de implementar, pero no proporciona el poder y la flexibilidad de la Estrategia de Control de Vista por Etiqueta Personalizada, ya que la distribución de las páginas individuales permanece embebida dentro de esas páginas. Así, aunque esta estrategia permite variar dinámicamente el contenido subyacente, cualquier cambio general en la distribución requeriría modificaciones individuales en muchas páginas JSP. Podemos verlo en el siguiente ejemplo:

<html> <body> <jsp:include page="/jsp/CompositeView/javabean/banner.html" flush="true"/> <table width="100%"> <tr align="left" valign="middle"> <td width="20%"> <jsp:include page="/jsp/CompositeView/javabean/ProfilePane.jsp" flush="true"/> </td> <td width="70%" align="center"> <jsp:include page="/jsp/CompositeView/javabean/mainpanel.jsp" flush="true"/> </td> </tr> </table> <jsp:include page="/jsp/CompositeView/javabean/footer.html" flush="true"/> </body> </html>

Cuando se crea una vista compuesta utilizando etiquetas estándar, se puede incluir tanto contenido estático, ficheros HTML, como contenido dinámico, páginas JSP. Además, el contenido se puede incluir en el momento de la traducción o durante la ejecución. Si el contenido se incluye durante la traducción, la vista permanecerá sin modificada hasta que se recompile la página JSP, momento en el que serán visibles las modificaciones incluidas en el contenido. En otras palabras, la página se distribuye y genera una sola vez, cada vez que la página JSP se recompila. El siguiente ejemplo, muestra una excepción de una página JSP que genera una vista compuesta de esta manera, utilizando la directiva <%@ include %> estándar de JSP que incluye el contenido en tiempo de traducción..

<table border=1 valign="top" cellpadding="2%" width="100%"> <tr> <td><%@ file="news/worldnews.html" %> </td> </tr> <tr> <td><%@ file="news/countrynews.html" %> </td> </tr> <tr> <td><%@ file="news/customnews.html" %> </td> </tr> <tr> <td><%@ file="news/astronomy.html" %> </td> </tr> </table>

La inclusión de contenido en tiempo de ejecución significa que los cambios en las subvistas son visibles en la página compuesta cada vez que el cliente accede a ella. Esto es mucho más dinámico y se puede conseguir utilizando la etiqueta <jsp:include> estándar de JSP, como se muestra en el siguiente ejemplo. Por supuesto que hay una sobrecarga del entorno de ejecución asociada con este tipo de generación de vistas, pero este es el incoveniente de mejorar la flexibilidad de las modificaciones de contenidos al-vuelo.

<table border=1 valign="top" cellpadding="2%" width="100%"> <tr>

Page 44: Catálogo de Patrones de Diseño J2EE

<td><jsp:include page="news/worldnews.jsp" flush="true"/> </td> </tr> <tr> <td><jsp:include page="news/countrynews.jsp" flush="true"/> </td> </tr> <tr> <td><jsp:include page="news/customnews.jsp" flush="true"/> </td> </tr> <tr> <td><jsp:include page="news/astronomy.jsp" flush="true"/> </td> </tr> </table>

Custom Tag View Management

El control de la vista se implementa mediante etiquetas personalizadas (JSP 1.1+), que es la estrategia preferida. La lógica implementada dentro de la etiqueta controla la distribución de la vista y la composición. Estas etiquetas son mucho más poderosas y flexibles que la etiqueta include estándar de JSP, pero también requiere un mayor nivel de esfuerzo. Las acciones personalizadas se pueden basar en la distribución y composición de la página o en otras cosas como los roles del usuario y las políticas de seguridad.

Utilizar esta estrategia requiere mucho más trabajo que las otras estrategias de control de vista ya que el desarrollo de etiquetas personalizadas es más complicado que simplemente utilizar componentes JavaBeans o etiquetas estándar. No sólo es más complicado el proceso de desarrollo, también hay mucha más complejidad en cuanto a la integración y el manejo de las etiquetas generadas. La utilización de esta estrategia requiere la generación de numerosos artefactos, incluyendo la propia etiqueta, un descriptor de librería de etiquetas, ficheros de configuración, y configurar el entorno con estos artefactos.

El siguiente fragmento de página JSP muestra una posible implementación de esta estrategia.

<region:render template='/jsp/CompositeView/templates/portal.jsp'> <region:put section='banner' content='/jsp/CompositeView/templates/banner.jsp' /> <region:put section='controlpanel' content= '/jsp/CompositeView/templates/ProfilePane.jsp' /> <region:put section='mainpanel' content= '/jsp/CompositeView/templates/mainpanel.jsp' /> <region:put section='footer' content= '/jsp/CompositeView/templates/footer.jsp' /> </region:render>

Transformer View Management

El control de la vista se implementa utilizando un Transformador XSL. Esta estrategia se complementaría con la Estrategia de Control de Vista por Etiqueta Personalizada, utilizando etiquetas personalizadas para implementar y delegar en los componentes apropiados. Utilizar esta estrategia nos puede ayudar a reforzar las separación entre el modelo y la vista, ya que muchas de las marcas de la vista se deben crear dentro de una hoja de estilos separada. Al mismo tiempo, implica tecnologías que requieren nuevas y sofisticadas habilidades para implementarlo correctamente, un problema que hace que esta estrategia sea impracticable en muchos entornos donde estas tecnologías no están aún establecidas.

El siguiente fragmento muestra el uso de una etiqueta personalizada dentro de una página JSP para convertir un modelo utilizando un hoja de estilo y un transformador:

<xsl:transform model="portfolioHelper" stylesheet="/transform/styles/generalPortfolio.xsl"/>

Page 45: Catálogo de Patrones de Diseño J2EE

Early-Binding Resource

Este es otro nombre para la inclusión de contenido durante la traducción, como se describe en la estrategia Control de Vista Mediate Etiqueta Estándar. Es apropiado para mantener y actualizar una plantilla relativamente estática y está recomendado si una vista incluye cabeceras y pies de página que no cambian muy a menudo.

Late-Binding Resource

Este es otro nombre para la inclusión de contenido durante la traducción, como se describe en la estrategia Control de Vista Mediate Etiqueta Estándar. Es apropiada para páginas compuestras que podrían cambiar con cierta frecuencia. Una advertencia: Si la subvista incluida en tiempo de ejecución es un recurso dinámico, como una página JSP, entonces ésta subvista también podría ser una vista compuesta, incluyendo más contenido en tiempo de ejecución. La flexibilidad ofrecida por dichas estructuras anidadas debería pesar mucho más que la sobrecarga del entorno que va crear.

Consecuencias

• Mejora la Modularidad y la Reutilización Este patrón promueve un diseño modular. Es posible reutilizar porciones atómicas de una plantilla, como una tabla de consulta de stocks, en varias vistas y redecorar estas porciones reutilizadas con información diferente. Este patrón permite que la tabla se mueva dentro de su propio módulo e incluirla simplemente donde sea necesario. Este tipo de distribución y composición dinámicos reduce la duplicación, fuerza la reutilización y mejora la manejabilidad.

• Mejora la Flexibilidad Una implementación sofisticada podría incluir condicionalmente fragmentos de plantillas de vista basándose en decisiones en tiempo de ejecuión, como los roles de usuario o las políticas de seguridad.

• Mejora el Mantenimiento y la Manejabilidad Es mucho más eficiente manejar cambios en porciones de una plantilla cuando la plantilla no está codificada directamente en las marcas de la vista. Cuando se mantienen separadas de la vista, es posible modificar prociones modulares del contenido de la plantilla independientemente de su distribución. Además, esos cambios están disponibles inmediatamente para los clientes, dependendiendo de la estrategia de implementación. Las modificaciones en la distribución de una págona también se maneja más fácilmente, ya que los cambios están centralizados.

• Reduce la Manejabilidad Agregar piezas atómicas para mostrarlas juntas y crear una sola vista presenta algunos potenciales problemas de presentación, ya que las subvistas son fragmentos de páginas. Esta es una limitación que se puede convertir en un problema de manejabilidad. Por ejemplo, si una página JSP está generando una página HTML utilizando una página principal que incluye tres subvistas, y cada una de las subvistas incluye las etiquetas de apertura y cierre de HTML (es decir, <HTML> y </HTML>), entonces la página compuesta no será válida. Por lo tanto, es importante cuando utilicemos este patrón tener cuidado de que las subvistas no deben ser vistas completas. Se deben contabilizar estrictamente las etiquetas utilizadas para crear vistas compuestas válidas, y así corregir este problema de manejabilidad.

• Impacto en el Rendimiento Generar una presentación que incluye numerosas subvistas podría empeorar el rendimiento. La inclusión en tiempo de ejecución de subvistas resultará en un retardo cada vez que se sirva la página a un cliente. En un entorno con Acuerdos de Nivel de Servicio que obligan a tiempos de respuesta específicos, dichas bajadas de rendimiento, aúnque típicamente sean mínimas, podrían no ser aceptables. Una alternativa es mover la inclusión de las subvistas al tiempo de la traducción, aunque esto limita a que las subvistas sólo cambien cuando se re-traduce la página.

Código de Ejemplo

El patrón de Vista Compuesta se puede implementar utilizando cualquier número de estrategias, pero una de las más populares es la Estrategia de Control de Vista por Etiqueta Personalizada. De hecho, actualmente hay disponibles varias librerías de etiquetas personalizadas para implementar vistas compuestas que separan las distribución de la vista de su contenido y proporciona plantillas de subvistas modulares y conectables.

Este ejemplo utilizará una librería de plantillas escrita por David Geary.

Page 46: Catálogo de Patrones de Diseño J2EE

La librería template describe tres componentes básicos: secciones, regiones y plantillas:

• Una seccion es un componente reutilizable que representa HTML o una página JSP. • Una región describe contenido definiendo secciones. • Una plantilla controla la distribución de la regiones y secciones en una página.

Aquí podemos ver una región y sus secciones

<region:render template='portal.jsp'> <region:put section='banner' content = 'banner.jsp' /> <region:put section = 'controlpanel' content = 'ProfilePane.jsp' /> <region:put section='mainpanel' content = 'mainpanel.jsp' /> <region:put section='footer' content='footer.jsp' /> </region:render>

Una región define su contenido correspondiendo nombres de secciones lógicas con una porción de contenido, como banner.jsp.

La distribución de la región y sus secciones está definida por una plantilla, a la que se asocia cada región. En este caso, la plantilla se llama portal.jsp, como se define en el siguiente ejemplo:

<region:render section='banner'/> <table width="100%"> <tr align="left" valign="middle"> <td width="20%"> <!-- menu region --> <region:render section='controlpanel' /> </td> <td width="70%" align="center"> <!-- contents --> <region:render section='mainpanel' /> </td> </tr> </table>

Un site con varias vistas y una sola distribución cosistente tiene un página JSP que contiene código que será similar a la definición del ejemplo anterior, y muchas páginas JSP que serán similares al primer fragmento de código de esta sección, que definen regiones y secciones alternativas.

Las secciones son fragmentos de páginas JSP que se utilizan como subvistas para construir una vista completa según se define en la plantilla. Abajo podemos ver la sección banner.jsp:

<table width="100%" bgcolor="#C0C0C0"> <tr align="left" valign="middle"> <td width="100%"> <TABLE ALIGN="left" BORDER=1 WIDTH="100%"> <TR ALIGN="left" VALIGN="middle"> <TD>Logo</TD> <TD><center>Sun Java Center</TD> </TR> </TABLE> </td> </tr> </table>

Las vistas compuestas son una forma modular, flexible y extensible de construir vistas de páginas JSP para nuestras aplicaciones J2EE.

Patrones Relacionados

• View Helper El patrón de vista compuesta se podría utilizar como la vista del patrón View Helper.

Page 47: Catálogo de Patrones de Diseño J2EE

• Composite [GoF] El patrón de Vista Compuesta está basado en el patrón Composite, que describe las herencias parte-totalidad cuando un objeto compuesto se compone de varias piezas, todas ellas tratadas como equivalente lógicos.

Page 48: Catálogo de Patrones de Diseño J2EE

Catálogo de Patrones de Diseño J2EE. I.- Capa de Presentación Autor: Sun

Traductor: Juan Antonio Palos (Ozito)

• Service to Worker o Contexto o Problema o Causas o Solución

� Estructura � Participantes y Responsabilidades � Estrategias

o Consecuencias o Código de Ejemplo o Patrones Relacionados

Service to Worker

Contexto

El sistema controla el flujo de ejecución y accede a los datos de negocio, desde los que crea el contenido de la presentación.

Nota: El patrón Service to Worker, igual que el patrón Dispatcher View, describe una combinación común de otros patrones del catálogo. Estos dos macro-patrones describen las combinación de un controlador y un dispatcher con vistas y helpers. Aunque describen esta estructura común, cada uno enfatiza un uso diferentes de los patrones.

Problema

El problema es una combinación de los problemas resueltos por los patrones Front Controller y View Helper de la capa de presentación. No hay un componente centralizado para manejar el control de acceso, la recuperación de contenido, o el manejo de la vista, y hay código de control duplicado esparcido por varias vistas. Además, la lógica de negocio y la de formateo de la presentación están mezcladas en esas vistas, haciendo que el sistema sea menos flexible, menos reutilizables y generalmente menos resistente a los cambios.

Mezclar lógica de negocio con el procesamiento de la vista también reduce la modularidad y proporciona una pobre separación de roles entre los equipos de producción Web y de desarrollo de software.

Causas

• Los chequeos de autentificación y autorización se completan en cada petición. • El código scriptlet dentro de las vistas se debería minimizar. • La lógica de negocio se debería encapsular en componentes distintos a la vista. • El control de flujo es relativamente complejo y se basa en valores del contenido dinámico. • La lógica de control de la vista es relativamente sofisticada, con varias vistas que potencialmente

se mapean a la misma petición.

Solución

Page 49: Catálogo de Patrones de Diseño J2EE

Combinar un controlador y un dispacher con vistas y helper (ver Front Controller y View Helper) para manejar peticiones de clientes y preparar una presentación dinámica como respuesta. Los controladores delegan la recuperación de contenido en los helpers, que manejan el relleno del modelo intermedio para la vista. Un dispatcher es el responsable del control de la vista y la navegación y puede encapsularse dentro de un controlador o de un componente separado.

Service to Worker describe la combinación de los patrones Front Controller y View Helper con un componente dispatcher.

Aunque este patrón y Dispatcher View describen una estructura similar, ambos sugieren diferentes divisiones de la labor entre los componentes. En Service to Worker, el controlador y el dispatcher tienen más responsabilidades.

Aunque los patrones Service to Worker y Dispatcher View representan una combinación de otros patrones del catálogo, el primero garantiza con su nombre una comunicación eficiente entre los desarrolladores. Mientras que el segundo sugiere una recuperación de contenido relegada al momento de procesamiento de la vista.

En el patrón Dispatcher View, el dispatcher normalmente juega un rol moderado en el control de la vista. En el patrón Service to Worker, el dispatcher juega un rol algo más elevado en el control de la vista.

Un rol limitado para el dispatcher ocurre cuando no se utiliza recursos exteriores para poder elegir la vista. La información encapsulada en la petición es suficiente para determinar la vista a la que despachar la petición. Por ejemplo:

http://some.server.com/servlet/Controller?next=login.jsp

La única responsabilidad del componente dispatcher en este caso es reenviar a la vista login.jsp.

Un ejemplo del dispatcher jugando un rol moderado es el caso donde el cliente envía una petición directamente al controlador con un parámetro de consulta que describe una acción a realizar:

http://some.server.com/servlet/Controller?action=login

Aquí la responsabilidad del dispatcher es traducir el nombre lógico login en el nombre del recurso de una vista apropiada, como login.jsp, y reenviar a esa vista. Para conseguir esta traducción, el dispatcher podría acceder a recursos como un fichero de configuración XML que especifica las vistas apropiadas a mostrar.

Por otro lado, en el patrón Service to Worker, el dispatcher podría invocar servicios de negocio para determinar la vista apropiada que se debe mostrar.

La estructrua compartida de Service to Worker y Dispatcher View consiste en un controlador trabajanado con un dispatcher, vistas y helpers.

Estructura

En la siguiente figura podemos ver el diagrama de clases que representa al patrón Service to Worker.

Page 50: Catálogo de Patrones de Diseño J2EE

Participantes y Responsabilidades

La siguiente figura muestra el diagrama de secuencia que representa al patrón Service to Worker.

Como hemos comentado, Service to Worker y Dispatcher View representan una estructura similar. La principal diferencia es que Service to Worker describe arquitecturas con un comportamiento más up front (más cercano) al controlador y al dispatcher, mientras que Dispatcher View describe arquitecturas donde se ha movido más comportamiento al momento del procesamiento de la vista. Así, los patrones sugieren una continuidad, donde el comportamiento se ha encapsulado más cerca de la vista o se ha movido hacía atrás en el flujo de proceso.

Controller

El controlador normalmente es el punto de contacto inicial para manejar una petición. Funciona con un dispatcher para completar el control de la vista y la navegación. El controlador maneja la autentificación, la autorización, la recuperación de contenido, la validación y otros aspectos del manejo de la petición. Delega en los helpers para completar partes de este trabajo.

Dispatcher

Page 51: Catálogo de Patrones de Diseño J2EE

Un dispatcher es el responsable del control de la vista y la navegación, controlando la elección de la siguiente vista a mostrar y proporciona el mecanismo para dirigir el control a este recurso.

Un dispatcher se puede encapsular dentro de un controlador (ver Front Controller) o puede ser un componente independiente que trabaja en coordinación con el controlador. El dispatcher puede proporcionar reenvío estático a la vista o podría proporcionar un mecanismo de reenvío dinámico más sofisticado.

El dispatcher utiliza el objeto RequestDispatcher (soportado en la especificación Servlet), pero también encapsula alguna información de procesamiento adicional. Cuantas más responsabilidades encapsule este componente, más se acercará al ideal del patrón Service to Worker. Y al controlario, cuando el diapatcher juega un papel más limitado, más se acercará al ideal del patrón Dispatcher View.

View

Una Vista representa una presentación de información al cliente. La información utilizada en esta presentación se recupera de un modelo. Los helpers soportan vistas encapsulando y adaptando un modelo para utilizarlo en una presentación.

Helper

Un helper es el responsable de ayudar a la vista o al controlador a completar su procesamiento. Así, los helpers tienen numerosas responsabilidades, incluyendo la obtención de los datos requeridos por la vista y almacenándolos en el modelo intermedio, en cuyo caso el helper es conocido como un value bean. Además, los helpers podrían adaptar este modelo de datos para que los utilice la vista. Los helpers pueden servir peticiones de datos desde la vista simplemente proporcionando acceso a los datos en bruto o formateándolos como contenido Web.

Una vista podría trabajar con cualquier número de helpers, que normalmente están implementados por componentes JavaBeans (JSP 1.0+) o componentes de etiquetas personalidas (JSP 1.1+). Además, un helper podría representar un objeto Command o delegate.

ValueBean

Un value bean es otro nombre para un helper que es responsable de contener el estado del modelo intermedio para que lo utilice una vista.

BusinessService

Servicio de Negocio es un rol que cumple el servicio al que el cliente quiere acceder. Normalmente, se accede al servicio de negocio mediante un Business delegate. El rol del business delegate es proporcionar control y protección para el servicio de negocio (puedes ver el patrón "Business Delegate" más adelante).

Estrategias

Servlet Front

Ver la estrategia "Servlet Front" en el patrón Front Controller.

JSP Front

Ver la estrategia "JSP Front" en el patrón Front Controller.

JSP page View

Ver la estrategia "JSP page View" en el patrón View Helper.

Page 52: Catálogo de Patrones de Diseño J2EE

Servlet View

Ver la estrategia "Servlet View" en el patrón View Helper.

JavaBean Helper

Ver la estrategia "JavaBean Helper" en el patrón View Helper.

Custom Tag Helper

Ver la estrategia "Custom Tag Helper" en el patrón View Helper.

Dispatcher in Controller

Ver la estrategia "Dispatcher in Controller" en el patrón Front Controller.

Como hemos visto, los patrones Service to Worker y Dispatcher View sugieren una continuidad, donde el comportamiento se ha encapsulado más cerca de la vista o se ha movido hacía atrás en el flujo de proceso. La siguiente figura describe un escenario en el que se ha cargado al controlador con mucho trabajo, pero la funcionalidad del dispatcher es mínima.

Transformer Helper

Ver la estrategia "Transformer Helper" en el patrón View Helper.

Consecuencias

• Centraliza el Control y Mejora la Modularidad y la Reutilización Este patrón sugiere proporcionar un lugar central para manejar los servicios del sistema y la lógica de negocio entre varias peticiones. El contolador maneja el procesamiento de la lógica de negocio y el manejo de peticiones. Hay que tener en cuenta, que como control centralizado, es posible introducir un sólo unto de fallo.

El patrón también promueve el particionamiento de la aplicación y aconseja la reutilización. El código común se mueve dentro de un controlador y es reutilizado por las peticiones y movido dentro de componentes helpers, en los que delegan los controladores y las vistas. La mejora de

Page 53: Catálogo de Patrones de Diseño J2EE

modularidad y de reutilización significa menos duplicación de código, que normalmente significa en entorno más libre de bugs.

• Mejora el Particionamiento de la Aplicación La utilización de helpers resulta en una separación clara entre la vista y el procesamiento de negocio en una aplicación. Los helpers, en la forma de JavaBeans (JSP 1.0+) o etiquetas personalizadas (JSP 1.1+), proporcionan un lugar donde construir la lógica de negocio fuera de la página JSP. Si la lógica de negocio se deja dentro de la página JSP, los grandes proyectos resultan embrollados.

• Mejora la Separación de Roles Al separar la lógica de formateo de la lógica de negocio de la aplicación también se reducen las dependencias de los mismos recursos entre individuos que cumplen diferentes roles. Sin esta separación, por ejemplo, un desarrollador de sofware poseería código que está embebido dentro de marcas HTML, mientras que un miembro del equipo de producción Web necesitaría modificar la distribución de una página y diseñar componentes que están mezclados con lógica de negocio. Como ningún individuo que cumple estos roles está familiarizado con las implementaciones específicas del trabajo del otro individuo, se puede llegar a un punto de confusión en que las modificaciones accidentales introduzcan errores el sistema.

Código de Ejemplo

Los siguientes fragmentos de códigos muestran una implementación del patrón Service to Worker, utilizando un servlet controlador, un helper command, un componente dispatcher y una vista. La implementación incluye las estrategias Servlet Front, Command and Controller, JSP View y JavaBean Helper. También se utiliza una vista compuesta muy básica, como se puede ver en la siguiente imagen:

El siguiente ejemplo muestra el controlador servlet, que delega en un objeto Command para completar el procesamiento del control. El objeto Command se recupera mediante una llamada a la factoría, que devuelve un Command de tipo genérico, como veremos más adelante. El ejemplo utiliza un LogManager para guardar mensajes.

public class Controller extends HttpServlet { /** Processes requests for both HTTP * <code>GET</code> and <code>POST</code> methods. * @param request servlet request

Page 54: Catálogo de Patrones de Diseño J2EE

* @param response servlet response */ protected void processRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, java.io.IOException { String next; try { // Log pattern info LogManager.recordStrategy(request, "Service To Worker", " ServletFront Strategy;" + " JSPView Strategy; JavaBean helper Strategy"); LogManager.logMessage(request, getSignature(), "Process incoming request. "); // Use a helper object to gather parameter // specific information. RequestHelper helper = new RequestHelper(request, response); LogManager.logMessage(request, getSignature(), "Getting command object helper"); // Get command object helper Command command = helper.getCommand(); // delegate processing to the command object, // passing request and response objects along next = command.execute(helper); /** If the above command returns a value, we * will dispatch from the controller. In this * example, though, the command will use a * separate dispatcher component to choose a * view and dispatch to that view. The command * object delegates to this dispatcher * component in its execute method, above, and * control should not return to this point **/ } catch (Exception e) { LogManager.logMessage( "EmployeeController(CommandStrategy)", e.getMessage() ); /** ApplicationResources provides a simple API * for retrieving constants and other * preconfigured values**/ next = ApplicationResources.getInstance(). getErrorPage(e); } dispatch(request, response, next); } /** Handles the HTTP <code>GET</code> method. * @param request servlet request * @param response servlet response */ protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, java.io.IOException { processRequest(request, response); } /** Handles the HTTP <code>POST</code> method. * @param request servlet request * @param response servlet response */ protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, java.io.IOException { processRequest(request, response); } /** Returns a short description of the servlet. */ public String getServletInfo() { return getSignature(); } /** dispatcher method */ protected void dispatch(HttpServletRequest request, HttpServletResponse response, String page) throws javax.servlet.ServletException, java.io.IOException { RequestDispatcher dispatcher =

Page 55: Catálogo de Patrones de Diseño J2EE

getServletContext().getRequestDispatcher(page); dispatcher.forward(request, response); } public void init(ServletConfig config) throws ServletException { super.init(config); } public void destroy() { } private String getSignature() { return "ServiceToWorker-Controller"; } }

En el siguiente fragmento tenemos el interface Command:

public interface Command { public String execute(RequestHelper helper) throws javax.servlet.ServletException, java.io.IOException; }

Todo objeto command implementa este interface genérico, que es un ejemplo del patrón Command de GoF. El objeto command es un ejemplar de la clase ViewAccountDetails, que podemos ver abajo. El ejemplar de command delega en un AccountingAdapter para hacer una llamada a la capa de negocio mediante Business Delegate.

public class ViewAccountDetailsCommand implements Command { public ViewAccountDetailsCommand() { } // view account details operation public String execute(RequestHelper helper) throws javax.servlet.ServletException, java.io.IOException { /** This will tell the user that a system error * has occured and will typically not be seen. It * should be stored in a resource file **/ String systemerror = "/jspdefaultprocessingerror.jsp"; LogManager.logMessage(helper.getRequest(), "ViewAccountDetailsCommand", "Get Account Details from an adapter object"); /** Use an adapter to retrieve data from business * service, and store it in a request attribute. * Note: Object creation could be avoided via * factory, but for example purposes object * instantiation is shown **/ AccountingAdapter adapter = new AccountingAdapter(); adapter.setAccountInfo(helper); LogManager.logMessage(helper.getRequest(), "ViewAccountDetailsCommand", "processing complete"); /** Note: Object creation could be avoided via * factory, but for example purposes object * instantiation is shown**/ Dispatcher dispatcher = new Dispatcher(); dispatcher.dispatch(helper); /** This return string will not be sent in a * normal execution of this scenario, because * control is forwarded to another resource * before reaching this point. Some commands do * return a String, though, so the return value * is included for correctness. **/ return systemerror; } }

Page 56: Catálogo de Patrones de Diseño J2EE

La clase adaptador, mostrada en el siguiente ejemplo, utiliza una componente dispatcher independiente para determinar la siguiente vista a la que se debería reenviar el control y para reenviar realmente el control a esa vista.

public class AccountingAdapter { public void setAccountInfo( RequestHelper requestHelper) { LogManager.logMessage( requestHelper.getRequest(), "Retrieving data from business tier"); // retrieve data from business tier via // delegate. Omit try/catch block for brevity. AccountDelegate delegate = new AccountDelegate(); AccountTO account = delegate.getAccount( requestHelper.getCustomerId(), requestHelper.getAccountKey()); LogManager.logMessage( requestHelper.getRequest(), "Store account Transfer Object in request attribute"); // transport data using request object requestHelper.getRequest().setAttribute( "account", account); } }

La invocación del servicio de negocio mediante el delegado tiene que ver con un objeto Account Transfer, que el adaptador almacena en un atributo de la petición para utilizarlo en la vista. El siguiente ejemplo muestra accountdetails.jsp, la página JSP que despachará la petición. El objeto Transfer Object se importa mediante la etiqueta estándar <jsp:useBean> y se accede a sus propiedades utilizando la etiqueta estándar <jsp:getProperty>. La vista también utiliza una estrategia Composite muy simple, haciendo la inclusión de la subvista trace.jsp durante la traducción, está subvista es la responsable de guardar información de la presentación sólo para propósitos de ejemplo.

<html> <head><title>AccountDetails</title></head> <body> <jsp:useBean id="account" scope="request" class="corepatterns.util.AccountTO" /> <h2><center> Account Detail for <jsp:getProperty name="account" property="owner" /> </h2> <br><br> <table border=3> <tr> <td> Account Number : </td> <td> <jsp:getProperty name "account" property="number" /> </td> </tr> <tr> <td> Account Type: </td> <td> <jsp:getProperty name="account" property="type" /> </td> </tr> <tr> <td> Account Balance: </td> <td> <jsp:getProperty name="account" property="balance" /> </td> </tr> <tr> <td>

Page 57: Catálogo de Patrones de Diseño J2EE

OverDraft Limit: </td> <td> <jsp:getProperty name="account" property="overdraftLimit" /> </td> </tr> </table> <br> <br> </center> <%@ include file="/jsp/trace.jsp" %> </body> </html>

Patrones Relacionados

• Front Controller y View Helper El patrón Service to Worker es el resultado de combinar el patrón View Helper con un dispatcher, en coordinación con el patrón Front Controller.

• Dispatcher View El patrón Dispatcher View es otro nombre para la combinación del patrón Front Controller con un dispatcher, y el patrón View Helper. Los patrones Service to Worker y Dispatcher View son idénticos con respecto a los componentes implicados, pero son diferentes en la división de labores entre esos componentes. El patrón Dispatcher View siguiere relegar la recuperación de contenido al momento en que se procesa la vista. Además, el dispatcher juega un rol más limitado en el control de la vista, ya que la elección de le vista normalmente ya está incluida en la petición.

Page 58: Catálogo de Patrones de Diseño J2EE

Catálogo de Patrones de Diseño J2EE. I.- Capa de Presentación Autor: Sun

Traductor: Juan Antonio Palos (Ozito)

• Dispatcher View o Contexto o Problema o Causas o Solución

� Estructura � Participantes y Responsabilidades � Estrategias

o Consecuencias o Código de Ejemplo o Patrones Relacionados

Dispatcher View

Contexto

El sistema controla el flujo de ejecución y accede al proceso de presentación, que es el responsable de generar el el contenido dinámico.

Nota: El patrón Dispatcher View, igual que el patrón Service to Worker, describe una combinación común de otros patrones del catálogo. Estos dos macro-patrones describen la combinación de un controlador y un dispatcher con vistas y helpers. Aunque describen esta estructura común, cada uno enfatiza un uso diferente de los patrones.

Problema

El problema es una combinación de los problemas resueltos por los patrones Front Controller y View Helper de la capa de presentación. No hay un componente centralizado para manejar el control de acceso, la recuperación de contenido, o el manejo de la vista, y hay código de control duplicado esparcido por varias vistas. Además, la lógica de negocio y la de formateo de la presentación están mezcladas en esas vistas, haciendo que el sistema sea menos flexible, menos reutilizable y generalmente menos resistente a los cambios.

Mezclar lógica de negocio con el procesamiento de la vista también reduce la modularidad y proporciona una pobre separación de roles entre los equipos de producción Web y de desarrollo de software.

Causas

• Los chequeos de autentificación y autorización se completan en cada petición. • El código scriptlet dentro de las vistas se debería minimizar. • La lógica de negocio se debería encapsular en componentes distintos a la vista. • El control de flujo es relativamente complejo y se basa en valores del contenido dinámico. • La lógica de control de la vista es relativamente sofisticada, con varias vistas que potencialmente

se mapean a la misma petición.

Solución

Page 59: Catálogo de Patrones de Diseño J2EE

Combinar un controlador y un dispatcher con vistas y helpers (ver Front Controller y View Helper) para manejar peticiones de clientes y preparar una presentación dinámica como respuesta. Los controladores delegan la recuperación de contenido en los helpers, que manejan el relleno del modelo intermedio para la vista. Un dispatcher es el responsable del control de la vista y la navegación y puede encapsularse dentro de un controlador o de un componente separado.

Dispatcher View describe la combinación de los patrones Front Controller y View Helper con un componente dispatcher. Aunque este patrón y Service to Worker describen una estructura similar, ambos sugieren diferentes divisiones de la labor entre los componentes. El controlador y el dispatcher tienen responsabilidades limitadas, comparadas con el patrón Service to Worker, porque la lógica de procesamiento y de control de vista son básicas. Además, si no se considera necesario el control de los recursos subyacentes, se puede eliminar el controlador y el dispatcher se podría mover dentro de una vista.

Aunque los patrones Service to Worker y Dispatcher View representan una combinación de otros patrones del catálogo, el primero garantiza con su nombre una comunicación eficiente entre los desarrolladores. Mientras que el segundo sugiere una recuperación de contenido relegada al momento de procesamiento de la vista.

En el patrón Dispatcher View, el dispatcher normalmente juega un rol moderado en el control de la vista. En el patrón Service to Worker, el dispatcher juega un rol algo más elevado en el control de la vista.

Un rol limitado para el dispatcher ocurre cuando no se utilizan recursos exteriores para poder elegir la vista. La información encapsulada en la petición es suficiente para determinar la vista a la que despachar la petición. Por ejemplo:

http://some.server.com/servlet/Controller?next=login.jsp

La única responsabilidad del componente dispatcher en este caso es reenviar a la vista login.jsp.

Un ejemplo del dispatcher jugando un rol moderado es el caso donde el cliente envía una petición directamente al controlador con un parámetro de consulta que describe una acción a realizar:

http://some.server.com/servlet/Controller?action=login

Aquí la responsabilidad del dispatcher es traducir el nombre lógico login en el nombre del recurso de una vista apropiada, como login.jsp, y reenviar a esa vista. Para conseguir esta traducción, el dispatcher podría acceder a recursos como un fichero de configuración XML que especifica las vistas apropiadas a mostrar.

Por otro lado, en el patrón Service to Worker, el dispatcher podría invocar servicios de negocio para determinar la vista apropiada que se debe mostrar.

La estructrua compartida de Service to Worker y Dispatcher View consiste en un controlador trabajanado con un dispatcher, vistas y helpers.

Estructura

En la siguiente figura podemos ver el diagrama de clases que representa al patrón Dispatcher View.

Page 60: Catálogo de Patrones de Diseño J2EE

Participantes y Responsabilidades

La siguiente figura muestra el diagrama de secuencia que representa al patrón Dispatcher View.

Aunque las responsabilidades del controlador están limitadas a los servicios del sistema, como la autentificación y autorización, todavía es beneficioso centralizar estos aspectos del sistema. Observa también que, al contrario que el patrón Service to Worker, el dispatcher no hace llamadas a servicios de negocio para poder completar el procesamiento de la vista.

La funcionalidad del dispatcer se podría encapsular en su propio componente. Al mismo tiempo, cuando las responsabilidades del dispatcher son limitadas, como se describen en este patrón, la funcionalidad del dispatcher se puede poner en otro componente, como en el controlador o la vista.

De hecho, la funcionalidad del dispatcher podría incluso ser completada por el contenedor, en el caso donde no sea necesaria una lógica extra a nivel de la aplicación. Un ejemplo es una vista llamada main.jsp a la que se le da el alias de first. El contenedor procesará la siguiente petición, traduciendo el nombre del alias al nombre del recurso físico, y reenviando directamente a ese recurso:

http://some.server.com/first --> /mywebapp/main.jsp

En este caso, nos hemos quedado con el patrón View Helper, donde la petición la maneja directamente la vista. Como la vista es el punto de contacto inicial para manejar peticiones, normalmente se utilizan etiquetas personalizadas para realizar el procesamiento de negocio o para delegar este procesamiento a otros componentes.

Page 61: Catálogo de Patrones de Diseño J2EE

Así, el patrón Dispatcher View describe una continuidad de escenarios relacionados, moviéndose de un escenario que es estructuralmente similar a Service to Worker a otro que es similar a View Helper.

Controller

El controlador normalmente es el punto de contacto inicial para manejar una petición. El controlador maneja la autentificación y la autorización, y delega en un dispatcher para hacer el control de la vista.

Dispatcher

Un dispatcher es el responsable del control de la vista y la navegación, controlando la elección de la siguiente vista a mostrar y proporciona el mecanismo para dirigir el control a este recurso.

Un dispatcher se puede encapsular dentro de un controlador (ver Front Controller) o puede ser un componente independiente que trabaja en coordinación con el controlador. El dispatcher puede proporcionar reenvío estático a la vista o podría proporcionar un mecanismo de reenvío dinámico más sofisticado.

View

Una Vista representa una presentación de información al cliente. La información utilizada en esta presentación se recupera de un modelo. Los helpers soportan vistas encapsulando y adaptando un modelo para utilizarlo en una presentación.

Helper

Un helper es el responsable de ayudar a la vista o al controlador a completar su procesamiento. Así, los helpers tienen numerosas responsabilidades, incluyendo la obtención de los datos requeridos por la vista y almacenándolos en el modelo intermedio, en cuyo caso el helper es conocido como un value bean. Además, los helpers podrían adaptar este modelo de datos para que los utilice la vista. Los helpers pueden servir peticiones de datos desde la vista simplemente proporcionando acceso a los datos en bruto o formateándolos como contenido Web.

Una vista podría trabajar con cualquier número de helpers, que normalmente están implementados por componentes JavaBeans (JSP 1.0+) o componentes de etiquetas personalidas (JSP 1.1+). Además, un helper podría representar un objeto Command o delegate.

ValueBean

Un value bean es otro nombre para un helper que es responsable de contener el estado del modelo intermedio para que lo utilice una vista.

BusinessService

Servicio de Negocio es un rol que cumple el servicio al que el cliente quiere acceder. Normalmente, se accede al servicio de negocio mediante un Business delegate. El rol del business delegate es proporcionar control y protección para el servicio de negocio (puedes ver el patrón "Business Delegate" más adelante).

Estrategias

Servlet Front

Ver la estrategia "Servlet Front" en el patrón Front Controller.

JSP Front

Page 62: Catálogo de Patrones de Diseño J2EE

Ver la estrategia "JSP Front" en el patrón Front Controller.

JSP page View

Ver la estrategia "JSP page View" en el patrón View Helper.

Servlet View

Ver la estrategia "Servlet View" en el patrón View Helper.

JavaBean Helper

Ver la estrategia "JavaBean Helper" en el patrón View Helper.

Custom Tag Helper

Ver la estrategia "Custom Tag Helper" en el patrón View Helper.

Dispatcher in Controller

Ver la estrategia "Dispatcher en Controller" en el patrón Front Controller.

Como hemos visto, los patrones Service to Worker y Dispatcher View sugieren una continuidad, donde el comportamiento se ha encapsulado más cerca de la vista o se ha movido hacía atrás en el flujo de proceso. La siguiente figura muestra las interacciones para esta estrategia:

El controlador no crea explicitámente un objeto dispatcher. En vez de eso, el controlar tiene cuidado de reenviar a la vista. Alternativamente, se podría implementar un dispatcher en el que el controldor puede delegar la función de reenvío.

Dispatcher in View

Si el controlador se eliminara debido a su rol limitado, el dispatcher se podría mover a una vista. Este diseño puede ser útil en casos donde es típico que una vista mapee a una petición específica, pero donde

Page 63: Catálogo de Patrones de Diseño J2EE

se podría utilizar de forma poco frecuente un vista secundaria. Por ejemplo, basándose en la información de la petición o los resultados de algún procesamiento de la vista, un helper de etiqueta personalizada en la vista podría dirigir el control a una vista secundaria. Un caso típico es cuando una petición de cliente es reenviada a una vista específica, y será servida por esa vista en cualquier otro caso.

Consideremos el caso donde el usuario no se ha autentificado, pero pide acceso a unas páginas JSP protegidas de la site. Como la site tiene sólo unas pocas páginas JSP protegidas, y el contenido dinámico es limitado, la autentificación se puede realizar dentro de esas páginas JSP, en lugar de utilizar un controlador centralizado para toda la site. Esas páginas que necesitan autentificacion incluyen un helper de etiqueta personalizada al principio de la página. Este helper realiza el chequeo de autentificación y muestra la página al usuario o lo reenvía a la página de autentificación. La siguiente figura representa este escenario:

Transformer Helper

Ver la estrategia "Transformer Helper" en el patrón View Helper.

Consecuencias

• Centraliza el Control y Mejora la Modularidad y la Reutilización Este patrón sugiere proporcionar un lugar central para manejar los servicios del sistema y la lógica de negocio entre varias peticiones. El contolador maneja el procesamiento de la lógica de negocio y el manejo de peticiones. Hay que tener en cuenta, que como control centralizado, es posible introducir un sólo unto de fallo.

• Mejora el Particionamiento de la Aplicación La utilización de helpers resulta en una separación clara entre la vista y el procesamiento de negocio en una aplicación. Los helpers, en la forma de JavaBeans (JSP 1.0+) o etiquetas personalizadas (JSP 1.1+), proporcionan un lugar donde construir la lógica de negocio fuera de la página JSP. Si la lógica de negocio se deja dentro de la página JSP, los grandes proyectos resultan embrollados.

• Mejora la Separación de Roles Al separar la lógica de formateo de la lógica de negocio de la aplicación también se reducen las dependencias de los mismos recursos entre individuos que cumplen diferentes roles. Sin esta separación, por ejemplo, un desarrollador de sofware poseería código que está embebido dentro de marcas HTML, mientras que un miembro del equipo de producción Web necesitaría modificar la distribución de una página y diseñar componentes que están mezclados con lógica de negocio. Como ningún individuo que cumple estos roles está familiarizado con las implementaciones específicas del trabajo del otro individuo, se puede llegar a un punto de confusión en que las modificaciones accidentales introduzcan errores el sistema.

Page 64: Catálogo de Patrones de Diseño J2EE

Código de Ejemplo

El siguiente código de ejemplo muestra una implementación del patrón Dispatcher View, utilizando un servlet controlador y una vista con helpers JavaBean y de etiquetas personalizadas. La implementación incluye las estrategias Servlet Front, Dispatcher in Controller, JSP View, JavaBean Helper y Custom Tag Helper. También utiliza una vista compuesta muy básica, mostrada en la siguiente imagen:

El siguiente ejemplo muestra el servlet controlador que simplemente completa el chequeo de autentificación y pasa el control a la vista apropiada. Observa que el controlador no delega directamente a ningún componente helper para poder hacer llamadas a la capa de negocio mediante Delegate. Estás responsabilidades se han relegado a la vista, que se llama accountdetails.jsp. El código de ejemplo utiliza un LogManager para guardar los mensajes.

public class Controller extends HttpServlet { /** Processes requests for both HTTP * <code>GET</code> and <code>POST</code> methods. * @param request servlet request * @param response servlet response */ protected void processRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, java.io.IOException { String nextview; try { LogManager.recordStrategy(request, "Dispatcher View", " Servlet Front Strategy; " + "JSP View Strategy; Custom tag helper Strategy"); LogManager.logMessage(request, getSignature(), "Process incoming request. "); // Use a helper object to gather parameter // specific information. RequestHelper helper = new RequestHelper(request, response); LogManager.logMessage(request, getSignature(), " Authenticate user"); Authenticator auth = new BasicAuthenticator(); auth.authenticate(helper); //This is an oversimplification for the sake of // simplicity. Typically, there will be a // mapping from logical name to resource name at // this point LogManager.logMessage(request, getSignature(),

Page 65: Catálogo de Patrones de Diseño J2EE

"Getting nextview"); nextview = request.getParameter("nextview"); LogManager.logMessage(request, getSignature(), "Dispatching to view: " + nextview); } catch (Exception e) { LogManager.logMessage( "Handle exception appropriately", e.getMessage() ); /** ApplicationResources provides a simple API * for retrieving constants and other * preconfigured values**/ nextview = ApplicationResources.getInstance(). getErrorPage(e); } dispatch(request, response, nextview); } /** Handles the HTTP <code>GET</code> method. * @param request servlet request * @param response servlet response */ protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, java.io.IOException { processRequest(request, response); } /** Handles the HTTP <code>POST</code> method. * @param request servlet request * @param response servlet response */ protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, java.io.IOException { processRequest(request, response); } /** Returns a short description of the servlet. */ public String getServletInfo(){ return getSignature(); } public void init(ServletConfig config) throws ServletException { super.init(config); } public void destroy() { } /** * dispatcher method */ protected void dispatch(HttpServletRequest request, HttpServletResponse response, String page) throws javax.servlet.ServletException, java.io.IOException { RequestDispatcher dispatcher = getServletContext(). getRequestDispatcher(page); dispatcher.forward(request, response); } private String getSignature() { return "DispatcherView-Controller"; } }

Observa que la vista utiliza helpers de etiquetas personalizadas para controlar la recuperación de contenido. ya que esta actividad no se realizó en el controlador. Cuando se utilizan las etiquetas personalizadas de esta forma, normalmente se convierten en estrechas fachadas para componentes solitarios en los que delegar para completar este procesamiento. De esta forma, la lógica de procesamiento general está pobremente acoplada a la implementación de la etiqueta. Si no se utilizan etiquetas personalizadas con Dispatcher View, la página JSP se terminará llenando de códgio scriptlet, una situación que debemos evitar.

<%@ taglib uri="/web-INF/corepatternstaglibrary.tld" prefix="corepatterns" %> <html>

Page 66: Catálogo de Patrones de Diseño J2EE

<head><title>AccountDetails</title></head> <body> <corepatterns:AccountQuery queryParams="custid,acctkey" scope="request" /> <h2><center> Account Detail for <corepatterns:Account attribute="owner" /></h2> <br><br> <tr> <td>Account Number :</td> <td><corepatterns:Account attribute="number" /></td> </tr> <tr> <td>Account Type:</td> <td><corepatterns:Account attribute="type" /></td> </tr> <tr> <td>Account Balance:</td> <td><corepatterns:Account attribute="balance" /></td> </tr> <tr> <td>OverDraft Limit:</td> <td><corepatterns:Account attribute="overdraftLimit" /></td> </tr> <table border=3> </table> </corepatterns:AccountQuery> <br> <br> </center> <%@ include file="/jsp/trace.jsp" %> </body> </html>

Patrones Relacionados

• Front Controller y View Helper El patrón Dispatcher View es el resultado de combinar el patrón View Helper con un dispatcher, en coordinación con el patrón Front Controller.

• Service to Worker El patrón Service to Worker es otro nombre para la combinación del patrón Front Controller con un dispatcher, y el patrón View Helper. Los patrones Service to Worker y Dispatcher View son idénticos con respecto a los componentes implicados, pero son diferentes en la división de labores entre esos componentes. El patrón Dispatcher View siguiere relegar la recuperación de contenido al momento en que se procesa la vista. Además, el dispatcher juega un rol más limitado en el control de la vista, ya que la elección de le vista normalmente ya está incluida en la petición.

Page 67: Catálogo de Patrones de Diseño J2EE

Catálogo de Patrones de Diseño J2EE. Y II: Capas de Negocio y de Integración Autor: Sun

Traductor: Juan Antonio Palos (Ozito)

Puedes encontrar la versión original de este tutorial en Inglés en: http://java.sun.com/blueprints/corej2eepatterns/Patterns/index.html

• Busisness Delegate o Contexto o Problema o Causas o Solución

� Estructura � Participantes y Responsabilidades � Estrategias

o Consecuencias o Código de Ejemplo: Implementar el Patrón Business Delegate o Patrones Relacionados

• Service Locator o Contexto o Problema o Causas o Solución

� Estructura � Participantes y Responsabilidades � Estrategias

o Consecuencias o Código de Ejemplo

� Implementar el Patrón Service Locator � Implementar la Estrategia Type Checked Service Locator

o Patrones Relacionados • Session Facade

o Contexto o Problema o Causas o Solución

� Estructura � Participantes y Colaboraciones � Estrategias

o Consecuencias o Código de Ejemplo

� Implementar el Session Facade o Patrones Relacionados

• Transfer Object o Contexto o Problema o Causas o Solución

� Estructura � Participantes y Responsabilidades � Estrategias

o Consecuencias o Código de Ejemplo

� Implementar el patrón Transfer Object � Implementar la Estrategia Updatable Transfer Objects � Implementar la Estrategia Multiple Transfer Objects � Implementar la Estrategia Entity Inherits Transfer Object � Implementar la Estrategia Transfer Object Factory

o Patrones Relacionados • Transfer Object Assembler

o Contexto o Problema

Page 68: Catálogo de Patrones de Diseño J2EE

o Causas o Solución

� Estructura � Participantes y Responsabilidades � Estrategias

o Consecuencias o Código de Ejemplo

� Implementar el Transfer Object Assembler o Patrones Relacionados

• Value List Handler o Contexto o Problema o Causas o Solución

� Estructura � Participantes y Colaboraciones � Estrategias

o Consecuencias o Código de Ejemplo

� Implementar el Value List Handler como un objeto Java o Patrones Relacionados

• Composite Entity o Contexto o Problema o Causas o Solución

� Estructura � Participantes y Responsabilidades � Estrategias

o Consecuencias o Codigo de Ejemplo

� Implementación del patrón Composite Entity � Implementar la Estrategia Lazy Loading � Implementar la Estrategia Store Optimization (Dirty Marker) � Implementar la Estrategia Composite Transfer Object

o Patrones Relacionados o Bean de Entidad como un Objeto Dependiende: Problemas y Recomendaciones

• Data Access Object o Contexto o Problema o Causas o Solución

� Estructura � Participantes y Responsabilidades � Estrategias

o Consecuencias o Código de Ejemplo

� Implementar el Patrón Data Access Object � Implementar la Estrategia Factory for Data Access Objects

o Patrones Relacionados • Service Activator

o Contexto o Problema o Causas o Solución

� Estructura � Participantes y Responsabilidades � Estrategias

o Consecuencias o Código de Ejemplo o Patrones Relacionados

Page 69: Catálogo de Patrones de Diseño J2EE

Catálogo de Patrones de Diseño J2EE. Y II: Capas de Negocio y de Integración Autor: Sun

Traductor: Juan Antonio Palos (Ozito)

• Busisness Delegate o Contexto o Problema o Causas o Solución

� Estructura � Participantes y Responsabilidades � Estrategias

o Consecuencias o Código de Ejemplo: Implementar el Patrón Business Delegate o Patrones Relacionados

Busisness Delegate

Contexto

Un sistema multi-capa distribuido requiere invocación remota de métodos para enviar y recibir datos entre las capas. Los clientes están expuestos a la complejidad de tratar con componentes distribuidos.

Problema

Los componentes de la capa de presentación interactúan directamente con servicios de negocio. Esta interacción directa expone los detalles de la implementación del API del servicio de negocio a la capa de presentación. Como resultado, los componentes de la capa de presentación son vulnerables a los cambios en la implementación de los servicios de negocio: cuando cambia la implementación del servicio de negocio, la implementación del codigo expuesto en la capa de presentación también debe cambiar.

Además, podría haber una reducción de rendimiento en la red porque los componentes de la capa de presentación que utilizan el API de servicio de negocio hacen damasiadas invocaciones sobre la red. Esto sucede cuando los componentes de la capa de presentación usan directamente el API subyacente, sin cambiar el mecanismo del lado del cliente o agregar servicios.

Por último, exponer directamente los APIs de servicios al cliente fuerza a éste a tratar con los problemas de red asociados con la naturaleza distribuida de la tecnología Enterprise JavaBeans (EJB).

Causas

• Los clientes de la capa de presentación necesitan acceder a servicios de negocio. • Diferentes clientes, dispositivos, clientes Web, y programas, necesitan acceder a los servicios de

negocio. • Los APIs de los servicios de negocio podrían cambiar según evolucionan los requerimientos del

negocio. • Es deseable miniminar el acoplamiento entre los clientes de la capa de presentación y los

servicios de negocio, y así ocultar los detalles de la implementación del servicio. • Los clientes podrían necesitar implementar mecanismos de caché para la información del

servicio de negocio. • Es deseable reducir el tráfico de red entre el cliente y los servicios de negocio.

Solución

Utilizamos un Business Delegate para reducir el acoplamiento entre los clientes de la capa de presentación y los servicios de negocio. El Business Delegate oculta los detalles de la

Page 70: Catálogo de Patrones de Diseño J2EE

implementación del servicio de negocio, como los detalles de búsqueda y acceso de la arquitectura EJB.

El Business Delegate actúa como una abstracción de negocio del lado del cliente; proporciona una abstracción para, y por lo tanto oculta, la implementación de los servicios del negocio. Utilizando Business Delegate se reduce el acoplamiento entre los clientes de la capa de presentación y los servicios de negocio del sistema. Dependiendo de la estrategia de implementación, Business Delegate podría aislar a los clientes de la posible volatilidad en la implementación del API de los servicios de negocio. Potencialmente, esto reduce el número de cambios que se deben hacer en el código de cliente de la capa de presentación cuando cambie el API del servicio de negocio o su implementación subyacente.

Sin embargo, los métodos de interface en el Business Delegate aún podría requerir modificaciones si cambia el API del servicio de negocio. Si bien es cierto, que los cambios se harán con más probabilidad en el servicio de negocio que en el Business Delegate.

Con frecuencia, los desarrolladores son excépticos cuando un objetivo de diseño como la abstracción de la capa de negocio provoca un trabajo adicional como pago por futuras ganancias. Sin embargo, utilizando este patrón o esta estrategia resulta sólo en una pequeña cantidad de trabajo extra y proporciona unos beneficios considerables. El principal beneficio es ocultar los detalles del servicio. Por ejemplo, el cliente puede ser transparente para los servicios de búsqueda y nombrado. El Business Delegate también maneja las excepciones de los servicios de negocio, como excepciones java.rmi.Remote, excepciones Java Messages Service (JMS), etc. El Business Delegate podría interceptar dichas excepciones a nivel de servicio y generar en su lugar excepciones a nivel de aplicación. Las excepciones de nivel de aplicacion son fáciles de manejar por los clientes, y pueden ser amigables para el usuario. El Business Delegate también podría realizar de forma transparene cualquier operación de reintento o de recuperación necesaria en el caso de un fallo en el servicio no exponer el cliente al problema hasta que se haya determinado que el problema no es solucionable. Estas ganancias representan una razón competitiva para utilizar el patrón.

Otro beneficio es que el delegado podría almacenar resultados y referencias a servicios de negocio remotos. El Caché puede mejorar el rendimiento de forma significativa, porque limita los innecesarios y potencialmente costosos viajes por la red.

Un Business Delegate utiliza un componente llamado Lookup Service. Este componente es el responsable de ocultar los detalles de implementación de código de búsqueda del servicio de negocio. El Lookup Service podría estar escrito como parte del Delegate, pero recomendamos que se implemente como un componento separado.

Cuando el Business Delegate se utiliza con un Session Facade, normalmente hay una relación uno-a-uno entre los dos. Esta relación existe porque la lógica que podría haber sido encapsulada en un Business Delegate en relación a su interacción con varios servicios de negocio (creando una relación uno-a-uno) normalmente se construye de vuelta en un Session Facade.

Finalmente, se debería tener en cuenta que este patrón se podría utilizar para reducir el acoplamiento entre otra capas, no simplemente entre las capas de presentación y de negocio.

Estructura

La siguiente figura muestra el diagrama de clases que representa al patrón Business Delegate. El cliente solicita al BusinessDelegate que le proporcione acceso al servicio de negocio subyacente. El BusinessDelegate utiliza un LookupService para localizar el componente BusinessService requerido.

Page 71: Catálogo de Patrones de Diseño J2EE

Participantes y Responsabilidades

Ls siguiente figura muestra el diagrama de secuencia que ilustra las interacciones típicas para este patrón:

Page 72: Catálogo de Patrones de Diseño J2EE

El BusinessDelegate utiliza un LookupService para localizar el servicio de negocio. El servicio de negocio se utiliza para invocar a los métodos de negocio por cuenta del cliente. El método Get_ID muestra que el BusinessDelegate puede obtener una versión String del handle (como un objeto EJBHandle) para el servicio de negocio y devolverlo al cliente como un String. El cliente puede utilizar la versión String del handle más tarde para reconectar con el servicio de negocio que estaba utilizando cuando obtuvo el handle. Esta técnica evitará nuevas búsquedas, ya que el handle es capáz de reconectar con su ejemplar del servicio de negocio. Se debería observar que los objetos handle los implementa el contenedor y podrían no ser portables entre contenedores de diferentes vendedores.

El diagrama de secuencia de la siguiente figura muestra la obtención de un BusinessService (como un bean de sesión o de entidad) utilizando este handle.

BusinessDelegate

El rol de BusinessDelegate es proporcionar control y protección para el servicio de negocio. BusinessDelegate puede exponer dos tipos de constructores al cliente. Un tipo de petición ejemplariza el BusinessDelegate sin una ID, mientras que el otro lo inicializa con un ID, donde ID es una versión String de la referencia al objeto remoto como un EJBHome o un EJBObject.

Cuando se inicializa sin una ID, el BusinessDelegate solicita el servicio al Lookup Service, normalmente implementado como un Service Locator (más adelante veremos el patrón Service Locator), que devuelve el Service Factory, como un EJBHome. El BusinessDelegate pide que el Service Factory localice, cree o elimine un BusinessService, como un bean enterprise.

Cuando se inicializa con un ID, el BusinessDelegate usa el ID para reconectar con el BusinessService. Así, el BusinessDelegate aisla al cliente de los detalles de la implementación del BusinessService de nombrado y búsqueda. Además, el cliente de la capa de presentación nunca hace llamadas remotas directas sobre un BusinessSession; en su lugar, el cliente utiliza el BusinessDelegate.

Page 73: Catálogo de Patrones de Diseño J2EE

LookupService

BusinessDelegate utiliza el objeto LookupService para localizar a BusinessService. LookupService encapsula los detalles de la implementación de la búsqueda de BusinessService.

BusinessService

BusinessService es un componente de la capa de negocio, como un bean enterprise o un componente JMS, que proprociona el servicio requerido por el cliente.

Estrategias

Delegate Proxy

El Business Delegate expone un interface que proporciona a los clientes acceso a los métodos subyacentes del API de servicios de negocio. En esta estrategia, un Business Delegate proporciona la función de proxy para pasar los métodos del cliente al bean de sesión que encapsula. Adicionalmente el Business Delegate podría hacer un caché con los datos necesarios, incluyendo las referencias remotas de los objetos home o remote del bean de sesión para mejorar el rendimiento reduciendo el número de búsquedas. El Business Delegate también podría convertir dichas referencias en identificadores de versión (IDs) y viceversa, utilizando los servicios de un Service Locator.

El ejemplo de implementación de esta estrategia se explica en la sección Código de Ejemplo de esta página.

Delegate Adapter

El Business Delegate parece ser bueno en un entorno B2B cuando se comunica con servicios basados en la plataforma J2EE. Los sistemas dispares podrían utilizar XML como el lenguaje de integración. Integrar un sistema en otro normalmente requiere un Adapter [GoF] para unir los dos sistemas dispares. La siguiente figura nos muestra un ejemplo:

Page 74: Catálogo de Patrones de Diseño J2EE

Consecuencias

• Reduce el Acoplamiento, Mejora la Manejabilidad El Business Delegate reduce el acoplamiento entre la capas de presentación y de negocio ocultando todos los detalles de implementación de la capa de negocio. Es fácil manejar los cambios porque están centralizados en un sólo lugar, el Business Delegate.

• Traduce las Excepciones del Servicio de Negocio El Business Delegate es el responsable de traducir cualquier excepción de red o relacionada con la infraestructura en excepciones de negocio, aislando a los clientes del conocimiento de las especifidades de la implementación.

• Implementa Recuperación de Fallos y Sincronización de Threads Cuando el Business Delegate encuentra un fallo en el servicio de negocio, puede implementar características de recuperación automática sin exponer el problema al cliente. Si la recuperación tiene éxito, el cliente no necesita saber nada sobre el fallo. Si el intento de recuperación no tiene éxito, entonces el Business Delegate necesita informar al cliente del fallo. Además, los métodos del Business Delegate podrían estar sincronizados, si fuera necesario.

• Expone un Interface Simple y Uniforme a la Capa de Negocio El Business Delegate, para servir mejor a sus clientes, podría proporcionar una variante del interface proporcionado por los beans enterprise subyacentes.

• Impacto en el Rendimiento El Business Delegate podría proporcionar servicio de caché (y un mejor rendimiento) a la capa de presentación para las peticiones de servicios comunes.

• Presenta una Capa Adicional El Business Delegate podría verse como la adicción de una capa innecesaria entre el cliente y el servicio, y con eso incrementar la complejidad y disminuir la flexibilidad. Algunos desarrolladores podrían sentir esto coomo un esfuerzo extra para desarrollar Business Delegates con implementaciones que utilizan la estrategia Delegate Proxy.

• Oculta los elementos Remotos Aunque la localización transparente es uno de los beneficios de este patrón, podría surgir un problema diferente debido a que el desarrolador está tratanto con un servicio remoto como si

Page 75: Catálogo de Patrones de Diseño J2EE

fuera un servicio local. Esto podría suceder si el desarrollador del cliente no entiende que el Business Delegate es cliente-proxy a un servicio remoto. Normalmente, unas llamadas a métodos en el Business Delegate resultan en unas llamadas a métodos remotos bajo la envoltura. Ignorando esto, el desarrollador podría tender a realiaar varias llamadas a métodos para realizar una sola tarea, lo que incrementará el tráfico en la red.

Código de Ejemplo: Implementar el Patrón Business Delegate

Consideremos una Aplicación de Servicios Profesional (PSA), donde un cliente de la capa Web necesita acceder a un bean de sesión que implementa el patrón Session Facade. Se puede aplicar el patrón Business Delegate para diseñar un clase Delegate como ResourceDelegate, que ecapsula la complejidad de tratar con el bean de sesión ResourceSession. En el siguientre fragmento de código podemos ver la implementación de la clase ResourceDelegate:

// imports ... public class ResourceDelegate { // Remote reference for Session Facade private ResourceSession session; // Class for Session Facade's Home object private static final Class homeClazz = corepatterns.apps.psa.ejb.ResourceSessionHome.class; // Default Constructor. Looks up home and connects // to session by creating a new one public ResourceDelegate() throws ResourceException { try { ResourceSessionHome home = (ResourceSessionHome) ServiceLocator.getInstance().getHome( "Resource", homeClazz); session = home.create(); } catch(ServiceLocatorException ex) { // Translate Service Locator exception into // application exception throw new ResourceException(...); } catch(CreateException ex) { // Translate the Session create exception into // application exception throw new ResourceException(...); } catch(RemoteException ex) { // Translate the Remote exception into // application exception throw new ResourceException(...); } } // Constructor that accepts an ID (Handle id) and // reconnects to the prior session bean instead // of creating a new one public BusinessDelegate(String id) throws ResourceException { super(); reconnect(id); } // Returns a String ID the client can use at a // later time to reconnect to the session bean public String getID() { try { return ServiceLocator.getId(session); } catch (Exception e) { // Throw an application exception } } // method to reconnect using String ID public void reconnect(String id) throws ResourceException { try { session = (ResourceSession) ServiceLocator.getService(id); } catch (RemoteException ex) { // Translate the Remote exception into // application exception throw new ResourceException(...); } }

Page 76: Catálogo de Patrones de Diseño J2EE

// The following are the business methods // proxied to the Session Facade. If any service // exception is encountered, these methods convert // them into application exceptions such as // ResourceException, SkillSetException, and so // forth. public ResourceTO setCurrentResource( String resourceId) throws ResourceException { try { return session.setCurrentResource(resourceId); } catch (RemoteException ex) { // Translate the service exception into // application exception throw new ResourceException(...); } } public ResourceTO getResourceDetails() throws ResourceException { try { return session.getResourceDetails(); } catch(RemoteException ex) { // Translate the service exception into // application exception throw new ResourceException(...); } } public void setResourceDetails(ResourceTO vo) throws ResourceException { try { session.setResourceDetails(vo); } catch(RemoteException ex) { throw new ResourceException(...); } } public void addNewResource(ResourceTO vo) throws ResourceException { try { session.addResource(vo); } catch(RemoteException ex) { throw new ResourceException(...); } } // all other proxy method to session bean ... }

El siguiente fragmento de código representa el interface remoto correspondiente al bean Session Facade ResourceSession:

// imports ... public interface ResourceSession extends EJBObject { public ResourceTO setCurrentResource( String resourceId) throws RemoteException, ResourceException; public ResourceTO getResourceDetails() throws RemoteException, ResourceException; public void setResourceDetails(ResourceTO resource) throws RemoteException, ResourceException; public void addResource(ResourceTO resource) throws RemoteException, ResourceException; public void removeResource() throws RemoteException, ResourceException; // methods for managing blockout time by the // resource public void addBlockoutTime(Collection blockoutTime) throws RemoteException, BlockoutTimeException; public void updateBlockoutTime( Collection blockoutTime) throws RemoteException, BlockoutTimeException;

Page 77: Catálogo de Patrones de Diseño J2EE

public void removeBlockoutTime( Collection blockoutTime) throws RemoteException, BlockoutTimeException; public void removeAllBlockoutTime() throws RemoteException, BlockoutTimeException; // methods for resource skillsets time by the //resource public void addSkillSets(Collection skillSet) throws RemoteException, SkillSetException; public void updateSkillSets(Collection skillSet) throws RemoteException, SkillSetException; public void removeSkillSet(Collection skillSet) throws RemoteException, SkillSetException; ... }

Patrones Relacionados

• Service Locator El patrón Service Locator se podría utilizar para crear el servicio de búsqueda del Business Delegate, ocultando los detalles de implementación de cualquier servicio de búsqueda y código de acceso.

• Proxy [GoF] Un Business Delegate podría actuar como un proxy, proporcionando suplentes para los objetos en la capa de negocio.

• Adapter [GoF] Un Business Delegate podría utilizar el patrón Adapter para proporcionar acoplamiento para sistemas dispares.

• Broker [POSA1] Un Business Delegate realiza el rol de un broker para desacoplar los objetos de la capa de negocio de los clientes de otras capas.

Page 78: Catálogo de Patrones de Diseño J2EE

Catálogo de Patrones de Diseño J2EE. Y II: Capas de Negocio y de Integración Autor: Sun

Traductor: Juan Antonio Palos (Ozito)

• Service Locator o Contexto o Problema o Causas o Solución

� Estructura � Participantes y Responsabilidades � Estrategias

o Consecuencias o Código de Ejemplo

� Implementar el Patrón Service Locator � Implementar la Estrategia Type Checked Service Locator

o Patrones Relacionados

Service Locator

Contexto

La búsqueda y creación de servicios implican interfaces complejos y operaciones de red.

Problema

Los clientes J2EE interactúan con componentes de servicio, como componentes JavaBeans Enterprise (EJB) y Java Message Service (JMS), que proporcionan servicios de negocio y capacidades de persistencia. Para interactúar con estos componentes, los clientes deben o lcalizar el componente de servicio (referido como una operación de búsqueda) o crear un nuevo componente. Por ejemplo, un cliente EJB debe localizar el objeto home del bean enterprise, que el cliente puede utilizar para encontrar un objeto o para crear uno o más beans enterprise. De forma similar, un cliente JMS primero debe localizar la Factoría de Conexiones JMS para obtener una Conexión JMS o una Sesión JMS.

Todos los clientes de aplicaciones J2EE utilizan la facilidad JNDI para buscar y crear componentes EJB o JMS. El API JNDI permite a los clientes obtener un objeto Contexto Inicial que contiene el nombre del componente a uniones de objetos. El cliente empieza obteniendo el contexto inicial para un objeto home de un bean. El contexto inicial permanece válido mientras sea válida la sesión del cliente. El cliente proporciona el nombre registrado en JNDI del objeto requerido para obtener una referencia a un objeto administrado. En el contexto de una aplicación EJB, un objeto administrado típico es un objeto home de un bean enterprise. Para aplicaciones JMS, el objeto administrado puede ser una Factoría de Conexiones JMS (para un Topic o una Queue) o un Destino JMS (un Topic o una Queue).

Por eso, localizar un objeto servicio administrado JNDI es un tarea común para todos los clientes que necesiten acceder al objeto de servicio. Por ejemplo, es fácil ver que muchos tipos de clientes utilizan repetidamente el servicio JNDI, y que el código JNDI aparece varias veces en esos clientes. Esto resulta en una duplicación de código innecesaria en los clientes que necesitan buscar servicios.

También, crear un objeto de contexto JNDI inicial y realizar una búsqueda para objeto home EJB utiliza muchos recursos. Si varios clientes requieren de forma repetitiva el mismo objeto home de un bean, dicha duplicación de esfuerzos puede impactar de forma negativa en el rendimiento de la aplicación.

Examinemos el proceso de búsqueda y creación de varios componentes J2EE.

1. La búsqueda y creación de Beans Enterprise trata sobre lo siguiente: o Una correcta configuración del entorno JNDI para que se conecte con el servicio de

nombrado y directorio utilizado por la aplicación. Esta configuración proporciona la

Page 79: Catálogo de Patrones de Diseño J2EE

localización del servicio de nombrado y las credenciales de autentificaciones necesarias para acceder a ese servicio.

o Entonces el servicio JNDI puede proporcionar al cliente un contexto inicial que actúa como un almacen para las uniones de componentes nombre-a-objeto. El cliente solicita a este contexto inicial que busque el objeto EJBHome del bean enterprise requerido proporcionando un nombre JNDI para ese objeto EJBHome.

o Encontrar el objeto EJBHome utilizando el contexto de búsqueda inicial o Después de obtener el objeto EJBHome, crea, elimina o encuentra el bean enterprise

utilizando los métodos create, move o find (solo para beans de entidad) del objeto EJBHome.

2. La búsqueda y creación de componentes JMS (Topic, Queue, QueueConnection, QueueSession, TopicConnection, TopicSession, etc.) implica los siguientes pasos. Observa que en estos pasos, Topic se refiere al modelo de mensajería publica/subscribe y que Queue se refiere al modelo de mensajería punto-a-punto.

o Una correcta configuración del entorno JNDI para que se conecte con el servicio de nombrado y directorio utilizado por la aplicación. Esta configuración proporciona la localización del servicio de nombrado y las credenciales de autentificaciones necesarias para acceder a ese servicio.

o Obtener el contexto inicial para el proveedor de servicio JMS desde el servicio de nombrado JNDI.

o Utilizar el contexto inicial para obtener un Topic o un Queue suministrando el nombre JNDI para ese Topic o Queue. Ambos son objetos JMSDestination.

o Utilizar el contexto inicial para obtener un TopicConnectionFactory o un QueueConnectionFactory suministrando el nombre JNDI para la factoría adecuada.

o Usar el TopicConnectionFactory para obtener un TopicConnection o el QueueConnectionFactory para obtener un QueueConnection.

o Utilizar el TopicConnection para obtener un TopicSession o el QueueConnection para obtener un QueueSession.

o Utilizar el TopicSession para obtener un TopicSubscriber o un TopicPublisher para el Topic Requerido. Utilizar el QueueSession para obtener un QueueReceiver o un QueueSender para el Queue requerido.

El proceso de búsqueda y creación de componentes implica una implementación de una factoria de contextos suministrada por un vendedor. Esto introduce dependencias del vendedor en los clientes de la aplicación que necesitan utilizar la facilidad de búsqueda JNDI para localizar beans enterprise y componentes JMS.

Causas

• Los clientes EJB necesitan utilizar el API JNDI para buscar objetos EJBHome utilizando el nombre registrado del bean enterprise.

• Los clientes JMS necesitan utilizar el API JNDI para buscar componentes JMS utilizando los nombres registrados en JDNI para esos componentes JMS.

• La factoría de contextos utilizada para la creación del contexto inicial JNDI la proporciona el vendedor del proveedor del servicio y por lo tanto es dependiente del vendedor. La factoría de contexto también es dependiente del tipo de objeto que se está buscando. El contexto para una JMS es diferente que el contexto para EJBs, con diferentes proveedores.

• La búsqueda y creación de componentes de servicio podría ser compleja y se podría utilizar repetidamente en múltiples clientes en la aplicación.

• La creación del contexto inicial y el servicio de búsqueda de objetos, si se utiliza frecuentemente, puede consumir muchos recursos e impactar en el rendimiento de la aplicación. Esto es especialmente cierto si los clientes y los servicios están localizados en diferentes capas.

• Los clientes EJB podrían necesitar reestablecer conexiones a un ejemplar de bean enterprise al que se ha accedido préviamente, teniendo solamente su objeto Handle.

Solución

Utilizar un objeto Service Locator para abstraer toda la utilización JNDI y para ocultar las complejidades de la creación del contexto inicial, de busqueda de objetos home EJB y de re-creación de objetos EJB. Varios clientes pueden reutilizar el objeto Service Locator para reducir la complejidad del código, proporcionando un punto de control, y mejorando el rendimiento proporcionando facilidades de caché.

Page 80: Catálogo de Patrones de Diseño J2EE

Este patrón reduce la complejidad del cliente que resulta de las dependencias del cliente y de la necesidad de realizar los procesos de búsqueda y creación, que consumen muchos recursos. Para eliminar estos problemas, este patrón proporciona un mecanismo para abstraer todas las dependencias y detalles de red dentro del Service Locator.

Estructura

La siguiente figura muestra el diagrama de clases que representa las relaciones para el patrón Service Locator.

Participantes y Responsabilidades

La siguiente figura contiene el diagrama de secuencia que muestra la interacción entre los distintos participantes en el patrón Service Locator.

Page 81: Catálogo de Patrones de Diseño J2EE

Client

Este es el cliente del Service Locator. El cliente es un objeto que normalmente requiere acceder a objetos de negocio como un Business Delegate.

Service Locator

El Service Locator abstrae el API de los servicios de búsqueda (nombrado), las dependencias del vendedor, las complejidades de la búsqueda, y la creación de objetos de negocio, y proporciona un interface simple para los clientes. Esto reduce la complejidad del cliente. Además, el mismo cliente y otros clientes pueden reutilizar el Service Locator.

InitialContext

El objeto InitialContext es el punto de inicio para los procesos de búsqueda y creación. Los proveedores de servicio proporcionan el objeto context, que varía dependiendeo del tipo de objetos de negocio proporcionados por el servicio de búsqueda y creación del Service Locator. Un Service Locator que proporciona los servicios para varios tipos de objetos de negocio (como beans enterprise, componentes JMS, etc.) utiliza varios tipos de objetos context, cada uno obtenido de un proveedor diferente (por ejemplo, el proveedor de contexto para un servidor de aplicaciones EJB podría ser diferente del proveedor de contexto para un servicio JMS).

ServiceFactory

El objeto ServiceFactory representa un objeto que proporciona control del ciclo de vida para objetos BusinessService. El objeto ServiceFactory para beans enterprise es un objeto EJBHome. El ServiceFactory

Page 82: Catálogo de Patrones de Diseño J2EE

para componentes JMS puede ser un objeto ConnectionFactory, como un TopicConnectionFactory o un QueueConnectionFactory.

BusinessService

BusinessService es un rol que cumple el servicio que el cliente ha solicitado. El objeto BusinessService se crea, se busca o se elimina mediante el objeto ServiceFactory. El objeto BusinessService en el contexto de una aplicación EJB es un bean enterprise. El objeto BusinessService en el contexto de una aplicación JMS puede ser un TopicConnection o un QueueConnection. TopicConnection y QueueConnection se pueden entonces utilizar para producir un objeto JMSSession, como un TopicSession o un QueueSession respectivamente.

Estrategias

EJB Service Locator

El Service Locator para componentes bean enterprise utiliza objetos EJBHome, como un BusinessHome en el rol del ServiceFactory. Una vez obtenido el objeto EJBHome, se puede almacenar en el ServiceLocator para un uso posterior y evitar así otra búsqueda JNDI cuando el cliente necesite de nuevo el objeto home. Dependiendo de la implementación, el objeto home puede ser devuelto al cliente, que puede entonces utilizarlo para buscar, crear o eliminar beans enterprise. Por otro lado, el ServiceLocator puede retener (en el caché) el objeto home y ganar la responsabilidad adicional de hacer de proxy de todas las llamadas de clientes al objeto home. En la siguiente figura podemos ver el diagram de clases para la Estrategia EJB Service Locator:

La interación entre todos los participantes en un Service Locator para un bean enterprise se puede ver en la siguiente figura:

Page 83: Catálogo de Patrones de Diseño J2EE

JMS Queue Service Locator

Esta estrategia se aplica para los requerimientos de mensajería punto-a-punto. El Service Locator para componentes JMS utiliza objetos QueueConnectionFactory en el rol del ServiceFactory. Se busca el QueueConnectionFactory utilizando su nombre JNDI. El ServiceLocator puede almacenar (en el caché) el objeto QueueConnectionFactory para un uso posterior. Esto evita repetidas llamadas JNDI para buscarlo cuando el cliente lo necesite de nuevo. Por otro lado, el ServiceLocator podría enviar el QueueConnectionFactory al cliente. Entonces, el cliente puede utilizarlo para crear una QueueConnection. Se necesita una QueueConnection para poder obtener una QueueSession o para crear un Message, un QueueSender (para enviar mensajes a la cola), o un QueueReceiver (para recibir mensajes de la cola). En la siguiente figura podemos ver el diagrama de clases para esta estrategia. En este diagrama, La cola es un objeto JMS Destination registrado como un objeto JNDI-administered que representa la cola. El objeto Queue se puede obtener directamente desde el contexto buscándolo por su nombre JNDI.

Page 84: Catálogo de Patrones de Diseño J2EE

La interacción entre los participantes en un Service Locator para mensajería punto-a-punto utilizando Queues JMS se puede ver en la siguiente figura:

JMS Topic Service Locator

Page 85: Catálogo de Patrones de Diseño J2EE

Esta estraregia es aplicable para requerimientos de mensajeria publica/subscribe. El Service Locator para componentes JMS utiliza objetos TopicConnectionFactory en el rol del ServiceFactory. Se busca el objeto TopicConnectionFactory utilizando sun nombre JNDI. El TopicConnectionFactory se puede almacenar (en el caché) mediante el ServiceLocator para su uso posterior. Esto evita repetidas llamadas JNDI para buscarlo cuando el cliente lo necesite de nuevo. Por otro lado, el ServiceLocator podría enviar el TopicConnectionFactory al cliente. Entonces el cliente puede utilizarlo para crear una TopicConnection. Se necesita una TopicConnection para poder obtener una TopicSession o para crear un Message, un TopicPublisher (para publicar un menaje para un topic), o un TopicSubscriber (para subscribirse a un topic). En la siguiente figura podemos ver el diagrama de clases para la estrategia JMS Topic Service Locator. En este diagrama, el Topic es un objeto JMS Destination registrado como un objeto adiministrado JNDI que representa el topic. El objeto Topic se puede obtener directamente desde el contexto buscándolo por su nombre JNDI.

La interacción entre los participantes en un Service Locator para mensajería pubicar/subscribir utilizando Topics JMS se puede ver en la siguiente figura:

Page 86: Catálogo de Patrones de Diseño J2EE

Combined EJB and JMS Service Locator

Estas estrategias para EJB y JMS se pueden utilizar para proporcionar implementaciones independientes de Service Locator, ya que los clientes para EJB y JMS podría ser más o menos mutuamente exclusivos. Sin embargo, si hay la necesidad de combinar estas estrategias, es posible hacerlo pafra proporcionar el Service Locator para todos los objetos bean enterprise y componentes JMS.

Type Checked Service Locator

Los dos últimos diagramas proporcionan facilidades de búsqueda pasándole el nombre en el servicio de búsqueda. Para una búsqueda de un bean enterprise, el Service Locator necesita una clase como parámetro del método PortableRemoteObject.narrow(). El Service Locator puede proporcionar un método getHome(), que acepta como argumento el nombre del servicio JNDI y el nombre de la clase del objeto EJBHome del bean enterprise. Utilizando este método de pasar nombres en servicios JNDI y clases de objetos EJBHome se pueden provocar errores en los clientes. Otra aproximación es definir estáticamente los servicios en el ServiceLocator, en lugar de pasarlos como parámetros string, el cliente los pasa como constantes.

Esta estrategia tiene inconvenientes. Reduce la flexibilidad de la búsqueda, que está en la estrategia Services Property Locator, pero añade chequeo de tipos en el paso de una constante al método ServiceLocator.getHome().

Service Locator Properties

Esta estrategia ayuda a corregir los inconvenientes de la estrategia anterior. Aquí se sugiere el uso de ficheros de propiedades y/o descriptores de ficheros para especificar los nombres JNDI y los nombres de las clases EJBHome. Para los clientes de la capa de presentación, dichas propiedades se pueden especificar en los descriptores de despliegue de la capa de presentación o en ficheros de propiedades. Cuando la capa de presentación accede a la capa de negocio, normalmente utiliza el patrón Business Delegate.

Page 87: Catálogo de Patrones de Diseño J2EE

El patrón Business Delegate interactúa con el Service Locator para localizar los componentes de negocio. Si la capa de presentación carga las propiedades durante la inicialización y puede proporcionar un servicio para manejar los nombres JNDI y los nombres de las clases EJB para los beans enterprise requeridos, el Business Delegate podría solicitar a este servicio que los obtuviera. Una vez que el Business Delegate tiene el nombre JNDI y el nombre de la clase EJBHome, puede pedirle al Service Locator el EJBHome pasándole estas propiedades como argumentos.

Entonces el Service Locator puede utilizar el método Class.forName(EJBHome ClassName) para obtener el objeto EJBHome y utilizar el método Portable RemoteObject.narrow() para forzar el objeto, según se verá en el método getHome() en el ejemplo de ServiceLocator. Lo único que cambia es de donde vienen los nombres JNDI y los objetos Class . Por lo tanto, esta estrategia evita introducir en el código los nombres JNDI y proporciona flexibilidad para su despliegue.

Consecuencias

• Abstrae la Complejidad El patrón Service Locator encapsula la complejidad de este proceso de búsqueda y creación (descrito en el problema) y lo mantiene oculto del cliente. El cliente no necesita tratar con la búsqueda de componentes ni factorías de objetos (EJBHome, QueueConnectionFactory, y TopicConnectionFactory, entre otros) porque se ha delegdo esta responsabilidad en el ServiceLocator.

• Proporciona a los Clientes un Acceso Uniforme a los Servicios El patrón Service Locator abstrae todas las complejidades, como acabamos de ver. Haciendo esto, proporciona un interface muy útil y preciso que todos los clientes pueden utilizar. Este interface asegura que todos los tipos de clientes de la aplicación acceden de forma uniforme a los objetos de negocio, en términos de búsqueda y creación. Esta uniformidad reduce la sobrecarga de desarrollo y mantenimiento.

• Facilita la Adicción de Nuevos Componentes de Negocio Como los clientes de beans enterprise no se preocupan de los objetos EJBHome, es posible añadir nuevos objetos EJBHome para beans enterprise y desplegarlos posteriormente sin impactar en los clientes. Los clientes JMS no se preocupan directamente de las factorías de conexiones JMS, por eso se pueden añadir nuevas factorías sin impactar en los clientes.

• Mejora el Rendimiento de la Red Los clientes no están implicados en la búsqueda JNDI y la creación de objetos (factory/home). Como el Service Locator realiza este trabajo, puede asumir las llamadas de red requeridas para buscar y crear objetos de negocio.

• Mejora el Rendimiento del Cliente mediante el Caché El Service Locator puede poner en un caché los objetos y referencias a objetos del contexto inicial para eliminar actividad JNDI inncesaria que ocurre cuando se obtiene el contexto inicial u otro objetos. Esto mejora el rendimiento de la aplicación.

Código de Ejemplo

Implementar el Patrón Service Locator

Aquí podemos ver un ejemplo de código para la implementación del patrón Service Locator.

package corepatterns.apps.psa.util; import java.util.*; import javax.naming.*; import java.rmi.RemoteException; import javax.ejb.*; import javax.rmi.PortableRemoteObject; import java.io.*; public class ServiceLocator { private static ServiceLocator me; InitialContext context = null; private ServiceLocator() throws ServiceLocatorException { try { context = new InitialContext(); } catch(NamingException ne) { throw new ServiceLocatorException(...); } }

Page 88: Catálogo de Patrones de Diseño J2EE

// Returns the instance of ServiceLocator class public static ServiceLocator getInstance() throws ServiceLocatorException { if (me == null) { me = new ServiceLocator(); } return me; } // Converts the serialized string into EJBHandle // then to EJBObject. public EJBObject getService(String id) throws ServiceLocatorException { if (id == null) { throw new ServiceLocatorException(...); } try { byte[] bytes = new String(id).getBytes(); InputStream io = new ByteArrayInputStream(bytes); ObjectInputStream os = new ObjectInputStream(io); javax.ejb.Handle handle = (javax.ejb.Handle)os.readObject(); return handle.getEJBObject(); } catch(Exception ex) { throw new ServiceLocatorException(...); } } // Returns the String that represents the given // EJBObject's handle in serialized format. protected String getId(EJBObject session) throws ServiceLocatorException { try { javax.ejb.Handle handle = session.getHandle(); ByteArrayOutputStream fo = new ByteArrayOutputStream(); ObjectOutputStream so = new ObjectOutputStream(fo); so.writeObject(handle); so.flush(); so.close(); return new String(fo.toByteArray()); } catch(RemoteException ex) { throw new ServiceLocatorException(...); } catch(IOException ex) { throw new ServiceLocatorException(...); } return null; } // Returns the EJBHome object for requested service // name. Throws ServiceLocatorException If Any Error // occurs in lookup public EJBHome getHome(String name, Class clazz) throws ServiceLocatorException { try { Object objref = context.lookup(name); EJBHome home = (EJBHome) PortableRemoteObject.narrow(objref, clazz); return home; } catch(NamingException ex) { throw new ServiceLocatorException(...); } } }

Implementar la Estrategia Type Checked Service Locator

Abajo puedes ver el código de ejemplo para la implementación de la estrategia Type Checked Service Locator:

package corepatterns.apps.psa.util; // imports ... public class ServiceLocator { // singleton's private instance private static ServiceLocator me; static { me = new ServiceLocator();

Page 89: Catálogo de Patrones de Diseño J2EE

} private ServiceLocator() {} // returns the Service Locator instance static public ServiceLocator getInstance() { return me; } // Services Constants Inner Class - service objects public class Services { final public static int PROJECT = 0; final public static int RESOURCE = 1; } // Project EJB related constants final static Class PROJECT_CLASS = ProjectHome.class; final static String PROJECT_NAME = "Project"; // Resource EJB related constants final static Class RESOURCE_CLASS = ResourceHome.class; final static String RESOURCE_NAME = "Resource"; // Returns the Class for the required service static private Class getServiceClass(int service){ switch( service ) { case Services.PROJECT: return PROJECT_CLASS; case Services.RESOURCE: return RESOURCE_CLASS; } return null; } // returns the JNDI name for the required service static private String getServiceName(int service){ switch( service ) { case Services.PROJECT: return PROJECT_NAME; case Services.RESOURCE: return RESOURCE_NAME; } return null; } /* gets the EJBHome for the given service using the ** JNDI name and the Class for the EJBHome */ public EJBHome getHome( int s ) throws ServiceLocatorException { EJBHome home = null; try { Context initial = new InitialContext(); // Look up using the service name from // defined constant Object objref = initial.lookup(getServiceName(s)); // Narrow using the EJBHome Class from // defined constant Object obj = PortableRemoteObject.narrow( objref, getServiceClass(s)); home = (EJBHome)obj; } catch( NamingException ex ) { throw new ServiceLocatorException(...); } catch( Exception ex ) { throw new ServiceLocatorException(...); } return home; } }

El código de cliente para utilizar el Service Locator mediante está estrategía, se podría parecer a esto:

public class ServiceLocatorTester { public static void main( String[] args ) { ServiceLocator serviceLocator = ServiceLocator.getInstance(); try {

Page 90: Catálogo de Patrones de Diseño J2EE

ProjectHome projectHome = (ProjectHome) serviceLocator.getHome( ServiceLocator.Services.PROJECT ); } catch( ServiceException ex ) { // client handles exception System.out.println( ex.getMessage( )); } } }

Esta estrategia trata sobre como aplicar el chequeo de tipos a la búsqueda del cliente. Encapsula los valores estáticos del servicio dentro del ServiceLocator y crea una clase Services interna, que declara las constantes del servicio (PROJECT y RESOURCE). El cliente de prueba obtiene un ejemplar del ServiceLocator y llama a getHome(), pasándole PROJECT. Entonces ServiceLocator obtiene el nombre de la entrada JNDI y la clase Home y devuelve el EJBHome.

Patrones Relacionados

• Business Delegate El patrón Business Delegate utiliza a Service Locator para obtener acceso a los objetos de negocio. Esto separa la complejidad de la localización del servicio del Business Delegate, rebajando el acoplamiento entre ellos e incrementando la manejabilidad.

• Session Facade El patrón Session Facade utiliza a Service Locator para obtener acceso a beans enterprise que están implicados en el flujo de trabajo. Session Facade podría utilizar directamente el patrón Service Locator o delegar el trabajo en un Business Delegate.

• Transfer Object Assembler El patrón Transfer Object Assembler utiliza a Service Locator para obtener acceso a varios beans enterprise que necesita para acceder a construir su objeto Transfer Object compuesto. Este patrón podría utilizar directamente el patrón Service Locator o delegar el trabajo en un Business Delegate.

Page 91: Catálogo de Patrones de Diseño J2EE

Catálogo de Patrones de Diseño J2EE. Y II: Capas de Negocio y de Integración Autor: Sun

Traductor: Juan Antonio Palos (Ozito)

• Session Facade o Contexto o Problema o Causas o Solución

� Estructura � Participantes y Colaboraciones � Estrategias

o Consecuencias o Código de Ejemplo

� Implementar el Session Facade o Patrones Relacionados

Session Facade

Contexto

Los Beans Enterprise encapsulan lógica y datos de negocio y exponen sus interfaces, y con ellos la complejidad de los servicios distribuidos, a la capa de cliente.

Problema

En un entorno de la aplicaciones multicapa de la Plataforma Java 2, se pueden encontrar los siguientes problemas:

• Acoplamiento fuerte, que provoca la dependencia directa entre los clientes y los objetos de negocio.

• Demasiadas llamadas a métodos entre el cliente y el servidor, abocando a problemas de rendimiento de la red.

• Falta de una estrategia de acceso uniforme de los clientes, exponiendo los objetos de negocio a una mala utilización.

Una aplicación J2EE multicapa tiene numerosos objetos del lado del servidor que se implementan como beans enterprise. Además, otros objetos arbitrarios podrían proporcionar servicios, datos o las dos cosas. A estos objetos los conocemos colectivamente como objetos de negocio, ya que encapsulan datos y lógica de negocio.

Las aplicaciones J2EE implementan objetos de negocio que proporcionan servicios de procesamiento como beans de sesión. Los objetos de negocio genéricos que representan una vista de un objeto de almacenamiento persistente y que es compartido por varios usuarios, normalmente se implementan como beans de entidad.

Los clientes de la aplicación necesitan acceder a los objetos de negocio para cumplir sus responsabilidades y para cumplir los requerimientos del usuario. Los clientes pueden interactúar directamente con estos objetos de negocio porque éstos exponen sus interfaces. Cuando exponemos nuestros objetos de negocio al cliente, el cliente debe entender y ser responsable de las relaciones con los objetos de datos de negocio, y debe poder manejar el flujo del proceso de negocio.

Sin embargo, la interacción directa entre el cliente y los objetos de negocio implica un acoplamiento fuerte entre los dos, y dicho acoplamiento hace que el cliente dependa de la implementación de los objetos de negocio. Dependencia directa significa que el cliente debe representar e implementar las interacciones complejas teniendo en cuenta la búsqueda y creación de objetos, y debe manejar la relación entre los

Page 92: Catálogo de Patrones de Diseño J2EE

objetos de negocio participantes así como entender la responsabilidad de la demarcación de transaciones.

Según se van incrementando los requerimientos del cliente, también se incrementa la complejidad de las interacciones entre los distintos objetos de negocio. El cliente se hace de mayor tamaño y más complejo para cumplir esos requerimientos. El cliente se vuelve muy susceptible a los cambios en la capa de objetos de negocio; además, exponemos inncesariamente el cliente a las complejidades subyacentes del sistema.

El acoplamiento fuerte entre objetos también se obtiene cuando los objetos manejan sus relaciones entre ellos mismos. Fruecuentemente, no está claro donde se manejan las relaciones. Esto provoca la complejidad de las relaciones entre los objetos de negocio y la rigidez en la aplicación. Esta falta de flexibilidad hace las aplicaciones menos manejables cuando se necesita hacer cambios.

Cuando acceden a beans enterprise, los clientes interactúan con objetos remotos. Se podrían producir problemas de rendimiento de red si el cliente interactúa directamente con todos los objetos de negocio participantes. Cuando se invoca a beans enterprise, cada llamada del cliente potencialmente es una llamada a un método remoto. Según se incrementa el número de participantes en un escenario, se incrementa el número de estas llamadas remotas. Y según aumentan las llamadas remotas, también se incrementa "el parloteo" entre el cliente y los objetos de negocio del lado del servidor . Esto podría resultar en una degradación del rendimiento de la red, porque el gran volumen de llamadas a métodos remotos incrementa la cantidad de interacciones sobre la capa de red.

Aparece otro problema cuando un cliente interactúa directamente con los objetos de negocio. Como los objetos de negocio se exponen directamente a los clientes, no hay una estrategia unificada para acceder a ellos. Sin dicha estrategia uniforme de acceso por parte del cliente, los objetos de negocio se ven expuestos a los clientes y se podría reducir su utilización consistente.

Causas

• Proporcionar a los clientes un interface sencillo que oculte todas interacciones complejas entre los componentes de negocio.

• Reducir el número de objetos de negocio que se exponen al cliente a través de la capa de servicio sobre la red.

• Ocultar al cliente las interacciones y las interdependencias entre los componentes de negocio. Esto proporciona una mejor manejabilidad, centralización de interacciones (responsabilidades), mayor flexibilidad, y mayor habilidad para soportar los cambios.

• Proporcionar una capa de servicio uniforme para separar la implementación de los objetos de negocio de la abstracción del servicio de negocio.

• Evitar la exposición directa de los objetos de negocio a los clientes para mantener el acoplamiento entre las dos capas al mínimo.

Solución

Usar un bean de sesión como una fachada (facade) para encapsular la complejidad de las interacciones entre los objetos de negocio participantes en un flujo de trabajo. El Session Facade maneja los objetos de negocio y proporciona un servicio de acceso uniforme a los clientes.

Session Facade abstrae las interacciones de los objetos de negocio y proporciona una capa de servicio que expone sólo los interfaces requeridos. Así, oculta a la vista de los clientes las interacciones complejas entre los participantes. El Session Facade controla las interacciones entre los datos de negocio y los objetos de servicio de negocio que participan en el flujo de trabajo, y encapsula la lógica de negocio asociada con los requerimientos, Así, el bean de sesión (representando al Session Facade) maneja las relaciones entre los objetos de negocio. El bean de sesión también maneja el ciclo de vida de los participantes creando, localizando (buscando), modificando, y borrándolos según lo requiera el flujo de trabajo. En una aplicación compleja, el Session Facade podría delegar este control del estilo de vida en un objeto separado. Por ejemplo, para controlar el estilo de vida de los beans de sesión e identidad participantes, Session Facade podría delegar este trabajo a un objeto Service Locator.

Es importante examinar las relaciones entre los objetos de negocio. Algunas de estas relaciones son temporales, lo que significa que la relación es aplicable sólo a esa interacción o escenario. Otras relaciones podrían ser más permanentes. Las relaciones temporales se modelan mejor como flujos de trabajo en una fachada, donde la fachada maneja las relaciones entre los objetos de negocio. Las

Page 93: Catálogo de Patrones de Diseño J2EE

relaciones permanentes entre dos objetos de negocio deberían estudiarse para determinar qué objeto de negocio (si no ámbos) mantiene la relación.

Casos de Utilización y Session Facades

Entonces, ¿cómo podemos identificar el Session Facades estudiando casos de uso? Mapear cada caso a un Session Facade resultará en demasiados Session Facades. Esto derrota la intención de tener menos beans de sesión genéricos. En su lugar,cuando derivemos los Session Facades durante nuestro modelado, debemos consolidarlos dentro de un menor número de beans de sesión basándonos en algún particionamiento lógico.

Por ejmplo, para una aplicación de banca, podríamos agrupar las interacciones relacionadas parar manejar una cuenta en una sola fachada. Los casos de utilización Crear una Nueva Cuenta, Cambiar Información de la Cuenta, Ver información de la Cuente, etc. tratarán con un objeto de entidad genérico llamado Account. No se recomienda crear una fachada para cada caso de utilización. Así, las funciones requeridas para soportar estos usos relacionados se podrían agrupar en un sólo Session Facade llamado AccountSessionFacade.

En este caso, el Session Facade se convertirá en un controlador genérico con métodos de alto nivel que puede facilitar cada interacción (es decir, createNewAccount, changeAccount, getAccount). Por lo tanto, recomendamos que diseñes los Session Facades para agregar un grupo de interacciones relacionadas en un sólo Session Facade. Esto resulta en menos Session Facades para la aplicación, y aprovecha los beneficios del patrón Session Facade.

Estructura

La siguiente figura representa el diagrama de clases del patrón Session Facade.

Participantes y Colaboraciones

Page 94: Catálogo de Patrones de Diseño J2EE

La siguiente figura contiene el diagrama de secuencia que muestra las interacciones de un Session Facade con dos beans de entidad, un bean de sesión y un DAO, todos actuando como participantes en el cumplimiento de la petición del cliente.

Client

Representa al cliente del Session Facade, que necesita acceder al servicio de negocio. Este cliente puede ser otro bean de sesión (Session Facade) en la misma capa de negocio o un Business Delegate en otra capa.

SessionFacade

El SessionFacade se implementa como un bean de sesión. Maneja la relaciones entre varios objetos de negocio y proporciona una abstracción de alto nivel para el cliente. El SessionFacade ofrece acceso genérico a los BusinessObject participantes representados por la llamada Invoke en el bean de sesión.

BusinessObject

BusinessObject es un objeto de rol que facilita la aplicación de diferentes estraegias, como beans de sesión, de entidad o DAO. Un BusinessObject proporciona datos y/o algún servicio del diagrama de clases. El SessionFacade interactúa con varios ejemplares de BusinessObject para proporcionar el servicio.

Estrategias

Session Facade es un objeto controlador de la capa de negocio que controla las interacciones entre el cliente y los datos de negocio y los objetos de servicio de negocio. En una aplicación compleja, podría haber numerosos Session Facades que pueden intermediar entre el cliente y esos objetos. Podemos identificar dónde podría ser útil un Session Facade estudiando los requerimientos e interacciones del cliente que normalmente se documentan en casos de utilización y escenarios. Este análisis nos permite identificar una capa controladora -- compuesta de Session Facades -- que puede actuar como fachadas para estos escenarios.

Stateless Session Facade

Page 95: Catálogo de Patrones de Diseño J2EE

Cuando se implemente Session Facade, primero debemos decidir si el bean del Facade Session va a ser un bean de sesión con o sin estado. Basamos esta decesión en los procesos de negocio que el Session Facade está modelando.

Un proceso de negocio que sólo necesita una llamada a un método para completar el servicio es un proceso de negocio no conversacional. Dichos procesos son ideales para implementarlos utilizando beans de sesión sin estado.

Un estudio cuidadoso de estos casos de utilización y escenarios nos permite determinar las definiciones del Session Facade. Si el caso de utilización no es conversacional, los clientes inician la utilización con un único método en el Session Facade. Cuando el método se completa, el caso de utilización también se completa. No hay necesidad de grabar el estado conversacional entre una llamada al método y la siguiente. En este escenario, el Session Facade se puede implementar como un bean de sesión sin estado.

Stateful Session Facade

Un proceso de negocio que necesita varias llamadas a métodos para completar el servicio es un proceso de negocio conversacional. El estado conversacional debe grabarse entre cada invocación del método por parte del cliente. En este escenario, sería una mejor aproximación utilizar un bean de sesión con estado para implementar el Session Facade.

En las dos estrategias anteriores, el rol del objeto de negocio se puede cumplir de diferentes formas, como se explica más abajo.

Estrategias para Objetos de Negocio

Podemos implementar un objeto de negocio como un bean de sesión, un bean de entidad, DAO, o un objeto normal Java. Las siguientes estrategias discuten todas estas opciones:

Session Bean

El objeto de negocio se puede implementar como un bean de sesión. El bean de sesión normalmente proporciona un servicio de negocio y, en algunos casos, también podría proporcionar datos de negocio. Cuando dicho bean de sesión necesite acceder a los datos, podría utilizar un DAO para manipularlos. El Session Facade puede envolver uno o más beans de sesión orientados a servicio u orientados a datos actuando como objetos de negocio.

Entity Bean

Representar el objeto de negocio mediante un bean de entidad es el uso más común de Session Facade. Cuando varios beans de entidad participan en el caso de utilización, no es necesario exponerlos todos al cliente. En su lugar, el Session Facade puede envolver estos beans de entidad y proporcionar métodos genéricos para realizar las funciones de negocio requeridas, y así ocultar la complejidad de las interacciones de beans de entidad.

Data Access Object (DAO)

El Session Facade puede utilizar directamente uno o más DAOs para representar los datos de negocio. Esto se hace cuando la aplicación es tan simple que no requiere beans de entidad, o cuando la arquitectura de la aplicación está basada sólo en beans de sesión y no utiliza beans de entidad. Utilizando DAOs dentro de beans de sesión se simula parcialmente la naturaleza persistente de los beans de entidad.

La aplicación podría necesitar los servicios porporcionados por un objeto Java normal (es decir, un objeto que no es un bean enterprise ni un DAO, aunque un DAO se podría considerar un objeto Java normal). En dicho caso, el Session Facade accede a este objeto arbitrario para proporcionar las funcionalidades necesarias.

Page 96: Catálogo de Patrones de Diseño J2EE

Consecuencias

• Introduce la Capa Controladora para la Capa de Negocio Los Session Facades pueden representar una capa controladora entre los clientes y la capa de negocios, identificado en el análisis del modelo. Un Session Facade acompasa las interacciones entre el cliente y los componentes de negocio. En una aplicación sofisticada, podemos identicar numerosos Session Facades que pueden intermediar entre el cliente y los objetos de la capa de negocio. Para aplicaciones más simples, se podría creer que un Session Facade no añade mucho valor, ya que actúa principalmente como un proxy entre las peticiones del cliente y un componente de negocio. Sin embargo, según se van complicando las aplicaciones con el tiempo, utilizar un Session Facade desde el principio nos benficiará en un estado posterior.

• Expone un Interface Uniforme Las interacciones entre los componentes de negocio pueden ser muy complejas. Un patrón Session Facade abstrae esta complejidad y le presenta al cliente un interface sencillo que es fácil de entender y de utilizar. Aplicando un Session Facade, podemos diseñar una capa de servicio que exponga interfaces sencillos al sistema como una totalidad. Así una fachada proporciona una capa de acceso uniforme para todos los tipos de clientes y puede proteger y ocultar los componentes de negocio participantes.

• Reduce el Acoplamiento, Incrementa la Manejabilidad Utilizar un Session Facade desacopla los objetos de negocio de los clientes, y así reduce el fuerte acoplamiento y la dependencia de los clientes de los objetos de negocio. Es mejor utilizar un Session Facade para manejar el flujo de trabajo entre los objetos de negocio, en vez de hacer que los objetos de negocio se preocupen unos de otros. Un objeto de negocio sólo debería ser responsable de su propio control (datos y lógica). Las interacciones de objetos de negocio se puede abstraer dentro de un flujo de trabajo en una fachada. Esto proporciona una mejor manejabilidad, centralización de interacciones (responsabilidad y flujo de trabajo), mayor flexibilidad, y mayor habilidad para soportar los cambios. Separar el flujo de trabajo en un Session Facade elimina la dependencia directa del cliente de los objetos participantes y promueve un diseño flexible. Aunque los cambios en los participantes podrían requerir cambios en el Session Facade, centralizar el flujo de trabajo en la fachada hace que dichos cambios sean más manejables. Podemos cambiar sólo el Session Facade en vez de tener que cambiar todos los clientes. El código de los clientes también se simplifica porque ahora delegan las responsabilidades del flujo de trabajo en el Session Facade. El cliente ya no maneja las interacciones complejas del flujo de trabajo entre los objetos de negocio, ni se preocupa de las interdependencias entre los objetos de negocio.

• Mejora el Rendimiento, Reduce los Métodos Específicos Session Facade también impacta en el rendimiento. Reduce la sobrecarga de la red entre el cliente y el servidor porque su uso elimina la interacción directa entre el cliente y los datos y objetos de negocio. En su lugar, todas las interacciones se enrutan mediante el Session Facade de manera genérica. El Session Facade y sus participantes se acercan unos a otros, haciendo más eficiente para la fachada el manejo de las interacciones entre los objetos participantes. Todas las trasferencias de datos y las llamadas a métodos desde la fachada a los participantes se realizan presumiblemente sobre una red de relativa alta velocidad. El rendimiento de red se puede ajustar proporcionando una máxima salida aplicando el patrón Transfer Object donde se pueda aplicar a los objetos participantes.

• Proporciona Acceso Genérico Session Facade se ha pensado para ser una abstración genérica del flujo de trabajo. Así, no es deseable tener un Session Facade por cada interacción con un bean de entidad, que podría representar una abstracción específica en vez de una genérica. Se debe analizar la interacción entre el cliente y los servicios de la aplicación, utilizando casos de utilización y escenarios para determinar la bulgaridad de la fachada. Determinamos la especificidad óptima del Session Facade para la aplicación particionando la aplicación en subsistemas lógicos y proporcionando un Session Facade para cada subsistema. Sin embargo, proporcionar una sóla fachada para todo el sistema puede resultar en un Session Facade muy grande cuyos numerosos métodos lo hagan ineficiente. Un sólo Session Face podría ser suficiente para aplicaciones muy simples que no requieran subsistemas.

• Centraliza el Control de Seguridad Las políticas de seguridad de la aplicación se pueden controlar al nivel del Session Facade, ya que ésta es la capa presentada a los clientes. Debido al acceso genérico del Session Facade, es más fácil y más manejable definir políticas de seguridad en este nivel que al nivel de los componentes de negocio. Los componentes de negocio ofrecen puntos de control específicos. Es más fácil manejar la seguidad mediante Session Facades que proporcionan acceso genérico, porque son relativamente pocos los métodos genéricos que tenemos que manejar de forma segura.

• Centraliza el Control de Transaciones Como el Session Facade representa el flujo de trabajo para los casos de utilización, es más

Page 97: Catálogo de Patrones de Diseño J2EE

lógico aplicar el control de transaciones a nivel del Session Facade. El control de transación centralizado tiene ventajas similares a las de la seguridad centralizada. La fachada ofrece un lugar central para manejar y definir el control de transaciones de un forma genérica. Hay mucho más trabajo que hacer cuando el control de transación se individualiza para cada componentes de negocio, especialmene porque son más específicos que la fachada. La no utilización de un Session Facade, además de hacer que el cliente acceda directamente a los beans enterprise, pone los límites de la transación en el lado de cliente y puede producir resultados no deseados.

• Expone Menos Interfaces Remotos a los Clientes Los clientes que interactúan directamente con los datos y servicios de negocio causan un incremento de la charlatanería entre el cliente y el servidor. Este incremento podría degradar el rendimiento de la red. Todos los accesos a los objetos de negocio se deben hacer mediante un alto nivel de abstracción representado en el fachada. Como la fachada presenta un mecanismo de acceso genérico a los componentes de negocio, se reduce el número de componentes de negocio que se exponen al cliente. Por lo tanto, se reduce el ámbito para la degradación del rendimiento de la aplicación debido al número limitado de interacciones entre los clientes y el Session Facade en comparación a la interacción directa entre el cliente y los componentes de negocio individuales.

Código de Ejemplo

Implementar el Session Facade

Considerando una Aplicación de Servicios Profesionales (PSA), donde el flujo de trabajo relacionado con los beans de entidad (como Project, Resource) se encapsula en ProjectResourceManagerSession, implementado utilizando el patrón Session Facade. El siguiente fragmento de código muestra la interacción con los beans de entidad Resource y Project, así como con otros componentes de negocio, como Value List Handlers y Transfer Object Assemblers.

package corepatterns.apps.psa.ejb; import java.util.*; import java.rmi.RemoteException; import javax.ejb.*; import javax.naming.*; import corepatterns.apps.psa.core.*; import corepatterns.util.ServiceLocator; import corepatterns.util.ServiceLocatorException; // Note: all try/catch details not shown for brevity. public class ProjectResourceManagerSession implements SessionBean { private SessionContext context; // Remote references for the // entity Beans encapsulated by this facade private Resource resourceEntity = null; private Project projectEntity = null; ... // default create public void ejbCreate() throws CreateException { } // create method to create this facade and to // establish connections to the required entity // beans // using primary key values public void ejbCreate( String resourceId, String projectId, ...) throws CreateException, ResourceException { try { // locate and connect to entity beans connectToEntities(resourceId, projectId, ...); } catch(...) { // Handle exceptions } } // method to connect the session facade to its // entity beans using the primary key values private void connectToEntities (

Page 98: Catálogo de Patrones de Diseño J2EE

String resourceId, String projectId) throws ResourceException { resourceEntity = getResourceEntity(resourceId); projectEntity = getProjectEntity(projectId); ... } // method to reconnect the session facade to a // different set of entity beans using primary key // values public resetEntities(String resourceId, String projectId, ...) throws PSAException { connectToEntities(resourceId, projectId, ...); } // private method to get Home for Resource private ResourceHome getResourceHome() throws ServiceLocatorException { return ServiceLocator. getInstance().getHome( "ResourceEntity", ResourceHome.class); } // private method to get Home for Project private ProjectHome getProjectHome() throws ServiceLocatorException { return ServiceLocator. getInstance().getHome( "ProjectEntity", ProjectHome.class); } // private method to get Resource entity private Resource getResourceEntity( String resourceId) throws ResourceException { try { ResourceHome home = getResourceHome(); return (Resource) home.findByPrimaryKey(resourceId); } catch(...) { // Handle exceptions } } // private method to get Project entity private Project getProjectEntity(String projectId) throws ProjectException { // similar to getResourceEntity ... } // Method to encapsulate workflow related // to assigning a resource to a project. // It deals with Project and Resource Entity beans public void assignResourceToProject(int numHours) throws PSAException { try { if ((projectEntity == null) || (resourceEntity == null)) { // SessionFacade not connected to entities throw new PSAException(...); } // Get Resource data ResourceTO resourceTO = resourceEntity.getResourceData(); // Get Project data ProjectTO projectTO = projectEntity.getProjectData(); // first add Resource to Project projectEntity.addResource(resourceTO); // Create a new Commitment for the Project CommitmentTO commitment = new CommitmentTO( ...); // add the commitment to the Resource projectEntity.addCommitment(commitment); } catch(...) { // Handle exceptions } } // Similarly implement other business methods to // facilitate various use cases/interactions public void unassignResourceFromProject()

Page 99: Catálogo de Patrones de Diseño J2EE

throws PSAException { ... } // Methods working with ResourceEntity public ResourceTO getResourceData() throws ResourceException { ... } // Update Resource Entity Bean public void setResourceData(ResourceTO resource) throws ResourceException { ... } // Create new Resource Entity bean public ResourceTO createNewResource(ResourceTO resource) throws ResourceException { ... } // Methods for managing resource's blockout time public void addBlockoutTime(Collection blockoutTime) throws RemoteException,BlockoutTimeException { ... } public void updateBlockoutTime( Collection blockoutTime) throws RemoteException, BlockoutTimeException { ... } public Collection getResourceCommitments() throws RemoteException, ResourceException { ... } // Methods working with ProjectEntity public ProjectTO getProjectData() throws ProjectException { ... } // Update Project Entity Bean public void setProjectData(ProjectTO project) throws ProjectException { ... } // Create new Project Entity bean public ProjectTO createNewProject(ProjectTO project) throws ProjectException { ... } ... // Other session facade method examples // This proxies a call to a Transfer Object Assembler // to obtain a composite Transfer Object. // See Transfer Object Assembler pattern public ProjectCTO getProjectDetailsData() throws PSAException { try { ProjectTOAHome projectTOAHome = (ProjectTOAHome) ServiceLocator.getInstance().getHome( "ProjectTOA", ProjectTOAHome.class); // Transfer Object Assembler session bean ProjectTOA projectTOA = projectTOAHome.create(...); return projectTOA.getData(...); } catch (...) { // Handle / throw exceptions } } // These method proxies a call to a ValueListHandler // to get a list of projects. See Value List Handler // pattern. public Collection getProjectsList(Date start, Date end) throws PSAException { try { ProjectListHandlerHome projectVLHHome = (ProjectVLHHome) ServiceLocator.getInstance().getHome(

Page 100: Catálogo de Patrones de Diseño J2EE

"ProjectListHandler", ProjectVLHHome.class); // Value List Handler session bean ProjectListHandler projectListHandler = projectVLHHome.create(); return projectListHandler.getProjects( start, end); } catch (...) { // Handle / throw exceptions } } ... public void ejbActivate() { ... } public void ejbPassivate() { context = null; } public void setSessionContext(SessionContext ctx) { this.context = ctx; } public void ejbRemove() { ... } }

En el siguiente ejemplo podemos ver el interface remoto para el Session Facade:

package corepatterns.apps.psa.ejb; import java.rmi.RemoteException; import javax.ejb.*; import corepatterns.apps.psa.core.*; // Note: all try/catch details not shown for brevity. public interface ProjectResourceManager extends EJBObject { public resetEntities(String resourceId, String projectId, ...) throws RemoteException, ResourceException ; public void assignResourceToProject(int numHours) throws RemoteException, ResourceException ; public void unassignResourceFromProject() throws RemoteException, ResourceException ; ... public ResourceTO getResourceData() throws RemoteException, ResourceException ; public void setResourceData(ResourceTO resource) throws RemoteException, ResourceException ; public ResourceTO createNewResource(ResourceTO resource) throws ResourceException ; public void addBlockoutTime(Collection blockoutTime) throws RemoteException,BlockoutTimeException ; public void updateBlockoutTime(Collection blockoutTime) throws RemoteException,BlockoutTimeException ; public Collection getResourceCommitments() throws RemoteException, ResourceException; public ProjectTO getProjectData() throws RemoteException, ProjectException ; public void setProjectData(ProjectTO project) throws RemoteException, ProjectException ; public ProjectTO createNewProject(ProjectTO project) throws RemoteException, ProjectException ; ...

Page 101: Catálogo de Patrones de Diseño J2EE

public ProjectCTO getProjectDetailsData() throws RemoteException, PSAException ; public Collection getProjectsList(Date start, Date end) throws RemoteException, PSAException ; ... }

En el siguiente código podemos ver el interface home para el Session Facade:

package corepatterns.apps.psa.ejb; import javax.ejb.EJBHome; import java.rmi.RemoteException; import corepatterns.apps.psa.core.ResourceException; import javax.ejb.*; public interface ProjectResourceManagerHome extends EJBHome { public ProjectResourceManager create() throws RemoteException,CreateException; public ProjectResourceManager create(String resourceId, String projectId, ...) throws RemoteException,CreateException; }

Patrones Relacionados

• Facade [GoF] Session Facade está basada en el patrón de diseño Facade.

• Data Access Object Una de las estrategias para los componentes de negocio del patrón Session Facade es utilizar el DAO. Este puede ser el caso de aplicaciones simples diseñadas utilizando beans de sesión y DAOs en lugar de beans de entidad.

• Service Locator Session Facade es un objeto genérico que permite la encapsulación del flujo de trabajo manejando datos de negocio y servicios de negocio. Los objetos de datos de negocio pueden ser beans de entidad o DAOs, y los servicios de negocio pueden ser beans de sesión u otros objetos que proporcionen servicios. Session Facade puede utilizar el patrón Service Locator para reducir la complejidad del código y explotar los beneficios ofrecidos por Service Locator.

• Business Delegate Busines Delegate utiliza un Session Facade cuando el cliente solicita acceso a servicios de negocio. El Business Delegate hace de proxy o adapta la petición del cliente a un Session Facade que proporciona el servicio requerido.

• Broker [POSA1] Session Facade realiza el rol de un broker para desacoplar los beans de entidad de sus clientes.

Page 102: Catálogo de Patrones de Diseño J2EE

Catálogo de Patrones de Diseño J2EE. Y II: Capas de Negocio y de Integración Autor: Sun

Traductor: Juan Antonio Palos (Ozito)

• Transfer Object o Contexto o Problema o Causas o Solución

� Estructura � Participantes y Responsabilidades � Estrategias

o Consecuencias o Código de Ejemplo

� Implementar el patrón Transfer Object � Implementar la Estrategia Updatable Transfer Objects � Implementar la Estrategia Multiple Transfer Objects � Implementar la Estrategia Entity Inherits Transfer Object � Implementar la Estrategia Transfer Object Factory

o Patrones Relacionados

Transfer Object

Contexto

Las aplicaciones cliente necesitan intercambiar datos con Beans Enterprise.

Problema

Las aplicaciones de la plataforma J2EE implementan componentes de negocio del lado del servidor como beans de sesión y de entidad. Algunos métodos expuestos por los componentes de negocio devuelven datos al cliente. Algunas veces, el cliente invoca a los métodos get de un objeto de negocio varias veces para obtener todos los valores de los atributos.

Los beans de sesión representan los servicios de negocio y no se comparten entre usuarios. Un bean de sesión proporciona métodos de servicios genéricos cuando se implementan para el patrón Session Facade.

Por otro lado, los beans de entidad, son objetos transacionales, multiusuario que representan datos persistentes. Un bean de entidad expone los valores de los atributos proporcionando un método accesor (también referidos como métodos get) por cada atributo que desea exponer.

Toda llamada a método hecha al objeto de servicio de negocio, ya sea a un bean de entidad o a un bean de sesión, potencialmente es una llamada remota. Así, en una aplicación de JavaBeans Enterprise (EJB) dichas llamadas remotas usan la capa de red sin importar la proximidad del cliente al bean, creando una sobrecarga en la red. Las llamadas a métodos de beans enterprise podría penetrar las capas de red incluso si tanto el cliente como el contenedor EJB que mantiene el bean de entidad se están ejecutando en la misma JVM (Máquina Virtual Java), el mismo Sistema Operativo o máquina física. Algunos vendedores podrían implementar mecanismos para reducir esta sobrecarga utilizando una aproximación de acceso más directa pasando por encima de la red.

Según se incrementa la utilización de estos métodos remotos, el rendimiento de la aplicación se puede degradar significativametne. Por lo tanto, utilizar varias llamadas a métodos get que devuelven simples valores de atributos es ineficiente para obtener valores de datos desde un bean enterprise.

Causas

Page 103: Catálogo de Patrones de Diseño J2EE

• Todos los accesos a un bean enterprise se realizan mediante interfaces remotos. Cada llamada a un bean enterprise potencialmente es una llamada a un método remoto con sobrecarga de red.

• Normalmente, las aplicaciones tienen transaciones de lectura con mayor frecuencia que las de actualización. El cliente solicita los datos desde la capa de negocio para su presentación, y otros tipos de procesamientos de sólo-lectura. El cliente actualiza los datos de la capa de negocio con mucha menos frecuencia con la que los lee.

• El cliente normalmente solicita valores que son algo más que un atributo o que son dependendientes del objeto de un bean enterprise. Así, el cliente podría invocar varias llamadas remotas para obtener los datos necesarios.

• El número de llamadas que un cliente hace al bean enterprise impactan en el rendimiento de la red. Las aplicaciones charlatanas -- aquellas que incremenetan el trafico entre las capas del cliente y del servidor -- suelen degradar el rendimiento de la red.

Solución

Utilizar un Transfer Object para encapsular los datos de negocio. Se utiliza una única llamada a un método para envíar y recuperar el Transfer Object. Cuando el cliente solicita los datos de negocio al bean enterprise, éste puede construir el Transfer Object, rellenarlo con sus valores de atributos y pasarlo por valor al cliente.

Los clientes normalmente solicitan más de un valor a un bean enterprise. Para reducir el número de llamadas remotas y evitar la sobrecarga asociada, es mejor el Transfer Objects para transportar los datos desde el bean enteprise al cliente.

Cuando un bean enterprise utiliza un Transfer Object, el cliente hace una sola llamada a un método remoto del bean enterprise para solicitar el Transfer Object en vez de numerosas llamadas remotas para obtener valores de atributos individuales. Entonces el bean enterprise construye un nuevo ejemplar Transfer Object, copia dentro los valores del objeto y lo devuelve al cliente. El cliente recibe el Transfer Object y puede entonces invocar los métodos accesores (o get) del Transfer Object para obtener los valores de atributos individuales del objeto Transfer Object. O, la implementación del Transfer Object podría hacer que todos los atributos fueran públicos. Como el Transfer Object se pasa por valor al cliente, todas las llamadas al ejemplar Transfer Object son llamadas locales en vez de invocaciones de métodos remotos.

Estructura

Ls siguiente figura muestra el diagrama de clases que representa el patrón Transfer Object en su forma más simple:

Page 104: Catálogo de Patrones de Diseño J2EE

Como se ve en este diagrama de clases, el Transfer Object lo construye bajo pedido el bean enterprise y lo devuelve al cliente remoto. Sin embargo, el patrón Transfer Object puede adoptar varias estrategias, dependendiendo de los requerimientos.

Participantes y Responsabilidades

La siguiente figura contiene el diagrama de secuencia que muestra las interacciones del patrón Transfer Object:

Page 105: Catálogo de Patrones de Diseño J2EE

Client

Representa al cliente del bean enterprise. El cliente puede ser una aplicación final de usuario, como es el caso de una aplicación que ha sido diseñada para acceder directamente a beans enterprise. El cliente puede utilizar Business Delegate u otro BusinessObject diferente.

BusinessObject

Representa un rol en este patrón que puede cumplir un bean de sesión, un bean de entidad, o un Data Access Object (DAO). BusinessObject es el responsable de crear el Transfer Object y devolverlo al cliente bajo pedido. El BusinessObject también podría recibir datos desde el cliente en la forma de un Transfer Object y utilizar esos datos para realizar una actualización.

TransferObject

TransferObject es un objeto Java serializable referenciado como un Transfer Object. Una clase Transfer Object podría proporcionar un constructor que acepte todos los atributos requeridos para crear el Transfer Object. El constructor podría aceptar todos los valores de atributos del bean de entidad para el que se ha diseñado el Transfer Object. Normalmente, los miembros del Transfer Object se definen como públicos, así eliminan la necesidad de los métodos get y set. Si se necesita alguna protección, los miembros podrían definirse como protected o private, y se definirían métodos get para sus valores. Si no ofrece métodos set para los valores, un Transfer Object está protegido frente a modificaciones después de su creación. Si sólo se permite la modificación de unos pocos miembros para facilitar las actualizaciones, entonces si que se de deben proporcionar métodos set. Por lo tanto, la creacción del Transfer Object varía dependiendo de los requerimientos de la aplicación.

Estrategias

Las dos primeras estrategias sen aplican cuando el bean enterprise se implementa como un bean de sesión o un bean de entidad.

Page 106: Catálogo de Patrones de Diseño J2EE

Las estrategias posteriores son aplicables sólo cuando el BusinessObject se iplementa como un bean de entidad.

Updatable Transfer Objects

En esta estrategia, el Transfer Object no sólo lleva los valores desde el BusinessObject hasta el cliente, también lleva los cambios realizados por el cliente de vuelta al objeto de negocio.

La siguiente figura es un diagrama de clases que muestra las relaciones entre el BusinessObject y el Transfer Object:

El BusinessObject crea el Transfer Object. Recuerda que un cliente podría necesitar aceder a los valores del BusinessObject no sólo para leerlos sino también para modificarlos. Para que el cliente pueda modificar los valores de los atributos del BusinessObject, éste debe proporcionar métodos mutadores. A éstos métodos también se les conoce como métodos set.

En lugar de proporcionar métodos set especificos para cada atributo, lo que resultaría en una sobrecarga de la red, el BusinessObject puede exponer un método setData() genérico que acepta un objeto Transfer Object como argumento. El Transfer Object pasado a este método contiene los valores actualizados por el cliente. Como el Transfer Object tiene que ser mutable, su clase tiene que proporcionar métodos set para todos los atributos que pueda modificar el cliente. Los métodos set del objeto Transfer Object pueden incluir validaciones a nivel de campo y chequeos de integridad si son necesarios. Una vez que el cliente obtiene un Transfer Object desde el BusinessObject, puede invocar localmente los métodos set necesarios para cambiar los valores de los atributos. Dichos cambios locales no impactan en el objeto BusinessObject hasta que se llame al método setData().

El método setData() serializa la copia que tiene el cliente del Transfer Object y la envía al BusinessObject. El BusinessObject recibe el Transfer Object modificado desde el cliente y mezcla los cambios con sus propios atributos. La operacion de mezcla podría complicar el diseño del BusinessObject y del Transfer Object; la sección sobre "Consecuencias" discute estas potenciales complicaciones. Una estrategia a utilizar aquí es actualizar sólo los atributos que han cambiado, en vez de actualizar todos los atributos. Una bandera de 'cambiado' situada en el Transfer Object se podría utilizar para determinar los atributos a actualizar, en vez de hacer una comparación directa.

Hay un impacto en el diseño cuando se usa esta estrategia en términos de propagación de actualización, sincronización y control de versión.

La siguiente figura muestra el diagram para toda la interacción de actualización:

Page 107: Catálogo de Patrones de Diseño J2EE

Multiple Transfer Objects

Algunos objetos de negocio de las aplicaciones pueden ser muy complejos. En dichos casos, es posible que un sólo objeto de negocio produzca diferentes Transfer Objects, dependiendo de la petición del cliente. Existe una relación uno-a-muchos entre el objeto de negocio y los muchos Transfer Objects que puede producir. En estas circunstancias debemos considerar la utilización de esta estrategia.

Por ejemplo, cuando el objeto de negocio se implementa como un bean de sesión, normalmente aplicando el patrón Session Facade, el bean podría interactúar con numerosos componentes de negocio para proporcionar el servicio. El bean de sesión produce su Transfer Object desde diferentes fuentes. De forma similar, cuando se implementa el BusinessObject como un bean de entidad genérico, normalmente aplicando el patrón Composite Entity, el bean de entidad tendrá relaciones complejas con varios objetos dependientes. En ambos casos, es una buena práctica proporcionar mecanismos para producir Transfer Objects que realmente representen a partes de los componentes genéricos.

Por ejemplo, en una aplicación tradicional, un Composite Entity que representa la cartera de un cliente puede ser un componente genérico muy complejo que puede producir Transfer Objects que proporcionen datos de las partes de la cartera, como información del cliente, lista de stocks, etc. Un ejemplo similar es un bean de sesión que maneja clientes y que proporciona servicios interactuando con otros muchos BusinessObjects y otros componentes para proporcionar su servicio. El bean controlador de clientes puede producir pequeños Transfer Objects, como direcciones de clientes, lista de contactos, etc., para representar partes de su modelo.

Page 108: Catálogo de Patrones de Diseño J2EE

Para ámbos escenarios, es posible adoptar y aplicar la estrategia Multiple Transfer Objects para que los componentes de negocio, tanto si son beans de sesión como de entidad, puedan crear varios tipos de Transfer Objects. En esta estrategia, la entidad de negocio proporciona varios métodos para obtener diferentes Transfer Objects. Cada uno de esos métodos crea y devuelve un tipo diferente de Transfer Object. En la siguiente figura podemos ver el diagrama de clases para esta estrategia.

Cuando un cliente necesita un Transfer Object del tipo TransferObjectA, lama al método getDataA() de la entidad, pidiendo un TransferObjectA. Cuando necesita un Transfer Object del tipo TransferObjectB, llama al método getDataB() de la entidad, pidiendo un TransferObjectB, etc. Esto se puede ver en la siguiente figura:

Page 109: Catálogo de Patrones de Diseño J2EE

Entity Inherits Transfer Object

Cuando el BusinessObject se implementa como un bean de entidad y los clientes normalmente necesitan acceder a todos los datos del bean de entidad, entonces el bean de entidad y el Transfer Object tienen los mismos atributos. En este caso, como existe una relación uno-a-uno entre el bean de entidad y su Transfer Object, el bean de entidad podría ser capaz de utilizar la herencia para evitar la duplicación de código.

En esta estrategia, el bean de entidad extiende (o desdeciende de) la clase del Transfer Object. La única asumpción es que el bean de entidad y el Transfer Object comparten las mismas definiciones de atributos. En la siguiente figura podemos ver el diagrama de clases para esta estrategia:

El TransferObject implementa uno o más métodos getData() como explicamos en la estrategia anterior. Cuando el bean de entidad desciende de esta clase Transfer Object, el cliente invoca un método getData() heredado del bean de entidad para obtener un Transfer Object.

Page 110: Catálogo de Patrones de Diseño J2EE

Así, esta estrategia elimina la duplicación de código entre el bean de entidad y el Transfer Object. También ayuda a manejar los cambios en los requerimientos del Transfer Object aislando los cambios de la clase Transfer Object y previniendo que los cambios afecten al bean de entidad.

Esta estrategia tiene un inconveniente relacionado con la herencia. Si el Transfer Object se comparte a través de su herencia, los cambios que se hagan en la clase Transfer Object afectarán a todas sus subclases, obligando potencialmente a modificar toda su descendencia. El diagrama de secuencia de la siguiente figura demuestra esta estrategia:

En la sección de Código de Ejemplo veremos esta implementación.

Transfer Object Factory

La estrategia Entity Inherits Transfer Object se puede extender para que un bean de entidad soporte múltiples Transfer Objects empleando una factoría de Transfer Object para crear Transfer Objects bajo demanda utilizando reflection. Esto resulta en una estrategia aún más dinámica para la creación de Transfer Object.

Para conseguir esto, definimos un interface diferente para cada tipo de Transfer Object que se debe devolver. La implementación que hace el bean de entidad de la superclase de Transfer Object debe implementar todos esos interfaces. Además, debemos crear una clase separada para implementar todos los interfaces definidos, como se muestra en el diagrama de clases de la siguiente figura.

Page 111: Catálogo de Patrones de Diseño J2EE

Una vez que se han definido e implementado todos los interfaces, creamos un método en la clase TransferObjectFactory al que se le pasan dos argumentos:

• El ejemplar del bean de entidad para el que se debe crear el Transfer Object. • El interface que identifica el tipo de Transfer Object a crear.

Entonces el TransferObjectFactory puede ejemplarizar un objeto de la clase correcta, configurar sus valores, y devolver el ejemplar de Transfer Object recientemente creado.

En la siguiente figura podemos ver el diagrama de secuencia para esta estrategia:

Page 112: Catálogo de Patrones de Diseño J2EE

El cliente le solicita un Transfer Object al BusinessEntity. Éste le pasa la clase del objeto Transfer Object requerido a TransferObjectFactory, que crea un nuevo Transfer Object de esa clase dada. El TransferObjectFactory usa reflection para obtener dinámicamente la información de clase para la clase del Transfer Object y construir un nuevo ejemplar. La obtención de los valores para el BusinessEntity que realiza el TransferObjectFactory se consigue utilizando invocación dinámica.

Estos son los beneficios de aplicar la estrategia Transfer Object Factory:

• Se tiene que escribir menos código para crear Transfer Objects. • La misma factoría de clases Transfer Object puede ser reutilizada por diferentes beans

enterprise. • Cuando cambia la definición de una clase Transfer Object, la factoría de objetos Transfer

Object maneja estos cambios sin ningún esfuerzo de codificación adicional. • Esto incrementa la manejabilidad y es menos propenso a errores cuando se cambian las

definiciones del Transfer Object.

La estrategia Transfer Object Factory tiene las siguienes consecuencias:

• Esta basada en el hehco de que la implementación del bean enterprise extiende (desciende) de la clase general Transfer Object.

Page 113: Catálogo de Patrones de Diseño J2EE

• El Transfer Object general necesita implementar todos los interfaces definidos para diferentes Transfer Objects que se necesiten suministar al bean de entidad.

• Se deben cumplir las convenciones de nombrado para poder hacer que esta estrategia funcione. • Como se utiliza reflection para inspeccionar y construir dinámicamente Transfer Objects, hay

una ligera pérdida de rendimiento en su construcción.Sin embargo, si se considera el tiempo total de comunicación, dicha pérdida es mínima en su comparación.

Hay un inconveniente asociado con esta estrategia. Su poder y flexibilidad se deben sopesar en comparación con la sobrecarga de rendimiento asociada con la utilización de reflection en tiempo de ejecución.

Consecuencias

• Simplifica el Bean de Entidad y el Interface Remoto El bean de entidad proporciona un método getData() para obtener el Transfer Object que contiene los valores de los atributos. Esto podría eliminarse implementando múltiples métodos get en el bean y definiéndolos en el interface remoto del bean. De forma similar, si el bean de entidad proporciona un método setData() para actualizar los valores de atributos del bean de entidad con una sola llamada a método, se podría eliminar implementando varios métodos set en el bean.

• Transfiere Más Datos en Menos Llamadas Remotas En lugar de realizar múltiples llamadas sobre la red al BusinessObject para obtener los valores de los atributos, esta solución proporciona una sola llamada a un método. Al mismo tiempo, esta única llamada obtiene una mayor cantidad de datos. Cuando consideremos la utilización de este patrón, debemos tener en cuenta el inconveniente dee el menor número de lamadas contra la mayor transmisión de datos por cada llamada. Alternativamente, podemos proporcionar ámbos tipos de métodos: métodos get y set específicos y métodos get y set genéricos. El desarrollador puede elegir la técnica apropiada según sus requerimentos.

• Reduce el Tráfico de Red Un Transfer Object transfiere los valores desde el bean de entidad al cliente en una llamada a un método remoto. El Transfer Object actúa como un transportista de datos y reduce el número de llamadas remotas requeridas para obtener los valores de los atributos del bean; y esto significa un mejor rendimiento de la red.

• Reduce la Duplicación de Código Usando las estrategias Entity Inherits Transfer Object y Transfer Object Factory, es posible reducir o eliminar la duplicación de código entre el bean de entidad y el Transfer Object. Sin embargo, con el uso de la estrategia Transfer Object Factory, se podría incrementar la complejidad de la implementación. También hay un coste asociado a la pérdida de rendimiento con esta estrategia al utilizar reflection dinámicamente. En la mayoría de los casos, la estrategia Entity Inherits Transfer Object podría ser suficiente para cumplir nuestras necesidades.

• Podría Introducir Transfer Objects Obsoletos Adoptando la estrategia Updatable Transfer Objects se permite al cliente realizar modificaciones en la copia local del Transfer Object. Una vez que se han completado las modificaciones, el cliente puede invocar el método setData() del bean de entidad y pasarle el Transfer Object modificado. El bean recibe las modificaciones y las mezcla con los valores que él tiene. Sin embargo, podría haber un problema con Transfer Objects obsoletos. El bean de entidad actualiza sus valores, pero no tiene en cuenta si otros clientes han solicitado el mismo Transfer Object con anterioridad. Estos clientes podrían contener en su copia local ejemplares de Transfer Object que no reflejan las copia real de los datos del bean. Como el bean no tiene en cuenta a estos clientes, es posible propagar la actualización de Transfer Objects obsoletos que mantienen otros clientes.

• Podría Incrementar la Complejidad Debido a la Sincronización y el Control de Versión El bean de entidad mezcla los valores modificados con sus propios valores cuando los recibe en un Transfer Object de un cliente. Sin embargo, el bean debe manejar la situación donde dos o más clientes simultáneamente solicitan actualizaciones conflictivas de los valores del bean. Permitir estas actualizaciones podría resultar en conflictos de datos. El control de versión es una forma de evitar estos conflictos. El bean de entidad puede incluir como uno de sus atributos un número de versión o una fecha de última-modificación. Este número de versión se copiaría en el Transfer Object. Una transación de actualización puede resolver conflictos utilizando el atributo del número de versión. Si un cliente que contiene un Transfer Object obsoleto intenta actualizar el bean, éste puede detectar el número de versión obsoleto e informar al cliente de esta condición de error. Entonces el cliente tiene que obtener el último Transfer Object y reintentar la actualización. En casos extremos esto podría resultar en un cliente hambriento -- el cliente nunca puede terminar su actualización.

Page 114: Catálogo de Patrones de Diseño J2EE

• Accesos y Transaciones Concurrentes Cuando dos o más clientes acceden de forma concurrente al BusinessObject, el contenedor aplica la semántica de transaciones de la arquitectura EJB. Si, para un bean entreprise, el nivel de aislamiento de transaciones se selecciona a TRANSACTION_SERIALIZED en el descriptor de despliegue, el contenedor proporciona la máxima protección a la transación y se asegura de su integridad. Por ejemplo, supongamos que el flujo de trabajo para la primera transación implica obtener un Transfer Object, luego el proceso contínua modificando el objeto BusinessObject. La segunda transación, como se han aislado las transaciones serializadas, obtendrá el Transfer Object con los valores correctos (los más actualizados). Sin embargo, para transaciones con menos restricciones, la protección es menos rígida, permitiendo inconsistencias en el Transfer Objects por accesos concurrentes. Además, tendremos que tratar con problemas relacionados con la sincronización, el control de versión y los Transfer Objects obsoletos.

Código de Ejemplo

Implementar el patrón Transfer Object

Consideremos un ejemplo donde se ha modelado un objeto de negocio llamado Project y se ha implementado como un bean de entidad. El bean de entidad Project necesita enviar datos a sus clientes en un Transfer Object cuando el cliente llame al método getProjectData(). Abajo podemos ver el código de la clase Transfer Object para este ejemplo, ProjectTO:

// Transfer Object to hold the details for Project public class ProjectTO implements java.io.Serializable { public String projectId; public String projectName; public String managerId; public String customerId; public Date startDate; public Date endDate; public boolean started; public boolean completed; public boolean accepted; public Date acceptedDate; public String projectDescription; public String projectStatus; // Transfer Object constructors... } ]>> </codigo> <para> Abajo podemos ver el código para el bean de entidad de este ejemplo: </para> <codigo> <![CDATA[ ... public class ProjectEntity implements EntityBean { private EntityContext context; public String projectId; public String projectName; public String managerId; public String customerId; public Date startDate; public Date endDate; public boolean started; public boolean completed; public boolean accepted; public Date acceptedDate; public String projectDescription; public String projectStatus; private boolean closed; // other attributes... private ArrayList commitments; ... // Method to get Transfer Object for Project data public ProjectTO getProjectData() { return createProjectTO(); } // method to create a new Transfer Object and // copy data from entity bean into the value // object private ProjectTO createProjectTO() { ProjectTO proj = new ProjectTO();

Page 115: Catálogo de Patrones de Diseño J2EE

proj.projectId = projectId; proj.projectName = projectName; proj.managerId = managerId; proj.startDate = startDate; proj.endDate = endDate; proj.customerId = customerId; proj.projectDescription = projectDescription; proj.projectStatus = projectStatus; proj.started = started; proj.completed = completed; proj.accepted = accepted; proj.closed = closed; return proj; } ... }

Implementar la Estrategia Updatable Transfer Objects

El ejemplo anterior se puede ampliar para implementar la estrategia Updatable Transfer Objects. En este caso, el bean de entidad debería proporcionar un método setProjectData() para actualizar el bean de entidad pasándole un Transfer Object que contenga los datos utilizados para realizar la actualización. Abajo está el código para esta estrategia:

... public class ProjectEntity implements EntityBean { private EntityContext context; ... // attributes and other methods as in Example 8.4 ... // method to set entity values with a Transfer Object public void setProjectData(ProjectTO updatedProj) { mergeProjectData(updatedProj); } // method to merge values from the Transfer Object into // the entity bean attributes private void mergeProjectData(ProjectTO updatedProj) { // version control check may be necessary here // before merging changes in order to // prevent losing updates by other clients projectId = updatedProj.projectId; projectName = updatedProj.projectName; managerId = updatedProj.managerId; startDate = updatedProj.startDate; endDate = updatedProj.endDate; customerId = updatedProj.customerId; projectDescription = updatedProj.projectDescription; projectStatus = updatedProj.projectStatus; started = updatedProj.started; completed = updatedProj.completed; accepted = updatedProj.accepted; closed = updatedProj.closed; } ... }

Implementar la Estrategia Multiple Transfer Objects

Consideremos un ejemplo donde los clientes acceden a un bean de entidad Resource para pedirle diferentes Transfer Objects. El primer tipo de Transfer Object, ResourceTO, se utiliza para transferir datos de un pequeño conjunto de atributos. El segundo tipo de Transfer Object, ResourceDetailsTO, se usa para transferir datos de un conjunto mayor de atributos. El cliente puede utilizar el primer Transfer Object si sólo necesita los datos básicos representados por ese Transfer Object, y puede utilizar el segundo si necesita información más detalla. Observa que esta estrategia se puede aplicar para producir dos o más Transfer Objects que contengan diferentes datos, y no sólo a un subconjunto o un superconjunto como se muestra aquí.

Código de ejemplo para el primer objeto Transfer Objects, ResourceTO:

// ResourceTO: This class holds basic information // about the resource

Page 116: Catálogo de Patrones de Diseño J2EE

public class ResourceTO implements java.io.Serializable { public String resourceId; public String lastName; public String firstName; public String department; public String grade; ... }

Código de ejemplo para el segundo objeto Transfer Objects, ResourceDetailsTO:

// ResourceDetailsTO This class holds detailed // information about resource public class ResourceDetailsTO { public String resourceId; public String lastName; public String firstName; public String department; public String grade; // other data... public Collection commitments; public Collection blockoutTimes; public Collection skillSets; }

Código del bean de entidad Resource:

// imports ... public class ResourceEntity implements EntityBean { // entity bean attributes ... // entity bean business methods ... // Multiple Transfer Object method : Get ResourceTO public ResourceTO getResourceData() { // create new ResourceTO instance and copy // attribute values from entity bean into TO ... return createResourceTO(); } // Multiple Transfer Object method : Get // ResourceDetailsTO public ResourceDetailsTO getResourceDetailsData() { // create new ResourceDetailsTO instance and copy // attribute values from entity bean into TO ... return createResourceDetailsTO(); } // other entity bean methods ... }

Código de Ejemplo del cliente del bean de entidad:

... private ResourceEntity resourceEntity; private static final Class homeClazz = corepatterns.apps.psa.ejb.ResourceEntityHome.class; ... try { ResourceEntityHome home = (ResourceEntityHome) ServiceLocator.getInstance().getHome( "Resource", homeClazz); resourceEntity = home.findByPrimaryKey( resourceId);

Page 117: Catálogo de Patrones de Diseño J2EE

} catch(ServiceLocatorException ex) { // Translate Service Locator exception into // application exception throw new ResourceException(...); } catch(FinderException ex) { // Translate the entity bean finder exception into // application exception throw new ResourceException(...); } catch(RemoteException ex) { // Translate the Remote exception into // application exception throw new ResourceException(...); } ... // retrieve basic Resource data ResourceTO vo = resourceEntity.getResourceData(); ... // retrieve detailed Resource data ResourceDetailsTO = resourceEntity.getResourceDetailsData(); ...

Implementar la Estrategia Entity Inherits Transfer Object

Consideremos un ejemplo donde un bean de entidad, ContactEntity hereda todas sus propiedades de un Transfer Object ContactTO. El siguiente ejemplo muestra el codigo para el Transfer Object ContactTO que ilustra esta estrategia:

// This is the Transfer Object class inherited by // the entity bean public class ContactTO implements java.io.Serializable { // public members public String firstName; public String lastName; public String address; // default constructor public ContactTO() {} // constructor accepting all values public ContactTO(String firstName, String lastName, String address){ init(firstName, lastName, address); } // constructor to create a new TO based // using an existing TO instance public ContactTO(ContactTO contact) { init (contact.firstName, contact.lastName, contact.address); } // method to set all the values public void init(String firstName, String lastName, String address) { this.firstName = firstName; this.lastName = lastName; this.address = address; } // create a new Transfer Object public ContactTO getData() { return new ContactTO(this); } }

En el siguiente ejemplo podemos ver el código más importante del bean de entidad para esta estrategia:

public class ContactEntity extends ContactTO implements javax.ejb.EntityBean { ... // the client calls the getData method // on the ContactEntity bean instance. // getData() is inherited from the Transfer Object // and returns the ContactTO Transfer Object ...

Page 118: Catálogo de Patrones de Diseño J2EE

}

Implementar la Estrategia Transfer Object Factory

El siguiente fragmento de código demuestra la estrategia Transfer Object Factory. El bean de entidad extiende un Transfer Object general llamado CustomerContactTO. El CustomerContactTO implementa dos interfaces, Customer y Contact. El Transfer Object CustomerTO implementa Customer, y el Transfer Object ContactTO implementa Contact.

public interface Contact extends java.io.Serializable { public String getFirstName(); public String getLastName(); public String getContactAddress(); public void setFirstName(String firstName); public void setLastName(String lastName); public void setContactAddress(String address); } public class ContactTO implements Contact { // member attributes public String firstName; public String lastName; public String contactAddress; // implement get and set methods per the // Contact interface here. ... } public interface Customer extends java.io.Serializable { public String getCustomerName(); public String getCustomerAddress(); public void setCustomerName(String customerName); public void setCustomerAddress(String customerAddress); } public class CustomerTO implements Customer { public String customerName; public String customerAddress; // implement get and set methods per the // Customer interface here. ... } public class CustomerContactTO implements Customer, Contact { public String firstName; public String lastName; public String contactAddress; public String customerName; public String customerAddress; // implement get and set methods per the // Customer and Contact interfaces here. ... }

En el siguiente codigo tenemos el bean de entidad para obtener estos tres Transfer Objects diferentes:

public class CustomerContactEntity extends CustomerContactTO implements javax.ejb.EntityBean { // implement other entity bean methods...not shown // define constant to hold class name // complete Transfer Object. This is required by // the TransferObjectFactory.createTransferObject(...) public static final String COMPLETE_TO_CLASSNAME = "CustomerContactTO"; // method to return CustomerContactTO Transfer Object public CustomerContactTO getCustomerContact() { return (CustomerContactTO) TransferObjectFactory.createTransferObject( this, "CustomerContactTO",

Page 119: Catálogo de Patrones de Diseño J2EE

COMPLETE_TO_CLASSNAME); } // method to return CustomerTO Transfer Object public CustomerTO getCustomer() { return (CustomerTO) TransferObjectFactory.createTransferObject( this, "CustomerTO", COMPLETE_TO_CLASSNAME); } // method to return ContactTO Transfer Object public ContactTO getContact() { return (ContactTO) TransferObjectFactory.createTransferObject( this, "ContactTO", COMPLETE_TO_CLASSNAME); } // other entity bean business methods ... }

Abajo tenemos el código de la clase TransferObjectFactory:

import java.util.HashMap; import java.lang.*; /** * The factory class that creates a Transfer Object for a * given EJB. */ public class TransferObjectFactory { /** * Use a HashMap to cache class information for * Transfer Object classes */ private static HashMap classDataInfo = new HashMap(); /** * Create a Transfer Object for the given object. The * given object must be an EJB Implementation and have * a superclass that acts as the class for the entity's * Transfer Object. Only the fields defined in this * superclass are copied in to the Transfer Object. */ public static java.io.Serializable createTransferObject(Object ejb, String whichTOType, String completeTOType) { try { // Get the class data for the complete // Transfer Object type ClassData cData = getClassData (completeTOType); // Get class data for the requested TO type ClassData voCData = getClassData (whichTOType); // Create the Transfer Object of the requested // Transfer Object type... java.lang.Object whichTO = Class.forName(whichTOType).newInstance(); // get the TO fields for the requested TO // from the ClassData for the requested TO java.lang.reflect.Field[] voFields = voCData.arrFields; // get all fields for the complete TO // from the ClassData for complete TO java.lang.reflect.Field[] beanFields = cData.arrFields; // copy the common fields from the complete TO // to the fields of the requested TO for (int i = 0; i < voFields.length; i++) { try { String voFieldName = voFields[i].getName(); for (int j=0; j < beanFields.length; j++) { // if the field names are same, copy value if ( voFieldName.equals( beanFields[j].getName())) { // Copy value from matching field

Page 120: Catálogo de Patrones de Diseño J2EE

// from the bean instance into the new // Transfer Object created earlier voFields[i].set(whichTO, beanFields[j].get(ejb)); break; } } } catch (Exception e) { // handle exceptions that may be thrown // by the reflection methods... } } // return the requested Transfer Object return (java.io.Serializable)whichTO; } catch (Exception ex) { // Handle all exceptions here... } return null; } /** * Return a ClassData object that contains the * information needed to create * a Transfer Object for the given class. This information * is only obtained from the * class using reflection once, after that it will be * obtained from the classDataInfo HashMap. */ private static ClassData getClassData(String className){ ClassData cData = (ClassData)classDataInfo.get(className); try { if (cData == null) { // Get the class of the given object and the // Transfer Object to be created java.lang.reflect.Field[] arrFields ; java.lang.Class ejbTOClass = Class.forName(className); // Determine the fields that must be copied arrFields = ejbTOClass.getDeclaredFields(); cData = new ClassData(ejbTOClass, arrFields); classDataInfo.put(className, cData); } } catch (Exception e) { // handle exceptions here... } return cData; } } /** * Inner Class that contains class data for the * Transfer Object classes */ class ClassData { // Transfer Object Class public Class clsTransferObject; // Transfer Object fields public java.lang.reflect.Field[] arrFields; // Constructor public ClassData(Class cls, java.lang.reflect.Field[] fields) { clsTransferObject = cls; arrFields = fields; } }

Patrones Relacionados

• Session Facade Session Facade, que es el interface de negocio para clientes de aplicaciones J2EE, frecuentemente utiliza Transfer Objects como mecanismo de intercambio con beans de entidad. Cuando la fachada actúa como un proxy para los servicios de negocio, el Transfer Object obtenido de los beans de entidad se puede pasar al cliente.

Page 121: Catálogo de Patrones de Diseño J2EE

• Transfer Object Assembler Transfer Object Assembler es un patrón que construye Transfer Objects compuestos desde diferentes fuentes de datos. Las fuentes de datos suelen ser beans de sesión o de entidad a los que se podría solicitar que proporcionaran sus datos al Transfer Object Assembler como Transfer Objects. Estos objetos se considerarán parte del objeto compuesto que ensambla el Transfer Object Assembler.

• Value List Handler Value List Handler es otro patrón que proporciona una lista de Transfer Objects construidos dinámicamente accediendo al almacenamiento persistente en el momento de la solicitud.

• Composite Entity El patrón Transfer Object corrige la necesidad de obtener los datos de BusinessObjects a través de las capas. Ciertamente este es uno de los aspectos de consideración de diseño para los beans de entidad. El patrón Composite Entity explica los problemas implicados en el diseño de beans de entidad genéricos. Este patrón corrige los requerimientos complejos y explica los factores y consideraciones implicados en el diseño de beans de entidad.

Page 122: Catálogo de Patrones de Diseño J2EE

Catálogo de Patrones de Diseño J2EE. Y II: Capas de Negocio y de Integración Autor: Sun

Traductor: Juan Antonio Palos (Ozito)

• Transfer Object Assembler o Contexto o Problema o Causas o Solución

� Estructura � Participantes y Responsabilidades � Estrategias

o Consecuencias o Código de Ejemplo

� Implementar el Transfer Object Assembler o Patrones Relacionados

Transfer Object Assembler

Contexto

En una aplicación de la plataforma J2EE, los componentes de negocio del lado del servidor se implementan utilizando beans de sesión, beans de entidad, DAOs, etc. Los clientes de la aplicación necesitan acceder a datos que frecuentemente se componen de múltiples objetos.

Problema

Los clientes de aplicaciones normalmente solicitan los datos del modelo o parte del modelo para presentarlos al usuario o para usarlos en un paso de procesamiento intermedio antes de proporcionar algún servicio. El modelo de la aplicación es una abstracción para los datos de negocio y la lógica de negocio implementada en el lado del servidor como componentes de negocio. Un modelo se podría se expresar como una colección de objetos que se han juntado de una manera organizada. En una aplicación J2EE, el modelo es una colección de objetos distribuidos como beans de sesión, beans de entidad, DAOs u otros objetos. Para que un cliente obtenga los datos del modelo, debe acceder individualmente a cada objeto distribuido que define el modelo. Esta aproximación tiene varios inconvenientes:

• Como el cliente debe acceder a todos los componentes distribuidos individualmente, hay un acoplamiento fuerte entre el cliente y los componentes de negocio del modelo sobre la red.

• El cliente acede a los componentes distribuidos mediante la capa de red, y eso puede provocar una degradación del rendimiento si el modelo es complejo y con númerosos componentes distribuidos. La degradación de la red y del cliente ocurre cuando el modelo de la aplicacón implementa varios componentes de negocio distribuidos y el cliente interactúa directamente con esos componentes para obtener los datos del modelo. Todos esos accesos resultan en llamadas a métodos remotos que sobrecargan la red e incrementan las charlatanería entre el cliente y la capa de negocio.

• El cliente debe reconstruir el modelo después de obtener sus partes desde los componentes distribuidos. Por lo tanto, el cliente necesita tener suficiente lógica de negocio para construir el modelo. Si la construcción del modelo es compleja y se ven implicados en su definición numerosos objetos, podría haber una sobrecarga adicional en el rendimiento del cliente debido al proceso de construcción. Además, el cliente debe contener lógica de negocio para manejar las relaciones entre los componentes, lo que resultará en un cliente más complejo y todavía más grande. Cuando el cliente construye el modelo de la aplicación, la cosntrucción sucede en el lado del cliente. La construcción de modelos complejos podría resultar en una sobrecarga importante en el rendimiento del lado del cliente para clientes con recursos limitados.

• Como el cliente está fuertemente acoplado con el modelo, los cambios en el modelo requieren cambios en el cliente. Además, si hay diferentes tipos de clientes, es más difícil manejar los cambios entre todos los tipos de cliente. Cuando hay acoplamiento fuerte entre el clente y la implementación del modelo, lo que ocurre cuando el cliente tiene conocimiento directo del

Page 123: Catálogo de Patrones de Diseño J2EE

modelo y maneja las relaciones de los componentes de negocio, los cambios en el modelo necesitan cambios en el cliente. Además, hay otro problema de duplicación de código para acceder al modelo, lo que ocurre cuando una aplicación tiene muchos tipos de clientes. Esta duplicación hace que los clientes (su código) sea difícil de manejar cuando el modelo cambia.

Causas

• Se requiere la separación de la lógica de negocio entre el cliente y los componentes del lado del servidor.

• Como el modelo consta de componentes distribuidos, el acceso a cada componente está asociado con un una sobrecarga de red. Es deseable minimizar el número de llamadas a métodos remotos.

• Normalmente el cliente sólo necesita obtener el modelo para presentarlo al usuario. Si el cliente debe interactúar con varios componentes para construir el modelo "al vuelo", se incrementa la comunicación entre el cliente y el servidor. Dicha sobrecarga de comunicación podría reducir el rendimiento de la red.

• Incluso si el cliente quiere realizar un actualización, normalmente sólo actualiza ciertas partes del modelo, y no el modelo completo.

• Los clientes no necesitan preocuparse de las intrincadas dependencias en la implementación del modelo. Es deseable perder acoplamiento entre los clientes y los componentes de negocio que implementan el modelo de la aplicación.

• Los clientes no necesitan poseer la lógica de negocio adicional requerida para construir el modelo partiendo de varios componentes de modelo.

Solución

Utilizar un Transfer Object Assembler para construir el modelo o submodelo requerido. El Transfer Object Assembler usa Transfer Objects para recuperar los datos de los distintos objetos de negocio y otros objetos que definen el modelo o una parte del modelo.

El Transfer Object Assember construye un Transfer Object compuesto que representa los datos de diferentes componentes de negocio. El Transfer Object transporta datos del modelo al cliente en una sóla llamada a método. Como los datos del modelo pueden ser complejos, se recomienda que el Transfer Object no sea modificable. Es decir, el cliente obtiene esos Transfer Objects con el único propósito de usarlos para presentaciones y procesamientos de sólo-lectura. Si está permitido que los clientes hagan cambios en los Transfer Objects.

Cuando el cliente necesita los datos del modelo, y si el modelo está representado por un único componente genérico (como un Composite Entity), el proceso de obtención del modelo es muy simple. El cliente simplemente solicita el componente genérico para su Transfer Object compuesto. Sin embargo, la mayoría de las aplicaciones del mundo real tienen un modelo compuesto de una combinación de muchos componentes genéricos y específicos. En este caso, el cliente debe interactuar con numerosos componentes de negocio para obtener todos los datos necesarios para representar el modelo. El inconveniente inmediato de esta aproximación se puede ver en que los clientes se convierte en fuertemente acoplados con la implementación del modelo (elementos del modelo) y que los los clientes tienden a realizar numerosas llamadas a métodos remotos para obtener los datos de cada componente individual.

En algunos casos, un único componente genérico proporciona el modelo o parte del modelo como único objeto Transfer Object (simple o compuesto). Sin embargo, cuando varios componentes representan el modelo, un único Transfer Object (simple o compuesto) podria no representar todo el modelo. Para representar el modelo, es necesario obtener Transfer Objects de varios componentes y ensamblarlos en un Transfer Object Compuesto. El servidor, no el cliente, debería realizar la construcción del modelo "al-vuelo".

Estructura

La siguiente figura muestra el diagrama de clases que representa las relaciones para el patrón Transfer Object Assembler:

Page 124: Catálogo de Patrones de Diseño J2EE

Participantes y Responsabilidades

El diagrama de secuencia de la siguiente figura muestra la interacción entre los distintos participantes en el patrón Transfer Object Assembler:

Page 125: Catálogo de Patrones de Diseño J2EE

TransferObjectAssembler

El TransferObjectAssembler es la clase principal de este patrón. Construye un nuevo Transfer Object basado en los requerimientos de la aplicación cuando el cliente solicita un Transfer Object compuesto. Entonces TransferObjectAssembler localiza los ojemplares BusinessObject requeridos para recuperar los datos para construir el Transfer Object compuesto. Los BusinessObjects son objetos de la capa de negocio como beans de entidad o de sesión, DAOs, etc.

Cliente

Si el TransferObjectAssembler se implementa como un objeto Java normal, el cliente normalmente es un Session Facade que proporciona la capa controladora para la capa de negocio. Si el TransferObjectAssembler se implementa como un bean de sesión, el cliente puede ser un Session Facade o un Business Delegate.

BusinessObject

BusinessObject participa en la construcción del nuevo Transfer Object proporcionando los datos requeridos por el TransferObjectAssembler. Por lo tanto, el BusinessObject es un rol que cumple un bean de sesión, un bean de entidad, un DAO, o un objeto normal de Java.

TransferObject

TransferObject es un Transfer Object compuesto construido por TransferObjectAssembler y devuelto al cliente. Este representa los datos complejos de varios componentes que definen el modelo de la aplicación.

BusinessObject

BusinessObject es un rol que pueden cumplir un bean de sesión, un bean de entidad, o un DAO. Cuando el ensamblador necesita obtener los datos directamente desde el almacenamiento persistente, para

Page 126: Catálogo de Patrones de Diseño J2EE

construir el Transfer Object, puede utilizar un DAO. Esto se puede ver en los diagramas como el objeto DataAccessObject.

Estrategias

Esta sección explica las diferentes estrategias para implementar el patrón Transfer Object Assembler.

Java Object

El TransferObjectAssembler puede ser un objeto normal de Java y no necesariamente un bean enterprise. En dichas implementaciones, se sitúa enfrente del TransferObjectAssembler un bean de sesión. Este bean de sesión normalmente es un Session Facade que realiza otras tareas relacionadas con proporcionar servicios de negocio. El TransferObjectAssembler se ejecuta en la capa de negocio, sin importar las estrategias de implementación. La motivación para esto es evitar las llamadas remotas desde el TransferObjectAssembler hacia los objetos fuente sin cruzar de capa.

Session Bean

Esta estrategia implementa el TransferObjectAssembler como un bean de sesión. Si se prefiere la implementación de un bean de sesión para proporcionar el TransferObjectAssembler como un servicio de negocio, normalmente se implementa como un bean de sesión sin estado. Los componentes de negocio que componen el modelo de la aplicación están constantemente implicados en transaciones con otros clientes. Como resultado, cuando un TransferObjectAssembler construye un nuevo Transfer Object desde varios componentes de negocio, produce una "foto" del modelo en el momento de la construcción. El modelo podría cambiar inmediatamente después si otro cliente modifica uno o más de sus componentes de negocio, cambiando efectivamente el modelo de negocio de la aplicación.

Por lo tanto, implementar TransferObjectAssembler como un bean de sesión con estado no proporciona ningún beneficio sobre su implementación como un bean de sesión sin estado, ya que es inútil preservar el estado del modelo compuesto cuando está cambiando el modelo subyacente. Si cambia éste modelo, hace que el Transfer Object contenido por el Assembler sea obsoleto. Cuando luego se le pide al TransferObjectAssembler, el Transfer Object, o devuelve un estado obsoleto o reconstruye el Transfer Object para obtener una imagen más reciente. Por lo tanto, se recomienda que el Assembler sea un bean de sesión sin estado.

Sin embargo, si el modelo subyacente no cambia o lo hace muy raramente, el ensamblador podría ser un bean de sesión con estado para retener el Transfer Object recientemente construido. En este caso, el TransferObjectAssembler debe incluir mecanismos para reconocer los cambios en el modelo subyacente y reconstruir el modelo para la próxima petición del cliente.

Business Object

Existen diferentes objetos que pueden soportar el rol del BusinessObject

• Puede ser un bean de sesión. El Transfer Object Assembler podría utilizar un Service Locator para localizar el bean de sesión requerido. El Transfer Object Assembler le pide a este bean de sesión que le proporcione los datos para construir el Transfer Object compuesto.

• El BusinessObject también puede ser un bean de entidad. El Transfer Object Assembler podría utilizar un Service Locator para localizar el bean de entidad requerido. El Transfer Object Assembler le pide a este bean de entidad que le proporcione los datos para construir el Transfer Object compuesto.

• O puede se run DAO. El Transfer Object Assembler le pide a este DAO que le proporcione los datos para construir el Transfer Object compuesto.

• También puede ser un objeto Java normal. El Transfer Object Assembler le pide a este objeto Java que le proporcione los datos para construir el Transfer Object compuesto.

• Por último, el BusinessObject puede ser otro Transfer Object Assembler. El primer Transfer Object Assembler le pide al segundo Transfer Object Assembler que le proporcione los datos para construir el Transfer Object compuesto.

Consecuencias

Page 127: Catálogo de Patrones de Diseño J2EE

• Independiza la Lógica de Negocio Cuando el cliente incluye lógica para manejar las interacciones con componentes distribuidos, es más díficil separar claramente la lógica de negocio de la capa de cliente. El Transfer Object Assembler contiene la lógica de negocio para mantener las relaciones entre objetos y para construir el Transfer Object compuesto que representa el modelo. El cliente no necesita conocer cómo construir el modelo o los distintos componentes que proporcionan los datos para ensamblar el modelo.

• Reduce el Acoplamiento entre los Clientes y el Modelo de la Aplicación El Transfer Object Assembler oculta a los clientes la complejidad de la construcción de los datos del modelo y establece un acoplamiento ligero entre los clientes y el modelo. Con este acoplamiento ligero, si el modelo cambia, el Transfer Object Assembler también requerirá ese cambio. Sin embargo, el cliente no depende de la constucción del modelo ni de las relaciones entre los componentes del modelo de negoico, por eso los cambios en el modelo no afectan directamente al cliente.

• Mejora el Rendimiento de Red El Transfer Object Assembler reduce drásticamente la sobrecarga de red que producen las llamadas a métodos remotos. El cliente puede solicitar datos del modelo de la aplicación desde el Transfer Object Assembler con una sóla llamada a método. El ensamblador construye y devuelve el Transfer Object compuesto. Sin embargo, este Transfer Object compuesto podría contener una gran cantidad de datos. Así, aunque el Transfer Object Assembler reduce el número de llamadas a través de la red, hay un incremento en la cantidad de datos transportados en cada llamada. Este inconveniente se debe tener en cuenta cuando apliquemos este patrón.

• Mejora el Rendimiento del Cliente El Transfer Object Assembler del lado del servidor construye el modelo como un Transfer Object compuesto sin utilizar ningún recurso del cliente. El cliente no utiliza su tiempo para ensamblar el modelo.

• Mejora el Rendimiento de la Transación Normalmente, las actualizaciones se aíslan en una parte muy pequeña del modelo y se pueden realizar mediante transaciones específicas. Estas transaciones se enfocan en las partes aisladas del modelo en lugar de bloquear el objeto genérico (modelo). Después de que el cliente obtiene el modelo y lo muestra o procesa localmente, el usuario (o cliente) podría necesitar actualizar o modificar el modelo. El cliente puede interactúar directamente con un Session Facade para conseguir esto a un nivel de especificidad aceptable. El Transfer Object Assembler no está implicado en la transación para actualizar o modificar el modelo. Hay un mejor control del rendimiento porque el trabajo de transaciones con el modelo sucede con el nivel de especificidad apropiado.

• Podría Presentar Transfer Objects Obsoletos El Transfer Object Assembler construye Transfer Objects bajo pedido. Estos Transfer Objects son fotografías del estado actual del modelo, representadas por varios componentes de negocio. Una vez que el cliente obtiene un Transfer Object del ensamblador, este Transfer Object es enteramente local al cliente. Como los Transfer Objects no se preocupan de la red, otros cambios hechos a los componentes de negocio usados para construir el Transfer Object no se verán reflejados en éste. Por lo tanto, despues de obtener el Transfer Object, se puede convertir en obsoleto rápidamente si hay transaciones sobre los componentes de negocio.

Código de Ejemplo

Implementar el Transfer Object Assembler

Consideremos una aplicación de control de proyectos donde varios componentes de la capa de negocio definen el modelo complejo. Supongamos que un cliente quiere obtener el modelo de datos compuesto de datos de varios objetos de negocio, como:

• Información del Proyecto desde el componente Project. • Información del Manager del Proyecto desde el componente ProjectManager. • Lista de Tareas del Proyecto desde el componente Project. • Información de Recursos desde el componente Resource.

Se puede definir un Transfer Object compuesto como se ve en el siguiente ejemplo. Se puede implementar un Transfer Object Assembler para ensamblar este Transfer Object.

public class ProjectDetailsData { public ProjectTO projectData;

Page 128: Catálogo de Patrones de Diseño J2EE

public ProjectManagerTO projectManagerData; public Collection listOfTasks; ... }

El listado de tareas de ProjectDetailsData es una collection de objetos TaskResourceTO. TaskResourceTO es una combinación de TaskTO y ResourceTO. Abajo podemos ver estas clase:

Ejemplo de la clase TaskResourceTO:

public class TaskResourceTO { public String projectId; public String taskId; public String name; public String description; public Date startDate; public Date endDate; public ResourceTO assignedResource; ... public TaskResourceTO(String projectId, String taskId, String name, String description, Date startDate, Date endDate, ResourceTO assignedResource) { this.projectId = projectId; this.taskId = taskId; ... this.assignedResource = assignedResource; } ... }

Ejemplo de la clase TaskTO:

public class TaskTO { public String projectId; public String taskId; public String name; public String description; public Date startDate; public Date endDate; public assignedResourceId; public TaskTO(String projectId, String taskId, String name, String description, Date startDate, Date endDate, String assignedResourceId) { this.projectId = projectId; this.taskId = taskId; ... this.assignedResource = assignedResource; } ... }

Ejemplo de la clase ResourceTO:

public class ResourceTO { public String resourceId; public String resourceName; public String resourceEmail; ... public ResourceTO (String resourceId, String resourceName, String resourceEmail, ...) { this.resourceId = resourceId; this.resourceName = resourceName; this.resourceEmail = resourceEmail; ... } }

Page 129: Catálogo de Patrones de Diseño J2EE

En el siguiente listado tenemos la clase ProjectDetailsAssembler que ensambla el objeto ProjectDetailsData:

public class ProjectDetailsAssembler implements javax.ejb.SessionBean { ... public ProjectDetailsData getData(String projectId){ // Construct the composite Transfer Object ProjectDetailsData pData = new ProjectDetailsData(); //get the project details; ProjectHome projectHome = ServiceLocator.getInstance().getHome( "Project", ProjectEntityHome.class); ProjectEntity project = projectHome.findByPrimaryKey(projectId); ProjectTO projTO = project.getData(); // Add Project Info to ProjectDetailsData pData.projectData = projTO; //get the project manager details; ProjectManagerHome projectManagerHome = ServiceLocator.getInstance().getHome( "ProjectManager", ProjectEntityHome.class); ProjectManagerEntity projectManager = projectManagerHome.findByPrimaryKey( projTO.managerId); ProjectManagerTO projMgrTO = projectManager.getData(); // Add ProjectManager info to ProjectDetailsData pData.projectManagerData = projMgrTO; // Get list of TaskTOs from the Project Collection projTaskList = project.getTasksList(); // construct a list of TaskResourceTOs ArrayList listOfTasks = new ArrayList(); Iterator taskIter = projTaskList.iterator(); while (taskIter.hasNext()) { TaskTO task = (TaskTO) taskIter.next(); //get the Resource details; ResourceHome resourceHome = ServiceLocator.getInstance().getHome( "Resource", ResourceEntityHome.class); ResourceEntity resource = resourceHome.findByPrimaryKey( task.assignedResourceId); ResourceTO resTO = resource.getResourceData(); // construct a new TaskResourceTO using Task // and Resource data TaskResourceTO trTO = new TaskResourceTO( task.projectId, task.taskId, task.name, task.description, task.startDate, task.endDate, resTO); // add TaskResourceTO to the list listOfTasks.add(trTO); } // add list of tasks to ProjectDetailsData pData.listOfTasks = listOfTasks; // add any other data to the Transfer Object ... // return the composite Transfer Object return pData; } ...

Page 130: Catálogo de Patrones de Diseño J2EE

}

Patrones Relacionados

• Transfer Object Transfer Object Assembler usa el patrón Transfer Object para poder crear y transportar Transfer Objects al cliente. Los Transfer Objects creados llevan los datos que representan el modelo de la aplicación desde la capa de negocio hasta los clientes que han solicitado los datos.

• Composite Entity El patrón Composite Entity promueve el diseño de un bean de entidad genérico, donde las entidades pueden producir Transfer Objects compuestos similares al producido por el Transfer Object Assembler. Sin embargo, el Transfer Object Assembler es más aplicable cuando el Transfer Object compuesto construido se deriva de varios componentes (beans de sesión, beans de entidad, DAOs, etc.), mientras que el patrón Composite Entity construye el Transfer Object desde sus propios datos (es decir, un único bean de entidad).

• Session Facade El Transfer Object Assembler se implementa típicamente como un bean de sesión sin estado. Como tal, podría ser visto como una aplicación especial limitada del patrón Session Facade. Más importante, Transfer Object Assembler construye Transfer Objects que no se pueden modificar. Por lo tanto, el cliente que lo recibe sólo puede utilizarlo para propósitos de presentación y procesamiento. El cliente no puede actualizar el Transfer Object. Si el cliente necesita actualizar los objetos de negocio de los que deriva el Transfer Object compuesto, podría tener que acceder al Session Facade (bean de sesión) que proporciona ese servicio de negocio.

• Data Access Object Una posible estrategia para el Transfer Object Assembler implica obtener datos para el Transfer Object compuesto desde almacenamiento persistente sin utilizar beans enterprise. En estos casos se puede aplicar el patrón Data Access, y aprovecharnos de sus beneficios parar proporcionar acceso al almacenamiento persistente al Transfer Object Assembler.

• Service Locator El Transfer Object Assembler necesita localizar y utilizar varios objetos de negocio. Se puede utilizar el patrón Service Locator en conjunción con el patrón Transfer Object Assembler siempre que sea necesario localizar un objeto o servicio de negocio.

Page 131: Catálogo de Patrones de Diseño J2EE

Catálogo de Patrones de Diseño J2EE. Y II: Capas de Negocio y de Integración Autor: Sun

Traductor: Juan Antonio Palos (Ozito)

• Value List Handler o Contexto o Problema o Causas o Solución

� Estructura � Participantes y Colaboraciones � Estrategias

o Consecuencias o Código de Ejemplo

� Implementar el Value List Handler como un objeto Java o Patrones Relacionados

Value List Handler

Contexto

El cliente le pide al servicio una lista de ítems para su presentación. El número de ítems de la lista no se conoce y puede ser muy grande en muchas circunstancias.

Problema

La mayoría de las aplicaciones de la plataforma J2EE tienen un requerimiento de búsqueda y consulta para buscar y listar ciertos datos. En algunos casos, dichas operaciones de búsqueda y consulta podrían traer resultados que pueden ser bastante grandes. No es práctico devolver toda la hoja de resultados cuando los requerimientos del cliente son moverese por los resultados, en vez de procesar el conjunto completo. Normalmente, un cliente usa los resultados de una consulta para propósitos de sólo-lectura, como mostrar una lista de resultados. Muchas veces el cliente sólo ve los primeros registros que devuelve la búsqueda, y podría descargar los registros restantes e intentar otra nueva consulta. La práctica de obtener uns lista de valores representados en un bean de entidad llamando al método ejbFind(), que devuelve una collection de objetos remotos, y luego llamar a cada bean de entidad para obtener el valor, consume muchos recursos de red y se considera una mala práctica.

Hay algunas consecuencias asociadas con el uso de los métodos búscadores de EJB que resultan en grandes conjuntos de datos. Cada implementación de contenedor tiene una cierta cantidad de métodos búscadores para crear una colección de referencias EJBObject. El rendimiento del método de búsqueda varía, dependiendo de la implementación del contenedor. De acuerdo a la especificación EJB, un contenedor podría invocar a métodos ejbActivate() sobre entidades encontradas con los métodos de búsqueda. Como mínimo, un método de búsqueda devuelve las claves primarias de las entidades encontradas, que el contenedor devuelve al cliente como una colección de referencias EJBObject. Este comportamiento se aplica para todas las implementaciones de contenedores. Algunas implementaciones podrían introducir una sobrecarga de métodos de búsqueda asociando los ejemplares de beans de entidad a esos ejemplares EJBObject para darle acceso al cliente a esos beans de entidad. Sin embargo, este es un uso pobre de los recursos si el cliente no está interesado en acceder al bean o en invocar sus métodos. Esta sobrecarga puede impedir el rendimiento de la aplicación si ésta incluye consultas que producen muchos resultados.

Causas

• La aplicación cliente necesita una facilidad de consulta eficiente para evitar tener que llamar al método ejbFind() de cada bean e invocar a cada objeto remoto devuelto.

• Se necesita una mecanismo de caché en la capa-servidor para servir a los clientes que no pueden recibir y procesar el cojunto de resultados completo.

Page 132: Catálogo de Patrones de Diseño J2EE

• Se puede optimizar una consulta que se ejecuta repetidamente sobre unos datos razonablemente estáticos para proporcionar resultados más rápidos. Esto depende de la aplicación y de la implementación de este patrón.

• Los métodos de búsqueda EJB no son apropiados para navegar por tablas enteras en la bases de datos o para buscar grandes conjuntos de resultados en una tabla.

• Los métodos de búsqueda se podrían considerar sobrecargados cuando se utilizan para encontrar grandes cantidades de resultados. El contenedor podría crear un gran número de objetos de infraestructura para facilitar las búsquedas.

• Los métodos de búsqueda EJB no sin apropiados para realizar caché de resultados. El cliente podría no ser capaz de manejar el conjunto completo de resultados en una sóla llamada. Si es así, el cliente podría necesitar un caché en el lado del cliente y funciones de navegación para moverese por el conjunto de resultados.

• Los métodos de búsqueda EJB tiene consultas predeterminadas y ofrecen una flexibilidad mínima. La especificación EJB 2.0 permite un lenguaje de consulta, EJB QL, para beans de entidad manejados por el contenedor. EJB QL hace más fácil escribir métodos de búsqueda portables y ofrece una mayor flexibilidad.

• El cliente quiere moverse hacia adelante o hacia atrás dentro de la hoja de resultados.

Solución

Utilizar un Value List Handler para controlar la búsqueda, hacer un caché con los resultados, y proporcionar los resultados al cliente en una hoja de resultados cuyo tamaño y desplazamiento cumpla los requerimientos del cliente.

Este patrón crea un ValueListHandler para controlar la funcionalidad de la ejecución de la consulta y el caché de resultados. El ValueListHandler accede directamente a un DAO que puede ejecutar la consulta requerida. El ValueListHandler almacena los resultados obtenidos del DAO como una colección de Transfer Objects. El cliente le pide al ValueListHandler que proporcione resultados de la consulta según los necesite. El ValueListHandler implementa un patrón Iterator [GoF] para proporcionar la solución.

Estructura

La siguiente figura muestra el diagrama de clases para el patrón Value List Handler:

Page 133: Catálogo de Patrones de Diseño J2EE

Participantes y Colaboraciones

La siguiente figura muestra el diagrama de secuencia del patrón Value List Handler:

Page 134: Catálogo de Patrones de Diseño J2EE

ValueListIterator

Page 135: Catálogo de Patrones de Diseño J2EE

Este interface podría proporcionar la facilidad de iteración con los siguientes métodos de ejemplo:

• getSize() obtiene el tamaño de la hoja de resultados. • getCurrentElement() obtiene el Transfer Object actual de la lista. • getPreviousElements(int howMany) obtiene una colección de Transfer Objects que son anteriores

en la lista al elemento actual. • getNextElements(int howMany) obtiene una colección de Transfer Objects que son posteriores en

la lista al elemento actual. • resetIndex() reinicia el índice para ir al inicio de la lista.

Dependiendo de las necesidades, se pueden incluir otros métodos de conveniencia en el interface ValueListIterator.

ValueListHandler

Este es el objeto que implementa el interface ValueListIterator. ValueListHandler ejecuta la consulta requerida cuando se lo solicita el cliente. Obtiene los resultados de la consulta, que maneja en una colección privada representada por el objeto ValueList. ValueListHandler crea y manipula la colección ValueList. Cuando el cliente solicita los resultados, ValueListHandler obtiene los Transfer Objects desde el ValueList cacheado, crea una nueva colección de Transfer Objects, serializa la colección y la envía de vuelta alcliente. ValueListHandler también sigue la pista del índice actual y del tamaño de la lista.

DataAccessObject

ValueListHandler puede hacer uso de un DataAccessObject para mantener separada la implementación del acceso a la base de datos. DataAccessObject proporciona un API sencillo para acceder a la base de datos (o cualquier otro almacenamiento persistente), ejecuta consultas y recupera resultados.

ValueList

ValueList es una colección (una lista) que contiene los resultados de la consulta. Los resultados se almacenan como objetos Transfer Objects. Si falla la consulta y no se devuelven resultados, entonces esta lista está vacía. El bean de sesión ValueListHandler puede almacenar ValueList para evitar repeticiones innecesarias de la consulta.

TransferObject

TransferObject representa una vista de un registro individual de los resultados de la consulta. Es un objeto serializable no modificable que proporciona un espacio para los datos de los atributos de cada registro.

Estrategias

Java Object

Se puede implementar el ValueListHandler como un objeto Java arbitario. En este caso, el ValueListHandler puede ser utilizado por cualquier cliente que necesite esa funcionalidad. Para aplicaciones que no usan beans enterprise, esta estrategia es muy útil. Por ejemplo se podría construir aplicaciones sencillas utilizando servlets, JSPs, Business Delegates, y DAOs. En este escenario, el Business Delegates puede utilizar un ValueListHandler implementado como un objeto Java para obtener una lista de valores.

Stateful Session Bean

Cuando una aplicación utiliza beans enterprise en la capa de negocio, se podría preferir implementar un bean de sesión que utilice el ValueListHandler. En este caso, el bean de sesión simplemente se enfrenta a un ejemplar de ValueListHandler. Así, el bean de sesión se podría implementar como un bean de sesión con estado para contener la lista y su estado, y esto simplemente podría actúar como una fachada o como un proxy.

Page 136: Catálogo de Patrones de Diseño J2EE

Consecuencias

• Proporciona Alternativas a los métodos find() de EJB para Grandes Consultas Normalmente, un método de búsqueda EJB es una forma cara de obtener una lista de ítems, ya que implica un número de referencias EJBObject. Value List Handler implementa un bean de sesión que utiliza un DAO para realizar la consulta y para proporcionar una colección de Transfer Objects que cumplen el criterio de búsqueda. Como los Transfer Objects tienen poca sobrecarga en comparación con las referencias EJBObject y su infraestructura asociada, este patrón proporciona beneficios cuando las aplicaciones clientes requieren grandes cantidades de resultados.

• Crea un Caché de Resultados de la Consulta en el Lado del Servidor Se necesita cachear los resultados obtenidos de la ejecución de la consulta cuando un cliente debe mostrarlos en pequeñas partes en vez de una gran lista. Sin embargo, no todos los clientes basados en navegador puede realizar dicho caché. Cuando no pueden, el servidor debe proporcionar esta funcioalidad. El patrón Value List Handler proporciona una facilidad de caché en el bean de sesión para contener los resultados de la consulta. El conjunto de resultados es una colección de Transfer Objects que se puede serializar si es necesario. Cuando el cliente solicita una colección, o un subconjunto de una colección, el baen manejador devuelve los resultados solicitados como una colección serializada de Transfer Objects. El cliente recibe la colección y ahora tiene una copia local de la información, que el cliente puede mostrar o procesar. Cuando el cliente necesita un subconjunto adicional de resultados, le solicita al manejador que devuelva otra colección serializada que contenga los resultados necesarios. El cliente puede procesar la consulta en trozos más pequeños y manejables. El bean manejador también le proporciona al cliente facilidades de navegación (anterior y siguiente) para que pueda moverse por la lista de la forma necesaria.

• Proporciona una Mayor Flexibilidad de Consulta Añadir una nueva consulta podría requerir la creación de un nuevo método finder o modificar uno existente, especialmente cuando se utilizan beans de entidad manejados por el bean (con beans de entidad manejados por el bean, el desarrollador implementa los métodos de búsqueda en la implementación del bean). Con un bean de entidad manejado por el contenedor, el desarrollador especifica los métodos finder del bean de entidad en el descriptor de despliegue del bean. Los cambios en una consulta para bean manejado por el contenedor requieren cambios en el descriptor de despliegue. Por lo tanto, estos métodos no son muy adecuados cuando los requerimientos de la consulta cambian dinámicamente. Podemos implementar un Value List Handler para que sea más flexible que los métodos del EJB proporcionado facilidades de consultas dinámicas, construyendo los argumentos de la consulta en el momento de la ejecución utilizando plantillas de métodos, etc. En otras palabras, un desarrollador de un Value List Handler puede implementar búsqueda inteligente y algoritmos de caché sin verse limitado por los métodos finder.

• Mejora el Rendimiento de Red El rendimiento de la red se ve mejorado porque sólo los datos solicitados, en vez de todos los datos, se envían (serializados) al cliente sobre unas necesidades básicas. Si el cliente muestra los primeros resultados y luego abandona la consulta, no se ha ocupado el ancho de banda de la red, ya que los datos se almacenan en el servidor y nunca se han envíado al cliente. Sin embargo, si el cliente procesa todos los resultados, hace varias llamadas remotas al servidor. Cuando el cliente conoce por adelantado que necesita todos los resultados, el bean manejador puede proporcionar un método que los envíe al cliente en una sola llamada a método, y no se utiliza el caché.

• Permite Atrasar las Transaciones del Bean de Entidad El caché de los resultados en el lado del servidor permite minimizar la sobrecarga de la búsqueda y podría mejorar el control de transaciones. Cuando el cliente está listo para procesar un bean de entidad, accede al bean dentro de un contexto de transación definido por el caso de utilización. Por ejemplo, una consulta para mostrar una lista de libros utiliza un Value List Handler para obtener la lista. Cuando el usuario quiere ver los detalles de un libro, implica al bean de entidad del libro en la transación.

Código de Ejemplo

Implementar el Value List Handler como un objeto Java

Consideremos un ejemplo donde se va a recuperar y mostrar una lista de objetos de negocio de un Proyecto. En este caso se puede aplicar el patrón Value List Handler. En el siguiente ejemplo tenemos la clase ProjectListHandler, que es responsable de proporcionar la lista de proyectos. Esta clase extiende la

Page 137: Catálogo de Patrones de Diseño J2EE

clase base ValueListHandler, que proporciona la funcionalidad de iteración genéricoa para todas las implementaciones de Value List Handler de esta aplicación.

package corepatterns.apps.psa.handlers; import java.util.*; import corepatterns.apps.psa.dao.*; import corepatterns.apps.psa.util.*; import corepatterns.apps.psa.core.*; public class ProjectListHandler extends ValueListHandler { private ProjectDAO dao = null; // use ProjectTO as a template to determine // search criteria private ProjectTO projectCriteria = null; // Client creates a ProjectTO instance, sets the // values to use for search criteria and passes // the ProjectTO instance as projectCriteria // to the constructor and to setCriteria() method public ProjectListHandler(ProjectTO projectCriteria) throws ProjectException, ListHandlerException { try { this.projectCriteria = projectCriteria; this.dao = PSADAOFactory.getProjectDAO(); executeSearch(); } catch (Exception e) { // Handle exception, throw ListHandlerException } } public void setCriteria(ProjectTO projectCriteria) { this.projectCriteria = projectCriteria; } // executes search. Client can invoke this // provided that the search criteria has been // properly set. Used to perform search to refresh // the list with the latest data. public void executeSearch() throws ListHandlerException { try { if (projectCriteria == null) { throw new ListHandlerException( "Project Criteria required..."); } List resultsList = dao.executeSelect(projectCriteria); setList(resultsList); } catch (Exception e) { // Handle exception, throw ListHandlerException } } }

En el siguiente código tenemos la clase ValueListHandler que implementa el interface del iterador genérico ValueListIterator, que veremos en siguientes listados.

package corepatterns.apps.psa.util; import java.util.*; public class ValueListHandler implements ValueListIterator { protected List list; protected ListIterator listIterator; public ValueListHandler() { } protected void setList(List list) throws IteratorException { this.list = list; if(list != null) listIterator = list.listIterator(); else throw new IteratorException("List empty"); }

Page 138: Catálogo de Patrones de Diseño J2EE

public Collection getList(){ return list; } public int getSize() throws IteratorException{ int size = 0; if (list != null) size = list.size(); else throw new IteratorException(...); //No Data return size; } public Object getCurrentElement() throws IteratorException { Object obj = null; // Will not advance iterator if (list != null) { int currIndex = listIterator.nextIndex(); obj = list.get(currIndex); } else throw new IteratorException(...); return obj; } public List getPreviousElements(int count) throws IteratorException { int i = 0; Object object = null; LinkedList list = new LinkedList(); if (listIterator != null) { while (listIterator.hasPrevious() && (i < count)){ object = listIterator.previous(); list.add(object); i++; } }// end if else throw new IteratorException(...); // No data return list; } public List getNextElements(int count) throws IteratorException { int i = 0; Object object = null; LinkedList list = new LinkedList(); if(listIterator != null){ while( listIterator.hasNext() && (i < count) ){ object = listIterator.next(); list.add(object); i++; } } / / end if else throw new IteratorException(...); // No data return list; } public void resetIndex() throws IteratorException{ if(listIterator != null){ listIterator = list.ListIterator(); } else throw new IteratorException(...); // No data } ... }

Abajo tenemos el código importante para acceder al objeto ProjectDAO, utilizado por ValueListHandler para ejecutar la consulta y obtener los resultados.

package corepatterns.apps.psa.dao; public class ProjectDAO {

Page 139: Catálogo de Patrones de Diseño J2EE

final private String tableName = "PROJECT"; // select statement uses fields final private String fields = "project_id, name," + "project_manager_id, start_date, end_date, " + " started, completed, accepted, acceptedDate," + " customer_id, description, status"; // the methods relevant to the ValueListHandler // are shown here. // See Data Access Object pattern for other details. ... private List executeSelect(ProjectTO projCriteria) throws SQLException { Statement stmt= null; List list = null; Connection con = getConnection(); StringBuffer selectStatement = new StringBuffer(); selectStatement.append("SELECT "+ fields + " FROM " + tableName + "where 1=1"); // append additional conditions to where clause // depending on the values specified in // projCriteria if (projCriteria.projectId != null) { selectStatement.append (" AND PROJECT_ID = '" + projCriteria.projectId + "'"); } // check and add other fields to where clause ... try { stmt = con.prepareStatement(selectStatement); stmt.setString(1, resourceID); ResultSet rs = stmt.executeQuery(); list = prepareResult(rs); stmt.close(); } finally { con.close(); } return list; } private List prepareResult(ResultSet rs) throws SQLException { ArrayList list = new ArrayList(); while(rs.next()) { int i = 1; ProjectTO proj = new ProjectTO(rs.getString(i++)); proj.projectName = rs.getString(i++); proj.managerId = rs.getString(i++); proj.startDate = rs.getDate(i++); proj.endDate = rs.getDate(i++); proj.started = rs.getBoolean(i++); proj.completed = rs.getBoolean(i++); proj.accepted = rs.getBoolean(i++); proj.acceptedDate = rs.getDate(i++); proj.customerId = rs.getString(i++); proj.projectDescription = rs.getString(i++); proj.projectStatus = rs.getString(i++); list.add(proj); } return list; } ... }

Código de ejemplo de la clase ValueListIterator:

package corepatterns.apps.psa.util; import java.util.List; public interface ValueListIterator { public int getSize() throws IteratorException; public Object getCurrentElement() throws IteratorException;

Page 140: Catálogo de Patrones de Diseño J2EE

public List getPreviousElements(int count) throws IteratorException; public List getNextElements(int count) throws IteratorException; public void resetIndex() throws IteratorException; // other common methods as required ... }

Patrones Relacionados

• Iterator [GoF] El patrón Value List Handler está basado en el patrón Iterator, descrito en el libro de Gof: Design Patterns: Elements of Reusable Object-Oriented Software.

• Session Facade Como el Value List Handler es un bean de sesión, podría aparecer como un Session Facade especializado. Sin embargo, aisladamente, es un bean de sesión especializado en vez de un Session Facade. Un Session Facade tiene otras motivaciones y características y es mucho más genérico.

Page 141: Catálogo de Patrones de Diseño J2EE

Catálogo de Patrones de Diseño J2EE. Y II: Capas de Negocio y de Integración Autor: Sun

Traductor: Juan Antonio Palos (Ozito)

• Composite Entity o Contexto o Problema o Causas o Solución

� Estructura � Participantes y Responsabilidades � Estrategias

o Consecuencias o Codigo de Ejemplo

� Implementación del patrón Composite Entity � Implementar la Estrategia Lazy Loading � Implementar la Estrategia Store Optimization (Dirty Marker) � Implementar la Estrategia Composite Transfer Object

o Patrones Relacionados o Bean de Entidad como un Objeto Dependiende: Problemas y Recomendaciones

Composite Entity

Contexto

Los beans de entidad no se han pensado para representar todos los objetos persistentes del modelo. Los beans de entidad son mejores para objetos de negocio persistentes genéricos.

Problema

En una aplicación de la plataforma J2EE, los clientes -- como aplicaciones, páginas JSP, servlets, componentes JavaBeans -- acceden a los beans de entidad mediante sus interfaces remotos. Así, toda llamada de cliente potencialmente se enruta a través de los stubs (trozos) y skeletons (esqueletos), incluso si el cliente y el bean enterprise están en la misma Máquina Virtual Java, en el mismo sistema operativo o en la misma máquina física. Cuando los beans enterprise son objetos específicos, los clientes tienden a invocar los métodosdel bean de forma más individual, resultando en una gran sobrecarga en la red.

Los beans de entidad representan objetos persistentes de negocio distribuidos. Siempre que desarrollemos o migremos una aplicación a la plataforma J2EE, la especificad de los objetos es muy importante cuando decidimos implementarlos como beans de entidad. Los beans de entidad deberían representar objetos de negocio genéricos, como aquellos que proporcionan un comportamiento complejo más allá de simplemente obtener y seleccionar valores de campos. Estos objetos genéricos normalmente tienen objetos dependientes. Un objeto dependiente es un objeto que no tiene un significado real cuando no está asociado con su padre genérico.

Un problema recurrente es el mapeo directo del modelo a un modelo de JavaBeans Enterpise (especificamente beans de entidad). Esto crea una relación entre los objetos beans de entidad en su consideración de genéricos contra objetos específicos (o dependientes). Determinar cuando utilizar objetos genéricos o especificados es muy dificil y se puede hacer mejor utilizando un modelo de relaciones mediante Unified Modeling Language (UML).

Hay varias áreas que se ven impactadas por la aproximación del diseño del bean de entidad específico:

• Relaciones de Entidades - Mapear directamente un objeto de modelo a un modelo EJB no tiene en cuenta el impacto de las relaciones entre objetos. Las relaciones inter-objetos se transforman directamente en relaciones inter-beans. Como resultado, un bean de entidad podría contener o almacenar una referencia remota a otro bean de entidad. Sin embargo, mantener referencias

Page 142: Catálogo de Patrones de Diseño J2EE

remotas a objetos distribuidos implica técnicas y semánticas diferentes a la de mantener referencias a objetos locales. Junto con el incremento de complejidad del código, se reduce la flexibilidad porque el bean de entidad debe cambiar si cambia alguna de sus relaciones. Además, no hay garantías de la validez de las referencias de un bean de entidad a otro bean de entidad en el tiempo. Dichas referencias se establecen dinámicamente utilizando el objeto home de la entidad y la clave primaria para ese ejemplar de bean. Esto implica una alta sobrecarga de mantenimiento para el chequeo de validez de la referencia por cada referencia de bean-de-entidad-a-bean-de-entidad.

• Manejabilidad - Implementar objetos específicos como beans de entidad resulta en un mayor número de beans de entidad en el sistema, el desarrollador debe proporcionar clases para el interface home, el interface remote, la implementación del bean y la clave primaria. Además, el contenedor podría generar clases para soportar la implementación del bean de entidad. Cuando se crea el bean, esta clases se convierten en objetos reales en el contenedor. En breve, el contenedor crea varios objetos para soportar cada ejemplar de bean de entidad. Un mayor número de beans de entidad significa más clases y código que mantener por el equipo de desarrollo. También resulta en un mayor número de objetos en el contenedor. Esto puede impactar negativamente en el rendimiento de la aplicación.

• Rendimiento de Red -- los beans de entidad específicos, potencialmente tienen más relaciones inter-beans. Los beans de entidad son objetos distribuidos. Cuando un bean de entidad invoca un método de otro bean de entidad, el contenedor trata esa llamada como una llamada remota, incluso si ambos beans están en el mismo contenedor. Si el número de relaciones bean-de-entidad-a-bean-de-entidad se incrementa, se reduce la escalabilidad del sistema debido a la fuerte sobrecarga de la red.

• Dependencia del Esquema de Base de Datos - Cuando los beans de entidad son específicos cada ejemplar normalmente representa una sóla fila de la base de datos. Esto no es una aplicación apropiada del diseño de beans de entidad, ya que los beans de entidad son más apropiados para componentes genéricos. La implementación de beans de entidad específicos normalmente es una representación directa del esquema de la base de datos subyacente en el diseño del bean de entidad. Cuando los clientes utilizan estos beans de entidad específicos, están operando esencialmente a nivel de fila en la base de datos, ya que cada bean de entidad efectivamente es una única fila. Como el bean de entidad modela directamente una sola fila de la base de datos, los clientes dependen del esquema de la base de datos. Cuando el esquema cambia, también deben cambiar las definiciones del bean de entidad. Además, como los clientes están operando con la misma especificidad, deben observar y reaccionar ante estos cambios. Esta dependencia del esquema causa una pérdida de flexibilidad e incrementa la sobrecarga de mentenimiento requerida cuando cambia el esquema.

• Especificidad del Objeto (Genérico frente a Específico) -- La especificidad del objeto impacta en la transferencia de datos entre el bean enterprise y el cliente. En muchas aplicaciones, los clientes necesitan un trozo de datos mayor que una o dos filas de la tabla. En dichos casos, implementar cada unos de estos objetos específicos como un bean de entidad significa que el cliente tendría que manejar las relaciones entre todos esos objetos específicos. Dependiendo de los requerimientos de datos, el cliente podría tener que realizar muchas búsquedas de varios beans de entidad para obtener la información necesaria.

Causas

• Los beans de entidad son mejores para implementar objetos genéricos debido a la alta sobrecarga asociada con todo bean de entidad. Todo bean de entidad se implementa utilizando muchos objetos, como el objeto home, el objetoremote, la implementación del bean, y la clave primaria, y todos son controlados por los servicios del contenedor.

• Las aplicaciones que mapean directamente un esquema de una base de datos relacional a beans de entidad (donde cada fila de la tabla se representa como un ejemplar de un bean de entidad) tienden a tener un mayor número de beans de entidad específicos. Es deseable mantener el número de beans de entidad genéricos y reducir el número de beans de entidad de la aplicación, y así reducir el número de beans deentidad de la aplicación.

• Mapear directamente el modelo de objetos al modelo EJB trae beans de entidad específicos. Estos beans normalmente se mapean al esquema de la base de datos. Este mapeo de entidades-a-fila-de-base-de-datos causa problemas relacionados con el rendimiento, la manejabilidad, la seguridad y el manejo de transaciones. Las relaciones entre las tablas se implementan como relaciones entre beans de entidad, lo que significa que los beans de entidad contienen referencias a otros beans de entidad para implementar las relaciones específicas. Es muy costoso manejar las relaciones entre estos beans porque deben establecerse dinámicamente, utilizando los objetos home de los beans de entidad y las claves primarias de los beans enterprise.

Page 143: Catálogo de Patrones de Diseño J2EE

• Los clientes no necesitan conocer la implementación del esquema de la base de datos para utilizar y soportar los beans de entidad. Con beans de entidad específicos, el mapeo se hace normalmente para que cada ejemplar de bean de entidad corresponda con una sóla fila de la base de datos. Este mapeo específico crea una dependencia entre el cliente y el esquema de la base de datos subyacente, ya que el cliente trata con beans específicos y éstos esencialmente son una representación directa del esquema subyacente. Esto resulta en un fuerte acoplamineto entre el esquema de la base de datos y los beans de entidad. Un cambio en el esquema causa el cambio correspondiente en el bean de entidad, y además requiere el cambio correspondiente en los clientes.

• Hay un incremento de la charlatanería de las aplicaciones debido a la intercomunicación entre los beans específicos. Esta excesiva comunicación entre los beans específicos frecuentemente provoca un cuello de botella en el rendimiento. Toda llamada a método del bean de entidad se hace mediante la capa de red, incluso si el llamador está en el mismo espacio de direcciones que el bean llamado (es decir, si tanto el cliente como el bean de entidad están en el mismo contenedor). Aunque algunos contenedores han optimizado este escenario, el desarrollador no puede esperar esta optimización en todos los contenedores.

• Se puede observar una charlataneria adicional entre el cliente y los beans de entidad porque el cliente podría tener que comunicar con muchos beans de entidad específicos para cumplir los requerimientos. Es deseable reducir la comunicación entre los beans de entidad para reducir la charlataneria entre el cliente y la capa del bean de entidad.

Solución

Utilizar Composite Entity para modelar, representar y manejar un conjunto de objetos persistentes relacionados en vez de representarlos como beans de entidad específicos individuales. Un bean Composite Entity representa un grupo de objetos.

Para entender esta solución, primero definamos que significan los objetos persistentes y discutiremos sus relaciones.

Un objeto persistente es un objeto que se almacena en algún tipo de almacenamiento. Normalmente múltiples clientes comparten los objetos persistentes. Los objetos persistentes se pueden clasificar en dos tipos: objetos genéricos y objetos dependientes.

Un objeto genérico es autosuficiente. Tiene su propio ciclo de vida y maneja sus relaciones con otros objetos. Todo objeto genérico podría referenciar o contener uno o más objetos. El objeto genérico normalmente maneja el ciclo devida de estos objetos. De ahí, que esos objetos sean conocidos como objetos dependientes. Un objeto dependiente puede ser un simple objeto auto-contenido o podría a su vez contener otros objetos dependientes.

El ciclo de vida de un objeto dependiente está fuertemente acoplado al ciclo de vida del objeto genérico. Un cliente sólo podría acceder al objeto dependiente através del objeto genérico. Es decir, los objetos dependientes no se exponen directamente a los clientes porque su objeto padre (genérico) los maneja. Los objetos dependientes no pueden existir por sí mismos. En su lugar, siempre necesitan tener su objeto genérido (o padre) para justificar su existencia.

Normalmente, podemos ver la relaciones entre un objeto genérico y sus objetos dependientes como un árbol. El objeto genérico es la raíz del árbol (el nodo raíz). Cada objeto dependiente puede ser un objeto dependientes solitario (un nodo hoja) que es hijo del objeto genérico. O, el objeto dependiente puede tener relaciones padre-hijo con otros objetos dependientes, en cuyo caso se considera un nodo rama.

Un bean Composite Entity puede representar un objeto genérico y todos sus objetos dependientes relacionados. Esta combinación de objetos persistentes relacionados dentro de un sólo bean de entidad reduce drásticamente el número de beans de entidad requeridos por la aplicación. Esto genera un bean de entidad altamente genérico que puede obtener mejor los beneficios de los beans de entidad que un bean de entidad específico.

Sin la aproximación Composite Entity, hay una tendencia a ver todos los objetos genéricos y dependientes como beans de entidad separados, suponiendo un gran número de beans de entidad.

Estructura

Page 144: Catálogo de Patrones de Diseño J2EE

Aunque hay muchas estrategias para implementar el patrón Composite Entity, la primera que vamos a explicar está representada en el siguiente diagrama de clases. Aquí el Composite Entity contiene el objeto genérico, y éste contiene objetos dependientes:

El diagrama de secuencia de la siguiente imagen muestra las interacciones para este patrón:

Participantes y Responsabilidades

CompositeEntity

CompositeEntity es un bean de entidad genérico. Podría ser un objeto genérico, o podría contener una referencia a un objeto genérico.

Coarse-Grained Object

Un objeto genérico es un objeto que tiene su propio ciclo de vida y que maneja sus propias relaciones con otros objetos. Un objeto genérico puede ser un objeto Java contenido en el CompositeEntity. O, el propio Composite Entity puede ser un objeto genérico que contiene objetos dependientes.

Page 145: Catálogo de Patrones de Diseño J2EE

DependentObject1, DependentObject2, y DependentObject3

Un objeto dependiente es un objeto que depende de un objeto genérico el cual gobierna el ciclo de vida del objeto dependiente. Un objeto dependiente puede contener otros objetos dependientes; así podría existir un árbol de objetos dentro del Composite Entity.

Estrategias

Esta sección explica las diferentes estrategias para implementar el patrón Composite Entity. Las estrategias consideran posibles alternativas y opciones para objetos persistentes (genéricos y dependientes), y la utilización deTransfer Objects.

El Composite Entity Contiene Objetos Genéricos

En esta estrategia, el Composite Entity almacena o contiene el objeto genérico. Este objeto genérico continúa teniendo relaciones con sus objetos dependientes. La sección Estructuradescribe esta estrategia principal.

El Composite Entity Implementa el Objeto Genérico

En esta estrategia, el propio Composite Entity es el objeto genérico y tiene atributos y métodos de objeto genérico. Los objetos dependientes son atributos del Composite Entity. Como el Composite Entity es el objeto genérico, el bean de entidad expresa y maneja todas las relaciones entre el objeto genérico y los objetos dependientes. La siguiente figura muestra el diagrama de clases de esta estrategia:

En esta otra figura podemos ver el diagrama de secuencia para esta estrategia:

Page 146: Catálogo de Patrones de Diseño J2EE

Lazy Loading

Un Composite Entity puede estar compuesto de muchos niveles de objetos dependientes en su árbol de objetos. Cargar todos los objetos dependientes cuando el contendor EJB llama al ejbLoad() del bean puede utilizar mucho tiempo y recursos. Una forma de optimizar esto es utilizar esta estrategia para cargar los objetos dependientes. Cuando se llama al método ejbLoad(), primero sólo carga aquellos objetos dependientes que son más cruciales para los clientes deComposite Entity. Luego, cuando el cliente accede a un objeto dependiente que no se haya cargado de la base de datos, el Composite Entity puede realizar una carga bajo demanda. Así, si no se utilizan algunos objetos dependientes, éstos no serán cargados en la inicialización. Sin embargo, cuando el cliente necesita dichos objetos dependientes, los carga en cualquier momento. Una vez que se han cargado los objetos dependientes, las siguientes llamadas al método ejbLoad() deben incluir esos objetos dependientes para recargar o sincronizar los cambios con el almacenamiento persistente.

Store Optimization (Dirty Marker)

Un problema común con la persistencia controlada por el bean ocurre cuando almacenamos todo el conjunto de objetos durante una operación ejbStore(). Como el contendor EJB no tiene forma de conocer que datos han cambiado en el bean de entidad y en sus objetos dependientes, pone el balón en el lado del desarrollador para que determine qué y como almacenar. Algunos contenedores EJB proporcionan una característica para indentificar qué objetos del Composite Entity se necesita almacenar debido a una actualización anterior. Esto se podría hacer si los desarrolladores implementaran un método especial en los objetos dependientes, como isDirty(), al que llame el contenedor para comprobar si el objeto se ha actualizado desde la última operación ejbStore().

Una solución genérica podría ser utilizar un interface, DirtyMarker, como se ve en el diagrama de la siguiente figura. La idea es hacer que los objetos dependientes implementen el interface DirtyMarker para dejarle al llamador (normalmente el método ejbStore()) saber si el estado del objeto dependiente ha cambiado. De esta forma, el llamador puede elegir si obtener los datos para su almacenamiento posterior.

La siguiente figura contiene un diagrama de secuencia que muestra un ejemplo de interacción para esta estrategia.

Page 147: Catálogo de Patrones de Diseño J2EE

El cliente realiza un actualización del Composite Entity, que resulta en un cambio en DependentObject3. Se accede a DependentObject3 mediante su padre DependentObject2. El Composite Entity es el padre de DependentObject2. Cuando se realiza la actualización, se llama al metodo setDirty() en DependentObject3. Posteriormente, cuando el contenedor llama al método ejbStore() sobre este ejemplar de Composite Entity, el método ejbStore() puede chequear qué objetos dependientes se han cambiado y grabar selectivamente en la base de datos aquellos que han cambiado. La marca dirty se resetea después de que la grabación haya tenido éxito.

El interface DirtyMarker también puede incluir métodos que reconozcan otros estados de persistencia del objeto dependiente. Por ejemplo, si se incluye un nuevo objeto dependiente dentro del Composite Entity, el método ejbStore() debería poder reconocer qué operación utilizar en este caso, el objeto dependiente no se ha modificado, sino que es nuevo. Extendiendo el interface DirtyMarker para incluir un método llamado isNewz(), el método ejbStore() puede invocar una operación de inserción en lugar de una operación de actualización. De forma similar, incluyendo un método llamado isDeleted(), el método ejbStore() puede invocar una operación de borrado cuando sea necesario.

En casos donde se llama a ejbStore() sin actualizaciones intermedias, del Composite Entity, ninguno de los objetos dependientes ha sido actualizado.

Esta estrategia evita la gran sobrecarga de tener que persistir todos los objetos dependientes en la base de datos siempre que el contenedor llame al método ejbStore().

Nota: La especificación EJB 2.0 corrige las estrategias Lazy Loading y Store Optimization. Es posible utilizar estas estrategias en implementaciones anteriores a la EJB 2.0.

Composite Transfer Object

Con un Composite Entity, un cliente puede obtener toda la información requerida con sólo una llamada a un método. Como el Composite Entity o implementa o contiene el objeto genérico y la herencia (o árbol) de objetos dependientes, puede crear el Transfer Object requerido y devolverlo al cliente aplicando el

Page 148: Catálogo de Patrones de Diseño J2EE

patrónTransfer Object. En la siguiente imagen podemos ver el diagrama de secuencia para esta estrategia:

El Transfer Object puede ser un objeto simple o compuesto que tenga subobjetos, dependiendo de los datos solicitados por el cliente. El Transfer Object es serializable y se pasa por valor al cliente. ElTransfer Object funciona sólo como un mero transmisor de objetos; no tiene responsabilidades con respecto a la seguridad, las transaciones, y la lógica de negocio. El Transfer Object empaqueta toda la información en un objeto, obteniendo la información con una llamada remota en vez de con múltiples llamadas. Una vez que el cliente recibe el Transfer Object, todas las llamadas posteriores del cliente al Transfer Object son llamadas locales.

La discusión apunta a cómo la entidad puede empaquetar todos sus datos en un Transfer Object compuesto y devolver este al cliente. Sin embargo,esta estrategia también permite al bean de entidad devolver sólo los datos que el cliente ha solicitado. Si el cliente sólo necesita los datos de un subconjunto de objetos dependientes, entonces el Transfer Object compuesto devuelto puede contener los datos derivados sólo de aquellas partes requeridas y no de todos los objetos dependientes. Esto podría ser una aplicación de la estrategia Multiple Transfer Objects del patrón Transfer Object.

Consecuencias

• Elimina las Relaciones Inter-Entidades Utilizando el patrón Composite Entity, los objetos dependientes se unen en un sólo bean de entidad, eliminando todas las relaciones entre beans de entidad. Este patrón proporciona un lugar central para manejar las relaciones y la herencia de los objetos.

• Mejora la Manejabilidad Reduciendo los Beans de Entidad Como se ha explicado, la implementación de objetos persistentes como beans de entidad específicos resulta en un mayor número de clases que necesitamos desarrollar y mantener. Utilizar Composite Entity reduce el número de clases EJB y el código, y hace más fácil el mantenimiento. Mejora la manejabilidad de la aplicación teniendo un menor número de componentes genéricos en lugar de tener muchos más componentes específicos.

• Mejora el Rendimiento de Red La unión de objetos dependientes mejora el rendimiento general. Esta unión elima toda la comunicación específica entre objetos dependientes a través de la red. Si todos los objetos dependientes se diseñaran como beans de entidad específicos, provocaría una sobrecarga en la red debido a la comunicación entre los beans de entidad.

• Reduce la Dependencia del Esquema de la Base de Datos Cuando se utiliza el patrón Composite Entity, resulta en implementaciones de beans de entidad genéricos. El esquema de la base de datos está oculto para los clientes, ya que el mapeo entre el bean de entidad y el esquema es interno del bean de entidad. Los cambios en el esquema de la base de daos podrían requerir cambios en los beans de CompositeEntity. Sin embargo, los clientes no se ven afectados porque los beans Composite Entity no exponen el esquema al mundo exterior.

Page 149: Catálogo de Patrones de Diseño J2EE

• Incrementa la Generalidad del Objeto Con un Composite Entity, el cliente normalmente busca un sólo bean de entidad en lugar de una gran cantidad de beans de entidad específicos. El cliente le pide los datos al Composite Entity. Éste puede crear un Transfer Object compuesto que contenga todos los datos del bean de entidad y devolverlo al cliente en una única llamada a método.

• Facilita la Creación de Transfer Object Compuestos Utilizando esta estrategia, se reduce la comunicación entre el cliente y el bean de entidad, ya que el bean Composite Entity puede devolver un Transfer Object compuesto proporcionando un mecanismo para enviar Transfer Objects serializados. Aunque un Transfer Object devuelva todos los datos en una llamada remota, la cantidad de datos devueltos con esta única llamada es mucho mayor que la cantidad de datos devuelta por llamadas remotas independientes para obtener propiedades de beans de entidad individuales. Este inconveniente se puede sobrellevar cuando el objetivo es evitar la repetición de llamadas remotas y búsquedas múltiples.

• Sobrecarga de Grupos de Objetos Dependientes Multi-Nivel Si el conjunto de objetos dependientes manejado por el Composite Entity tiene muchos niveles, entonces se incrementa la carga y almacenamiento de objetos dependientes. Esto se puede reducir utilizando estrategias de optimización para la carga y almacenamiento, peo entonces podría aparecer otra sobrecarga asociada con el chequeo de objetos obsoletos para su almacenamiento y la carga de los objetos requeridos.

Codigo de Ejemplo

Consideremos una aplicación de Servicio Profesional de automatización (PSA) donde se implementa un objeto de negocio: Resource utilizando el patrón Composite Entity. Resource representa el recurso "empleado" que está asignado a los proyectos. Cada objeto Resource puede tener diferentes objetos dependientes, de esta forma:

• BlockOutTime - Este objeto dependiente representa el periodo de tiempo que el recurso no está disponible por razones como formación, vacaciones, días libres, etc. Como cada recurso puede tener varios periodos de absentismo, la relación Resource-a-BlockOutTime es de uno-a-muchos.

• SkillSet - Este objeto dependiente representa las "Habilidades" (Skills) que posee un recurso. Como cada recurso puede tener mútiples habilidades, la relación de Resource-a-SkillSet es de uno a muchos.

Implementación del patrón Composite Entity

El patrón para el objeto de negocio Resource se implementa como un Composite Entity (ResourceEntity), como se ve en el siguiente código. La relación uno-a-muchos con sus objetos dependientes (objetos BlockOutTime y SkillSet) se implementa utilizando collections.

package corepatterns.apps.psa.ejb; import corepatterns.apps.psa.core.*; import corepatterns.apps.psa.dao.*; import java.sql.*; import javax.sql.*; import java.util.*; import javax.ejb.*; import javax.naming.*; public class ResourceEntity implements EntityBean { public String employeeId; public String lastName; public String firstName; public String departmentId; public String practiceGroup; public String title; public String grade; public String email; public String phone; public String cell; public String pager; public String managerId; // Collection of BlockOutTime Dependent objects public Collection blockoutTimes; // Collection of SkillSet Dependent objects public Collection skillSets;

Page 150: Catálogo de Patrones de Diseño J2EE

... private EntityContext context; // Entity Bean methods implementation public String ejbCreate(ResourceTO resource) throws CreateException { try { this.employeeId = resource.employeeId; setResourceData(resource); getResourceDAO().create(resource); } catch(Exception ex) { throw new EJBException("Reason:" + ...); } return this.employeeId; } public String ejbFindByPrimaryKey(String primaryKey) throws FinderException { boolean result; try { ResourceDAO resourceDAO = getResourceDAO(); result = resourceDAO.selectByPrimaryKey(primaryKey); } catch(Exception ex) { throw new EJBException("Reason:" + ...); } if(result) { return primaryKey; } else { throw new ObjectNotFoundException(...); } } public void ejbRemove() { try { // Remove dependent objects if(this.skillSets != null) { SkillSetDAO skillSetDAO = getSkillSetDAO(); skillSetDAO.setResourceID(employeeId); skillSetDAO.deleteAll(); skillSets = null; } if(this.blockoutTime != null) { BlockOutTimeDAO blockouttimeDAO = getBlockOutTimeDAO(); blockouttimeDAO.setResourceID(employeeId); blockouttimeDAO.deleteAll(); blockOutTimes = null; } // Remove the resource from the persistent store ResourceDAO resourceDAO = new ResourceDAO(employeeId); resourceDAO.delete(); } catch(ResourceException ex) { throw new EJBException("Reason:"+...); } catch(BlockOutTimeException ex) { throw new EJBException("Reason:"+...); } catch(Exception exception) { ... } } public void setEntityContext(EntityContext context) { this.context = context; } public void unsetEntityContext() { context = null; } public void ejbActivate() { employeeId = (String)context.getPrimaryKey(); } public void ejbPassivate() { employeeId = null; } public void ejbLoad() { try { // load the resource info from ResourceDAO resourceDAO = getResourceDAO(); setResourceData((ResourceTO) resourceDAO.load(employeeId));

Page 151: Catálogo de Patrones de Diseño J2EE

// Load other dependent objects, if necessary ... } catch(Exception ex) { throw new EJBException("Reason:" + ...); } } public void ejbStore() { try { // Store resource information getResourceDAO().update(getResourceData()); // Store dependent objects as needed ... } catch(SkillSetException ex) { throw new EJBException("Reason:" + ...); } catch(BlockOutTimeException ex) { throw new EJBException("Reason:" + ...); } ... } public void ejbPostCreate(ResourceTO resource) { } // Method to Get Resource Transfer Object public ResourceTO getResourceTO() { // create a new Resource Transfer Object ResourceTO resourceTO = new ResourceTO(employeeId); // copy all values resourceTO.lastName = lastName; resourceTO.firstName = firstName; resourceTO.departmentId = departmentId; ... return resourceTO; } public void setResourceData(ResourceTO resourceTO) { // copy values from Transfer Object into entity bean employeeId = resourceTO.employeeId; lastName = resourceTO.lastName; ... } // Method to get dependent Transfer Objects public Collection getSkillSetsData() { // If skillSets is not loaded, load it first. // See Lazy Load strategy implementation. return skillSets; } ... // other get and set methods as needed ... // Entity bean business methods public void addBlockOutTimes(Collection moreBOTs) throws BlockOutTimeException { // Note: moreBOTs is a collection of // BlockOutTimeTO objects try { Iterator moreIter = moreBOTs.iterator(); while(moreIter.hasNext()) { BlockOutTimeTO botTO = (BlockOutTimeTO)moreIter.next(); if (! (blockOutTimeExists(botTO))) { // add BlockOutTimeTO to collection botTO.setNew(); blockOutTime.add(botTO); } else { // BlockOutTimeTO already exists, cannot add throw new BlockOutTimeException(...); } } } catch(Exception exception) { throw new EJBException(...); } } public void addSkillSet(Collection moreSkills) throws SkillSetException { // similar to addBlockOutTime() implementation ... } ...

Page 152: Catálogo de Patrones de Diseño J2EE

public void updateBlockOutTime(Collection updBOTs) throws BlockOutTimeException { try { Iterator botIter = blockOutTimes.iterator(); Iterator updIter = updBOTs.iterator(); while (updIter.hasNext()) { BlockOutTimeTO botTO = (BlockOutTimeTO) updIter.next(); while (botIter.hasNext()) { BlockOutTimeTO existingBOT = (BlockOutTimeTO) botIter.next(); // compare key values to locate BlockOutTime if (existingBOT.equals(botTO)) { // Found BlockOutTime in collection // replace old BlockOutTimeTO with new one botTO.setDirty(); //modified old dependent botTO.resetNew(); //not a new dependent existingBOT = botTO; } } } } catch (Exception exc) { throw new EJBException(...); } } public void updateSkillSet(Collection updSkills) throws CommitmentException { // similar to updateBlockOutTime... ... } ... }

Implementar la Estrategia Lazy Loading

Cuando el contenedor carga por primera vez el Composite Entity utilizando el método ejbLoad(), asumimos que sólo se han cargado los datos del recurso. Esto incluye los atributos listados en el bean ResourceEntity, excluyendo las colecciones de objetos dependientes. Los objetos dependientes entonces sólo se pueden cargar si el cliente invoca un método de negocio que necesita que se carguen esos objetos dependientes. Consiguientemente, el método ejbLoad() necesita seguir la pista de los objetos dependientes cargados de esta manera e incluirlos para su recarga.

Abajo podemos ver los métodos más importantes de la clase ResourceEntity:

... public Collection getSkillSetsData() { throws SkillSetException { checkSkillSetLoad(); return skillSets; } private void checkSkillSetLoad() throws SkillSetException { try { // Lazy Load strategy...Load on demand if (skillSets == null) skillSets = getSkillSetDAO(resourceId).loadAll(); } catch(Exception exception) { // No skills, throw an exception throw new SkillSetException(...); } } ... public void ejbLoad() { try { // load the resource info from ResourceDAO resourceDAO = new ResourceDAO(employeeId); setResourceData((ResourceTO)resourceDAO.load()); // If the lazy loaded objects are already // loaded, they need to be reloaded. // If there are not loaded, do not load them

Page 153: Catálogo de Patrones de Diseño J2EE

// here...lazy load will load them later. if (skillSets != null) { reloadSkillSets(); } if (blockOutTimes != null) { reloadBlockOutTimes(); } ... throw new EJBException("Reason:"+...); } } ...

Implementar la Estrategia Store Optimization (Dirty Marker)

Para utilizar la estrategia Store Optimization, necesitamos que los objetos dependientes implementen el interface DirtyMarker, como se ve en el siguiente ejemplo:

public class SkillSetTO implements DirtyMarker, java.io.Serializable { private String skillName; private String expertiseLevel; private String info; ... // dirty flag private boolean dirty = false; // new flag private boolean isnew = true; // deleted flag private boolean deleted = false; public SkillSetTO(...) { // initialization ... // is new TO setNew(); } // get, set and other methods for SkillSet // all set methods and modifier methods // must call setDirty() public setSkillName(String newSkillName) { skillName = newSkillName; setDirty(); } ... // DirtyMarker methods // used for modified Transfer Objects only public void setDirty() { dirty = true; } public void resetDirty() { dirty = false; } public boolean isDirty() { return dirty; } // used for new Transfer Objects only public void setNew() { isnew = true; } public void resetNew() { isnew = false; } public boolean isNew() { return isnew; } // used for deleted objects only public void setDeleted() { deleted = true; } public boolean isDeleted() { return deleted; } public void resetDeleted() { deleted = false;

Page 154: Catálogo de Patrones de Diseño J2EE

} }

En el siguiente ejemplo podemos ver el método ejbStore() optimizado para utilizarlo con esta estrategia:

... public void ejbStore() { try { // Load the mandatory data getResourceDAO().update(getResourceData()); // Store optimization for dependent objects // check dirty and store // Check and store commitments if (skillSets != null) { // Get the DAO to use to store SkillSetDAO skillSetDAO = getSkillSetDAO(); Iterator skillIter = skillSet.iterator(); while(skillIter.hasNext()) { SkillSetTO skill = (SkillSetTO) skillIter.next(); if (skill.isNew()) { // This is a new dependent, insert it skillSetDAO.insert(skill); skill.resetNew(); skill.resetDirty(); } else if (skill.isDeleted()) { // delete Skill skillSetDAO.delete(skill); // Remove from dependents list skillSets. remove(skill); } else if (skill.isDirty()) { // Store Skill, it has been modified skillSetDAO.update(skill); // Saved, reset dirty. skill.resetDirty(); skill.resetNew(); } } } // Similarly, implement store optimization // for other dependent objects such as // BlockOutTime, ... ... } catch(SkillSetException ex) { throw new EJBException("Reason:"+...); } catch(BlockOutTimeException ex) { throw new EJBException("Reason:"+...); } catch(CommitmentException ex) { throw new EJBException("Reason:"+...); } } ...

Implementar la Estrategia Composite Transfer Object

Ahora consideremos el requerimiento donde el cliente necesita obtener todos los datos del ResourceEntity, y no sólo una parte. Esto se puede hacer utilizando la estrategia Composite Transfer Object como se muestra en el siguiente ejemplo:

public class ResourceCompositeTO { private ResourceTO resourceData; private Collection skillSets; private Collection blockOutTimes; // Transfer Object constructors ... // get and set methods ... }

Page 155: Catálogo de Patrones de Diseño J2EE

El ResourceEntity proporciona un método getResourceDetailsData() para devolver el objeto Transfer Object compuesto ResourceCompositeTO:

... public ResourceCompositeTO getResourceDetailsData() { ResourceCompositeTO compositeTO = new ResourceCompositeTO (getResourceData(), getSkillsData(), getBlockOutTimesData()); return compositeTO; } ...

Patrones Relacionados

• Transfer Object El patrón Composite Entity usa el patrón Transfer Object para crear el objeto que devuelve al cliente. El patrón Transfer Object se utiliza para serializar el árbol de objetos genéricos y de objetos dependientes, o parte de ese árbol, según se requiera.

• Session Facade Si los objetos dependientes tienden a ser beans de entidad en vez de objetos Java normales, debemos intentar de utilizar el patrón Session Facade para manejar las relaciones entre beans de entidad.

• Transfer Object Assembler Este patrón es similar al patrón Transfer Object. Sin embargo, en este caso, las fuentes de datos para todos los Transfer Objects del compuesto son parte del propio Composite Entity, mientras que para el Transfer Object Assembler, los datos puede ser diferentes beans de entidad, beans de sesión, DAOs, objetos Java, etc.

Bean de Entidad como un Objeto Dependiende: Problemas y Recomendaciones

Típicamente, diseñamos los objetos dependientes como objetos Java que tienen una relación directa con el objeto genérico padre. Sin embargo, podría hacer situaciones en las que un objeto dependiente podría aparecer como un propio bean de entidad:

1. Si el objeto dependiente es dependiente de dos objetos padre diferentes (como es el caso de la asociación de clases.

2. Si el objeto dependiente ya existe como un bean de entidad en la misma aplicación o se importa desde una aplicación diferente.

En estos casos, el ciclo de vida del objeto dependiente podría parecer que no está directamente relacionado y manejado por un sólo objeto genérico padre. Entonces, ¿qué podemos hacer cuando un objeto dependiente es un bean de entidad? ¿y, cuándo veamos un objeto dependiente que no es totalmente dependiente de su objeto padre? ¿o, cuándo no podamos identificar su único objeto padre?

Consideremos cada caso con un poco más de detalle:

• Caso 1: El Objeto Dependiente depende de Dos Objetos Padre.

Exploremos esto con el siguiente ejemplo. Un Commitment representa una asociación entre un Resource y un Project. La siguiente figura muestra un ejemplo de diagrama de clases con las relaciones entre Project, Resource y Commitment.

Page 156: Catálogo de Patrones de Diseño J2EE

Commitment es un objeto dependiente. Projects y Resources son objetos genéricos. Cada Project tiene una relación uno-a-muchos con objetos Commitment. Entonces. ¿Commitment es un objeto dependiente de Project o de Resource? La respuesta trata de analizar las interacciones para los casos de utilización que implican estos tres componentes. Si hacemos que el objeto Commitment sea dependiente del objeto Project, entonces cuando un Resource acceda a su lista de objetos Commitment, tiene que hacerlo a través del objeto Project. Por otro lado, si Commitment es un objeto dependiente de un Resource, cuando Project acceda a su lista de objeto Commitment, tiene que hacerlo mediante el Resource. Estas dos elecciones introducen en el diseño las relaciones bean-de-entidad-a-bean-de-entidad.

Pero, ¿qué pasa si Commitment es un bean de entidad en vez de un objeto dependiente? Entonces la relación entre Project y su lista de objetos Commitment, y entre un Resource y su lista de objetos Commitment, será una relación entidad-a-bean-de-entidad. Esto empeora el problema en que ahora tenemos dos relaciones bean-de-entidad-a-bean-de-entidad.

Este tipo de relaciones no es recomendable debido a la sobrecarga asociada con su manejo y sustentación.

• Caso 2: El Objeto Dependiente ya existe como un Bean de Entidad

En este caso, podría parecer que una forma de modelar esta relación es almacenar la clave primaria del objeto dependitne en el objeto genérico. Cuando el objeto genérico necesite acceder al objeto dependiente, el resultado será una llamada bean-de-entidad-a-bean-de-entidad. Abajo podemos ver el diagrama de clases para este ejemplo:

Page 157: Catálogo de Patrones de Diseño J2EE

En la siguiente figura podemos ver el diagrama de secuencia para este escenario. El Composite Entity utiliza las referencias al objeto dependiente para buscar el bean de entidad requerido. En este caso el objeto dependiente es un proxy al bean de entidad dependiente:

Aunque esto podría corrige el requerimiento de utilizar un bean de entidad dependiente partiendo de un bean de entidad padre, no es una solución elegante. En su lugar, para evitar la complejidad del diseño y manejo de las relaciones entre-entidades, consideremos la utilización de un bean de sesión para manejar las relaciones entre los beans de entidad. Según nuestra experiencia, hemos encontrado que el patrón Session Facade nos ayudó a solucionar este problema y nos proporcionó una mejor forma de manejar las relaciones bean-de-entidad-a-bean-de-entidad.

Por eso, recomendamos evitar las relaciones bean-de-entidad-a-bean-de-entidad como la mejor práctica, y para optimizar las relaciones dentro de un bean de sesión recomendamos la utilización del patrón Session Facade.

Page 158: Catálogo de Patrones de Diseño J2EE

Catálogo de Patrones de Diseño J2EE. Y II: Capas de Negocio y de Integración Autor: Sun

Traductor: Juan Antonio Palos (Ozito)

• Data Access Object o Contexto o Problema o Causas o Solución

� Estructura � Participantes y Responsabilidades � Estrategias

o Consecuencias o Código de Ejemplo

� Implementar el Patrón Data Access Object � Implementar la Estrategia Factory for Data Access Objects

o Patrones Relacionados

Data Access Object

Contexto

El acceso a los datos varía dependiendo de la fuente de los datos. El acceso al almacenamiento persistente, como una base de datos, varía en gran medida dependiendo del tipo de almacenamiento (bases de datos relacionales, bases de datos orientadas a objetos, ficheros planos, etc.) y de la implementación del vendedor.

Problema

Muchas aplicaciones de la plataforma J2EE en el mundo real necesitan utilizar datos persistentes en algún momento. Para muchas de ellas, este almacenamiento persistente se implementa utilizando diferentes mecanismos, y hay marcadas diferencias en los APIS utilizados para acceder a esos mecanismos de almacenamiento diferentes. Otras aplicaciones podrían necesitar acceder a datos que residen en sistemas diferentes. Por ejemplo, los datos podrían residir en sitemas mainframe, repositorios LDAP, etc. Otro ejemplo es donde los datos los proporcionan servicios a través de sistemas externos como los sistemas de integración negocio-a-negocio (B2B), servicios de tarjetas de crédito, etc.

Normalmente, las aplicaciones utilizan componentes distribuidos y compartidos como los beans de entidad para representar los datos persistentes. Se considera que una aplicación emplea consistencia manejada por el bean (BMP) cuando sus beans de entidad acceden explícitamente al almacenamiento persistente -- el bean de entidad incluye código para hacer esto. Una aplicación con requerimientos sencillos podría por lo tanto utilizar beans de entidad en lugar de beans de sesión o servlets para acceder al almacenamiento persistente y recuperar o modificar los datos. O, la aplicación podría usar beans de entidad con persistencia manejada por el contenedor, y así dejar que el contenedor maneje los detalles de las transaciones y de la persistencia.

Las aplicaciones pueden utilizar el API JDBC para acceder a los datos en un sistema de control de bases de datos relacionales (RDBMS). Este API permite una forma estándar de acceder y manipular datos en un almacenamineto persistente, como una base de datos ralacional. El API JDBC permite a las aplicaciones J2EE utilizar sentencias SQL, que son el método estándar para acceder a tablas RDBMS. Sin embargo, incluso dentro de un entorno RDBMS, la síntaxis y formatos actuales de las sentencias SQL podrían variar dependiendo de la propia base de datos en particular.

Incluso hay una mayor variación con diferentes tipos de almacenamientos persistentes. Los mecanimos de acceso, los APIs soportados, y sus características varian entre los diferentes tipos de almacenamientos persistentes, como bases de datos relacionales, bases de datos orientadas a objetos, ficheros planos, etc. Las aplicaciones que necesitan acceder a datos de un sistema legal o un sistema dispar (como un mainframe o un servicio B2B) se ven obligados a utilizar APIs que podrían ser

Page 159: Catálogo de Patrones de Diseño J2EE

propietarios. Dichas fuentes de datos dispares ofrecen retos a la aplicación y potencialmente pueden crear una dependencia directa entre el código de la aplicación y el código de acceso a los datos. Cuando los componentes de negocio -- beans de entidad, beans de sesión e incluso componentes de presentación como servlets y beans de apoyo para páginas JSP -- necesitan acceder a una fuente de datos, pueden utilizar el API apropiado para conseguir la conectividad y manipular la fuente de datos. Pero introducir el código de conectividad y de acceso a datos dentro de estos componentes genera un fuerte acoplamiento entre los componentes y la implementación de la fuente de datos. Dichas dependencias de código en los componentes hace difícil y tedioso migrar la aplicación de un tipo de fuente de datos a otro. Cuando cambia la fuente de datos, también deben cambiar los componentes para manejar el nuevo tipo de fuente de datos.

Causas

• Los componentes como los beans de entidad controlados por el bean, los beans de sesión, los servlets, y otros objetos como beans de apoyo para páginas JSP necesitan recuperar y almacenar información desde almacenamientos persistentes y otras fuentes de datos como sistemas legales, B2B, LDAP, etc.

• Los APIs para almacenamiento persistente varían dependiendo del vendedor del producto. Otras fuentes de datos podrían tener APIS que no son estándar y/o propietarios. Estos APIs y sus capacidades también varían dependiendo del tipo de almacenamiento -- bases de datos relacionales, bases de datos orientadas a objetos, documentos XML, ficheros planos, etc. Hay una falta de APIs uniformes para corregir los requrimientos de acceso a sistemas tan dispares.

• Los componentes normalmente utilizan APIs propietarios para acceder a sistemas externos y/o legales para recuperar y almacenar datos.

• La portabilidad de los componentes se ve afectada directamente cuando se incluyen APIs y mecanismos de acceso específicos.

• Los componentes necesitan ser transparentes al almacenamiento persistente real o la implementación de la fuente de datos para proporcionar una migración sencilla a diferentes productos, diferentes tipos de almacenamiento y diferentes tipos de fuentes de datos.

Solución

Utilizar un Data Access Object (DAO) para abstraer y encapsular todos los accesos a la fuente de datos. El DAO maneja la conexión con la fuente de datos para obtener y almacenar datos.

El DAO implementa el mecanismo de acceso requerido para trabajar con la fuente de datos. Esta fuente de datos puede ser un almacenamiento persistente como una RDMBS, un servicio externo como un intercambio B2B, un repositorio LDAP, o un servicio de negocios al que se accede mediante CORBA Internet Inter-ORB Protocol (IIOP) o sockets de bajo nivel. Los componentes de negocio que tratan con el DAO utilizan un interface simple expuesto por el DAO para sus clientes. El DAO oculta completamente los detalles de implementación de la fuente de datos a sus clientes. Como el interface expuesto por el DAO no cambia cuando cambia la implementación de la fuente de datos subyacente, este patrón permite al DAO adaptarse a diferentes esquemas de almacenamiento sin que esto afecte a sus clientes o componentes de engocio. Esencialmente, el DAO actúa como un adaptador entre el componente y la fuente de datos.

Estructura

La siguiente figura muestra el diagrama de clases que representa las relaciones para el patrón DAO:

Page 160: Catálogo de Patrones de Diseño J2EE

Participantes y Responsabilidades

La siguiente figura muestra el diagrama de secuecnia de la interacción entre los distintos participantes en este patrón:

BusinessObject

Page 161: Catálogo de Patrones de Diseño J2EE

BusinessObject representa los datos del cliente. Es el objeto que requiere el acceso a la fuente de datos para obtener y almacenar datos. Podríamos implementar un BusinessObject como un bean de sesión, un bean de entidad o cualquier otro objeto Java, además de como un Servlet o como un bean de apoyo.

DataAccessObject

DataAccessObject es el objeto principal de este patrón. DataAccessObject abstrae la implementación del acceso a datos subyacente al BusinessObject para permitirle un acceso transparente a la fuente de datos. El BusinessObject también delega las operaciones de carga y almacenamiento en el DataAccessObject.

DataSource

Representa la implementación de la fuente de datos. Una fuente de datos podría ser una base de datos como un RDBMS, un OODBMS, un repositorio XML, un fichero plano, etc. También lo pueden ser otros sitemas (mainframes/legales), servicios (servicio B2B u oficina de tarjetas de crédito), o algún tipo de repositorio (LDAP).

TransferObject

Representa un Transfer Object utilizado para el transporte de datos. DataAccessObject podría utilizar un Transfer Object para devolver los datos al cliente. El DataAccessObject también podría recibir datos desde el cliente en un Transfer Object para actualizar los datos en la fuente de datos.

Estrategias

Automatic DAO Code Generation

Como cada BusinessObject corresponde a un DAO específico, es posible establecer relaciones entre el BusinessObject, el DAO, y las implementaciones subyacentes (como en las tablas de una RDBMS). Una vez que se han establecido las relaciones, es posible escribir una utilidad de generación-de-código-específica-de-aplicación que genere el código para todos los DAOs que necesita la aplicación. Los metadatos para generar el DAO pueden venir de un fichero descriptor definido por el desarrollador. Como alternativa, el generador de código puede inspeccionar la base de datos y proporcionar los DAOs necesarios para acceder a ella.

Si los requerimientos de los DAOs son lo suficientemente complejos, debemos considerar la utilización de herramientas de terceras partes que proporcionan un mapeo de objeto-a-relacional para bases de datos RDBMS. Estas herramientas normalmente incluyen utilidades GUI para mapear los objetos de negocio a objetos de almacenamiento persistente y además definir los DAOs intermedios. Estas herramientas generan el código automáticamente una vez que se termina el mapeo, y podrían proporcionar otras características valiosas como el caché de resultados y de consultas, integración con servidores de aplicaciones, integración con otros productos de terceras partes, etc.

Factory for Data Access Objects

El patrón DO se puede flexibilizar adoptando los patrones Abstract Factory [GoF] y Factory Method [GoF].

Cuando el almacenamiento subyacente no está sujeto a cambios de una implemantación a otra, esta estrategia se puede implementar utilizando el patrón Factory Method para producir el número de DAOs que necesita la aplicación. En la siguiente figura podemos ver el diagrama de clases para este caso:

Page 162: Catálogo de Patrones de Diseño J2EE

Cuando el almacenamiento subyacente si está sujeto a cambios de una implementación a otra, esta estrategia se podría implementar usando el patrón Abstract Factory. Este patrón a su vez puede construir y utilizar la implementación Factory Method. En este caso, esta estrategia proporciona un objeto factoría abstracta de DAOs (Abstract Factory) que puede construir varios tipos de factorías concretas de DAOs, cada factoría soporta un tipo diferente de implementación del almacenamiento persistente. Una vez que obtenemos la factoría concreta de DAOs para una implementación específica, la utilizamos para producir los DAOs soportados e implementados en esa implementación.

En la siguiente figura podemos ver el diagrama de clases para esta estrategia. En él vemos una factoría base de DAOs, que es una clase abstracta que extienden e implementan las diferentes factorías concretas de DAOs para soportar el acceso específico a la implementación del almacenamiento. El cliente puede obtener una implementación de la factoría concreta del DAO como una RdbDAOFactory y utilizarla para obtener los DAOs concretos que funcionan en la implementación del almacenamiento. Por ejemplo, el cliente puede obtener una RdbDAOFactory y utilizarlas para obtener DAOs específcios como RdbCustomerDAO, RdbAccountDAO, etc. Los DAOs pueden extender e implementar una clase base genérica (mostradas como DAO1 y DAO2) que describa específicamente los requerimientos del DAO para el objeto de negocio que soporta. Cada DAO concreto es responsable de conectar con la fuente de datos y de obtener y manipular los datos para el objeto de negocio que soporta.

Page 163: Catálogo de Patrones de Diseño J2EE

En la siguiente figura podemos ver el diagrama de secuencia para esta estrategia:

Page 164: Catálogo de Patrones de Diseño J2EE

Consecuencias

• Permite la Transpariencia Los objetos de negocio puede utilizar la fuente de datos sin conocer los detalles específicos de su implementación. El acceso es transparente porque los detalles de la implementación se ocultan dentro del DAO.

• Permite una Migración más Fácil Una capa de DAOs hace más fácil que una aplicación pueda migrar a una implementación de base de datos diferente. Los objetos de negocio no conocen la implementación de datos subyacente, la migración implica cambios sólo en la capa DAO. Admás, si se emplea la estrategia de factorías, es posible proporcionar una implementación de factorías concretas por cada implementación del almacenamiento subyacente. En este caso, la migración a un almacenamiento diferente significa proporcionar a la aplicación una nueva implementación de la factoría.

• Reduce la Complejidad del Código de los Objetos de Negocio Como los DAOs manejan todas las complejidades del acceso a los datos, se simplifica el código de los objetos de negocio y de otros clientes que utilizan los DAOs. Todo el código relacionado con la implementación (como las sentencias SQL) están dentro del DAO y no en el objeto de negocio. Esto mejora la lectura del código y la productividad del desarrollo.

Page 165: Catálogo de Patrones de Diseño J2EE

• Centraliza Todos los Accesos a Datos en un Capa Indenpendinte Como todas las operaciones de acceso a los datos se ha delegado en los DAOs, esto se puede ver como una capa que aisla el resto de la aplicación de la implementación de acceso a los datos. Esta centralización hace que la aplicación sea más sencilla de mantener y de manejar.

• No es útil para Persistencia Manejada por el Contenedor Como el contenedor EJB maneja los beans de entidad con persistencia manejada por el contenedor (CMP), sirve automáticamente todo el acceso al almacenamiento persistente. Las aplicación que utilizan este tipo de beans no necesitan la capa DAO, ya que el servidor de aplicaciones proporciona de forma transparente esta funcionalidad. Sin embargo, los DAOS aún son útiles cuando se necesita una combinación de CMP (para beans de entidad) y BMP (para beans de seión, servlets).

• Añade una Capa Extra Los DAOs crean un capa de objetos adicional entre el cliente y la fuente de datos que necesitamos diseñar e implementar para obtener los beneficios de este patrón. Pero para obtener estos beneficios debemos pagarlos con un esfuerzo adicional.

• Necesita Diseñar un Árbol de Clases Cuando se utiliza una estrategia de factorías, necesitamos diseñar e implementar el árbol de factorías concretas y el árbol de productos concretos producidos por las factorías. Necesitamos justificar este esfuerzo adicional para ver si merece la pena dicha flexibilidad. Esto incrementa la complejidad del diseño. Sin embargo, podemos elegir la implementación de la estrategia de factorías empezando primero con el patrón Factory Method, y luego avanzar hasta el patrón Abstract Factory si es necesario.

Código de Ejemplo

Implementar el Patrón Data Access Object

Abajo podemos ver el código de un DAO de ejemplo para un objeto persistente que representa información de un cliente. CloudscapeCustomerDAO crea un Transfer Object Customer cuando se llama al método findCustomer():

// CloudscapeCustomerDAO implementation of the // CustomerDAO interface. This class can contain all // Cloudscape specific code and SQL statements. // The client is thus shielded from knowing // these implementation details. import java.sql.*; public class CloudscapeCustomerDAO implements CustomerDAO { public CloudscapeCustomerDAO() { // initialization } // The following methods can use // CloudscapeDAOFactory.createConnection() // to get a connection as required public int insertCustomer(...) { // Implement insert customer here. // Return newly created customer number // or a -1 on error } public boolean deleteCustomer(...) { // Implement delete customer here // Return true on success, false on failure } public Customer findCustomer(...) { // Implement find a customer here using supplied // argument values as search criteria // Return a Transfer Object if found, // return null on error or if not found } public boolean updateCustomer(...) { // implement update record here using data // from the customerData Transfer Object // Return true on success, false on failure or // error }

Page 166: Catálogo de Patrones de Diseño J2EE

public RowSet selectCustomersRS(...) { // implement search customers here using the // supplied criteria. // Return a RowSet. } public Collection selectCustomersTO(...) { // implement search customers here using the // supplied criteria. // Alternatively, implement to return a Collection // of Transfer Objects. } ... }

Abajo podemos ver el código para utilizar este DAO:

... // create the required DAO Factory DAOFactory cloudscapeFactory = DAOFactory.getDAOFactory(DAOFactory.DAOCLOUDSCAPE); // Create a DAO CustomerDAO custDAO = cloudscapeFactory.getCustomerDAO(); // create a new customer int newCustNo = custDAO.insertCustomer(...); // Find a customer object. Get the Transfer Object. Customer cust = custDAO.findCustomer(...); // modify the values in the Transfer Object. cust.setAddress(...); cust.setEmail(...); // update the customer object using the DAO custDAO.updateCustomer(cust); // delete a customer object custDAO.deleteCustomer(...); // select all customers in the same city Customer criteria=new Customer(); criteria.setCity("New York"); Collection customersList = custDAO.selectCustomersTO(criteria); // returns customersList - collection of Customer // Transfer Objects. iterate through this collection to // get values. ...

La siguiente figura representa el diagrama de clases para este ejemplo:

Page 167: Catálogo de Patrones de Diseño J2EE

Implementar la Estrategia Factory for Data Access Objects

Utilizar el Patrón Factory Method

Consideremos un ejemplo donde hemos implementado esta estrategia en la que una factoría de DAOs produce muchos DAOs para una única implementación de una base de datos (por ejemplo, Oracle). La factoría produce DAOs como CustomerDAO, AccountDAO, OrderDAO, etc. Abajo podemos ver el diagrama de clases para este ejemplo:

Page 168: Catálogo de Patrones de Diseño J2EE

Y aquí tenemos el código de ejemplo para la factoría de DAOs (CloudscapeDAOFactory):

// Cloudscape concrete DAO Factory implementation import java.sql.*; public class CloudscapeDAOFactory extends DAOFactory { public static final String DRIVER= "COM.cloudscape.core.RmiJdbcDriver"; public static final String DBURL= "jdbc:cloudscape:rmi://localhost:1099/CoreJ2EEDB"; // method to create Cloudscape connections public static Connection createConnection() { // Use DRIVER and DBURL to create a connection // Recommend connection pool implementation/usage } public CustomerDAO getCustomerDAO() { // CloudscapeCustomerDAO implements CustomerDAO return new CloudscapeCustomerDAO(); } public AccountDAO getAccountDAO() { // CloudscapeAccountDAO implements AccountDAO return new CloudscapeAccountDAO(); } public OrderDAO getOrderDAO() { // CloudscapeOrderDAO implements OrderDAO return new CloudscapeOrderDAO(); } ... }

Utilizar el Patrón Abstract Factory

Consideremos un ejemplo donde implementemos esta estrategia para tres bases de datos diferenets. En este caso, se puede emplear el patrón Abstract Factory. En la siguiente figura podemos ver el diagrama de clases para este ejmplo.

Page 169: Catálogo de Patrones de Diseño J2EE

El siguiente fragmento de código muestra parte de la clase DAOFactory. Esta factoría produce DAOs como CustomerDAO, AccountDAO, OrderDAO, etc. Esta estrategia utiliza el patrón Factory Method en las factorías producidas por el Abstract Factory.

// Abstract class DAO Factory public abstract class DAOFactory { // List of DAO types supported by the factory public static final int CLOUDSCAPE = 1; public static final int ORACLE = 2; public static final int SYBASE = 3; ... // There will be a method for each DAO that can be // created. The concrete factories will have to // implement these methods. public abstract CustomerDAO getCustomerDAO(); public abstract AccountDAO getAccountDAO(); public abstract OrderDAO getOrderDAO(); ... public static DAOFactory getDAOFactory( int whichFactory) { switch (whichFactory) { case CLOUDSCAPE: return new CloudscapeDAOFactory(); case ORACLE : return new OracleDAOFactory(); case SYBASE : return new SybaseDAOFactory(); ... default : return null; } } }

El ejemplo de código para CloudscapeDAOFactory se ve en el siguiente listado. La implementación para OracleDAOFactory y SybaseDAOFactory son similares excepto para especifidades de cada implementación, como el driver JDBC, la URL de la base de datos, y diferencias en la síntaxis SQL, si existe-

// Cloudscape concrete DAO Factory implementation import java.sql.*; public class CloudscapeDAOFactory extends DAOFactory { public static final String DRIVER= "COM.cloudscape.core.RmiJdbcDriver"; public static final String DBURL= "jdbc:cloudscape:rmi://localhost:1099/CoreJ2EEDB"; // method to create Cloudscape connections public static Connection createConnection() { // Use DRIVER and DBURL to create a connection // Recommend connection pool implementation/usage } public CustomerDAO getCustomerDAO() { // CloudscapeCustomerDAO implements CustomerDAO return new CloudscapeCustomerDAO(); } public AccountDAO getAccountDAO() { // CloudscapeAccountDAO implements AccountDAO return new CloudscapeAccountDAO(); } public OrderDAO getOrderDAO() { // CloudscapeOrderDAO implements OrderDAO return new CloudscapeOrderDAO(); } ... }

El interface CustomerDAO mostrado en el siguiente listado, define los métodos DAO para los objetos Customer persistentes que son implementados por todas las implementaciones de DAOs concretos como CloudscapeCustomerDAO, OracleCustomerDAO, y SybaseCustomerDAO. Similares, aunque no se listan aquí, son los interfaces AccountDAO y OrderDAO que definen los metodos DAO para los objetos de negocio Account y Order respectivamente.

Page 170: Catálogo de Patrones de Diseño J2EE

// Interface that all CustomerDAOs must support public interface CustomerDAO { public int insertCustomer(...); public boolean deleteCustomer(...); public Customer findCustomer(...); public boolean updateCustomer(...); public RowSet selectCustomersRS(...); public Collection selectCustomersTO(...); ... }

CloudscapeCustomerDAO implementa CustomerDAO como se ven en el siguiente listado. La implementación de los otros DAOs, como CloudscapeOrderDAO, OracleCustomerDAO, OracleAccountDAO, etc. son similares:

// CloudscapeCustomerDAO implementation of the // CustomerDAO interface. This class can contain all // Cloudscape specific code and SQL statements. // The client is thus shielded from knowing // these implementation details. import java.sql.*; public class CloudscapeCustomerDAO implements CustomerDAO { public CloudscapeCustomerDAO() { // initialization } // The following methods can use // CloudscapeDAOFactory.createConnection() // to get a connection as required public int insertCustomer(...) { // Implement insert customer here. // Return newly created customer number // or a -1 on error } public boolean deleteCustomer(...) { // Implement delete customer here // Return true on success, false on failure } public Customer findCustomer(...) { // Implement find a customer here using supplied // argument values as search criteria // Return a Transfer Object if found, // return null on error or if not found } public boolean updateCustomer(...) { // implement update record here using data // from the customerData Transfer Object // Return true on success, false on failure or // error } public RowSet selectCustomersRS(...) { // implement search customers here using the // supplied criteria. // Return a RowSet. } public Collection selectCustomersTO(...) { // implement search customers here using the // supplied criteria. // Alternatively, implement to return a Collection // of Transfer Objects. } ... }

La clase Transfer Object Customer del siguiente listado la utilizan los DAOs para enviar y recibir datos desde los clientes. La utilización del patrón Transfer Object se explica con más detalles en la página Transfer Object:

Page 171: Catálogo de Patrones de Diseño J2EE

public class Customer implements java.io.Serializable { // member variables int CustomerNumber; String name; String streetAddress; String city; ... // getter and setter methods... ... }

El siguiente ejemplo muestra la utilización del factoría de DAOs y del DAO, Si la implementación cambiara de Cloudscape a otro producto, el único cambio que tendríamos que hacer es que el método getDAOFactory() llame a la factoría adecuada.

... // create the required DAO Factory DAOFactory cloudscapeFactory = DAOFactory.getDAOFactory(DAOFactory.DAOCLOUDSCAPE); // Create a DAO CustomerDAO custDAO = cloudscapeFactory.getCustomerDAO(); // create a new customer int newCustNo = custDAO.insertCustomer(...); // Find a customer object. Get the Transfer Object. Customer cust = custDAO.findCustomer(...); // modify the values in the Transfer Object. cust.setAddress(...); cust.setEmail(...); // update the customer object using the DAO custDAO.updateCustomer(cust); // delete a customer object custDAO.deleteCustomer(...); // select all customers in the same city Customer criteria=new Customer(); criteria.setCity("New York"); Collection customersList = custDAO.selectCustomersTO(criteria); // returns customersList - collection of Customer // Transfer Objects. iterate through this collection to // get values. ...

Patrones Relacionados

• Transfer Object Un DAO utiliza Transfer Objects para transportar los datos desde y hacia sus clientes.

• Factory Method [GoF] y Abstract Factory [GoF] La Factoría para la estrategia Data Access Objects usa el patrón Factory Method para implementar las factorías concretas y sus productos (DAOs). Para añadir flexibilidad, se podría emplear el patrón Abstract Factory como se explica en la sección de estrategias.

• Broker [POSA1] El patrón DAO está relacionado con el patrón Broker, que describe aproximaciones para desacoplar clientes y servidores en sistemas distribuidos. El patrón DAO aplica este patrón específicamente para desacoplar la capa de recursos de los clientes en otra capa, como las capas de negocio o presentación.

Page 172: Catálogo de Patrones de Diseño J2EE

Catálogo de Patrones de Diseño J2EE. Y II: Capas de Negocio y de Integración Autor: Sun

Traductor: Juan Antonio Palos (Ozito)

• Service Activator o Contexto o Problema o Causas o Solución

� Estructura � Participantes y Responsabilidades � Estrategias

o Consecuencias o Código de Ejemplo o Patrones Relacionados

Service Activator

Contexto

Los beans enterprise y otros servicios de negocio necesitan una forma de activarse asíncronamente.

Problema

Cuando un cliente necesita acceder a un bean enteprise, primero busca el objeto home del bean. Luego el cliente le pide a ese objeto home que le proporcione una referencia remota al bean enterprise requerido. Entonces el cliente invoca a los métodos de negocio sobre la referencia remota para acceder a los servicios del bean enterpise. Estas llamadas a métodos, así como las búsquedas y las llamdas a métodos remotos, son síncronos. El cliente tiene que esperar hasta que esos metodos retornan.

Otro factor a considerar es el ciclo de vida de un bean enterprise. La especificación EJB permite que el contenedor pasivice un bean enterprise a un almacenamiento intermedio. Como resultado, el contenedor EJB no tiene un mecanismo mediante el cual poder proporcionar un servicio estilo-proceso para mantener un bean enterprise constantemente activado y en estado listo. Como el cliente debe interactúar con el bean enterprise utilizando su interface remoto, incluso si el bean está en estado activado en el contenedor, el cliente áun necesita obtener su interface remoto mediante un proceso de búsqueda y todavía actúa con el bean de forma síncrona.

Si una aplicación necesita procesamiento síncrono para sus componentes de negocio del lado del servidor, entonces los beans enterprise son una opción apropiada. Algunas aplicaciones cliente podrían requerir este tipo de procesamiento porque los clientes no necesitan esperar o no tienen tiempo para esperar a que se complete el procesamiento. En caso donde la aplicación necesita una forma de procesamiento asíncrono, los beans enterpise no ofrecen esta capacidad en implementaciones anteriores a la especificación EJB 2.0.

La especificación EJB 2.0 proporciona integración introduciendo el bean dirigido-a-mensaje, que es un tipo especial de bean de sesión sin estado que ofrece capacidades de invocación asíncrona. Sin embargo, la nueva especificación no ofrece invocación asíncrona para otros tipos de beans enterprise, como los beans con estado o de entidad.

En general, un servicio de negocio como un bean de sesión o de entidad sólo proporciona procesamiento síncrono y esto representa un reto para implemenetar procesamiento asíncrono.

Causas

• Los beans enterprise se exponen a sus clientes mediante sus interfaces remotos, que sólo permiten acceso síncrono.

Page 173: Catálogo de Patrones de Diseño J2EE

• El contenedor maneja los beans enterprise, permitiendo interacciones sólo mediante referencias remotas. El contenedor EJB no permite acceso directo a la implementación del bean y sus métodos. Así, implementar el oyente de mensajes JMS no es factible en un bean enterprise, ya que esto viola la especificación EJB al permitir el acceso directo a la implementación del bean.

• Una aplicación necesita proporcionar un marco de mensajería publicar/suscribir o punto-a-punto donde los clientes puedan publicar peticiones a beans enterprise para procesamiento asíncrono.

• Los clientes necesitan capacidades de procesamiento asíncrono en los beans entreprise y otros componentes de negocio que sólo pueden ofrecer acceso síncrono, para que el cliente pueda enviar una petición para su procesamiento sin esperar los resultados.

• Los clientes quieren utilizar interfaces de la capa media orientados a mesnajes (MOM) ofrecidos por el API Java Messaging Service (JMS). Estos interfaces no están integrados en los productos servidores EJB que están basados en especificaciones anteriores a la la EJB 2.0.

• Una aplicación necesita proporcionar un servicio estilo "criado(daemon)" para que un bean enterprise pueda estar en modo callado hasta que un evento (o un mensaje) dispare su actividad.

• Los beans enterprise están sujetos al control de su ciclo de vida por parte del contenedor, lo que incluye la pasivización debido a periodos de inactividad y control de recursos. El cliente tendrá que invocar a un bean enteprise para activarlo de nuevo.

• La especificación EJB 2.0 introdujo un bean dirigio-a-mensaje como un bean de sesión sin estado, pero no es posible invocar otros tipos diferentes de beans enteprise de forma asíncrona.

Solución

Utilizar un Service Activator para recibir peticiones y mensajes asíncronos de los clientes. Cuando se recibe un mensaje, el Service Activator localiza e invoca a los métodos de de los componentes de negocio necesarios para cumplir la petición de forma asíncrona.

El ServiceActivator es un oyente JMS y un servicio de delegación que requiere la implementación de un oyente de mensajes JMS. Se puede implementar como un servicio independiente. Los clientes actúan como generadores de mensajes, generando eventos basándose en su actividad.

Cualquier cliente que necesite invocar asíncronamente un servicio de negocio, como un bean enteprise, podría crear y enviar un mensaje al Service Activator. Éste recibe el mensaje y lo analiza para interpretar la petición del cliente. Una vez que se ha analizado y desempaquetado la petición del cliente, el Service Activator identifica y localiza los componentes de servicio de negocio necesarios e invoca a sus métodos de negocio para completar el procesamiento de la petición del cliente de forma asíncrona.

Service Activator opcionalmente podría enviar un reconocimiento al cliente después de que haya completado el procesamiento de la petición. También podría notificar al cliente o a otros servicios si ocurre un fallo durante el procesamiento de la petición.

El Service Activator podría utilizar los servicios de un Service Locator.

Estructura

La siguiente figura representa las relaciones entre clases para el patrón Service Activator:

Page 174: Catálogo de Patrones de Diseño J2EE

Participantes y Responsabilidades

La siguiente figura muestra las interacciones entre los distintos participantes en el patrón Service Activator:

Client

El cliente solicita una facilidad de procesamiento asíncrono de los objetos de negocio que participan en un flujo de trabajo. El cliente puede ser cualquier tipo de aplicación que tenga la capacidad de crear y enviar mensajes JMS. El cliente también puede ser un componente EJB que necesite invocar los métodos de negocio de otro componente EJB de forma asíncrona. El cliente puede utilizar los servicios ofrecidos por el patrón Service Locator para buscar o crear componentes EJB, servicios JMS, u objetos JMS, según necesite.

Page 175: Catálogo de Patrones de Diseño J2EE

Request

Request es el objeto message creado po el cliente y enviado al ServiceActivator mediante MOM. De acuerdo a la especificación JMS, Request es un objeto que implementa el interface javax.jms.Message. El API JMS proporciona varios tipos de mesnajes, como TextMessage, ObjectMessage, etc.

ServiceActivator

ServiceActivator es la clase principal del patrón. Implementa el interface javax.jms.MessageListener, definido por la especificación JMS. ServiceActivator implementa un método onMessage() que se invoca cuando llega un nuevo mensaje. ServiceActivator analiza (desempaqueta) el mensaje (la petición) para determinar qué debe hacer. El ServiceActivator podría utilizar los servicios de un patrón Service Locator para buscar o crear los componentes de servicio de negocio que necesita.

BusinessObject

BusinessObject es el objeto destino al que el cliente necesita acceder de forma asíncrona. El objeto de negocio es un rol que pueden cumplir los beans de sesión o de entidad. También es posible que el BusinessObject sea un servicio externo en vez de un bean de entidad.

Estrategias

Entity Bean

Tanto los beans de sesión como los de entidad puede cumplir el rol de un BusinessObject. Cuando las aplicaciones J2EE implementan un patrón Session Façade para proporcionar acceso genérico a los beans de entidad y para encapsular el flujo de trabajo, el bean de sesión del Session Façade cumple el rol del BusinessObject.

En aplicaciones sencillas con flujo de trabajo mínimo, un bean de entidad podría cumplir el rol del BusinessObject. Sin embargo, para flujos de trabajo complejos que implican varios beans de entidad y otros objetos de negocio, el ServiceActivator normalmente interactúa con un Session Facade que encapsula dicho flujo de trabajo.

Session Bean

Cuando un bean de sesión cumple el rol del BusinessObject, los requerimientos del negocio determinan si el bean debería ser con o sin estado. Como el cliente del BusinessObject es un ServiceActivator que activa el BusinessObject cuando recibe un nuevo mensaje, el flujo de trabajo para procesar el mensaje puede definir si el bean debería tener estado o no. En muchos casos, un mensaje simplemente activa un sólo método en el BusinessObject que delega el procesamiento dentro del mensaje. En estos casos se puede utilizar un bean de sesión sin estado. Si el ServiceActivator necesita invocar varios métodos en el BusinessObject o necesita trabajar con más de un BusinessObject para cumplir los requerimientos de procesamiento, podría ser útil considerar la utilización de un bean de sesión con estado para retener el estdo entre varias llamadas.

ServiceActivator Server

La estrategia más correcta para implementar el oyente o ServiceActivator es una aplicación JMS independiente que escuche y procese mensajes JMS.

Una alternativa es implementar el ServiceActivator como servicio del servidor de aplicaciones. Esto hace más fácil de manejar el ServiceActivator, porque utiliza las características del servidor de aplicaciones para monitorizar el estado del ServiceActivator y para arrancar, reiniciar y parar el ServiceActivator cuando sea necesario, manual o automáticamente.

Enterprise Bean as Client

Page 176: Catálogo de Patrones de Diseño J2EE

El Cliente puede ser cualquier cliente, incluyendo otro bean enterprise que requiera procesamiento asíncrono. Cuando se integran aplicaciones legales en la plataforma J2EE, es lógico elegir aplicaciones Java como clientes para actúar como generadores de mensajes basándose en la actividad del sistema legal. El ServiceActivator puede recibir mensajes y realizar cualquier invocación a bean de enterprise necesaria para procesar la petición desde sistemas legales.

Consecuencias

• Integra JMS en implementaciones Pre-EJB 2.0 Anteriormente a la especificación EJB 2.0 no había integración entre beans enterprise y componentes JMS. Este partrón proporciona una forma de integrar JMS en una aplicación EJB y permite procesamiento asíncrono. La especificación EJB 2.0 define un nuevo tipo de bean de sesión, llamado bean dirigido-a-mensaje, para integrar JMS y componentes EJB. Este bean especial implementa el interface javax.jms.MessageListener y recibe mensajes asíncronos. En este caso, el servidor de aplicaciones juega el rol del Service Activator. Este patrón hace posible ejecutar aplicaciones en implementaciones EJB 2.0 así como en implementaciones de EJB anteriores.

• Proporciona Procesamiento Asíncrono para cualquier Bean Enterprise En la especificación EJB 2.0, el bean dirigido-a-mensaje es un bean de sesión sin estado. Utilizando el patrón Service Activator, es posible proporcionar invocación asíncrona a todos los tipos de beans enterprise, incluyeno beans de sesión con o sin estado y beans de entidad. Como se explicó anteriormente, ya que el Service Activator se implementa abiertamente, sin las limitaciones del bean dirigido-a-mensaje, el Service Activator puede realizar llamadas asíncronas a cualquier tipo de servicio de negocio. Así, este patrón presenta una forma de proporcionar procesamiento asíncrono para clientes que no necesitan esperar los resultados o no quieren esperar a que se complete el procesamiento. El procesamiento se puede retrasar y realizarse más tarde, permitiendo que el cliente complete el servicio en menos tiempo.

• Proceso Independiente Service Activator se puede ejecutar como un proceso independiente. Sin embargo, en una aplicación crítica, necesitamos monitorizar el Service Activator para asegurarnos de su disponibilidad. Este control y manteminiento adicional puede añadir una pequeña sobrecarga a la aplicación.

Código de Ejemplo

Consideremos una aplicación de procesamiento de pedidos donde los clientes compran on-line y los otros procesos se realizan en segundo plano. En algunos casos, el cumplimiento del pedido podría externalizarse a un almacen de terceros. En dichos casos, el almacen on-line necesita invocar estos servicios de forma asíncrona. Este es un ejemplo que demuestra la utilización de la mensajería punto-a-punto (PTP) para conseguir el procesamiento asíncrono. Sin embargo, la utilización de la mensajería publicar/suscribir sería similar, excepto en que se utilizaría un Topic en lugar de una Queue. La elección del método autilizado, PTP o publicar/suscribir, depedende de los requerimientos de la aplicación, y se sale fuera del ámbito de este patrón.

En la siguiente figura podemos ver el diagrama de clases como los métodos importantes de este ejemplo:

Page 177: Catálogo de Patrones de Diseño J2EE

El siguiente fragmento de código demuestra la implementación de un Service Activator. Esta es la clase que se puede ejemplarizar en un servidor de aplicaciones o ejecutar como un servicio independiente, como se explicó en las estrategias anteriores:

public class OrderServiceActivator implements javax.jms.MessageListener{ // Queue session and receiver: see JMS API for // details private QueueSession orderQueueSession; private QueueReceiver orderQueueReceiver; // Note: values should come from property files or // environment instead of hard coding. private String connFactoryName = "PendingOrdersQueueFactory"; private String queueName = "PendingOrders"; // use a service locator to locate administered // JMS components such as a Queue or a Queue // Connection factory private JMSServiceLocator serviceLocator; public OrderServiceActivator(String connFactoryName, String queueName) { super(); this.connFactoryName = connFactoryName; this.queueName = queueName; startListener(); } private void startListener() { try { serviceLocator = new JMSServiceLocator (connFactoryName); qConnFactory = serviceLocator.getQueueConnectionFactory(); qConn = qConnFactory.createQueueConnection(); // See JMS API for method usage and arguments orderQueueSession = qConn.createQueueSession (...); Queue ordersQueue = serviceLocator.getQueue(queueName); orderQueueReceiver = orderQueueSession.createReceiver(ordersQueue); orderQueueReceiver.setMessageListener(this); } catch (JMSException excp) { // handle error } } // The JMS API specifies the onMessage method in the // javax.jms.MessageListener interface. // This method is asynchronously invoked // when a message arrives on the Queue being // listened to by the ServiceActivator. // See JMS Specification and API for more details. public void onMessage(Message msg) { try { // parse Message msg. See JMS API for Message. ... // Invoke business method on an enterprise // bean using the bean's business delegate. // OrderProcessorDelegate is the business // delegate for OrderProcessor Session bean. // See Business Delegate pattern for details. OrderProcessorDelegate orderProcDeleg = new OrderProcessorDelegate(); // Use data values from the parsed message to // invoke business method on bean via delegate orderProcDeleg.fulfillOrder(...); // send any acknowledgement here... } catch (JMSException jmsexcp) { // Handle JMSExceptions, if any } catch (Exception excp) { // Handle any other exceptions } }

Page 178: Catálogo de Patrones de Diseño J2EE

public void close() { try { // cleanup before closing orderQueueReceiver.setMessageListener (null); orderQueueSession.close(); } catch(Exception excp) { // Handle exception - Failure to close } } }

En el siguiente códido está el ejemplo del Session Facade para despachar los pedidos a este servicio asíncromo. El cliente Service Activator puede ser un bean de sesión que implementa el patrón Session Façade para proporcionar servicio de procesamiento de pedidos a una aplicación de almacen on-line. Cuando se llama al método createOrder(), después de validar y de crear satisfactoriamente un nuevo pedido, se llama la método sendOrder() para despachar el nuevo pedido al sevicio de envío:

// imports... public class OrderDispatcherFacade implements javax.ejb.SessionBean { ... // business method to create new Order public int createOrder(...) throws OrderException { // create new business order entity bean ... // successfully created Order. send Order to // asynchronous backend processing OrderSender orderSender = new OrderSender(); orderSender.sendOrder(order); // close the sender, if done... orderSender.close(); // other processing ... } }

El código JMS se puede separar en una clase diferente para que pueda ser reutilizado por diferentes clientes. Podemos ver esta clase en el siguiente listado:

// imports... public class OrderSender { // Queue session and sender: see JMS API for details private QueueSession orderQueueSession; private QueueSender orderQueueSender; // These values could come from some property files private String connFactoryName = "PendingOrdersQueueFactory"; private String queueName = "PendingOrders"; // use a service locator to locate administered // JMS components such as a Queue or a Queue. // Connection factory private JMSServiceLocator serviceLocator; ... // method to initialize and create queue sender private void createSender() { try { // using ServiceLocator and getting Queue // Connection Factory is similar to the // Service Activator code. serviceLocator = new JMSServiceLocator (connFactoryName); qConnFactory = serviceLocator.getQueueConnectionFactory(); qConn = qConnFactory.createQueueConnection(); // See JMS API for method usage and arguments orderQueueSession = qConn.createQueueSession (...); Queue ordersQueue = serviceLocator.getQueue(queueName); orderQueueSender = orderQueueSession.createSender(ordersQueue);

Page 179: Catálogo de Patrones de Diseño J2EE

catch(Exception excp) { // Handle exception - Failure to create sender } } // method to dispatch order to fulfillment service // for asynchronous processing public void sendOrder(Order newOrder) { // create a new Message to send Order object ObjectMessage objMessage = queueSession.createObjectMessage(order); // set object message properties and delivery // mode as required. // See JMS API for ObjectMessage // Set the Order into the object message objMessage.setObject(order); // send the message to the Queue orderQueueSender.send(objMessage); ... } catch (Exception e) { // Handle exceptions } ... } ... public void close() { try { // cleanup before closing orderQueueReceiver.setMessageListener (null); orderQueueSession.close(); } catch(Exception excp) { // Handle exception - Failure to close } } }

Patrones Relacionados

• Session Facade El patrón Session Facade encapsula la complejidad del sistema y prorporciona acceso genérico a los objetos de negocio. Este patrón Service Activator podria acceder a un Session Façade como objeto de negocio principal para invocar métodos de negocio de otros servicios de forma asíncrona por cuenta del cliente.

• Business Delegate El patrón Service Activator podría utilizar un Business Delegate para acceder al Session Façade u otra implementación de bean enterprise. Esto resulta en un código más sencillo para el Service Activator y en que el Business Delegate se pueda reutilizar entre diferentes capas.

• Service Locator El cliente puede utilizar el patrón Service Locator para buscar y crear objetos de servicios relacionados con JMS. El Service Activator puede utilizar el patrón Service Locator para buscar y crear componentes beans enterprise.

• Half-Sync/Half-Async [POSA2] El patrón Service Activator está relacionado con el patrón Half-Sync/Half-Async, que describe el desacoplamiento arquitectural de procesamientos síncronos y asíncronos sugiriendo diferentes capas para síncronos y asíncronso con una cola intermedia entre ellos.