From the point of view of the client, a message-driven bean is a message receiver (message consumer) in the sense of the Java Message Service. The client cannot address a message-driven bean directly, for it has no home, remote, local home, or local interface. The client can address a message-driven bean only indirectly, by sending a message over a particular channel (destination) of the JMS, from which it knows that the recipient is a particular message-driven bean. The client has no influence over the life cycle of a message-driven bean. This is governed completely by the EJB container. The client also has no influence over how many instances of a message-driven bean are used for the processing of messages of a particular channel. That is also controlled exclusively by the container. The instances of a message-driven bean are not exclusively available to the client, and they cannot store a state for it. They are stateless, like stateless session beans.
The client can localize a particular channel of the message service via JNDI (see the earlier section in this chapter on sending messages). A client that uses (indirectly) message-driven beans is not differentiated from a normal JMS client. If the client uses a session or entity bean, it is tightly coupled to it. If the client uses a message-driven bean over a channel of a message service, then it is loosely coupled to this Enterprise Bean. If the message-driven bean were to be exchanged at run time for another recipient (for example, another message-driven bean or a traditional Java class), this would have no effect on the client. The API of the Java Message Service stands between them as an indirection layer.
The life cycle of a message-driven bean is run exclusively by the EJB container. An instance of a message-driven bean can assume the following states:
Nonexistent: The instance does not exist;
Pooled ready: The instance exists and is available for method calls.
Figure 6-6 illustrates graphically the life cycle of a message-driven bean. It is clear from the figure that only the container can initiate a state change.
Figure 6-6: Life cycle of a message-driven bean.
Instances of a message-driven bean are as a rule generated at the startup of the EJB container or when the EJB container increases the pool size for the purpose of ensuring relatively fast processing of incoming messages. The life cycle of a message-driven bean begins with a call to the constructor. Then the container calls the method setMessageDrivenContext and thereby gives the instance its context. A message-driven bean can communicate with the EJB container via the context. Then the container calls the method ejbCreate, which places the instance of the message-driven bean in the state Pooled Ready. From this time on, the bean can be used by the container for the processing of incoming messages that were sent by a client over a particular channel of the message service. The EJB container uses an arbitrary instance of a particular type of message-driven bean for the processing of an incoming message. It maintains a pool of instances for each type of message-driven bean.
When the EJB container no longer needs an instance of a message-driven bean, it calls the ejbRemove method. With the call to this method the life cycle of a message-driven bean comes to an end. That is the case when the EJB container is powered down or the EJB container decides to reduce the pool of instances of a particular bean type. The life cycle of a message-driven bean ends also when the message-driven bean instance throws an exception of type RuntimeException during the processing of a message.
Since the EJB container as a rule uses several instances of a message-driven bean, it can divide the processing of incoming messages arriving in sequence among several instances. Messages can be processed in parallel by instances of a single type of message-driven bean (see Figure 6-7). The container takes care of thread management and provides the message-driven bean the types of services that we have seen in regard to other bean types.
Figure 6-7: Parallel processing with message-driven beans.
The concept that the EJB container implements for parallel processing of messages resembles closely the concept of the server session pool introduced earlier. One might give the following simplified formulation:
Server Session Pool + stateless session bean - Home/Remote or
Local Home/Local Interface = Message-Driven Bean.
Parallel processing means that a particular instance of a message-driven bean does not process messages in the order in which they are sent by the client.
The container must ensure that the calls to bean instances for processing messages are serialized. Thus the implementation of the bean class need not be thread-secure, which simplifies the development of message-driven beans.