C H A P T E R  7

Developing Message-Driven Beans

The EJB Builder in the Sun ONE Studio 5 IDE enables you to develop the message-driven beans that you need to support an application client's requests for asynchronous processes. This chapter discusses the process of creating and working with message-driven beans. These beans' transactions are normally managed by the EJB container, but you can provide the transaction-management code yourself, if you prefer.

There are several reasons to use a message-driven bean:

However, message-driven beans aren't always the right answer. For example, an alternative would probably work better in the following cases:

For more pros and cons, see Understanding Message-Driven Beans.

The IDE provides a wizard that lets you create the single bean class required for a message-driven bean. Because a message-driven bean merely takes messages from a client and uses them to start other bean processes, no interface classes are needed. The wizard automates much of the task of creating a message-driven bean, and you finish the task using the IDE's Source Editor and property sheets.

When programming message-driven beans, you have options besides those described in this chapter. Although the Sun ONE Studio 5 IDE is designed to take care of much of your coding work, the IDE also supports those options flexibly and leaves many decisions up to you. For more information, refer to the resources listed in Before You Read This Book, or to one of the many excellent texts on programming enterprise beans.


Using the EJB Builder With Message-Driven Beans

The EJB Builder is a collection of wizards, property sheets, and editors with which you can build enterprise beans consistently and easily. To see if the EJB Builder is installed, go to the main window and choose Tools right arrow Options right arrow IDE Configuration right arrow System right arrow Modules right arrow J2EE Support. If you see EJB 2.0 Builder in the list of modules, and the Enabled field in the property sheet is set to True, the EJB Builder is ready for use.

You can take several approaches to creating message-driven beans in the IDE. However, you get the most comprehensive support and, in general, the fastest path to bean completion, if you use the approach recommended in this chapter. The methodology described here takes full advantage of the IDE's ability to ensure consistency and its adherence to the J2EE standard.

For best results, use the EJB Builder to program message-driven beans by:

The logical node is the best place to do work on a message-driven bean. All logical nodes appear in the Explorer with this icon:Bean icon.

From a message-driven bean's logical node, you can validate the bean's code.


Deciding on Transaction Management

Before you begin creating a message-driven bean, first consider whether to have the EJB container manage any transactions that your bean will do, or whether to write that code yourself. You use different processes in the IDE's EJB Builder to create the two kinds of bean. TABLE 7-1 highlights the design considerations.

TABLE 7-1 Deciding Between Container-Managed and Bean-Managed Transactions

Issue

Container-Managed Transactions

Bean-Managed Transactions

Transaction manager

The container itself is the transaction manager.

You write code to manage transactions by using JTA. This can include transactions for other resources such as JDBC.

Setting of transaction boundaries

The EJB container decides when to begin and commit a transaction according to the Java 2 Platform, Enterprise Edition Specification.

The programmer explicitly codes the transaction's boundaries to obtain more granular control over transactions.

Transaction timing

The message-driven bean receives a message and performs its business logic in the same transaction.

The transaction doesn't start until after the message-driven bean receives the message.

Problem handling

The container rolls back the transaction and has the bean acknowledge the message.

The message-driven bean responds according to the acknowledgment mode you specified after you generated the bean.


For more information on these selections, refer to the chapter on transactions in the book Building J2EE Applications.

The rest of this chapter addresses how to create message-driven beans of each kind and the issues to consider during development.


Defining a Message-Driven Bean

The EJB Builder wizard automates much of the task of creating the one bean class that your message-driven bean requires. To define a message-driven bean, you take the following steps:

1. Select or create a package to contain the bean.

2. Use the EJB Builder wizard to generate the infrastructure of your message-driven bean.

3. Complete the body of the onMessage method and, if necessary, the setMessageDrivenContext and ejbCreate methods.

These basic steps are explained in detail next.

After you finish the steps covered in this chapter, you must add information to your finished bean's property sheet so that it can interact with other beans, find its resources, and listen for the appropriate messages. These steps, which prepare your finished bean to work in an application, are discussed in Chapter 8.

Creating a Package

If you need to create a package to house your message-driven bean, select a filesystem, right-click, and choose New Java Package.

Starting the EJB Builder Wizard

When you're ready to create a message-driven bean, do as follows:

1. In the IDE's main window, choose View right arrow Explorer to open the Explorer window.

2. In the Filesystems tab of the Explorer, select the package or filesystem where you want your message-driven bean to reside.

3. Right-click and choose New right arrow J2EE right arrow Message-Driven EJB.

The EJB Builder wizard appears, displaying New Wizard-Message-Driven EJB in the window's title bar.

Generating the Basic Message-Driven Bean

In the EJB Builder`s Message-Driven Bean Name and Properties page, name your message-driven bean and decide how to manage any transactions the bean performs. The default is Container-Managed Transactions, but you can decide to provide all transaction management code in the bean class if you wish.

When you have made your selection, you can click Finish. (Or, you can click Next to go to the page in which you can specify an existing bean class for your message-driven bean. After that, you click Finish.)

Your newly created message-driven bean appears in the Filesystems tab of the IDE's Explorer. The bean's infrastructure (its basic bean class and its two component methods) has been generated automatically by the EJB Builder.


Looking at a Message-Driven Bean's Components

FIGURE 7-1 shows how a typical message-driven bean appears in the Explorer's Filesystems tab.

 FIGURE 7-1 Default Class and Methods of a Typical Message-Driven Bean

Screenshot showing the generated elements of a message-driven bean as shown in the Explorer window. [ D ]

Of the two primary nodes shown, one is a logical node (marked with a bean icon) and one represents the actual class (marked with a class icon). Do all your editing in the logical node. The bean's two primary nodes are described next.

The Classes node contains the bean class code, which includes both methods. The Create Method node points to the code that initializes your message-driven bean. The OnMessage Method node points to the method that is invoked when a message is received.

Expanding the Nodes

When you expand the two nodes under your message-driven bean's package node, you see something like the tree view in FIGURE 7-2.

 FIGURE 7-2 Explorer's Detailed View of a Typical Message-Driven Bean

Screenshot showing an expanded tree view of the example message-driven bean AddToOrder.[ D ]

Reviewing the Generated Class

The wizard automatically places certain default methods in each message-driven bean: a create method, an onMessage method, and two life-cycle methods. As shown in TABLE 7-2, the create method, ejbCreate, behaves much like create methods in other types of enterprise beans, but onMessage is a new and different kind of method.

TABLE 7-2 Purpose of ejbCreate and onMessage Methods in a Message-Driven Bean's Bean Class

Method

Purpose

ejbCreate

This method initializes the message-driven bean, if necessary.

onMessage

This method opens the message the message-driven bean has received, decides what to do with it, and processes it.


The wizard also adds the default life-cycle methods described in TABLE 7-3.

TABLE 7-3 Purpose of Default Life-Cycle Methods in a Message-Driven Bean's Bean Class

Method

Purpose

setMessageDrivenContext

This method is called before ejbCreate, and it associates the message-driven bean with a context object.

ejbRemove

This method is called just before the message-driven bean instance is removed, to free up resources that are no longer needed. In a simple message-driven bean, this method might not even be used.



Completing Your Message-Driven Bean

To complete your message-driven bean, do the following:

The ejbCreate and ejbRemove methods are not needed in simple message-driven beans. However, if necessary, ejbCreate can be used to allocate resources and ejbRemove to let the resources go.

Make your additions in the Explorer by clicking bean components under the logical bean node to open the Source Editor.

Using Recommended Approaches When Working With Enterprise Beans

Appendix A discusses the best ways to make changes in your enterprise beans, and the errors and anomalies that you might see if you use other approaches. As a general rule, you should work through the logical node rather than the individual class nodes, use the bean's property sheets or the Customizer dialog box to edit methods, and use the IDE's Source Editor to complete or edit any bean code that isn't available to you through one of the dialog boxes.

Completing the onMessage Method

A single instance of your message-driven bean can handle only one message at a time, and the bean can have only one onMessage method. An example of a completed method follows.

public void onMessage(Message inMessage) { 	TextMessage msg = null; 	try { 			if (inMessage instanceof TextMessage) { 				msg = (TextMessage) inMessage; 				System.out.println("MESSAGE BEAN: Message " + 				"received: " + msg.getText()); 			} else { 				System.out.println("Message of wrong type: " +				inMessage.getClass().getName()); 			} 	} catch (JMSException e) { 			System.err.println("MessageBean.onMessage: " + 			"JMSException: " + e.toString()); 			context.setRollbackOnly(); 	} catch (Throwable te) { 			System.err.println("MessageBean.onMessage: " + 			"Exception: " + te.toString()); 	}}

Completing the setMessageDrivenContext Method

The setMessageDrivenContext method stores the message-driven context reference in a field and populates non-persistent fields. You can, if necessary, use this method to allocate resources that are independent of the bean object and last as long as the bean exists. These resources might include a queue-connection or topic-connection factory.

By default, the EJB Builder wizard generates code that assigns the message-driven context to a non-persistent field named context. Ordinarily, you don't need to add anything to the generated method. However, if you need to complete it, copy the generated context into the instance variable. For example:

TABLE 7-4 Example of a setMessageDrivenContext Method
public void setMessageDrivenContext(javax.ejb.MessageDrivenContext aContext ) {
	this.context=context;
}


After Creating Your Message-Driven Bean

Your message-driven bean is now finished, except for a few steps that prepare the bean to work in its eventual environment. You must specify the following in the bean's property sheet:

  • The bean's message-driven destination, that is, whether the bean gets its messages from a queue or a topic
  • If the bean listens to a topic, whether its subscription is durable or non-durable
  • Whether a message selector (filter) has been applied to the bean to narrow down the messages it gets

If your message-driven bean will receive messages from a client, and you plan to deploy your bean to Sun ONE Application Server 7, you must specify the destination in the Sun ONE AS tab of the bean's property sheet.

If your message-driven bean will act as a client itself, sending messages to a destination, you must specify the following in the References tabbed interface of the bean's property sheet:

  • The bean's resource references (the connection factories it uses to access its message-driven destinations)
  • The bean's resource environment references (the actual destinations: queues or topics)

These property settings are discussed next.

Specifying a Message-Driven Destination

To specify whether the message-driven bean will be a queue listener or a topic listener, do as follows:

1. In the IDE's Explorer window, right-click the message-driven bean's logical node and choose Properties.

The property sheet for the bean appears.

2. In the Properties tabbed interface, click the Message-Driven Destination field and then the ellipsis (...) button.

The property editor appears.

3. Select Queue, Topic, or (Not Set).

  • Select Queue if clients will send messages only to this particular bean and you need to use the point-to-point model.
  • Select Topic if you need to allow multiple clients to send messages to this bean, using the publish-subscribe model. If you choose Topic, you must also specify whether the bean's subscription is durable or non-durable.
    • Select Durable if messages should be persisted until the bean consumes them. This way, even if the bean's application server crashes, the messages are available when the bean is next available.
    • Select Non-durable if the bean should get only messages published while the bean is available. All other messages are deleted.
  • Leave the Destination field blank (using the Not Set value) if you will set this property later.

4. Click OK to dismiss the property editor.

Specifying a Message Selector

If you want to filter your bean's incoming messages, do as follows:

1. Click the Message Selector field and then the ellipsis (...) button.

A property editor appears.

2. Specify a filter if you wish to reduce the number of messages for which your bean must listen.

3. Click OK to dismiss the property editor.

Specifying Resources for Client Message-Driven Beans

The References tabbed interface of a message-driven bean's property sheet contains the Resource Reference and Resource Environment Reference fields. These fields are completed on behalf of the client that sends messages. For example, your message-driven bean might be part of an application in which a web module sends messages to a queue for consumption by your bean. In that case, this Resource Reference and this Resource Environment Reference should be specified by the provider of the web module.

Or, if your message-driven bean is meant to act as a client within its own module, sending messages to a queue or topic, you specify the resource factory and the resource here.

Specifying Resource Factories

To associate the message-driven bean with a factory object that will create the destination object, do as follows:

1. In the References tabbed interface, click the Resource References field and then the ellipsis (...) button.

In the property editor are fields for specifying the connection factory that the client (or message-driven bean as client) will use to gain access to its messaging resource.

2. Click the Add button.

The Add Resource Reference dialog box appears with two tabbed interfaces, Standard and Sun ONE App Server.

  • In the Standard tabbed interface:
    • Type the reference name of the object that will create your bean's connection to its queue or topic. Notice that this name has only to match what you wrote in your bean's lookup code.
    • In the Type combo box, select the type of resource factory your bean will use. This type should correspond to the choice you made in the Message-driven Destination field of the Properties tabbed interface. See Specifying Resource Environment References for an explanation of the various types of resource factories.
    • In the Authorization field, specify whether the EJB container or the application client will authorize the bean to use the resource.
    • In the Sharing Scope field, specify whether the connection to this resource can be shared by another enterprise bean in the same application. If two or more beans can use the same resource in the same transaction context, the container can carry out transactions locally and save time.

If you are deploying your message-driven bean to Sun ONE Application Server 7, also complete the following fields.

  • In the Sun ONE App Server tab:
    • In the JNDI Name field, type the actual JNDI name by which the server can locate the resource factory. Notice that this name must match the JMS resource set in the Runtime tab of the IDE's Explorer window.
    • In the User Information fields, provide any information needed to gain access to the resource.

3. When you're finished, click OK to dismiss the dialog box.

Specifying Resources

To associate the message-driven bean with a particular destination object, do as follows:

1. In the References tabbed interface, click the Resource Environment References field and then the ellipsis (...) button.

In the property editor are fields for specifying the actual resources to which the client (or message-driven bean as client) will send messages.

2. Click the Add button.

The Add Resource Environment Reference dialog box appears with two tabbed interfaces.

  • In the Standard tab:
    • Type the reference name of the queue or topic to which your client or bean will send messages. Notice that this name has only to match what you wrote in the bean's lookup code.
    • In the combo box, select a resource type.

If you plan to deploy your message-driven bean to Sun ONE Application Server 7, also complete the following field.

  • In the Sun ONE App Server tab, type the actual JNDI name by which the server can locate the message resource (the queue or topic). Notice that this name must match the JMS resource set in the Runtime tab of the IDE's Explorer window.

3. When you're finished, click OK to dismiss the dialog box.

Read more about message-driven destinations in Chapter 2 and more about setting properties in Chapter 8.

Recommendations for working with finished enterprise beans are given in Appendix A.


Avoiding Pitfalls of Message-Driven Beans

The messaging tier of your application will run into fewer problems if you understand the following possible complications.

  • Order of Messages. Your message-driven beans should be prepared to handle messages that arrive out of sequence. A JMS server might deliver messages in any order to a pool of message-driven beans.
  • Dropped ejbRemove Invocations. A simple message-driven bean doesn't need to use an ejbCreate or ejbRemove method. However, if your bean is more complex and does use those methods, be aware that under certain circumstances (such as a system or container crash), ejbRemove might not be called. In this case, you should provide for the bean to do its own clean-up. This depends on the behavior of your application server; for details, see your server's documentation.
  • Poison messages. When you're using the EJB Builder wizard to generate the infrastructure of a message-driven bean that manages its own transactions, you can set the bean property Acknowledge Mode to Auto. This setting makes the bean automatically acknowledge each message it gets. This way, you avoid the situation in which a transaction fails, the message destination never hears that the message was received, and the destination keeps sending the message over and over.

For detailed design considerations, refer to Enterprise JavaBeans Specification, version 2.0 and to texts on programming enterprise beans.


Further Reading

Enterprise beans can be a very powerful and flexible part of your application. Creating the basic parts of an enterprise bean can be very simple, especially with a tool like the Sun ONE Studio 5 IDE. However, completing the bean so that it satisfies the needs of your application can be somewhat more complex. For details, refer to Enterprise JavaBeans Specification, version 2.0 at:
http://java.sun.com/products/ejb/docs.html and to
The J2EE Tutorial Addendum at: http://java.sun.com/j2ee/1.4/docs/tutorial/doc/JMSJ2EEex.html