Java Message Service

Java Message Service (JMS) is used for communication between software applications or software components.  I have used JMS when I needed to send information to another component within the application but did not need to wait for an immediate response.  There are two different ways that messages are sent and consumed.

  • Synchronously. A subscriber or a receiver explicitly fetches the message from the destination by calling the receive method. The receive method can block until a message arrives or can time out if a message does not arrive within a specified time limit.
  • Asynchronously. A client can register a message listener with a consumer. A message listener is similar to an event listener. Whenever a message arrives at the destination, the JMS provider delivers the message by calling the listener's onMessage method, which acts on the contents of the message.

(Taken from Sun’s website)

Below is the code that configures the queues.  This information is stored in a *-service.xml file so that the Application Server can recognize that mbeans are contained inside the file.

davis-jms-service.xml




<server>
<mbean code="org.jboss.mq.server.jmx.Queue" name="jboss.mq.destination:service=Queue,name=BookRequestQueue">
<attribute name="JNDIName"> queue/davis/BookRequestQueue</attribute>
<depends optional-attribute name="DestinationManager">jboss.mq:service=DestinationManager</depends>
 </mbean>
  <mbean code="org.jboss.mq.server.jmx.Queue" name="jboss.mq.destination:service=Queue,name=BookResponseQueue">
<attribute name="JNDIName">queue/davis/BookResponseQueue</attribute>
<depends optional-attribute- name="DestinationManager">jboss.mq:service=DestinationManager</depends>
   </mbean>
</server>


This is the Service Locator that uses the JNDI names configured in the davis-jms-service.xml file to get access to the Connection Factory and the Queues.

 

ServiceLocator.java

 

/**
* Returns Queue Connection factory
* @return
* @throws ServiceLocatorException
*/
public QueueConnectionFactory getDavisConnectionFactory()throws ServiceLocatorException{
QueueConnectionFactory factory = null;
try{
Context context = getInitialContextLocal();
factory = (QueueConnectionFactory) context.lookup(“ConnectionFactory)”;
}
catch(Exception e){
throw new ServiceLocatorException(e);
}
return factory;
}
/**
* Returns reference to queue
* @return
* @throws ServiceLocatorException
*/
public Queue getBookRequestQueue() throws ServiceLocatorException{
Queue queue = null;
try{
Context context = getInitialContextLocal();
queue = (Queue) context.lookup(“queue/davis/BookRequestQueue”);
}
catch(Exception e){
throw new ServiceLocatorException(e);
}
return queue;
}

The BookListener.java class implements the onMessage method that is called whenever there is a message in the “queue/davis/BookRequestQueue" queue.  It checks to see if the message received is of type TextMessage, if so, it will extract the text portion of the message and call the BookMgrDelegate to process the book.

 



BookListener .java


@MessageDriven(activationConfig =
{
@ActivationConfigProperty(propertyName="destinationType", propertyValue="javax.jms.Queue"),
@ActivationConfigProperty(propertyName="destination", propertyValue="queue/davis/BookRequestQueue")
})

public class BookListener implements MessageListener {

public BookListener() { }

public void onMessage(Message message) {
TextMessage msg = null;

if (message instanceof TextMessage){
try{
msg = (TextMessage) message;
String strMessage = msg.getText();

BookMgrDelegate delegate = new BookMgrDelegate();
delegate.processBooks(strMessage);
}
catch(Exception e){
e.printStackTrace();
}
}
}

The BookMgrBean.java class is the actual call that inserts a record into the queue. The setupPTP function gets access to the Service Locator and then populates the needed Connections and Queues.  The submitBooks function very simple uses the session to create a sender and receiver.  It then sets the BookListener as the message listener for the receiver queue.

 



BookMgrBean.java


QueueConnection qConn = null;
QueueSession session = null;
Queue queue = null;

public void setupPTP() throws BookException {
try {
ServiceLocator locator = ServiceLocator.getInstance();
QueueConnectionFactory factory = locator.getDavisQueueConnectionFactory();
queue = locator.getBookRequestQueue ();
qConn = factory.createQueueConnection();
session = qConn.createQueueSession(false, QueueSession.AUTO_ACKNOWLEDGE);
qConn.start();
} catch (ServiceLocatorException se) {
throw new BookException(se);
} catch (JMSException je) {
throw new BookException(je);
}
}

public void submitBooks(String message) throws BookException {
setupPTP();
QueueSender sender = null;

try {
BookListener bkListener = new BookListener();
QueueReceiver receiver = session.createReceiver(queue);
receiver.setMessageListener(bkListener);
sender = session.createSender(queue);

TextMessage message = session.createTextMessage(message);
sender.send(message);
sender.close();
} catch (JMSException je) {
throw new BookException(je);
}
}

That’s it!

Basic Spring Transactions

Spring offers a few options when it comes to transaction management. I used the Programmatic Transaction option this was the easiest to implement in the architecture that I was working with. Here are the steps that I used.

First I modified the jboss-spring.xml file to include a bean reference to the JTA Transaction Manager.

jboss-spring.xml

<bean id="transactionManager"
class="org.springframework.transaction.jta.JtaTransactionManager" />

Then in the classes that I wanted to use transactions I added a reference to the bean as a property in the jboss-spring.xml file.

<bean id="bookMgmtHandler" class="com.davis.bo.BookMgmtHandler">
<property name="bookDAO" ref="bookDAO" />
<property name="transactionManager" ref="transactionManager"/>
</bean>

I then needed to add some imports to the actual Java class that would contain the transactional references. The additional functions that I needed to add related to Spring transactions are highlighted in bold.

BookMgmtHandler.java


import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.DefaultTransactionDefinition;

public class BookMgmtHandler {

BookDAO BookDAO = null;
BookDataAggregator BookDataAggregator = null;

private PlatformTransactionManager transactionManager = null;

private TransactionDefinition getDefinition() {
DefaultTransactionDefinition def = new DefaultTransactionDefinition
(TransactionDefinition.PROPAGATION_REQUIRED);

return def;
}

public Book saveBook(Book book) throws BOOKException {

Book retBook = null;
TransactionStatus status = transactionManager.getTransaction(getDefinition());

// put a block for catching exceptions to rollback the transaction
try {
BookDAO.updateBook(book);
retBook = BookDataAggregator.getBook(book.getBookID());
}
catch (BOOKException be){
transactionManager.rollback(status);
throw be;
}

transactionManager.commit(status);
return retBook;
}


public void setTransactionManager(PlatformTransactionManager platformTransactionManager) {
this.transactionManager = platformTransactionManager;
}
}

And that’s it!