SeamFramework.orgCommunity Documentation

JBoss Seam 3 JMS Module

Reference Guide


1. Introduction
1.1. Mission statement
1.2. Seam 3 JMS Module Overview
2. Installation
3. Resource Injection
3.1. JMS Resource Injection
3.1.1. Destination Based Injection
3.1.2. Resource Configuration
4. Bridging the Gap
4.1. Event Routing
4.1.1. Routes
4.1.2. @EventRouting
4.2. Routing CDI Events to JMS
4.2.1. Usage
4.3. CDI Events from JMS Messages
4.3.1. Usage
5. Messaging API
5.1. Message Manager
5.2. Durable Messaging Capabilities
5.3. MessageListeners versus Message Driven Beans
6. Annotation Routing APIs
6.1. Observer Method Interfaces

Seam extends the CDI programming model into the messaging world by allowing you to inject JMS resources into your beans. Further, Seam bridges the CDI event bus over JMS; this gives you the benefits of CDI-style type-safety for inter-application communication.

Seam JMS can be used by including a few libraries in your application's library folder:

Seam JMS has a combined build that can reduce your dependencies to:

Seam JMS has an optional dependency on Seam Servlet, for application loading capabilities:

If you are using Maven as your build tool use the following dependency:


<dependency>
   <groupId>org.jboss.seam.jms</groupId>
   <artifactId>seam-jms</artifactId>
   <version>${seam.jms.version}</version>
</dependency>

Alternatively, you can use the API at compile time and only include the implementation at runtime. This protects you from inadvertantly depending on an implementation class.


<dependency>
   <groupId>org.jboss.seam.jms</groupId>
   <artifactId>seam-jms-api</artifactId>
   <version>${seam.jms.version}</version>
   <scope>compile</scope>
</dependency>
<dependency>
   <groupId>org.jboss.seam.jms</groupId>
   <artifactId>seam-jms-impl</artifactId>
   <version>${seam.jms.version}</version>
   <scope>runtime</scope>
</dependency>

Tip

Define or replace the property ${seam.jms.version} with a valid version of Seam JMS.

The runtime of Seam JMS is defined in two sections. The first section is related to creating observers, which happens within the Seam JMS CDI Extension. Observers need to be defined prior to starting up the container, and cannot be created once the application is running. This part happens automatically. The second section is related to creating listeners. This is managed in the component org.jboss.seam.jms.bridge.RouteBuilder.

Tip

In order to start any listeners, you may need to inject an instance of the RouteBuilder in to your class.

If you are running within a Servlet Container, and include the Seam Servlet module, RouteBuilder will automatically start up.

In this chapter we'll look at how to inject some of the common JMS resource types.

This chapter is designed to detail how to configure the CDI to JMS event bridge. Routing has two sides, sending of events to JMS destinations and translating received messages from JMS destinations back into CDI events. The sections of this chapter will describe how to achieve both.

Simply sending or receiving a message over JMS involves a few players: Connection, Session, Destination, and the message itself. Surely you can inject all required resources and perform the routing yourself but that takes away from the whole reason you're using a tool in the first place! Let's let a little configuration do the work for us.

Routing CDI events to and from JMS can be configured by defining a Route. As you would normally create an observer method for an event you can define a route to control which events get forwarded to what destination. Or conversely, what message types sent to which destinations generate CDI events.

public interface Route
   {
      public <D extends Destination> Route connectTo(Class<D> d, D destination);
      public Route addQualifiers(Annotation... qualifiers);
   }
                

Routes allows for simple mapping of event types, complete with qualifiers, to a set of destinations. They can be configured by adding qualifiers and providing destinations they should interact with and are created from a RouteManager. Here's a simple route that forwards CDI events on to a queue:

@EventRouting public Route registerMyRoute(RouteManager routeManager)
{
   Queue myQueue = lookupQueue("/jms/MyQueue");
   return routeManager.createRoute(RouteType.EGRESS, MyEvent.class).connectTo(Queue.class, myQueue);
}
                

A RouteManager is a factory object for creating new Routes. An instance of it is injected into every @EventRouting method. Classes with methods that are decorated with EventRouting must meet a few criteria items:

These requirements exist because of when the generation of Routes must happen. There are no CDI beans active within the context. A class identified for routing will automatically be veto'd from the context.

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 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);
                    }
                }
                

This chapter is meant to describe the behavior of mapping interfaces, where event mapping to data flowing through JMS Queues and Topics are handled via events. These APIs are an alternate way to define routes as mentioned earlier in the document.

Observer Method Interfaces are simple Plain Old Java Interfaces (POJIs) that define either a route. These interfaces exist within your code and are read at deployment time. This is a sample interface:

           public interface MappingInterface {
               @Routing(RouteType.INGRESS)
               public void routeStringsFromTopic(@Observes String s, @JmsDestination(jndiName="jms/MyTopic") Topic t);
               
               @Routing(RouteType.EGRESS)
               public void routeLongsToQueue(@Observes Long l, @JmsDestination(jndiName="jms/MyQueue") Queue q);

               @Routing(RouteType.BOTH)
               public void bidirectionRouteDoublesToQueue(@Observes Double d, @JmsDestination(jndiName="jms/DblQueue") Queue q);
           }
       

This interface defines three routes. The first one being an ingress route - messages coming in to the topic jms/MyTopic will be fired as events with the type String. The second being an egress route - events fired of type Long will be turned into ObjectMessages and using a MessageProducer sent to the queue jms/MyQueue. The last is a bidirectional route, it defines messages that get fired

The object being observed can have qualifiers. These qualifiers will be carried over in the fired event and follow the CDI rules for observer method selection. In all cases, the return type of the method is ignored.

The destinations can have any qualifier. In addition, there is basic support for @Resource on the method level to define the destination. This in general not 100% portable from the application developer perspective, we recommend heavy testing of the behavior on your application server.

In order to work with these routes, you raise events in CDI. In order to fire an event, first inject the Event object into your code with necessary annotations, for any egress route. For ingress routes, you need to define an observer method. Taking the third route as an example, here is how you would raise events to it

            @Inject @Routing(RouteType.EGRESS) Event<Double> doubleEvent
            ...
            doubleEvent.fire(d);
       

and this is the appropriate observer method to handle the incoming mesages.

           public class MyEventObserverBean {
            public void sendMessage(@Observes @Routing(RouteType.INGRESS) Double d) {
                System.out.println(d);
            }
           }