Message linking is a new feature to EJB
2.1 that allows the messages being sent by any enterprise bean to be
routed to a specific message-driven bean in the same deployment. By
using message linking, you can orchestrate a flow of messages between
components in the same application. For example, in the beginning of
this chapter, the TravelAgent EJB from Chapter 11 was re-implemented so that it sent a JMS
message with the ticket information to a Topic destination.
Here’s a different implementation of the TravelAgent
EJB’s bookPassage( )
method, this
time using an ObjectMessage
type:
public TicketDO bookPassage(CreditCardDO card, double price) throws IncompleteConversationalState { if (customer == null || cruise == null || cabin == null) { throw new IncompleteConversationalState( ); } try { ReservationHomeLocal resHome = (ReservationHomeLocal) jndiContext.lookup("java:comp/env/ejb/ReservationHomeLocal"); ReservationLocal reservation = resHome.create(customer, cruise, cabin, price, new Date( )); Object ref = jndiContext.lookup ("java:comp/env/ejb/ProcessPaymentHomeRemote"); ProcessPaymentHomeRemote ppHome = (ProcessPaymentHomeRemote) PortableRemoteObject.narrow(ref, ProcessPaymentHomeRemote.class); ProcessPaymentRemote process = ppHome.create( ); process.byCredit(customer, card, price); TicketDO ticket = new TicketDO(customer,cruise,cabin,price); TopicConnectionFactory factory = (TopicConnectionFactory) jndiContext.lookup("java:comp/env/jms/TopicFactory"); Topic topic = (Topic) jndiContext.lookup("java:comp/env/jms/TicketTopic"); TopicConnection connect = factory.createTopicConnection( ); TopicSession session = connect.createTopicSession(true,0); TopicPublisher publisher = session.createPublisher(topic); ObjectMessage objectMsg = session.createObjectMessage( ); objectMsg.setObject(ticket); publisher.publish(objectMsg); connect.close( ); return ticket; } catch(Exception e) { throw new EJBException(e); } }
When we discussed this method earlier in the chapter, we never really mentioned where the ticket message was being sent. It could go to the reservation agent or some other department of Titan Cruises. However, message linking makes sure that the message goes directly to a message-driven bean that we deploy.
For example, we might deploy a message driven bean, the TicketDistributor EJB, that is responsible for distributing ticket information to several different targets such as legacy databases, partner organizations, marketing, etc. Figure 12-5 shows how the TicketDistributor EJB (an MDB) works with the TravelAgent EJB to distribute ticket information to several different targets.
The TicketDistributor distributes the ticket information to a variety of disparate targets, including a separate relational database using JDBC, a legacy system (e.g., IMS, CICS, etc.) using a J2EE Connector, and email using JavaMail. The TravelAgent EJB could have handled this type of distribution directly, but defining a separate MDB to do distribution provides more flexibility and better performance.
The TicketDistributor MDB is more flexible because the routing for the message can be changed without modifying the TravelAgent EJB. The TravelAgent EJB always sends messages to the same JMS topic; it’s the responsibility of the TicketDistributor to distribute the ticket information to other sources. The TicketDistributor also improves performance, because the TicketAgent doesn’t have to wait on the various targets (a separate database, legacy system, and email) to accept and process the message before finishing the reservation. The TicketAgent just sends the ticket information and forgets about it. It’s the responsibility of the TicketDistribution MDB to distribute the ticket information to the appropriate parities. In addition, the TravelAgent EJB doesn’t have to coordinate a distributed transaction across different resources, which can create significant bottlenecks and affect throughput.
In order to link the outgoing messages sent by the TravelAgent EJB
with the incoming messages consumed and processed by the
TicketDistribution MDB, we need to define
<message-destination-link>
elements in the
deployment descriptor. The
<message-destination-link>
element is
defined by the <message-destination-ref>
element of the TravelAgent EJB. The TicketDistributor EJB also
declares the <message-destination-link>
element. Both elements reference the same logical
destination declared in the assembly descriptor:
<ejb-jar ...> <enterprise-beans> ... <session> <ejb-name>TravelAgentBean</ejb-name> ... <resource-ref> <res-ref-name>jms/TopicFactory</res-ref-name> <res-type>javax.jms.TopicConnectionFactory</res-type> <res-auth>Container</res-auth> </resource-ref> <message-destination-ref> <message-destination-ref-name> jms/TicketTopic </message-destination-ref-name> <message-destination-type>javax.jms.Topic</message-destination-type> <message-destination-usage>Produces</message-destination-usage> <message-destination-link> Distributor </message-destination-link> </message-destination-ref> ... </session> <message-driven> <ejb-name>TicketDistributorEJB</ejb-name> <ejb-class> com.titan.distributor.TicketDistributorBean </ejb-class> <messaging-type>javax.jms.MessageListener</messaging-type> <transaction-type>Bean</transaction-type> <message-destination-type> javax.jms.Topic </message-destination-type> <message-destination-link> Distributor </message-destination-link> ... </message-driven> ... </enterprise-beans> <assembly-descriptor> ... <message-destination> <message-destination-name>Distributor</message-destination-name> </message-destination> ... </assembly-descriptor> </ejb-jar>
As you know, a
<message-destination-ref>
element declares the destination to
which an enterprise beans sends or receives messages. When the
<message-destination-ref>
includes a
<message-destination-link>
element, it means
that message senders and receivers will be sharing a logical
destination described in the assembly-descriptor. In the example
above, the TravelAgent EJB’s
<message-destination-ref>
declares a
<message-destination-link>
, which points to the
<message-destination>
element in the
<assembly-descriptor>
that has the name
Distributor
. The
<message-destination-link>
defined by the
TicketDistributor MDB points to the same
<message-destination>
element. This means
the messages sent by the TravelAgent EJB to the
Distributor
message destination will go to the
TicketDistributor MDB.
Message-driven beans always consume messages from the destination
defined by the
<message-destination-link>
element defined directly under the
<message-bean>
element. However, they can
also produce messages that are sent to a logical message destination
if they use the message API described by their own
<message-destination-ref>
element. The
following listing shows that the
TicketDistributor consumes messages
from the Distributor
destination, but also uses
the JMS to send messages to a completely different destination,
called Partner
:
<ejb-jar ...> <enterprise-beans> ... <message-driven> <ejb-name>TicketDistributorEJB</ejb-name> <ejb-class> com.titan.distributor.TicketDistributorBean </ejb-class> <messaging-type>javax.jms.MessageListener</messaging-type> <transaction-type>Bean</transaction-type> <message-destination-type> javax.jms.Topic </message-destination-type> <message-destination-link> Distributor </message-destination-link> ... <message-destination-ref> <message-destination-ref-name> jms/PartnerCompany </message-destination-ref-name> <message-destination-type>javax.jms.Topic</message-destination-type> <message-destination-usage>Produces</message-destination-usage> <message-destination-link> Partner </message-destination-link> </message-destination-ref> ... </message-driven> ... </enterprise-beans> <assembly-descriptor> ... <message-destination> <message-destination-name>Distributor</message-destination-name> </message-destination> <message-destination> <message-destination-name>Partner</message-destination-name> </message-destination> ... </assembly-descriptor> </ejb-jar>
At deployment time, each of the
<message-destination>
elements are mapped to
a real messaging destination in the target environment. In most
cases, this will be a JMS topic or queue, but it could be a
destination of some other type of messaging system.
The J2EE application server doesn’t have to route the messages through an actual destination. It can asynchronously send them from the sender to the receiver; in this case, from the TravelAgent EJB to the TicketDistributor MDB. However, if the application server handles message delivery itself, rather than going through a messaging provider, it must follow the semantics of the messaging system. For JMS, transactions, persistence, durability, security, and acknowledgments should be handled correctly whether the message is sent directly from one component to another, or via a JMS provider.
Although any enterprise bean can consume (receive) messages from a logical destination as well as produce (send) messages, only MDBs should consume messages. The reasons for this limitation were discussed earlier in this chapter (see “Entity and Session Beans Should Not Receive Messages”).