SeamFramework.orgCommunity Documentation

Chapter 70. Messaging API

70.1. QueueBuilder and TopicBuilder
70.2. Message Manager
70.3. Durable Messaging Capabilities
70.4. MessageListeners versus Message Driven Beans

The Seam JMS Messaging API is a higher level abstraction of the JMS API to provide a number of convenient methods for creating consumers, producers, etc.

The QueueBuilder and TopicBuilder interfaces are meant to ease the integration of JMS while still sticking close to the base APIs. Within the single class you can work with both listeners and send messages. References to these classes can be injected.

Some example usages.

@RequestScoped

@Named
public class FormBean {
  private String formData;
  @Inject QueueBuilder queueBuilder;
  @Inject TopicBuilder topicBuilder;
  @Resource(mappedName="jms/SomeQueue") Queue someQueue;
  @Resource(mappedName="jms/SomeTopic") Topic someTopic;
  @Resource(mappedName="jms/ConnectionFactory") ConnectionFactory cf;
  public void sendFormDataToQueue() {
      queueBuilder.connectionFactory(cf).destination(someQueue).sendString(formData);
  }
  public void sendFormDataToTopic() {
      topicBuilder.connectionFactory(cf).destination(someTopic).sendString(formData);
  }
}

It is strongly recommended that you proxy the injection of the builders to avoid repeating your configuration. If you are often times connecting to the same queues/topics, you can provide your own producer method.



public class OrderTopicProducer {
  @Inject BuilderFactory factory;
  @Resource(mappedName="jms/OrderTopic") Topic orderTopic;
  @Resource(mappedName="jms/ConnectionFactory") ConnectionFactory cf;
  @Produces @OrderTopic
  public TopicBuilder sendFormDataToQueue() {
      return factory.newTopicBuilder().connectionFactory(cf).destination(orderTopic);
  }
}

The MessageManager interface (org.jboss.seam.jms.MessageManager) is the main consolidated API for Seam JMS. It provides almost all of the background functionality for Seam JMS's features (Observer Interfaces, Routing API). The default implementation works against javax.naming.Context assuming running within the same local application server.

public interface MessageManager {

    public ObjectMessage createObjectMessage(Object object);
    public TextMessage createTextMessage(String string);
    public MapMessage createMapMessage(Map<Object,Object> map);
    public BytesMessage createBytesMessage(byte[] bytes);
    public void sendMessage(Message message, String... destinations);
    public void sendObjectToDestinations(Object object, String... destinations);
    public void sendTextToDestinations(String string, String... destinations);
    public void sendMapToDestinations(Map map, String... destinations);
    public void sendBytesToDestinations(byte[] bytes, String... destinations);
    public void sendMessage(Message message, Destination... destinations);
    public void sendObjectToDestinations(Object object, Destination... destinations);
    public void sendTextToDestinations(String string, Destination... destinations);
    public void sendMapToDestinations(Map map, Destination... destinations);
    public void sendBytesToDestinations(byte[] bytes, Destination... destinations);
    public Session getSession();
    public MessageProducer createMessageProducer(String destination);
    public TopicPublisher createTopicPublisher(String destination);
    public QueueSender createQueueSender(String destination);
    public MessageConsumer createMessageConsumer(String destination, MessageListener... listeners);
    public MessageConsumer createMessageConsumer(Destination destination, MessageListener... listeners);
    public TopicSubscriber createTopicSubscriber(String destination, MessageListener... listeners);
    public QueueReceiver createQueueReceiver(String destination, MessageListener... listeners);
}

The interface above defines a full set of capabilities for creating and sending messages. In addition, we expose methods for creating producers (and A destination specific publisher and sender) as well as consumers (and A destination specific subscriber and receiver). In addition, if injected within a session scoped object, or similar, you can define a durable subscriber and unsubscriber for that subscriber. Below is an example.

The durable subscriber pattern works very well for session based message management. If you want to define a durable subscriber per user session, this is the easiest way to do it.

@SessionScoped

                public class UserSession {
                    @Inject MessageManager messageManager;
                    @Inject MySessionJMSListener listener;
                    private String clientId;
                    @PostConstruct
                    public void registerListener() {
                        clientId = UUID.randomUUID().toString();
                        messageManager.createDurableSubscriber("jms/UserTopic",clientId,listener);
                    }
                    @PreDestroy
                    public void shutdownListener() {
                        messageManager.unsubscribe(clientId);
                    }
                }
                

Seam JMS provides a Messaging API around the JMS Durable Topic Subscriber concept. In order to use it within your code, you need to inject a DurableMessageManager.



            @Inject @Durable DurableMessageManager durableMsgManager;
            

This implementation of MessageManager provides additional methods to first login to the connection with a ClientID, additional methods to create subscribers and an unsubscribe that can be called to unsubscribe a listener.



            public void login(String clientId);
            public TopicSubscriber createDurableSubscriber(String topic, String id, MessageListener... listeners);
            public TopicSubscriber createDurableSubscriber(Topic topic, String id, MessageListener... listeners);
            public void unsubscribe(String id);
            

One of the difficult choices we had to make was support for Message-Driven Beans. MDBs are a little complicated in CDI as they are not managed within the CDI life cycle. This makes integration with them a bit cumbersome. We wouldn't be able to work with a JMS Session in these cases, as an example. As a result, Seam JMS only supports defining instances of javax.jms.MessageListener. To support this, we have created a partial implementation - org.jboss.seam.jms.AbstractMessageListener. This special MessageListener is designed for bridging the external context of a JMS Message into the application you are working with. We do this by tweaking classloaders.

The best way to work with MessageListeners is to simply instantiate your own based on our base implementation.



                //where cl is the class loader, and beanManager is the BeanManager
                MessageListener ml = new SessionListener(beanManager,cl,this);
                messageManager.createTopicSubscriber("/jms/myTopic", ml);
            

Or you may define your own subclass that makes specific invocations to the parent. Here is an example of that:

@SessionScoped

                public class SessionListener extends AbstractMessageListener {
                    private MySessionObject mso;
                    public SessionListener(BeanManager beanManager, ClassLoader classLoader,
                        MySessionObject mso){
                        super(beanManager,classLoader);
                        this.mso = mso;
                    }
                    @Override
                    protected void handlMessage(Message msg) throws JMSException {
                        //your business logic goes here
                    }
                }