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
Contact Us© 2001-2002 Sun Microsystems,
Inc. All Rights Reserved.
|