21.2. Messaging in Seam
It is easy to send and receive JMS messages to and from Seam components.
21.2.1. Configuration
To configure Seam infrastructure to send JMS messages, you must first tell Seam about the topics and queues you want to send messages to, and where to find the
QueueConnectionFactory and TopicConnectionFactory, depending on your requirements.
By default, Seam uses
UIL2ConnectionFactory, the default connection factory with JBossMQ. If you use another JMS provider, you must set one or both of queueConnection.queueConnectionFactoryJndiName and topicConnection.topicConnectionFactoryJndiName, in either seam.properties, web.xml, or components.xml.
To install Seam-managed
TopicPublishers and QueueSenders, you must also list topics and queues in components.xml:
<jms:managed-topic-publisher name="stockTickerPublisher" auto-create="true" topic-jndi-name="topic/stockTickerTopic"/> <jms:managed-queue-sender name="paymentQueueSender" auto-create="true" queue-jndi-name="queue/paymentQueue"/>
21.2.2. Sending messages
Once configuration is complete, you can inject a JMS
TopicPublisher and TopicSession into any component:
@Name("stockPriceChangeNotifier") public class StockPriceChangeNotifier { @In private TopicPublisher stockTickerPublisher; @In private TopicSession topicSession; public void publish(StockPrice price) { try { stockTickerPublisher.publish(topicSession .createObjectMessage(price)); } catch (Exception ex) { throw new RuntimeException(ex); } } }
Or, to work with a queue:
@Name("paymentDispatcher") public class PaymentDispatcher { @In private QueueSender paymentQueueSender; @In private QueueSession queueSession; public void publish(Payment payment) { try { paymentQueueSender.send(queueSession.createObjectMessage(payment)); } catch (Exception ex) { throw new RuntimeException(ex); } } }
21.2.3. Receiving messages using a message-driven bean
You can process messages with any EJB3 message-driven bean. Message-driven beans can sometimes be Seam components, in which case, you can inject other event- and application-scoped Seam components. The following is an example of the payment receiver, which delegates to the payment processor.
Note
You may need to set the
create attribute on the @In annotation to true so that Seam can create an instance of the component to be injected. (This is necessary only if the component does not support auto-creation — that is, it is not annotated with @Autocreate.)
First, create a message-driven bean to receive the message:
@MessageDriven(activationConfig = {@ActivationConfigProperty(propertyName = "destinationType", propertyValue = "javax.jms.Queue"), @ActivationConfigProperty(propertyName = "destination", propertyValue = "queue/paymentQueue") }) @Name("paymentReceiver") public class PaymentReceiver implements MessageListener { @Logger private Log log; @In(create = true) private PaymentProcessor paymentProcessor; @Override public void onMessage(Message message) { try { paymentProcessor.processPayment((Payment) ((ObjectMessage) message).getObject()); } catch (JMSException ex) { log.error("Message payload did not contain a Payment object", ex); } } }
Next, implement the Seam component to which the receiver will delegate payment processing:
@Name("paymentProcessor") public class PaymentProcessor { @In private EntityManager entityManager; public void processPayment(Payment payment) { // perhaps do something more fancy entityManager.persist(payment); } }
If you want to perform transaction operations in your message-driven bean, ensure that you are working with an XA datasource, or you will not be able to roll back database changes in the event that a database transaction commits, but a subsequent message operation fails.
21.2.4. Receiving messages in the client
Seam Remoting lets you subscribe to a JMS topic from client-side JavaScript. You can find more information in Chapter 24, Remoting.