JMS Design for Concurrency
To call EJB concurrently, you can design a set of classes that a client uses to divide a task into subtasks. Then the client can send each subtask asynchronously as a message to JMS Queue for processing by the MDB. The MDB can call the SessionBean to process the subtask and then to send an acknowledgement back to the client.
The following section discusses both the client-side and server-side designs for using EJBs concurrently.
On the client side, you need two JMS Queues: one to send subtasks and the other to receive acknowledgement for the processed subtasks. Figure 1 shows the class diagram for client-side processing logic.
|Figure 1: Class Diagram for Client-side Processing Logic|
The TaskManager (see Listing 1) interface has common behavior that must be implemented by any class that provides implementation for subtask management. The JMSTaskManager (see Listing 2) implements the TaskManager and provides JMS implementation for calling the EJB concurrently. For example, you could use the TaskManager to implement Web services for managing and processing subtasks.
You use three methods collectively to perform subtask management for the clients:
sendSubTasks() sends a collection of subtasks to the EJB for processing.
listenForAcknowledgement() listens for the acknowledgement messages for all the subtasks sent to MDB.
waitForAcknowledgement() makes the client wait for the acknowledgement to come from MDB.
As explained previously, the JMSTaskManager class (see Listing 2) provides JMS implementation for the TaskManager. The default constructor initializes totalMessagesSent and totalMessagesReceived variables, which hold messages (subtasks) sent and acknowledgements received, respectively, for the processed subtasks. It also constructs the InitialContext for JNDI lookup and calls
setupQueue(), which constructs JMS Queue objects to perform point-to-point communication between JMSTaskManager and the MDB.
sendSubTasks() method takes a collection of subtasks, adds each subtask into JMS message, and sends it to the JMS Server. The
createMessage() method is an abstract of this class that takes QueueSession and a subtask and then returns a message for
sendSubTasks() to send. Since
createMessage() is an abstract, subclasses can construct all types of JMS messages, such as BytesMessage, MapMessage, ObjectMessage, StreamMessage, TextMessage, etc. The constructed message is used to set the JMS Reply Queue on the server side. Once the MDB processes a subtask, it sends an acknowledgement message back to JMSTaskManager (using the Reply Queue). The name of the Reply Queue is obtained from
getReceiveQueueName(), which is an abstract method in this class. Finally, the QueueSender object is used to send the messages to the JMS Server.
sendMessage() is provided for convenience. If, for any reason, a client wants to send only one subtask, this method can be used for that.
onMessage() method is automatically executed when the MDB sends an acknowledgement message to the Reply Queue and increments the totalMessageReceived counter by one.
listenForAcknowledgement() method uses
getReceiveQueueName() to create a Queue to receive the acknowledgements from the MDB. The JMSTaskManager implements the MessageListener interface so that it can listen for acknowledgements from MDB. The QueueSession is used to create the QueueReceiver, which is used to set the MessageListener object to get acknowledgements from MDB.
You can create two types of QueueReceivers, one using MessageSelector and one without using it. If the subclass of JMSTaskManager provides a MessageSelector, you use it to construct the QueueReceiver. The JMSTaskManager uses the date as the MessageSelector to filter the acknowledgement messages from the MDB. This feature allows more than one client to use the JMSMessageManager to send subtasks concurrently to MDB (see Figure 17). In this scenario, each client uses its own date as the message Selector so that JMSTaskManager receives acknowledgement messages for its date.
waitForAcknowledgement() method waits in a loop until the JMSTaskManager receives all the acknowledgement messages from MDB.
This section discusses what the server-side classes require to process subtasks using JMS Messages. On the server side, you need to design a MDB so that when the client sends a message, the JMS Server notifies the MDB to process that message.
Like all other MDB classes, the SubtaskMessageBean class (see Listing 3) implements MessageDrivenBean and MessageListener interfaces (see Figure 2).
|Figure 2: QueueConstants Interface Holds Various Queue Constants |
The JMS Server calls the
onMessage() method when the JMSTaskManager sends a message with the subtask embedded in it. Typically, this method calls a SessionBean to process the Subtask. For simplicity, in this example it prints the date and the subtask passed by the client. At the end, it calls the
sendAcknowledgement() method to send the acknowledgement message to the client.
sendAcknowledgement() method receives the Reply Queue that the JMSTaskManager sends and uses it to send the text acknowledgement message back to it. It uses the client-passed date to set the message Selector by calling the setStringProperty in the TextMessage. This helps the client accept the acknowledgement messages based on the date it passed to the SubtaskMessageBean.
The QueueConstants interface (see Listing 4) holds various Queue constants that both the client-side and server-side Java classes use.