Using JMS Queues or Topics with Message-Driven Beans

   

Creating a Message-Driven Bean

Creating a message-driven bean is not that complicated. They really are much easier to create than session or entity beans because you don't have to worry about creating a home or a component interface for them. To create a message-driven bean, your class must implement two required interfaces:

  • The javax.ejb.MessageDrivenBean interface

  • The javax.jms.MessageListener interface

The MessageDrivenBean Interface

The first interface that your message-driven bean must implement is the javax.ejb.MessageDrivenBean interface. Table 11.1 lists the methods that are part of the MessageDrivenBean interface.

Table 11.1. Methods of the javax.ejb.MessageDrivenBean Interface

Name

Description

ejbRemove

Called by the container before an instance is removed from service.

setMessageDrivenContext

The container passes an instance of a MessageDrivenContext interface to a bean. The bean will normally hold this context as part of its instance state and allows the bean to get access to the context held onto by the container.

The ejbRemove method will be called on a message-driven bean just before the instance is about to be removed by the container. The bean should release any resources that it is holding. The resources could be JDBC connections, a JavaMail session, or other finite resources that need to be cleaned up and released.

Note

The message-driven bean does not have to clean up any resources related to the JMS destination it's listening on. The container will handle those responsibilities.


The setMessageDrivenContext method takes a single argument, which is an object that implements the javax.ejb.MessageDrivenContext interface. This object provides access to the runtime message-driven context that the container associates with each message-driven bean. The MessageDrivenContext interface extends the javax.ejb.EJBContext and therefore provides access to security and transactional properties and methods. Table 11.2 describes the methods that are available through the MessageDrivenContext instance passed to the message-driven bean instance.

Table 11.2. Methods of the MessageDrivenContext Interface

Name

Description

getCallerPrincipal

Obtain the security principal that identifies the caller.

getEJBHome

Obtain the bean's home interface. Because the message-driven bean doesn't have a home interface, this is not a valid method to call on this type of bean.

setRollbackOnly

Mark the current transaction for rollback. A transaction marked for rollback can never commit.

getRollbackOnly

Test to see whether the current transaction has been marked for rollback.

getUserTransaction

Obtain the transaction demarcation interface. This is only used for bean-managed transactions.

isCallerInRole

Test to see whether the caller has a specific security role.

getEJBLocalHome

Obtain the bean's local home interface.

The MessageDrivenContext interface doesn't define any new methods itself. The methods displayed in Table 11.2 are inherited from the EJBContext interface. Future versions of this interface might define more methods that are specific to the message-driven bean context.

Caution

Because the message-driven bean doesn't have a home interface, calling the getEJBHome or getEJBLocalHome method on a message-driven bean will throw a java.lang. IllegalStateException . It's only there because the specific context classes for the three enterprise beans all extend EJBContext .


Note

There are several methods in the EJBContext interface that have been deprecated. Those methods are not listed in Table 11.2. See Appendix A for a complete listing of the deprecated methods in the EJBContext interface.


The JMS MessageListener Interface

The second required interface is the javax.jms.MessageListener interface. All message-driven beans must also implement this interface. This is the same interface that regular JMS message consumers must implement also. The MessageListener interface defines a single method that must be implemented:

 public void onMessage(javax.jms.Message msg); 

The container calls this method when a message arrives at the JMS destination and the bean instance should service it. The onMessage method is the method where your business logic should go. Obviously, you can have other public and private methods in your message-driven bean and call those from the onMessage method, but it all starts from here.

The onMessage method contains a single argument, which is the javax.jms.Message that the container is asking the bean instance to handle.

Note

Remember that javax.jms.Message is an interface, and several JMS message types implement this interface. If you are not sure which message type to expect, you can determine it programmatically using the instanceof operator.


If you need a refresher on the JMS interfaces and classes, see "Java Message Service," p. 265 .

The onMessage method should not be declared final or static. It must be declared public and have a void return type. It must also not throw any application or runtime exceptions. If something happens that would normally cause one of these exceptions, you should just catch the exception, log the information, and return.

For more information on exception handling for message-driven beans, see "Exception Handling," p. 363 .

The EJB 2.0 Specification supports only JMS messaging, so all message-driven beans currently are JMS message-driven beans, to be precise. This is why the requirement to implement the javax.jms.MessageListener interface is an absolute one. When other messaging types are supported by the specification, you'll have choices other than javax.jms.MessageListener for your message-driven beans.

Creating the Message-Driven Bean Class

The main work in creating the actual message-driven bean class is ensuring that you have implemented the two required interfaces and that you provide the business logic when the onMessage method is called. The rest of the work for creating the message-driven bean class is done during deployment.

In Chapter 10, "Java Message Service," we created a class called AuctionNotificationConsumer in Listing 10.2. We mentioned in that section that we would eventually replace this consumer with a message-driven bean. We'll show an example of using a message-driven bean to listen on a JMS Queue for messages and then send an e-mail message using an e-mail service that we'll build later. We will be developing the details of the e-mail service and some other common services for an application later in Chapter 21, "Horizontal Services."

Listing 11.1 shows the equivalent of the class from Chapter 10 now implemented as a message-driven bean.

Listing 11.1 AuctionNotificationConsumer from Chapter 10 Implemented as a Message-Driven Bean
 /**   * Title:        AuctionNotificationMessageBean   * Description:  The Message-driven bean gets messages from a Queue   *               and delegates to the horizontal service.  */  package com.que.ejb20.notification;  import javax.ejb.MessageDrivenBean;  import javax.ejb.MessageDrivenContext;  import javax.jms.JMSException;  import javax.jms.Message;  import javax.jms.ObjectMessage;  import javax.jms.MessageListener;  import com.que.ejb20.services.email.Emailable;  import com.que.ejb20.services.email.EmailService;  import com.que.ejb20.services.email.EmailException;  public class AuctionNotificationMessageBean implements    MessageDrivenBean, MessageListener {   private MessageDrivenContext ctx = null;    // Default Constructor    public AuctionNotificationMessageBean() {     super();    }    // This is where the real work happens    public void onMessage( Message jmsMessage ) {     if ( jmsMessage instanceof ObjectMessage) {       try {         Object obj = ((ObjectMessage)jmsMessage).getObject();          if ( obj instanceof Emailable ) {           sendEmail( (Emailable)obj );          }        }catch( JMSException ex ) {         ex.printStackTrace();        }      }    }    // Delegate to the horizontal service    private void sendEmail( Emailable emailableMsg ){     try{       EmailService.sendEmail( emailableMsg );      }catch( EmailException ex ){       // Just print out the exception and move on        ex.printStackTrace();      }    }    // Associate the private reference with this context so    // that this bean can use the context if neccessary    public void setMessageDrivenContext( MessageDrivenContext ctx ){     this.ctx = ctx;    }    public void ejbCreate(){     // This method is required, but you      // don't have to do anything with it    }    public void ejbRemove(){     // This method is required, but you      // are not required to do anything    }  } 

The main difference you should see between Listing 11.1 and Listing 10.2 from Chapter 10 is that you don't have to worry about getting connected to the JMS-administered objects. All you need to worry about is implementing the onMessage method and performing the business logic correctly. This is nice and in line with the EJB architecture because it allows the bean provider to focus more on the business logic.

In the onMessage in Listing 11.1, the business logic is simply to ensure that the message is of the correct type and then to call the e-mail horizontal service.

If you are curious about how the e-mail is implemented by the horizontal service, see "Building an E-Mail Horizontal Service," p. 552 .

The other thing to notice from Listing 11.1 is that, even though you might not want to do anything with the ejbCreate or ejbRemove methods, you must still implement them in your class.

There are some general restrictions for the message-driven bean class. The following list summarizes the rules that you must follow when creating your message-driven bean classes:

  • The bean must be declared as public .

  • The bean can't be declared as final or abstract .

  • The bean must declare a no-argument constructor.

  • The bean must not implement the finalize method.

You are allowed to declare superclasses and subclasses for the message-driven bean. If you do use these, you are allowed to implement the setMessageDrivenContext or ejbRemove methods in a parent class so that all the subclasses can just inherit those implementations . Listing 11.2 illustrates an abstract implementation for a message-driven bean. Subclasses only need to provide an implementation for the onMessage method when extending this class.

Listing 11.2 An Abstract Implementation that Message-Driven Beans Can Extend
 /**   * Title:        AbstractMessageDrivenBean   * Description:  An abstract implementation for a MessageDrivenBean. Subclasses   *               only need to implement the onMessage method   */  package com.que.ejb20.notification;  import javax.ejb.MessageDrivenBean;  import javax.ejb.MessageDrivenContext;  import javax.jms.Message;  import javax.jms.MessageListener;  import com.que.ejb20.services.email.Emailable;  import com.que.ejb20.services.email.EmailService;  import com.que.ejb20.services.email.EmailException;  abstract public class AbstractMessageDrivenBean implements    MessageDrivenBean, MessageListener {   private MessageDrivenContext ctx = null;    // Associate the private reference with this context so    // that this bean can use the context if necessary    public void setMessageDrivenContext( MessageDrivenContext ctx ){     this.ctx = ctx;    }    public void ejbCreate(){     // This method is required, but you      // don't have to do anything with it    }    public void ejbRemove(){     // This method is required, but you      // are not required to do anything    }    // Concrete subclasses must provide an implementation for    // the onMessage method    abstract public void onMessage();  } 

If we modified the AuctionNotificationMessageBean class from Listing 11.1 to extend the abstract message-driven bean class in Listing 11.2, it would look like the class in Listing 11.3.

Listing 11.3 NewAuctionNotificationMessageBean Extending the AbstractMessageDrivenBean
 /**   * Title:        NewAuctionNotificationMessageBean   * Description:  The Message-driven bean gets messages from a Queue   *               and delegates to the horizontal service.   */  package com.que.ejb20.notification;  import javax.jms.Message;  import javax.jms.ObjectMessage;  import javax.jms.JMSException;  import com.que.ejb20.services.email.Emailable;  import com.que.ejb20.services.email.EmailService;  import com.que.ejb20.services.email.EmailException;  public class NewAuctionNotificationMessageBean    extends AbstractMessageDrivenBean {   // Default Constructor    public NewAuctionNotificationMessageBean() {     super();    }    // This is where the real work happens    public void onMessage( Message jmsMessage ) {     if ( jmsMessage instanceof ObjectMessage) {       try {         Object obj = ((ObjectMessage)jmsMessage).getObject();          if ( obj instanceof Emailable ) {           sendEmail( (Emailable)obj );          }        }catch( JMSException ex ) {         ex.printStackTrace();        }      }    }    // Delegate to the horizontal service    private void sendEmail( Emailable emailableMsg ){     try{       EmailService.sendEmail( emailableMsg );      }catch( EmailException ex ){       // Just print out the exception and move on        ex.printStackTrace();      }    }  } 

The benefit of putting some of the behavior up in the parent class is that the concrete classes are a little smaller and easier to maintain. You also get the normal benefits that you get with inheritance and using default implementations.



Special Edition Using Enterprise JavaBeans 2.0
Special Edition Using Enterprise JavaBeans 2.0
ISBN: 0789725673
EAN: 2147483647
Year: 2000
Pages: 223

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net