ServiceActivator Pattern

Context

Enterprise beans and other business services need a way to be activated asynchronously.

Problem

When a client needs to access an enterprise bean, it first looks up the bean's home object. The client requests the Enterprise JavaBeans (EJB) component's home to provide a remote reference to the required enterprise bean. The client then invokes business method calls on the remote reference to access the enterprise bean services. All these method calls, such as lookup and remote method calls, are synchronous. The client has to wait until these methods return.

Another factor to consider is the life cycle of an enterprise bean. The EJB specification permits the container to passivate an enterprise bean to secondary storage. As a result, the EJB container has no mechanism by which it can provide a process-like service to keep an enterprise bean constantly in an activated and ready state. Because the client must interact with the enterprise bean using the bean's remote interface, even if the bean is in an activated state in the container, the client still needs to obtain its remote interface via the lookup process and still interacts with the bean in a synchronous manner.

If an application needs synchronous processing for its server-side business components, then enterprise beans are an appropriate choice. Some application clients may require asynchronous processing for the server-side business objects because the clients do not need to wait or do not have the time to wait for the processing to complete. In cases where the application needs a form of asynchronous processing, enterprise beans do not offer this capability in implementations prior to the EJB 2.0 specification.

The EJB 2.0 specification provides integration by introducing message-driven bean, which is a special type of stateless session bean that offers asynchronous invocation capabilities. However, the new specification does not offer asynchronous invocation for other types of enterprise beans, such as stateful or entity beans.

In general, a business service such as a session or entity bean provides only synchronous processing and thus presents a challenge to implementing asynchronous processing.

Forces

  • Enterprise beans are exposed to their clients via their remote interfaces, which allow only synchronous access.
  • The container manages enterprise beans, allowing interactions only via the remote references. The EJB container does not allow direct access to the bean implementation and its methods. Thus, implementing the JMS message listener in an enterprise bean is not feasible, since this violates the EJB specification by permitting direct access to the bean implementation.
  • An application needs to provide a publish/subscribe or point-to-point messaging framework where clients can publish requests to enterprise beans for asynchronous processing.
  • Clients need asynchronous processing capabilities from the enterprise beans and other business components that can only provide synchronous access, so that the client can send a request for processing without waiting for the results.
  • Clients want to use the message-oriented middleware (MOM) interfaces offered by the Java Messaging Service (JMS). These interfaces are not integrated into EJB server products that are based on the pre-EJB 2.0 specification.
  • An application needs to provide daemon-like service so that an enterprise bean can be in a quiet mode until an event (or a message) triggers its activity.
  • Enterprise beans are subject to the container life cycle management, which includes passivation due to time-outs, inactivity and resource management. The client will have to invoke on an enterprise bean to activate it again.

The EJB 2.0 specification introduces a message-driven bean as a stateless session bean, but it is not possible to invoke other types of enterprise beans asynchronously.

Solution

Use a Service Activator to receive asynchronous client requests and messages. On receiving a message, the Service Activator locates and invokes the necessary business methods on the business service components to fulfill the request asynchronously.

The ServiceActivator is a JMS Listener and delegation service that requires implementing the JMS message listener-making it a JMS listener object that can listen to JMS messages. The ServiceActivator can be implemented as a standalone service. Clients act as the message generator, generating events based on their activity.

Any client that needs to asynchronously invoke a business service, such as an enterprise bean, may create and send 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 parsed or unmarshalled, the Service Activator identifies and locates the necessary business service component and invokes business methods to complete processing of the client's request asynchronously.

The Service Activator may optionally send an acknowledgement to the client after successfully completing the request processing. The Service Activator may also notify the client or other services on failure events if it fails to complete the asynchronous request processing.

The Service Activator may use the services of a Service Locator to locate a business component. See "Service Locator" on page 368.

Structure

Figure 9.9 represents the class relationships for the Service Activator pattern.


Figure 9.9 Service Activator class diagram

Participants and Responsibilities

Figure 9.10 shows the interactions between the various participants in the Service Activator pattern.


Figure 9.10 Service Activator sequence diagram

Client

The client requires an asynchronous processing facility from the business objects participating in a workflow. The client can be any type of application that has the capability to create and send JMS messages. The client can also be an EJB component that needs to invoke another EJB component's business methods in an asynchronous manner. The client can use the services offered by the Service Locator pattern to look up or create EJB components, JMS services, and JMS objects, as necessary.

Request

The Request is the message object created by the client and sent to the ServiceActivator via the MOM. According to the JMS specification, the Request is an object that implements the javax.jms.Message interface. The JMS API provides several message types, such as TextMessage, ObjectMessage, and so forth, that can be used as request objects.

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 may use the services offered by a Service Locator (see Service Locator) pattern to look up or create Business Service components such as enterprise beans.

BusinessObject

BusinessObject is the target object to which the client needs access in an asynchronous mode. The business object is a role fulfilled by either a session or entity bean. It is also possible that the BusinessObject is an external service instead of an entity bean.

Strategies

Entity Bean Strategy

Both session and entity beans can fulfill the role of a BusinessObject. When J2EE applications implement a Session Façade pattern to provide coarse-grained access to entity beans and to encapsulate the workflow, then the session bean from the Session Façade fulfills the BusinessObject role.

In simple applications with minimal workflow, an entity bean may fulfill the BusinessObject role. However, for complex workflow involving multiple entity beans and other business objects, the ServiceActivator typically interacts with a Session Facade which encapsulates such workflow.

Session Bean Strategy

When a session bean fulfills the role of the BusinessObject, the business requirements determine whether the bean should be stateful or stateless. Since the client for the BusinessObject is a ServiceActivator that activates the BusinessObject on receiving a new message, the workflow to process the message can define whether the bean should be stateful or not. In most cases, a message delivery simply activates a single method in the BusinessObject that delegates the processing of the message within. A stateless session bean can be used in these cases. If the ServiceActivator needs to invoke multiple methods in the BusinessObject or to work with more than one BusinessObject to fulfill the processing requirements for a message, it may be useful to consider a stateful session bean to retain state between multiple invocations. See "Stateless Session Facade Strategy" on page 296 and "Stateful Session Facade Strategy" on page 297.

ServiceActivator Server Strategy

The most straightforward strategy for implementing the listener or ServiceActivator is as a standalone JMS application that listens and processes JMS messages.

An alternative is to implement the ServiceActivator as a service of the application server. This may make it easier to manage the ServiceActivator, because it uses the application server features to monitor the ServiceActivator state and to start, restart, and stop the ServiceActivator as needed, either manually or automatically.

Enterprise Bean as Client Strategy

The Client can be any client, including another enterprise bean that requires asynchronous processing from the enterprise bean. When integrating legacy applications to the J2EE platform, it is logical to choose Java application clients to act as the message generators based on the activity in the legacy system. The ServiceActivator can receive messages and perform the necessary enterprise bean invocations to process the request from the legacy system.

Consequences

  • Integrates JMS into Pre-EJB 2.0 Implementations
    Prior to the EJB 2.0 specification, there was no integration between enterprise bean and JMS components. This pattern provides a means to integrate JMS into an EJB application and enable asynchronous processing. The EJB 2.0 specification defines a new type of session bean, called a message-driven bean, to integrate JMS and EJB components. This special bean implements the JMS Message Listener interface and it receives asynchronous messages. In this case, the application server plays the role of the Service Activator. This pattern makes it possible to run applications in EJB 2.0 implementations as well as pre-EJB 2.0 implementations.
  • Provides Asynchronous Processing for any Enterprise Beans
    In the EJB 2.0 specification, the message-driven bean is a stateless session bean. Using the Service Activator pattern, it is possible to provide asynchronous invocation on all types of enterprise beans, including stateless session beans, stateful session beans, and entity beans. As previously explained, since the Service Activator is implemented in its own right, without any limitations of the message-driven bean, the Service Activator can perform asynchronous invocations on any type of business service. Thus, this pattern provides a way to enable asynchronous processing for clients that either have no need to wait for the results or do not want to wait for processing to complete. The processing can be deferred and performed at a later time, enabling the client to complete the service in less time.
  • Standalone Process
    The Service Activator can be run as a standalone process. However, in a critical application, Service Activator needs to be monitored to ensure availability. The additional management and maintenance of this process can add to application support overhead.

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.

The class diagram with only the relevant methods for this example is shown in Figure 9.11.


Figure 9.11 Service Activator for Order Processing example - class diagram

The code excerpt shown in Example 9.7 demonstrates a sample Service Activator implementation. This is the class that can be instantiated in an application server or run in a stand-alone server, as explained in the Service Activator Server strategy.

Example 9.7 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

    }

  }

}



 

The sample session facade code responsible to dispatch orders to this asynchronous service is shown in the code excerpt in Example 9.8. The Service Activator client can be a session bean that implements the Session Façade pattern to provide order processing services to the online store application. When the session bean's createOrder() method is called, after successfully validating and creating a new order, it invokes sendOrder() to dispatch the new order to the backend order fulfillment service.

Example 9.8 Session Facade as Client for Service Activator

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

    ...

  }

}



 

The JMS code can be separated into a different class so that it can be reused by different clients. This JMS delegate class is shown as OrderSender in the Example 9.9 code listing.

Example 9.9 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

    }

  }

}



 

Related Patterns

  • Session Facade
    The Session Facade pattern encapsulates the complexity of the system and provides coarse-grained access to business objects. This Service Activator pattern may access a Session Façade as the primary business object to invoke business service methods in the Session Façade asynchronously on behalf of the client.
  • Business Delegate
    The Service Activator pattern may use a Business Delegate to access the Session Façade or other enterprise bean implementations. This results in simpler code for the Service Activator and results in Business Delegate reuse across different tiers, as intended by the Business Delegate pattern.
  • Service Locator
    The client can use the Service Locator pattern to look up and create JMS-related service objects. The Service Activator can use the Service Locator pattern to look up and create enterprise bean components.
  • Half-Sync/Half-Async [POSA2]
    The Service Activator pattern is related to the Half-Sync/Half-Async pattern, which describes architectural decoupling of synchronous and asynchronous processing by suggesting different layers for synchronous, asynchronous and an intermediate queueing layer inbetween