Service Activator Pattern



In enterprise applications, a majority of the processing is done in a synchronous manner. A client invokes a business service and waits until the business service returns from processing. However, the business processing in some use cases takes considerable time and resources. The business processing might even span applications, possibly integrating with applications inside and outside the enterprise. For these long-lived processes, it is not feasible for application clients to
wait until the business processing completes so in this case we want to invoke services asynchronously.

Some long-running use cases take a long time. Instead of blocking the users, we can run these asynchronously
JMS is a good example of Service Activator Pattern - JMS (Java Message Service) is an API that provides the facility to create, send and read messages. It provides loosely coupled reliable and asynchronous communication.

This pattern is divided into a number of sections for simplicity like problem, forces, solution etc.

Problem

(Problem section describes the design issues faced by the developer)

You want to invoke services asynchronously.

Forces

(This section describes Lists the reasons and motivations that affect the problem and the solution. The list of forces highlights the reasons why one might choose to use the pattern and provides a justification for using the pattern)
  • You want to invoke business services, POJOs, or EJB components in an asynchronous manner.
  • You want to integrate publish/subscribe and point-to-point messaging to enable asynchronous processing services.
  • You want to perform a business task that is logically composed of several business tasks.

Solution

(Here solution section describes the solution approach briefly and the solution elements in detail)

Use a Service Activator to receive asynchronous requests and invoke one or more business services.
The Service Activator is implemented as a JMS Listener and delegation service
that can listen to and receive JMS messages.

How this pattern works?

Any client that needs to asynchronously invoke a business service, such as an EJB or a POJO service, creates and sends a message to the Service Activator. The Service Activator receives the message and parses it to interpret the client request. Once the client’s request is unmarshalled, the Service Activator identifies and locates the correct business service component and invokes it to process the client’s request asynchronously.

After the processing is completed, the client might want to receive the results. To inform the client about the outcome of the processing, the Service Activator can send a response to the client. This response can indicate to the client whether the processing was successful and provide the results or a handle to the results. In case of a failure in processing, the response can include the details of the failure.
The details can indicate how the client can recover, whether by either resubmitting the request or by rectifying the problems causing the failure. The Service Activator might use a Service Locator to locate a business component.

Structure

I used UML class diagrams to show the basic structure of the solution and the UML Sequence diagrams in this section present the dynamic mechanisms of the solution. Below is the class diagram representing the relationships for the Service Locator pattern.

Class diagram

Service Activator Class Diagram

Sequence Diagram

Service Activator Sequence Diagram

Participants and Responsibilities

Client -  The Client is any client in the application that needs to invoke a business service in an asynchronous manner. The client can be any type of application components, such as a POJO or an EJB component, that can create and send JMS messages.
Request - The Request is the message object created by the Client and sent to the ServiceActivator using the message-oriented middleware (MOM). According to the JMS specification, the Request object must implement the javax.jms.Message interface. The JMS API provides several message types, such as TextMessage, ObjectMessage, and so on, that can be used as request objects depending on what type of message you want to send.
ServiceActivator - The ServiceActivator is the main class of the pattern. It implements the javax.jms.MessageListener interface, which is defined by the JMS specification. The ServiceActivator implements an onMessage() method that is invoked when a new message arrives. The ServiceActivator parses (unmarshals) the message (request) to determine what needs to be done. The ServiceActivator might use a Service Locator to look up or create BusinessService components.
BusinessService - The BusinessService is the target object that the client asks to do asynchronous processing.
Response - The Response is a message object created and sent either by the ServiceActivator or a BusinessService. The response can be an acknowledgment to let the Client know that the Request was received. The Response can also be the results of the asynchronous processing.

Sample Code

Consider an order processing application where the customers shop online and the order fulfillment process happens in the background. In some cases, order fulfillment may be outsourced to a third-party warehouse. In such cases, the online store needs to invoke these fulfillment services asynchronously. This is an example that demonstrates usage of point-to-point (PTP) messaging to accomplish asynchronous processing. However, using publish/subscribe messaging would be similar, except that Topic is used instead of a Queue. Choosing which method to use, PTP or publish/subscribe, depends on the business and application requirements, and hence is outside the scope of this pattern.
Step 1: Create Order Service Activator.
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
    }
  }
}
Step 2 : Session Facade as Client for Service Activator.
The sample session facade code responsible to dispatch orders to this asynchronous service.
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
    ...
  }
}
Step 3: OrderSender: Used to Dispatch Orders to Queue.

// 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
    }
  }
}

Typical Use Case

  • Some long-running use cases take a long time. Instead of blocking the users, we can run these asynchronously.
  • JMS is a good example of Service Activator Pattern - JMS (Java Message Service) is an API that provides the facility to create, send and read messages. It provides loosely coupled reliable and asynchronous communication.

Consequences


  • Integrates JMS into enterprise applications
  • Provides asynchronous processing for any business-tier component
  • Enables standalone JMS listener

Comments