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

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

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

. 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

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:

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

COMPARTE ESTE ARTÍCULO

ENVIAR A UN AMIGO
COMPARTIR EN FACEBOOK
COMPARTIR EN TWITTER
COMPARTIR EN GOOGLE +
ARTÍCULO ANTERIOR

¡SÉ EL PRIMERO EN COMENTAR!
Conéctate o Regístrate para dejar tu comentario.