Message-driven beans are without doubt a positive addition to the component model of Enterprise JavaBeans. With message-driven beans both asynchronous and parallel processing of application logic are possible. This dual functionality is an important building block in the development of business applications. These functions help in system performance enhancement. Asynchronicity and parallel processing are very complex and difficult themes in information science. They bring many advantages and are indispensable for the use of many systems. However, they are difficult to implement. With message-driven beans the use of asynchronicity and parallel processing in the implementation of business logic are simplified for the bean provider.
A traditional JMS client can also enjoy the benefits of asynchronicity and parallel processing relatively easily and without too much programming effort, since a message service behaves ansynchronously by default, and parallel processing can be achieved by the JMS client through the use of several JMS sessions or the use of a server session pool. A JMS client is, however, in comparison to message-driven beans, not necessarily a component and is reusable only in rare cases. Furthermore, a message-driven bean enjoys services offered by the EJB container, in particular, distributed transactions.
A message-driven bean is not permitted to use the acknowledge mechanism (see the subsection on transactions and acknowledgment in the first section of this chapter). In many cases it would be very useful if the bean could, in fact, use this mechanism. The LoggerBean from the previous section could also profit from the acknowledge mechanism. It would then not have to log each incoming message, but could carry out the logging in blocks, without taking on the risk of losing information. A blockwise logging of data would reduce the burden on the database. Instead of the acknowledge mechanism, the LoggerBean could use simple transactions of the JMS session upon the receipt of messages in order to execute the logging in a secure manner. That is also impossible with message-driven beans. For that the message-driven bean must have access to the JMS session over which messages are delivered to it by the JMS provider. This JMS session is, however, under the control of the EJB container and for the message-driven bean inaccessible, since there is no way to synchronize the instances of the instance pool of a particular message-driven bean type. A particular instance is therefore incapable of deciding when an acknowledge or commit is reasonable, since it does not know which or how many messages have already been processed by the other instances in the pool.
The EJB container could, for example, offer the message-driven bean, via the message-driven context, a possibility to synchronize with the other instances in the pool and thereby use simple transactions of the JMS provider or the acknowledge mechanism. The message-driven context could contain a method acknowledge for this purpose and a method recover, which would have the same functionality as javax.jms.Message.acknowledge() or javax.jms.Session.recover(). However, by way of this indirection the EJB container would have the possibility of keeping track of the acknowledgment and commit functions and intervene as necessary.
On the other hand, in the case of the LoggerBean, parallel processing would not be absolutely necessary. One instance of this bean would suffice to process messages (assuming moderate message traffic). In any case, the queue would function as a secure buffer. There is no way of informing the EJB container that one instance of the message-driven bean suffices to reach the desired goal. The resources of the EJB container would be less burdened at run time, since it does not have to manage a pool of LoggerBean instances. Moreover, the use of the acknowledge mechanism or simple transactions of the JMS session would be possible in this case, since there are no other instances of the message-driven bean with which synchronization would be required. The bean instance would in this case would be quite well able to decide when an acknowledge or commit made sense.
In the method onMessage the message-driven bean may throw neither self-defined nor system nor run-time exceptions. In the example of the LoggerBean, for example, a database access error results in the transaction being canceled. The message service would resend the message in such a case. If a problem that interferes with the correct processing of a message is of only a transitory nature, then the processing can perhaps be successfully continued after a resending of the message. Matters become difficult when the problem is of a fundamental nature or of lengthy duration. For example, say a LoggerBean is waiting for a message of type javax.jms.TextMessage. If a client were to place a message of type javax.jms.ObjectMessage in the LoggerQueue (for example, due to a programming error), that would lead to an exception of type ClassCastException in the method ClassCastException of the LoggerBean. The LoggerBean would break off the transaction and terminate processing. The breaking off of the transaction would lead to the message service resending the message. The LoggerBean has no way of informing the EJB container or the message service that it cannot process the message due to a fundamental problem. And furthermore, none of the other instances of the LoggerBean class will be able to process the message correctly after a resending. Thus the processing of the message is caught in an infinite loop. The LoggerQueue would then be blocked by this defective message and would grow continually. The handling of such a problem is solely the responsibility of the implementation of the message-driven bean. To solve such a problem one might do the following: In addition to the LoggerQueue there could be established an additional queue, the LoggerErrorQueue. The LoggerQueue could first place all messages that experience a fundamental problem that makes the correct processing of a message impossible in the LoggerErrorQueue (a sort of rerouting) and then continue as if the message had been processed correctly. Figure 6-9 depicts such a situation.
Figure 6-9: Error-handling with message-driven beans.
With this mechanism the LoggerQueue cannot be blocked by a defective message. The LoggerErrorQueue could have, for example, a normal JMS client as recipient that has messages delivered and then notifies the administrator (say, by e-mail). The administrator could inspect the contents of the LoggerErrorQueue with some tool (for example, with the help of a queue browser; see ) and analyze the problem. After the problem has been resolved, it could manually resend the message and remove it from the LoggerErrorQueue.
From the point of view of the bean provider it would, of course, be desirable for such a mechanism, or something similar, to be available to the EJB container. A message-driven bean could report, upon an exception of a particular type (e.g., javax.ejb.EJBException) being thrown, that the message cannot be processed correctly due to a fundamental problem and that the message-driven bean is incapable of resolving the problem. The EJB container could ensure that the message was removed from the queue or topic to avoid an infinite repetition of the message being sent via the message service. The EJB container could transform the message into another persistent state, for example, direct it into an "error" queue or topic or database or write it to a file. The EJB container provider could make tools available that support the administrator in analysis and repair of defective messages.