ONJava.com -- The Independent Source for Enterprise Java
oreilly.comSafari Books Online.Conferences.

advertisement

AddThis Social Bookmark Button

EJB Message-Driven Beans
Pages: 1, 2, 3, 4, 5, 6, 7

Sending messages from a message-driven bean

MDB can also send messages using JMS. The deliverTicket() method sends the ticket information to a destination defined by the sending JMS client:

public void deliverTicket(MapMessage reservationMsg, TicketDO ticket)
    throws NamingException, JMSException{
    
    Queue queue = (Queue)reservationMsg.getJMSReplyTo();
    
    QueueConnectionFactory factory = (QueueConnectionFactory)
        jndiContext.lookup("java:comp/env/jms/QueueFactory");
        
    QueueConnection connect = factory.createQueueConneciton();
 
    QueueSession session = connect.createQueueSession(true,0);
 
    QueueSender sender = session.createSender(queue);
 
    ObjectMessage message = session.createObjectMessage();
    message.setObject(ticket);
    
    sender.send(message);
        
    connect.close();
 
}
Obviously, if the destination identified by the JMSReplyTo attribute is of type Queue, the point-to-point (queue-based) messaging model must be used. If the destination type identified by the JMSReplyTo attribute is Topic, the publish-and-subscribe (topic-based) messaging model must be used.

As stated earlier, every message type has two parts: a message header and a message body (a.k.a. payload). The message header contains routing information and may also have properties for message filtering and other attributes, including a JMSReplyTo attribute. When a JMS client sends a message, it may set the JMSReplyTo attribute to be any destination accessible to its JMS provider. In the case of the reservation message, the sender set the JMSReplyTo attribute to the queue to which the resulting ticket should be sent. Another application can access this queue to read tickets and distribute them to customers or store the information in the sender's database.

You can also use the JMSReplyTo address to report business errors that occur while processing the message. For example, if the Cabin is already reserved, the ReservationProcessor EJB might send an error message to the JMSReplyTo queue explaining that the reservation could not be processed. Including this type of error handling is left as an exercise for the reader.

XML deployment descriptor

MDBs have XML deployment descriptors, just like entity and session beans. They can be deployed alone or, more often than not, together with other enterprise beans. For example, the ReservationProcessor EJB would have to be deployed in the same JAR using the same XML deployment descriptor as the Customer, Cruise, and Cabin beans if it's going to use their local interfaces.

Here's the XML deployment descriptor that defines the ReservationProcessor EJB. This deployment descriptor also defines the Customer, Cruise, Cabin, and other beans, but these are left out here for brevity:

<enterprise-beans>
  ...
  <message-driven>
    <ejb-name>ReservationProcessorEJB</ejb-name>
    <ejb-class>
      com.titan.reservationprocessor.ReservationProcessorBean
    </ejb-class>
    <transaction-type>Container</transaction-type>
    <message-selector>MessageFormat = 'Version 3.4'</message-selector>
    <acknowledge-mode>Auto-acknowledge</acknowledge-mode>   
    <message-driven-destination>
      <destination-type>javax.jms.Queue</destination-type>
    </message-driven-destination>   
    <ejb-ref>
      <ejb-ref-name>ejb/ProcessPaymentHomeRemote</ejb-ref-name>
      <ejb-ref-type>Session</ejb-ref-type>
      <home>com.titan.processpayment.ProcessPaymentHomeRemote</home>
      <remote>com.titan.processpayment.ProcessPaymentRemote</remote>
    </ejb-ref>
    <ejb-ref>
      <ejb-ref-name>ejb/CustomerHomeRemote</ejb-ref-name>
      <ejb-ref-type>Entity</ejb-ref-type>
      <home>com.titan.customer.CustomerHomeRemote</home>
      <remote>com.titan.customer.CustomerRemote</remote>
    </ejb-ref>
    <ejb-local-ref>
      <ejb-ref-name>ejb/CruiseHomeLocal</ejb-ref-name>
      <ejb-ref-type>Entity</ejb-ref-type>
      <local-home>com.titan.cruise.CruiseHomeLocal</local-home>
      <local>com.titan.cruise.CruiseLocal</local>
    </ejb-local-ref>
    <ejb-local-ref>
      <ejb-ref-name>ejb/CabinHomeLocal</ejb-ref-name>
      <ejb-ref-type>Entity</ejb-ref-type>
      <local-home>com.titan.cabin.CabinHomeLocal</local-home>
      <local>com.titan.cabin.CabinLocal</local>
    </ejb-local-ref>
    <ejb-local-ref>
      <ejb-ref-name>ejb/ReservationHomeLocal</ejb-ref-name>
      <ejb-ref-type>Entity</ejb-ref-type>
      <local-home>com.titan.reservation.ReservationHomeLocal</local-home>
      <local>com.titan.reservation.ReservationLocal</local>
    </ejb-local-ref>
    <security-identity>
      <run-as>
        <role-name>everyone</role-name>
      </run-as>
    </security-identity>
    <resource-ref>
      <res-ref-name>jms/QueueFactory</res-ref-name>
      <res-type>javax.jms.QueueConnectionFactory</res-type>
      <res-auth>Container</res-auth>
    </resource-ref>
  </message-driven>
  ...
</enterprise-beans>

An MDB is declared in a <message-driven> element within the <enterprise-beans> element, alongside <session> and <entity> beans. Similar to <session> bean types, it defines an <ejb-name>, <ejb-class>, and <transaction-type>, but it does not define component interfaces (local or remote). MDBs do not have component interfaces, so these definitions aren't needed.

<message-selector>

An MDB can also declare a <message-selector> element, which is unique to message-driven beans:

<message-selector>MessageFormat = 'Version 3.4'</message-selector>

Message selectors allow an MDB to be more selective about the messages it receives from a particular topic or queue. Message selectors use Message properties as criteria in conditional expressions. (Message selectors are also based on message headers, which are outside the scope of this chapter.) These conditional expressions use Boolean logic to declare which messages should be delivered to a client.

Message properties, upon which message selectors are based, are additional headers that can be assigned to a message. They give the application developer or JMS vendor the ability to attach more information to a message. The Message interface provides several accessor and mutator methods for reading and writing properties. Properties can have a String value or one of several primitive values (boolean, byte, short, int, long, float, double). The naming of properties, together with their values and conversion rules, is strictly defined by JMS.

The ReservationProcessor EJB uses a message selector filter to select messages of a specific format. In this case the format is "Version 3.4"; this is a string Titan uses to identify messages of type MapMessage that contain the name values CustomerID, CruiseID, CabinID, CreditCard, and Price. In other words, by specifying a MessageFormat on every reservation message, we can write MDBs that are designed to process different kinds of reservation messages. If a new business partner needs to use a different type of Message object, Titan would use a new message version and an MDB to process it.

This is how a JMS producer would go about setting a MessageFormat property on a Message:

Message message = session.createMapMessage();
message.setStringPropery("MessageFormat","Version 3.4");
 
// set the reservation named values
 
sender.send(message);

The message selectors are based on a subset of the SQL-92 conditional expression syntax that is used in the WHERE clauses of SQL statements. They can become fairly complex, including the use of literal values, Boolean expressions, unary operators, and so on.

Message selector examples

Here are three complex selectors used in hypothetical environments. Although you will have to use your imagination a little, the purpose of these examples is to convey the power of the message selectors. When a selector is declared, the identifier always refers to a property name or JMS header name. For example, the selector UserName !='William' assumes that there is a property in the message named UserName, which can be compared to the value 'William'.

Managing claims in an HMO

Due to some fraudulent claims, an automatic process is implemented using MDBs that will audit all claims submitted by patients who are employees of the ACME manufacturing company for visits to chiropractors, psychologists, and dermatologists:

<message-selector>
<![CDATA[
  PhysicianType IN ('Chiropractic','Psychologists','Dermatologist')
  AND PatientGroupID LIKE 'ACME%'
]]>
</message-selector>

TIP:   MDB <message-selector> statements are declared in XML deployment descriptors. XML assigns special meaning to a variety of characters, such as the greater than (>) and less than (<) symbols, so using these symbols in the <message-selector> statements will cause parsing errors unless CDATA sections are used. This is the same reason CDATA sections were needed in EJB QL <ejb-ql> statements, as explained in Chapter 8.

Notification of certain bids on inventory

A supplier wants notification of requests for bids on specific inventory items at specific quantities:

<message-selector>
<![CDATA[
  InventoryID ='S93740283-02' AND Quantity BETWEEN 1000 AND 13000
]]>
</message-selector>

Selecting recipients for a catalog mailing

An online retailer wants to deliver a special catalog to any customer that orders more than $500.00 worth of merchandise where the average price per item ordered is greater than $75.00 and the customer resides in one several states. The retailer creates an MBD that subscribes to the order-processing topic and processes catalog deliveries for only those customers that meet the defined criteria:

<message-selector>
<![CDATA[
    TotalCharge >500.00 AND ((TotalCharge /ItemCount)>=75.00)
    AND State IN ('MN','WI','MI','OH')
]]>
</message-selector>

<acknowledge-mode>

JMS has the concept of acknowledgment, which means that the JMS client notifies the JMS provider (message router) when a message is received. In EJB, it's the MDB container's responsibility to send an acknowledgment to the JMS provider when it receives a message. Acknowledging a message tells the JMS provider that MDB container has received the message and processed it using an MDB instance. Without an acknowledgment, the JMS provider will not know whether the MDB container has received the message, so it will try to redeliver it. This can cause problems. For example, once we have processed a reservation message using the ReservationProcessor EJB, we don't want to receive the same message again.

When transactions are involved, the acknowledgment mode set by the bean provider is ignored. In this case, the acknowledgment is performed within the context of the transaction. If the transaction succeeds, the message is acknowledged. If the transaction fails, the message is not acknowledged. If the MDB is using container-managed transactions, as it will in most cases, the acknowledgment mode is ignored by the MDB container. When using container-managed transactions with a Required transaction attribute, the <acknowledge-mode> is usually not specified; however, we included it in the deployment descriptor for the sake of discussion:

<acknowledge-mode>Auto-acknowledge</acknowledge-mode>

When the MDB executes with bean-managed transactions, or with the container-managed transaction attribute NotSupported (see Chapter 14), the value of <acknowledge-mode> becomes important.

Two values can be specified for <acknowledge-mode>: Auto-acknowledge and Dups-ok-acknowledge. Auto-acknowledge tells the container that it should send an acknowledgment to the JMS provider soon after the message is given to an MDB instance to process. Dups-ok-acknowledge tells the container that it doesn't have to send the acknowledgment immediately; any time after the message is given to the MDB instance will be fine. With Dups-ok-acknowledge, it's possible for the MDB container to delay acknowledgment so long that the JMS provider assumes that the message was not received and sends a "duplicate" message. Obviously, with Dups-ok-acknowledge, your MDBs must be able to handle duplicate messages correctly.

Auto-acknowledge avoids duplicate messages because the acknowledgment is sent immediately. Therefore, the JMS provider won't send a duplicate. Most MDBs use Auto-acknowledge, to avoid processing the same message twice. Dups-ok-acknowledge exists because it may allow a JMS provider to optimize its use of the network. In practice, though, the overhead of an acknowledgment is so small, and the frequency of communication between the MDB container and JMS provider is so high, that Dups-ok-acknowledge doesn't have a big impact on performance.

<message-driven-destination>

The <message-driven-destination> element designates the type of destination from which the MDB receives messages. The allowed values for this element are javax.jms.Queue and javax.jms.Topic. In the ReservationProcessor EJB this value is set to javax.jms.Queue, indicating that the MDB is getting its messages via the p2p messaging model from a queue:

<message-driven-destination>
    <destination-type>javax.jms.Queue</destination-type>
</message-driven-destination>

When the MDB is deployed, the deployer will map the MDB so that it listens to a real queue on the network.

When the <destination-type> is a javax.jms.Topic, the <subscription-durability> element must be declared with either Durable or NonDurable as its value:

<message-driven-destination>
  <destination-type>javax.jms.Topic</destination-type>
  <subscription-durability>Durable</subscription-durability>
</message-driven-destination>

The <subscription-durability> element determines whether or not the MDB's subscription to the topic is Durable. A Durable subscription outlasts an MDB container's connection to the JMS provider, so if the EJB server suffers a partial failure, is shut down, or is otherwise disconnected from the JMS provider, the messages that it would have received will not be lost. While a Durable MDB container is disconnected from the JMS provider, it is the responsibility of the JMS provider to store any messages the subscriber misses. When the Durable MDB container reconnects to the JMS provider, the JMS provider sends it all the unexpired messages that accumulated while it was down. This behavior is commonly referred to as store-and-forward messaging. Durable MDBs are tolerant of disconnections, whether they are intentional or the result of a partial failure.

If <subscription-durability> is NonDurable, any messages the bean would have received while it was disconnected will be lost. Developers use NonDurable subscriptions when it is not critical that all messages be processed. Using a NonDurable subscription improves the performance of the JMS provider but significantly reduces the reliability of the MDBs.

When <destination-type> is javax.jms.Queue, as is the case in the ReservationProcessor EJB, durability is not a factor because of the nature of p2p or queue-based messaging systems. With a queue, messages may be consumed only once and remain in the queue until they are distributed to one of the queue's listeners.

The rest of the elements in the deployment descriptor should already be familiar. The <ejb-ref> element provides JNDI ENC bindings for a remote EJB home object while the <ejb-local-ref> elements provide JNDI ENC bindings for local EJB home objects. Note that the <resource-ref> element that defined the JMS QueueConnectionFactory used by the ReservationProcessor EJB to send ticket messages is not accompanied by a <resource-env-ref> element. The queue to which the tickets are sent is obtained from the JMSReplyTo header of the MapMessage itself, and not from the JNDI ENC.

Pages: 1, 2, 3, 4, 5, 6, 7

Next Pagearrow