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

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:
    • 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.
    • 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.
    • Encontrar el objeto EJBHome utilizando el contexto de b�squeda inicial
    • 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.
    • 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.
    • Obtener el contexto inicial para el proveedor de servicio JMS desde el servicio de nombrado JNDI.
    • Utilizar el contexto inicial para obtener un Topic o un Queue suministrando el nombre JNDI para ese Topic o Queue. Ambos son objetos JMSDestination.
    • Utilizar el contexto inicial para obtener un TopicConnectionFactory o un QueueConnectionFactory suministrando el nombre JNDI para la factor�a adecuada.
    • Usar el TopicConnectionFactory para obtener un TopicConnection o el QueueConnectionFactory para obtener un QueueConnection.
    • Utilizar el TopicConnection para obtener un TopicSession o el QueueConnection para obtener un QueueSession.
    • 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�.

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.

.�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 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:

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

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

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:

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

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(...);
    }
  }
    
  // 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();
  }
    
  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 {
      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.

COMPARTE ESTE ARTÍCULO

COMPARTIR EN FACEBOOK
COMPARTIR EN TWITTER
COMPARTIR EN LINKEDIN
COMPARTIR EN WHATSAPP
ARTÍCULO ANTERIOR