1 / 81

Message-Driven Beans

Message-Driven Beans. The message-driven bean was introduced in EJB 2.0 to support the processing of asynchronous messages from a JMS provider. EJB 2.1 expanded the definition of the message-driven bean so that it can support any messaging system, not just JMS through the JCA.

maine
Download Presentation

Message-Driven Beans

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. Message-Driven Beans • The message-driven bean was introduced in EJB 2.0 to support the processing of asynchronous messages from a JMS provider. • EJB 2.1 expanded the definition of the message-driven bean so that it can support any messaging system, not just JMS through the JCA. • EJB 3.0 does not really expand on the feature set of earlier specification versions, but it does simplify configuration with the use of annotations.

  2. JMS and Message-Driven Beans • All EJB 3.0 vendors must support a JMS provider. Most vendors have a JMS provider built in and must support other JMS providers through the JCA. • However, regardless of whether your vendor has its own JMS provider or allows you to integrate some other provider, a JMS provider is an absolute necessity for supporting message-driven beans. • By forcing the adoption of JMS, Sun has guaranteed that EJB developers can expect to have a working JMS provider on which messages can be both sent and received.

  3. JMS as a Resource • JMS is a vendor-neutral API that can be used to access enterprise messaging systems. • Enterprise messaging systems (a.k.a. message-oriented middleware) facilitate the exchange of messages between software applications over a network. • Applications that use JMS are called JMS clients , and the messaging system that handles routing and delivery of messages is called the JMS provider. • In EJB, enterprise beans of all types can use JMS to send messages.

  4. JMS as a Resource • The messages are consumed by other Java applications or by message-driven beans. • JMS facilitates sending messages from enterprise beans using a messaging service , sometimes called a message broker or router. • Message brokers have been around for a couple of decades - the oldest and most established is IBM's MQSeries - but JMS is fairly new, and it is specifically designed to deliver a variety of message types from one Java application to another.

  5. Reimplementing the TravelAgent EJB with JMS • We can modify the TravelAgent EJB developed earlier so that it uses JMS to alert some other Java application that a reservation has been made. @Resource(mappedName="ConnectionFactoryNameGoesHere") private ConnectionFactory connectionFactory; @Resource(mappedName="TicketTopic") private Topic topic; @Remove public TicketDO bookPassage(CreditCardDO card, double price) throws IncompleteConversationalState { if (customer == null || cruise == null || cabin == null) { throw new IncompleteConversationalState( ); } try { Reservation reservation = new Reservation( customer, cruise, cabin, price, new Date( ));

  6. Reimplementing the TravelAgent EJB with JMS entityManager.persist(reservation); process.byCredit(customer, card, price); TicketDO ticket = new TicketDO(customer, cruise, cabin, price); Connection connect = factory.createConnection( ); Session session = connect.createSession(true,0); MessageProducer producer = session.createProducer(topic); TextMessage textMsg = session.createTextMessage( ); textMsg.setText(ticketDescription); producer.send(textMsg); connect.close( ); return ticket; } catch(Exception e) { throw new EJBException(e); } }

  7. ConnectionFactory and Topic • In order to send a JMS message, we need a connection to the JMS provider and a destination address for the message. • A JMS connection factory makes the connection to the provider possible; the destination address is identified by a Topic object. • Both the connection factory and the Topic object are obtained by using @javax.annotation.Resource to inject these objects directly into the fields of the TravelAgent EJB:

  8. ConnectionFactory and Topic @Resource(mappedName="ConnectionFactoryNameGoesHere") private ConnectionFactory connectionFactory; @Resource(mappedName="TicketTopic") private Topic topic; • The ConnectionFactory is similar to a DataSource in JDBC. • Just as the DataSource provides a JDBC connection to a database, the ConnectionFactory provides a JMS connection to a message router.

  9. Connection and Session • The ConnectionFactory is used to create a Connection, which is an actual connection to the JMS provider: Connection connect = connectionFactory.createConnection( ); Session session = connect.createSession(true,0); • Once you have a Connection , you can use it to create a Session. A Session allows you to group the actions of sending and receiving messages. • In this case, you need only a single Session. Using multiple Sessions is helpful if you wish to produce and consume messages in different threads.

  10. Connection and Session • Session objects use a single-threaded model, which prohibits concurrent access to a single Session from multiple threads. • The createSession( ) method has two parameters: createSession(boolean transacted, int acknowledgeMode) • According to the EJB specifications, these arguments are ignored at runtime because the EJB container manages the transaction and acknowledgment mode of any JMS resource obtained from the JNDI ENC. • Unfortunately, not all vendors adhere to this part of the specification. Some vendors ignore these parameters; others do not.

  11. MessageProducer • The Session is used to create a MessageProducer, which sends messages from the TravelAgent EJB to the destination specified by the Topic object. Any JMS clients that subscribe to that topic will receive a copy of the message: MessageProducer producer = session.createProducer(topic); TextMessage textMsg = session.createTextMessage( ); textMsg.setText(ticketDescription); producer.send(textMsg);

  12. Message types • In JMS, a message is a Java object with two parts: a header and a message body. • The header is composed of delivery information and metadata, and the message body carries the application data, which can take several forms: text, serializable objects, byte streams, etc. • The JMS API defines several message types (TextMessage, MapMessage, ObjectMessage , and others) and provides methods for delivering messages to and receiving messages from other applications. • For example, we can change the TravelAgent EJB so that it sends a MapMessage rather than a TextMessage:

  13. Message types TicketDO ticket = new TicketDO(customer,cruise,cabin,price); ... MessageProducer producer = session.createProducer(topic); MapMessage mapMsg = session.createMapMessage( ); mapMsg.setInt("CustomerID", ticket.customerID.intValue( )); mapMsg.setInt("CruiseID", ticket.cruiseID.intValue( )); mapMsg.setInt("CabinID", ticket.cabinID.intValue( )); mapMsg.setDouble("Price", ticket.price); producer.send(mapMsg); • In addition to TextMessage, MapMessage, and ObjectMessage, JMS provides two other message types: StreamMessage and BytesMessage . • StreamMessage can take the contents of an I/O stream as its payload. • BytesMessage can take any array of bytes, which it treats as opaque data.

  14. JMS Application Client • To get a better idea of how JMS is used, we can create a Java application whose sole purpose is receiving and processing reservation messages. This application is a simple JMS client that prints a description of each ticket as it receives the messages. We'll assume that the TravelAgent EJB is using TextMessage to send a description of the ticket to the JMS clients. Here's how the JMS application client might look: import javax.jms.Message; import javax.jms.TextMessage; import javax.jms.ConnectionFactory; import javax.jms.Connection; import javax.jms.Session; import javax.jms.Topic; import javax.jms.JMSException; import javax.naming.InitialContext;

  15. JMS Application Client public class JmsClient_1 implements javax.jms.MessageListener { public static void main(String [] args) throws Exception { if(args.length != 2) throw new Exception("Wrong number of arguments"); new JmsClient_1(args[0], args[1]); while(true){Thread.sleep(10000);} } public JmsClient_1(String factoryName, String topicName) throws Exception { InitialContext jndiContext = getInitialContext( ); ConnectionFactory factory = (ConnectionFactory) jndiContext.lookup("ConnectionFactoryNameGoesHere"); Topic topic = (Topic)jndiContext.lookup("TopicNameGoesHere"); Connection connect = factory.createConnection( ); Session session = connect.createSession(false,Session.AUTO_ACKNOWLEDGE);

  16. JMS Application Client MessageConsumer consumer = session.createConsumer(topic); consumer.setMessageListener(this); connect.start( ); } public void onMessage(Message message) { try { TextMessage textMsg = (TextMessage)message; String text = textMsg.getText( ); System.out.println("\n RESERVATION RECEIVED\n"+text); } catch(JMSException jmsE) { jmsE.printStackTrace( ); } } public static InitialContext getInitialContext( ) { // create vendor-specific JNDI context here } }

  17. JMS Application Client • The MessageConsumer can receive messages directly or delegate message processing to a javax.jms.MessageListener . • We chose to have JmsClient_1 implement the MessageListener interface so that it can process the messages itself. • MessageListener objects implement a single method, onMessage( ), which is invoked every time a new message is sent to the subscriber's topic. • In this case, every time the TravelAgent EJB sends a reservation message to the topic, the JMS client's onMessage( ) method is invoked to receive and process a copy of the message:

  18. JMS Application Client public void onMessage(Message message) { try { TextMessage textMsg = (TextMessage)message; String text = textMsg.getText( ); System.out.println("\n RESERVATION RECEIVED:\n"+text); } catch(JMSException jmsE) { jmsE.printStackTrace( ); } }

  19. JMS Is Asynchronous • One of the principal advantages of JMS messaging is that it's asynchronous. In other words, a JMS client can send a message without having to wait for a reply. • Contrast this flexibility with the synchronous messaging of Java RMI or JAX-RPC. Each time a client invokes a bean's method, it blocks the current thread until the method completes execution. • This lock-step processing makes the client dependent on the availability of the EJB server, resulting in a tight coupling between the client and the enterprise bean. • JMS clients send messages asynchronously to a destination (topic or queue) from which other JMS clients can also receive messages.

  20. JMS Is Asynchronous • A TravelAgent session bean can use JMS to notify other applications that a reservation has been processed, as shown in below. Using JMS with the TravelAgent EJB

  21. JMS Is Asynchronous • In this case, the applications receiving JMS messages from the TravelAgent EJB may be message-driven beans, other Java applications in the enterprise, or applications in other organizations that benefit from being notified that a reservation has been processed. • Because messaging is inherently decoupled and asynchronous, the transactions and security contexts of the sender are not propagated to the receiver.

  22. JMS Messaging Models • JMS provides two types of messaging models: publish-and-subscribe and point-to-point . The JMS specification refers to these as messaging domains. In JMS terminology, publish-and-subscribe and point-to-point are frequently shortened to pub/sub and p2p (or PTP), respectively. • In the simplest sense, publish-and-subscribe is intended for a one-to-many broadcast of messages, and point-to-point is intended for one-to-one delivery of messages (see Figure below).

  23. JMS Messaging Models JMS messaging domains

  24. Publish-and-subscribe • In publish-and-subscribe messaging, one producer can send a message to many consumers through a virtual channel called a topic. • Consumers can choose to subscribe to a topic. Any messages addressed to a topic are delivered to all the topic's consumers. • The pub/sub messaging model is largely a push-based model , in which messages are automatically broadcast to consumers without the consumers having to request or poll the topic for new messages.

  25. Point-to-point • The point-to-point messaging model allows JMS clients to send and receive messages both synchronously and asynchronously via virtual channels known as queues. • The p2p messaging model has traditionally been a pull- or polling-based model, in which messages are requested from the queue instead of being pushed to the client automatically. • A queue may have multiple receivers, but only one receiver may receive each message.

  26. Session Beans Should Not Receive Messages • JmsClient_1 was designed to consume messages produced by the TravelAgent EJB. Can another session bean receive those messages also? The answer is yes, but it's a really bad idea. • For example, when the business method on the Hypothetical EJB is called, it sets up a JMS session and then attempts to read a message from a queue: @Stateless public class HypotheticalBean implements HypotheticalRemote { @Resource(mappedName="ConnectionFactory"); private ConnectionFactory factory; @Resource(mappedName="MyQueue") private Queue queue;

  27. Session Beans Should Not Receive Messages public String businessMethod( ) { try{ Connection connect = factory.createConnection( ); Session session = connect.createSession(true,0); MessageConsumer receiver = session.createConsumer(queue); TextMessage textMsg = (TextMessage)receiver.receive( ); connect.close( ); return textMsg.getText( ); } catch(Exception e) { throws new EJBException(e); } } ... }

  28. Session Beans Should Not Receive Messages • The message consumer is used to proactively fetch a message from the queue. • While this operation has been programmed correctly, it is dangerous because a call to the MessageConsumer.receive( ) method blocks the thread until a message becomes available. • If a message is never delivered, the thread is blocked indefinitely! • If no one ever sends a message to the queue, the MessageConsumer just sits there waiting, forever.

  29. JMS-Based Message-Driven Beans • Message-driven beans (MDBs) are stateless, server-side, transaction-aware components for processing asynchronous messages delivered via JMS. • While a message-driven bean is responsible for processing messages, its container manages the component's environment, including transactions, security, resources, concurrency, and message acknowledgment. • It's particularly important to note that the container manages concurrency.

  30. JMS-Based Message-Driven Beans • The thread safety provided by the container gives MDBs a significant advantage over traditional JMS clients, which must be custom built to manage resources, transactions, and security in a multithreaded environment. • An MDB can process hundreds of JMS messages concurrently because numerous instances of the MDB can execute concurrently in the container. • While a message-driven bean has a bean class, it does not have a remote or local business interface. These interfaces are absent because the message-driven bean responds only to asynchronous messages.

  31. The ReservationProcessor EJB • The ReservationProcessor EJB is a message-driven bean that receives JMS messages notifying it of new reservations. • The ReservationProcessor EJB is an automated version of the TravelAgent EJB that processes reservations sent via JMS. • These messages might come from another application in the enterprise or from an application in some other organization - perhaps another travel agent. • This process is illustrated in the figure below.

  32. The ReservationProcessor EJB The ReservationProcessor EJB processing reservations

  33. The ReservationProcessorBean Class • Here is a partial definition of the ReservationProcessorBean class. Some methods are left empty; they will be filled in later. • Notice that the onMessage( ) method contains the business logic; it is similar to the business logic developed in the bookPassage( ) method of the TravelAgent EJB described in previous Lecture 11. Here's the code: package com.titan.reservationprocessor; import javax.jms.*; import com.titan.domain.*; import com.titan.processpayment.*; import com.titan.travelagent.*; import java.util.Date; import javax.ejb.*; import javax.annotation.*; import javax.persistence.*;

  34. The ReservationProcessorBean Class @MessageDriven(activationConfig={ @ActivationConfigProperty( propertyName="destinationType", propertyValue="javax.jms.Queue"), @ActivationConfigProperty( propertyName="messageSelector", propertyValue="MessageFormat = 'Version 3.4'"), @ActivationConfigProperty( propertyName="acknowledgeMode", propertyValue="Auto-acknowledge")}) public class ReservationProcessorBean implements javax.jms.MessageListener { @PersistenceContext(unitName="titanDB") private EntityManager em; @EJB private ProcessPaymentLocal process; @Resource(mappedName="ConnectionFactory") private ConnectionFactory connectionFactory;

  35. The ReservationProcessorBean Class public void onMessage(Message message) { try { MapMessage reservationMsg = (MapMessage)message; int customerPk = reservationMsg.getInt("CustomerID"); int cruisePk = reservationMsg.getInt("CruiseID"); int cabinPk = reservationMsg.getInt("CabinID"); double price = reservationMsg.getDouble("Price"); // get the credit card Date expirationDate = new Date(reservationMsg.getLong("CreditCardExpDate")); String cardNumber = reservationMsg.getString("CreditCardNum"); String cardType = reservationMsg.getString("CreditCardType"); CreditCardDO card = new CreditCardDO(cardNumber, expirationDate, cardType); Customer customer = em.find(Customer.class,customerPk);

  36. The ReservationProcessorBean Class Cruise cruise = em.find(Cruise.class, cruisePk); Cabin cabin = em.find(Cabin.class, cabinPk); Reservation reservation = new Reservation( customer, cruise, cabin, price, new Date( )); em.persist(reservation); process.byCredit(customer, card, price); TicketDO ticket = new TicketDO(customer,cruise,cabin,price); deliverTicket(reservationMsg, ticket); } catch(Exception e) { throw new EJBException(e); } } public void deliverTicket(MapMessage reservationMsg, TicketDO ticket) { // send it to the proper destination } }

  37. MessageDrivenContext • Message-driven beans also have a context object that is similar in functionality to that of the javax.ejb.SessionContext described in Lecture 11. This object may be injected using the @javax.annotation.Resource annotation: @Resource MessageDrivenContext context; • The MessageDrivenContext simply extends the EJBContext ; it does not add any new methods. • Only the transactional methods that MessageDrivenContext inherits from EJBContext are available to message-driven beans.

  38. MessageDrivenContext • The home methods - getEJBHome( ) and getEJBLocalHome( ) - throw a RuntimeException if invoked, because MDBs do not have home interfaces or EJB home objects. • The security methods - getCallerPrincipal( ) and isCallerInRole( ) - also throw a RuntimeException if invoked on a MessageDrivenContext. • When an MDB services a JMS message, there is no "caller," so there is no security context to be obtained from the caller. • Remember that JMS is asynchronous and doesn't propagate the sender's security context to the receiver - that wouldn't make sense, since senders and receivers tend to operate in different environments.

  39. MessageListener interface • MDBs usually implement the javax.jms.MessageListener interface, which defines the onMessage( ) method. This method processes the JMS messages received by a bean. package javax.jms; public interface MessageListener { public void onMessage(Message message); } • Although MDBs usually implement this interface, we will see later in this chapter that MDBs can integrate with other messaging systems that define a different interface contract.

  40. Taskflow and integration for B2B: onMessage( ) • The onMessage( ) method is where all the business logic goes. • As messages arrive, the container passes them to the MDB via the onMessage( ) method. • When the method returns, the MDB is ready to process a new message. • In the ReservationProcessor EJB, the onMessage() method extracts information about a reservation from a MapMessage and uses that information to create a reservation in the system:

  41. Taskflow and integration for B2B: onMessage( ) public void onMessage(Message message) { try { MapMessage reservationMsg = (MapMessage)message; int customerPk = reservationMsg.getInt("CustomerID"); int cruisePk = reservationMsg.getInt("CruiseID"); int cabinPk = reservationMsg.getInt("CabinID"); double price = reservationMsg.getDouble("Price"); // get the credit card Date expirationDate = new Date(reservationMsg.getLong("CreditCardExpDate")); String cardNumber = reservationMsg.getString("CreditCardNum"); String cardType = reservationMsg.setString("CreditCardType"); CreditCardDO card = new CreditCardDO(cardNumber, expirationDate, cardType);

  42. Taskflow and integration for B2B: onMessage( ) • JMS is frequently used as an integration point for business-to-business (B2B) applications, so it's easy to imagine the reservation message coming from one of Titan's business partners (perhaps a third-party processor or branch travel agency).

  43. Sending messages from a message-driven bean • An 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 JMSException{ Queue queue = (Queue)reservationMsg.getJMSReplyTo( ); Connection connect = connectionFactory.createConnection( ); Session session = connect.createSession(true,0); MessageProducer sender = session.createProducer(queue); ObjectMessage message = session.createObjectMessage( ); message.setObject(ticket); sender.send(message); connect.close( ); }

  44. @MessageDriven • MDBs are identified using the @javax.ejb.MessageDriven annotation or, alternatively, are described in an EJB deployment descriptor. • An MDB can be deployed alone, but it's more often deployed with the other enterprise beans that it references. • For example, the ReservationProcessor EJB uses the ProcessPayment EJB as well as the Titan EntityManager, so it is feasible to deploy all of these beans within the same Java EE deployment.

  45. @ActivationConfigProperty • We'll see later that because MDBs can receive messages from arbitrary messaging providers, the configuration must be very flexible to be able to describe the proprietary properties that different providers will have. • JCA-based MDBs don't necessarily use JMS as the message service, so this requirement is very important. • To facilitate this, the @MessageDriven.activationConfig( ) attribute takes an array of @ActivationConfigProperty annotations. • These annotations are simply a set of name/value pairs that describe the configuration of your MDB.

  46. @ActivationConfigProperty @MessageDriven(activationConfig={ @ActivationConfigProperty( propertyName="destinationType", propertyValue="javax.jms.Queue"), @ActivationConfigProperty( propertyName="messageSelector", propertyValue="MessageFormat = 'Version 3.4'"), @ActivationConfigProperty( propertyName="acknowledgeMode", propertyValue="Auto-acknowledge")}) public class ReservationProcessorBean implements javax.jms.MessageListener { ... }

  47. Message selector • An MDB can declare a 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. These conditional expressions use Boolean logic to declare which messages should be delivered. • A message selector is declared using the standard property name, messageSelector, in an activation configuration element: • Message selectors are also based on message headers, which are outside the scope of this chapter.

  48. Message selector @ActivationConfigProperty( propertyName="messageSelector", propertyValue="MessageFormat = 'Version 3.4'"), • 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 that Titan uses to identify messages of type MapMessage that contain the name values CustomerID, CruiseID, CabinID, CreditCard, and Price . • In other words, adding a MessageFormat to each reservation message allows us to write MDBs that are designed to process different kinds of reservation messages.

  49. Message selector • 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. • Here's how a JMS producer would go about setting a MessageFormat property on a Message: Message message = session.createMapMessage( ); message.setStringProperty("MessageFormat","Version 3.4"); // set the reservation named values sender.send(message);

  50. Message selector • 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.

More Related