10. Spring messaging – Spring in Action, Second Edition

Chapter 10. Spring messaging

This chapter covers

  • Sending and receiving asynchronous messages

  • Creating message-driven POJOs

  • Asynchronous remoting with Lingo

It’s 4:55 PM on Friday. You’re only minutes away from starting a much-anticipated vacation. You have just enough time to drive to the airport and catch your flight. But before you pack up and head out, you need to be sure that your boss and colleagues know the status of the work you’ve been doing so that they can pick up where you left off on Monday. Unfortunately, some of your colleagues have already skipped out for an early weekend departure... and your boss is tied up in a meeting. What do you do?

You could call your boss’s cell phone... but it’s not necessary to interrupt his meeting for a mere status report. Maybe you could stick around and wait until he returns from the meeting. But it’s anyone’s guess how long the meeting will last and you have a plane to catch. Or perhaps you could leave a sticky note on his monitor... right next to 100 other sticky notes that it will blend in with.

The most practical way to communicate your status and still catch your plane is to send a quick email to your boss and your colleagues, detailing your progress and promising to send a postcard. You don’t know where they are or when they’ll actually read the email, but you do know that they’ll eventually return to their desk and read it. Meanwhile, you’re on your way to the airport.

Sometimes it’s necessary to talk to someone directly. If you injure yourself and need an ambulance, you’re probably going to pick up the phone—emailing the hospital just won’t do. Often, however, sending a message is sufficient and offers some advantages over direct communication—such as letting you get on with your vacation.

In the past few chapters, you’ve seen how to use RMI, Hessian, Burlap, HTTP invoker, and web services to enable communication between applications. All of these communication mechanisms employ synchronous communication in which a client application directly contacts a remote service and waits for the remote procedure to complete before continuing.

Synchronous communication has its place, but it is not the only style of interapplication communication available to developers. Asynchronous messaging is a way of indirectly sending messages from one application to another without waiting for a response. There are several advantages of asynchronous messaging over synchronous messaging, as you’ll soon see.

The Java Message Service (JMS) is a standard API for asynchronous messaging. In this chapter, we’re going to look at how Spring simplifies sending and receiving messages with JMS. In addition to basic sending and receiving of messages, we’ll look at Spring’s support for message-driven POJOs, a way to receive messages that resembles EJB’s message-driven beans (MDBs). Finally, we’ll wrap up with a look at Lingo, a remoting extension for Spring that uses JMS as its transport mechanism.

But before we dive into the nuts and bolts of Spring’s JMS support, let’s review the basics of asynchronous messaging with JMS.

A brief introduction to JMS

Much like the remoting mechanisms we’ve covered in previous chapters, JMS is all about applications communicating with one another. JMS differs from those other mechanisms, however, in how information is transferred between systems.

Remoting options like RMI and Hessian/Burlap are synchronous. As illustrated in figure 10.1, when the client invokes a remote method, the client must wait for the method to complete before moving on. Even if the remote method doesn’t return anything back to the client, the client will be put on hold until the service is done.

Figure 10.1. When communicating synchronously, the client must wait for the service to complete.

JMS, on the other hand, provides Java applications with the option of communicating asynchronously. When messages are sent asynchronously, as shown in figure 10.2, the client does not have to wait for the service to process the message or even for the message to be delivered. The client sends its message and then moves along with the assumption that the service will eventually receive and process the message.

Figure 10.2. Asynchronous communication is a no-wait form of communication.

Asynchronous communication through JMS offers several advantages over synchronous communication. We’ll take a closer look at these advantages in a moment. First, let’s see how messages are sent using JMS.

Architecting JMS

Most of us take the postal service for granted. Millions of times every day, people place letters, cards, and packages in the hands of postal workers, trusting that they’ll get to the desired destination. The world’s too big of a place for us to handdeliver these things ourselves, so we rely on the postal system to handle it for us. We address it, place the necessary postage on it, and then drop it in the mail to be delivered without giving a second thought to how it might get there.

The key to the postal service is indirection. When Grandma’s birthday comes around, it’d be very inconvenient if we had to deliver a card directly to her. Depending on where she lives, we’d have to set aside anywhere from a few hours to a few days to deliver a birthday card. Fortunately, they’ll deliver the card to her while we go about our lives.

Similarly, indirection is the key to JMS. When one application sends information to another through JMS, there is no direct link between the two applications. Instead, the sending application places the message in the hands of a service that will ensure delivery to the receiving application.

There are two main concepts in JMS: message brokers and destinations.

When an application sends a message, it hands it off to a message broker. A message broker is JMS’s answer to the post office. The message broker will ensure that the message is delivered to the specified destination, leaving the sender free to go about other business.

When you send a letter through the mail, it’s important to address it so that the postal service knows where it should be delivered. Likewise, in JMS, messages are addressed with a destination. Destinations are like mailboxes where the messages are placed until someone comes to pick them up.

However, unlike mail addresses, which may indicate a specific person or street address, destinations are less specific. Destinations are only concerned about where the message will be picked up—not who will pick them up. In this way, destinations are like sending a letter addressed “To resident.”

In JMS, there are two types of destination: queues and topics. Each of these is associated with a specific messaging model, either point-to-point (for queues) or publish-subscribe (for topics).

Point-to-point messaging model

In the point-to-point model, each message has exactly one sender and one receiver, as illustrated in figure 10.3. When the message broker is given a message, it places the message in a queue. When a receiver comes along and asks for the next message in the queue, the message is pulled from the queue and delivered to the receiver. Because the message is removed from the queue as it is delivered, it is guaranteed that the message will be delivered to only one receiver.

Figure 10.3. A message decouples a message sender from the message receiver. While a queue may have several receivers, each message is picked up by exactly one receiver.

Although each message in a message queue is delivered to only one receiver, this does not imply that there is only one receiver pulling messages from the queue. In fact, it’s quite likely that there are several receivers processing messages from the queue. But they’ll each be given their own messages to process.

This is analogous to waiting in line at the bank. As you wait, you may notice that there are multiple tellers available to help you with your financial transaction. After each customer is helped and a teller is freed up, she will call for the next person in line. When it’s your turn at the front of the line, you’ll be called to the counter and helped by one teller. The other tellers will help other banking customers.

Another observation to be made at the bank is that when you get in line, you probably won’t know which teller will eventually help you. You could count how many people are in line, match that up with the number of available tellers, note which teller is fastest, and then come up with a guess as to which teller will call you to their window. But chances are you’ll be wrong and end up at a different teller’s window.

Likewise, in JMS, if there are multiple receivers listening to a queue, there’s no way of knowing which one will actually process a specific message. This uncertainty is actually a good thing because it enables an application to scale up message processing by simply adding another listener to the queue.

Publish-subscribe messaging model

In the publish-subscribe messaging model, messages are sent to a topic. As with queues, many receivers may be listening to a topic. However, unlike queues where a message is delivered to exactly one receiver, all subscribers to a topic will receive a copy of the message, as shown in figure 10.4.

Figure 10.4. Like queues, topics decouple message senders from message receivers. Unlike queues, however, a topic message could be delivered to many topic subscribers.

As implied by its name, the publish-subscribe message model is very much like the model of a magazine publisher and its subscribers. The magazine (a message) is published, sent to the postal service, and then all subscribers receive their own copy.

The magazine publisher analogy breaks down, however, when you realize that in JMS, the publisher has no idea of who its subscribers are. The publisher only knows that its message will be published to a particular topic—not who is listening to that topic. This also implies that the publisher has no idea of how the message will be processed.

Now that we’ve covered the basics of JMS, let’s see how JMS messaging compares to synchronous RPC.

Assessing the benefits of JMS

Even though it’s very intuitive and simple to set up, synchronous communication imposes several limitations on the client of a remote service. Most significantly:

  • Synchronous communication implies waiting. When a client invokes a method on a remote service, it must wait for the remote method to complete before the client can continue. If the client communicates frequently with the remote service and/or the remote service is slow to respond, this could negatively impact performance of the client application.

  • The client is coupled to the service through the service’s interface. If the interface of the service changes, all of the service’s clients will also need to change accordingly.

  • The client is coupled to the service’s location. A client must be configured with the service’s network location so that it knows how to contact the service. If the network topology changes, the client will need to be reconfigured with the new location.

  • The client is coupled to the service’s availability. If the service becomes unavailable, the client is effectively crippled.

While synchronous communication has its place, these shortcomings should be taken into account when deciding what communication mechanism is a best fit for your application’s needs. If these constraints are a concern for you, you may want to consider how asynchronous communication with JMS addresses these issues.

No waiting

When a message is sent with JMS, the client doesn’t need to wait around for it to be processed or even delivered. The client drops the message off with the message broker and moves along with faith that the message will make it to the appropriate destination.

Since it doesn’t have to wait, the client will be freed up to perform other activities. With all of this free time, the client’s performance can be dramatically improved.

Message-oriented

Unlike RPC communication that is typically oriented around a method call, messages sent with JMS are data-centric. This means that the client isn’t fixed to a specific method signature. Any queue or topic subscriber that can process the data sent by the client can process the message. The client doesn’t need to be aware of any service specifics.

Location independence

Synchronous RPC services are typically located by their network address. The implication of this is that clients are not resilient to changes in network topology. If a service’s IP address changes or if it’s configured to listen on a different port, the client must be changed accordingly or the client will be unable to access the service.

In contrast, JMS clients have no idea who will process their messages or where the service is located. The client only knows the queue or topic through which the messages will be sent. As a result, it doesn’t matter where the service is located, as long as it can retrieve messages from the queue or topic.

In the point-to-point model, it’s possible to take advantage of location independence to create a cluster of services. If the client is unaware of the service’s location and if the service’s only requirement is that it must be able to access the message broker, there’s no reason why multiple services can’t be configured to pull messages from the same queue. If the service is being overburdened and falling behind in its processing, all we need to do is turn up a few more instances of the service to listen to the same queue.

Location independence takes on another interesting side effect in the publish-subscribe model. Multiple services could all be subscribed to a single topic, receiving duplicate copies of the same message. But each service could process that message differently. For example, let’s say you have a set of services that together process a message that details the new hire of an employee. One service might add the employee to the payroll system, another to the HR portal, and yet another makes sure that the employee is given access to the systems they’ll need to do their job. Each service works independently on the same data that they each received from a topic.

Guaranteed delivery

In order for a client to communicate with a synchronous service, the service must be listening at the IP address and port specified. If the service were to go down or otherwise become unavailable, the client wouldn’t be able to proceed.

However, when sending messages with JMS, the client can rest assured that its messages will be delivered. Even if the service is unavailable when a message is sent, it will be stored until the service is available again.

Now that you have a feel for the basics of JMS and asynchronous messaging, let’s set up a JMS message broker that we’ll use in our examples. Although you’re free to use any JMS message broker you’d like, we’re going to use the popular ActiveMQ message broker.

Setting up ActiveMQ in Spring

ActiveMQ is a great open source message broker and a wonderful option for asynchronous messaging with JMS. Although ActiveMQ began its life as a Codehaus project, it is in the process of moving to Apache. As this is being written, ActiveMQ 4.1.0 is in Apache’s incubator program.

To get started with ActiveMQ, you’ll need to download the binary distribution from www.activemq.org. Once you’ve downloaded ActiveMQ, unzip it to your local hard drive. In the base directory of the unzipped distribution, you’ll find incubator-activemq-4.1.0.jar. This is the JAR file you’ll need to add to the application’s classpath to be able to use ActiveMQ’s API.

In the bin directory, you’ll find a script that starts ActiveMQ: activemq for Unix users or activemq.bat for Windows users. Run the script and within moments ActiveMQ will be ready and waiting to broker your messages.

Creating a connection factory

Throughout this chapter, we’re going to see different ways that Spring can be used to both send and receive messages through JMS. In all cases, we’ll need a JMS connection factory to be able to send messages through the message broker. Since we’re using ActiveMQ as our message broker, we’ll have to configure the JMS connection factory so that it knows how to connect to ActiveMQ. ActiveMQConnectionFactory is the JMS connection factory that comes with ActiveMQ, and it is configured in Spring like this:

<bean id="connectionFactory"
    class="org.apache.activemq.ActiveMQConnectionFactory">
  <property name="brokerURL" value="tcp://localhost:61616"/>
</bean>

Later in this chapter we’ll use this connectionFactory bean a lot. But for now, suffice it to say that the brokerURL property tells the connection factory where the message broker is located. In this case, the URL in the brokerURL property tells ActiveMQConnectionFactory to connect to ActiveMQ on the local machine at port 61616 (which is the port that ActiveMQ listens to by default).

Declaring an ActiveMQ message destination

In addition to a connection factory, we’ll need a destination for the messages to be passed along on to. The destination can be either a queue or a topic, depending on the needs of the application.

Regardless of whether you are using a queue or a topic, you must configure the destination bean in Spring using a message broker–specific implementation class. For example, the following <bean> declaration declares an ActiveMQ queue:

<bean id="rantzDestination"
    class="org.apache.activemq.command.ActiveMQQueue">
  <constructor-arg index="0" value="rantz.marketing.queue"/>
</bean>

Similarly, the following <bean> declares a topic for ActiveMQ:

<bean id="rantzDestination"
    class="org.apache.activemq.command.ActiveMQTopic">
  <constructor-arg index="0" value="rantz.marketing.topic"/>
</bean>

Again, these beans illustrate how to configure destinations for ActiveMQ 4.1.0. If you’re using a different message broker, be sure to configure destination beans using implementations that are appropriate to the message broker you’re using.

With the common beans out of the way, we’re ready to start sending and receiving messages. For that, we’ll use JmsTemplate, the centerpiece of Spring’s JMS support. But first, let’s gain an appreciation for what JmsTemplate provides by looking at what JMS is like without JmsTemplate.

Using JMS with Spring

As you’ve seen, JMS gives Java developers a standard API for interacting with message brokers and for sending and receiving messages. Furthermore, virtually every message broker implementation available supports JMS, so there’s no reason to learn a proprietary messaging API for every message broker you deal with.

But while JMS offers a universal interface to all message brokers, its convenience comes at a cost. Sending and receiving messages with JMS is not a simple matter of licking a stamp and placing it on an envelope. As you’ll see, JMS demands that you also fuel up the mail carrier’s truck.

Tackling runaway JMS code

In chapter 5 (see section 5.3.1) you saw how conventional JDBC code can be an unwieldy mess of code to handle connections, statements, result sets, and exceptions. Unfortunately, conventional JMS follows a similar model, as you’ll observe in listing 10.1.

Example 10.1. Sending a message using conventional (non-Spring) JMS

At the risk of sounding repetitive—holy runaway code, Batman! Just as with the JDBC example, there are almost 20 lines of code here just to send a “Hello world!” message. Actually, only a few lines actually send the message. The rest are merely setting the stage for sending a message.

As you can see in listing 10.2, it’s not any prettier for the receiving end, either.

Example 10.2. Receiving a message using conventional (non-Spring) JMS

Again, just as in listing 10.1, there’s a lot of code here to do something so simple. If you take a line-by-line comparison, you’ll find that they’re almost identical. And if you were to look at a thousand other JMS examples, you’d find them all to be strikingly similar. Some may retrieve their connection factories from JNDI and some may use a topic instead of a queue. Nevertheless, they all follow roughly the same pattern.

A consequence of all of this boilerplate code is that you’ll find that you repeat yourself every time you work with JMS. Worse still, you’ll find yourself repeating other developers’ JMS code.

We’ve already seen in chapter 5 how Spring’s JdbcTemplate handles runaway JDBC boilerplate. Now let’s look at how Spring’s JmsTemplate can do the same thing for JMS boilerplate code.

Working with JMS templates

JmsTemplate is Spring’s answer to verbose and repetitive JMS code. JmsTemplate takes care of creating a connection, obtaining a session, and the actual sending and receiving of messages. This leaves you to focus your development efforts on constructing the message to send or processing messages that are received.

What’s more, JmsTemplate can handle any clumsy JMSException that may be thrown along the way. If a JMSException is thrown in the course of working with JmsTemplate, JmsTemplate will catch it and rethrow it as one of the unchecked subclasses of JmsException in the first column of table 10.1.

Table 10.1. The subclasses of Spring’s JmsException compared to the subclasses of JMSException.

Spring (org.springframework.jms.*)

JMS (javax.jms.*)

support.destination.DestinationResolutionException

Spring specific—Thrown when Spring can’t resolve a destination name.

IllegalStateException

IllegalStateException

InvalidClientIDException

InvalidClientIDException

InvalidDestinationException

InvalidDestinationException

InvalidSelectorExeption

InvalidSelectorException

JmsSecurityException

JMSSecurityException

listener.adapter.Listener

ExecutionFailedException

Spring specific—Thrown when execution of a listener method fails.

support.converter.Message

ConversionException

Spring specific—Thrown when message conversion fails.

MessageEOFException

MessageEOFException

MessageFormatException

MessageFormatException

MessageNotReadableException

MessageNotReadableException

MessageNotWritableException

MessageNotWritableException

ResourceAllocationException

ResourceAllocationException

TransactionInProgressException

TransactionInProgressException

TransactionRolledBackException

TransactionRolledBackException

UncategorizedJmsException

Spring specific—Thrown when no other exception applies.

In fairness to the JMS API, JMSException does come with a rich and descriptive set of subclasses that give you a better sense of what went wrong. Nevertheless, all of these subclasses of JMSException are checked exceptions and thus must be caught. JmsTemplate will attend to that for you.

Wiring JmsTemplate

To use JmsTemplate, we’ll need to declare it as a bean in the Spring configuration file. The following XML should do the trick:

<bean id="jmsTemplate"
    class="org.springframework.jms.core.JmsTemplate">
  <property name="connectionFactory" ref="connectionFactory" />
</bean>

Because JmsTemplate needs to know how to get connections to the message broker, we had to set the connectionFactory property with a bean that implements JMS’s ConnectionFactory interface. Here we’ve wired it with a reference to the connectionFactory bean that we declared earlier in section 10.1.3.

JmsTemplate has a few other properties that come in handy in certain circumstances. But we’ll defer discussion of those until we need them. For now, the JmsTemplate we’ve configured is good enough to get started.

That’s all you need to do to configure JmsTemplate—it is now ready to go. Let’s send a message!

Sending messages

One of the benefits of signing up as a motorist in the RoadRantz application is that you can opt in to be sent coupons and great deals on car washes, oil changes, and other automotive products and services. Motorists who are interested in third-party offers are tracked in a separate marketing system from the main RoadRantz application. When a user registers as a RoadRantz motorist, if they elect to receive third-party offers, their name and email address is sent to the RoadRantz marketing system as a JMS message.

On the RoadRantz side, we’re going to use JmsTemplate to send the motorist information to the RoadRantz marketing system. Listing 10.3 shows RantzMarketingGatewayImpl, which is the class through which RoadRantz will interact with the marketing system.

Example 10.3. Sending a motorist message using JmsTemplate

The sendMotoristInfo() method is the centerpiece of RantzMarketingGatewayImpl. It does nothing more than use the JmsTemplate’s send() method to send the message.

The first parameter to the send() method is the name of the JMS Destination that the message will be sent to. Here we’re using the destination property that will be given to RantzMarketingGatewayImpl through setter injection. When the send() method is called, JmsTemplate will deal with obtaining a JMS connection and session and will send the message on behalf of the sender (see figure 10.5).

Figure 10.5. JmsTemplate deals with the complexities of sending a message on behalf of the sender.

As for the message itself, it is constructed using a MessageCreator, implemented here as an anonymous inner class. In MessageCreator’s createMessage() method, we simply assemble a JMS MapMessage and populate it with the motorist’s name and email address. The createMessage() method is a callback method that JmsTemplate will use to construct the message that will be sent.

The JmsTemplate and the Destination are injected into RantzMarketingGatewayImpl using setter injection. Therefore, when we configure the RantzMarketingGatewayImpl class in Spring, we must wire in references to the jmsTemplate and rantzDestination beans:

<bean id="marketingGateway"
    class="com.roadrantz.marketing.RantzMarketingGatewayImpl">
  <property name="jmsTemplate" ref="jmsTemplate" />
  <property name="destination" ref="rantzDestination" />
</bean>

And that’s it! Notice that the sendMotoristInfo() method is focused entirely on assembling and sending a message. There’s no connection or session management code—JmsTemplate handles all of that for us. And there’s no need to catch JMSExeptionJmsTemplate will catch any JMSException that is thrown and then rethrow it as one of Spring’s unchecked exceptions from table 10.1.

Setting a default destination

In listing 10.3, we explicitly specified a specific Destination that the motorist message would be sent to in the send() method. That form of the send() method comes in handy when we want to programmatically choose a destination. But in the case of RantzMarketingGatewayImpl, we will always be sending the motorist messages to the same destination, so the benefits of that form of send() aren’t as clear.

Instead of explicitly specifying a destination each time we send a message, we could opt for wiring a default destination into JmsTemplate:

<bean id="jmsTemplate"
    class="org.springframework.jms.core.JmsTemplate">
  <property name="connectionFactory" ref="connectionFactory" />
  <property name="defaultDestination" ref="rantzDestination" />
</bean>

Now the call to JmsTemplate’s send() method can be simplified slightly by removing the first parameter:

jmsTemplate.send(
    new MessageCreator() {
...
    });

This form of the send() method only takes a MessageCreator. With no destination specified, JmsTemplate will assume that you want the message sent to the default destination. Since JmsTemplate will always assume the correct destination, we no longer need to inject a destination into RantzMarketingGatewayImpl. Its declaration can be simplified to this:

<bean id="marketingGateway"
    class="com.roadrantz.marketing.RantzMarketingGatewayImpl">
  <property name="jmsTemplate" ref="jmsTemplate" />
</bean>

Because we’re no longer injecting the destination into RantzMarketingGatewayImpl, the destination property and its setter method can also be removed.

Consuming messages

Now you’ve seen how to send a message using JmsTemplate. But what if you’re on the receiving end? Can JmsTemplate be used to receive messages?

Yes, it can. In fact, it’s even easier to receive a message with JmsTemplate. All you need to do is call JmsTemplate’s receive() method, as shown in listing 10.4.

Example 10.4. Receiving a message using JmsTemplate

When the receive() method is called, JmsTemplate will once again piece together all of the parts needed to interact with the message broker—the JMS connection, session, and message consumer. Then it will call the receive() method on the message consumer on behalf of the receiving application, as illustrated in figure 10.6.

Figure 10.6. Receiving messages with JmsTemplate is as simple as calling the receive() method. JmsTemplate takes care of the rest.

JmsTemplate’s receive() method is synchronous. By default, a call to receive() will block until a message appears on the destination—waiting forever, if necessary. To avoid an eternal wait for messages, you can specify a receive timeout by setting the receiveTimeout property when configuring the JmsTemplate. The following configuration configures JmsTemplate to time out on receives after one minute (60,000 milliseconds):

<bean id="jmsTemplate"
    class="org.springframework.jms.core.JmsTemplate">
  <property name="connectionFactory" ref="connectionFactory" />
  <property name="defaultDestination" ref="rantzDestination" />
  <property name="receiveTimeout" value="60000" />
</bean>

As used in listing 10.4, the receive() method receives a message from the default destination. If you’d prefer to specify a destination, you can do so by passing in the destination:

MapMessage message =
    (MapMessage) jmsTemplate.receive(destination);

Alternatively, you can specify a destination by name and let Spring’s destination resolver automatically resolve the destination:

MapMessage message =
    (MapMessage) jmsTemplate.receive("rantz.marketing.queue");

Synchronous receipt of messages is not the only option offered by Spring. We’ll look at how Spring supports asynchronous receiving in section 10.3. But first, let’s explore JmsTemplate a bit more by looking at how it can be configured to automatically convert messages to and from Java objects.

Converting messages

In listing 10.3, the message that is sent is constructed by the anonymous MessageCreator instance in its createMessage() method. The message is constructed by taking the properties of the Motorist object and placing them in a MapMessage object. Meanwhile, on the receiving end, MarketingReceiverGatewayImpl’s receiveSpammedMotorist() method pulls values out of the received message to populate a SpammedMotorist object.

For our simple example, it’s not that big of a deal to place the message conversion code directly alongside the code that sends and receives the message. But if you find yourself sending and/or receiving the same message at multiple points in your application, you may want to avoid unnecessary duplication of the mapping code by consolidating it into a message converter.

Although it wouldn’t be hard to extract the message conversion code into a utility class of your own design, you’d still need to explicitly invoke the utility class to do the conversion. Fortunately, Spring supports message conversion through its MessageConverter interface:

public interface MessageConverter {
  public Message toMessage(Object object, Session session);
  public Object fromMessage(Message message);
}

The MessageConverter interface is very straightforward. It has only two methods that must be implemented. For sending messages, the toMessage() method converts an object to a Message. On the receiving end, the fromMessage() method converts an incoming Message into an Object.

Because MessageConverter is an interface, we’ll need to provide an implementation for our application. Listing 10.5 shows MotoristMessageConverter, an implementation of MessageConverter that transforms Motorist objects into messages and messages into SpammedMotorist objects.

Example 10.5. A message converter that converts a Motorist to a message and a message to a SpammedMotorist

When the RoadRantz application needs to send a Motorist to the marketing system, the toMessage() method will be used to convert the Motorist object into a MapMessage object. In the marketing system, the fromMessage() method will convert the received message into a SpammedMotorist object that the marketing system will use to send special offers to the user.

So now when we send and receive messages, all we’ll need to do is call the toMessage() and fromMessage() methods on the converter object, right? Well... not exactly.

Sending and receiving converted messages

While we could call the toMessage() method before sending a message and fromMessage() upon receipt of a message, Spring offers a better way.

Instead of explicitly calling the toMessage() method before sending a message, we can simply call the convertAndSend() method of JmsTemplate. That way, the sendMotoristInfo() method in RantzMarketingGatewayImpl becomes significantly simpler:

public void sendMotoristInfo(final Motorist motorist) {
  jmsTemplate.convertAndSend(motorist);
}

JmsTemplate’s convertAndSend() method automatically calls the toMessage() method before sending the message to the destination. As used here, the message will be sent to the JmsTemplate’s default destination (assuming one has been specified). But we could also select a specific destination when calling convertAndSend():

jmsTemplate.convertAndSend(destination, motorist);

Optionally, we can specify the destination by name:

jmsTemplate.convertAndSend("rantz.marketing.queue", motorist);

On the receiving end, we won’t need to call fromMessage() to convert the message returned from JmsTemplate’s receive(). Instead, we’ll replace the call to receive() with a call to receiveAndConvert():

public SpammedMotorist receiveSpammedMotorist() {
   return (SpammedMotorist) jmsTemplate.receiveAndConvert();
}

Again, unless otherwise specified, receiveAndConvert() receives messages from the default destination. But we can also choose a destination by passing it as a parameter to receiveAndConvert():

return (SpammedMotorist) jmsTemplate.receiveAndConvert(destination);

or, using the destination’s name:

return (SpammedMotorist)
   jmsTemplate.receiveAndConvert("rantz.marketing.queue");

There’s but one small detail that we’ve left out. If JmsTemplate’s convertAndSend() and receiveAndConvert() methods use the message converter to convert the messages then how does JmsTemplate know about the message converter?

Wiring a message converter

For the message converter to work, we’ll need to configure it as a <bean> in Spring. The following XML will handle that:

<bean id="motoristConverter"
    class="com.roadrantz.marketing.MotoristMessageConverter" /

Finally, the JmsTemplate needs to know about the message converter. To accommodate that, we’ll wire the motoristConverter bean into JmsTemplate’s messageConverter property:

<bean id="jmsTemplate"
    class="org.springframework.jms.core.JmsTemplate">
  <property name="connectionFactory" ref="connectionFactory" />
  <property name="defaultDestination" ref="rantzDestination" />
  <property name="messageConverter" ref="motoristConverter" />
</bean>

You’ve already seen many parallels between how JdbcTemplate and JmsTemplate work. But there’s still one more parallel that you may be interested in. Let’s look at how Spring provides support for building JMS gateways through its JmsGatewaySupport class.

Using Spring’s gateway support classes for JMS

You may remember from chapter 5 that Spring makes working with JdbcTemplate a little easier by providing JdbcDaoSupport, a base class for writing JDBC-based DAOs. Similarly, Spring comes with JmsGatewaySupport, a base class for JMS gateway classes.

Thus far, we have explicitly created jmsTemplate and destination properties in RantzMarketingGatewayImpl to hold the JmsTemplate and Destination. Although this didn’t add a lot of extra code, just imagine how many times those properties (and their setter methods) would be duplicated in an application that has several JMS gateways. To clean things up a bit, we could have written RantzMarketingGatewayImpl to subclass JmsGatewaySupport instead, as shown in listing 10.6.

Example 10.6. A new version of RantzMarketingGatewayImpl rewritten to use JmsGatewaySupport

Notice that the jmsTemplate property and the setJmsTemplate() method are gone. Instead of using an injected jmsTemplate property as before, this version of RantzMarketingGatewayImpl makes a call to getJmsTemplate() to receive the JmsTemplate that’s managed by JmsGatewaySupport. Therefore, jmsTemplate and its setter method are no longer needed.[1]

Where does JmsGatewaySupport get its JmsTemplate? It depends. You can inject a JmsTemplate directly into the jmsTemplate property, as we did with the original RantzMarketingGatewayImpl. Or, you can short-circuit the need for a JmsTemplate bean altogether by wiring the connection factory into the connectionFactory property:

<bean id="marketingGateway"
    class="com.roadrantz.marketing.RantzMarketingGatewayImpl">
  <property name="connectionFactory" ref="connectionFactory" />
</bean>

When configured this way, JmsGatewaySupport will automatically create a JmsTemplate object based on the injected connection factory. You no longer need to declare a JmsTemplate bean in Spring.

Before you get too excited about wiring a connection factory directly into the gateway class, you should be aware that there are a couple of shortcomings to this approach:

  • You can only specify a default destination on a JmsTemplate. If JmsGatewaySupport creates its own JmsTemplate, you won’t get a chance to specify a default destination. You’ll need to always explicitly choose a destination when calling send() or receive().

  • You can only wire a message converter into a JmsTemplate. If JmsGatewaySupport creates its own JmsTemplate, you won’t be able to use a message converter. You’ll need to explicitly handle message conversion in your gateway code.

You’ve already seen how to receive messages using JmsTemplate. But as you saw, the receive() method blocks until a message is available. Coming up next, let’s have a look at a new feature in Spring 2.0 that makes it possible to asynchronously receive messages.

Creating message-driven POJOs

During one summer in college, I had the great privilege of working in Yellowstone National Park. The job wasn’t one of the high-profile jobs like park ranger or the guy who turns Old Faithful on and off.[2] Instead, I held a position in housekeeping at Old Faithful Inn, changing sheets, cleaning bathrooms, and vacuuming floors. Not glamorous, but at least I was working in one of the most beautiful places on earth.

Every day after work, I would head over to the local post office to see if I had any mail. I was away from home for several weeks, so it was nice to receive a letter or card from my friends back at school. I didn’t have my own post box, so I’d walk up and ask the man sitting on the stool behind the counter if I had received any mail. That’s when the wait would begin.

You see, the man behind the counter was approximately 195 years old. And like most people that age he had a difficult time getting around. He’d drag his keister off the stool, slowly scoot his feet across the floor, and then disappear behind a partition. After a few moments, he’d emerge, shuffle his way back to the counter, and lift himself back up onto the stool. Then he would look at me and say, “No mail today.”

JmsTemplate’s receive() method is a lot like that aged postal employee. When you call receive(), it goes away and looks for a message in the queue or topic and doesn’t return until a message arrives or until the timeout has passed. Meanwhile, your application is sitting there doing nothing, waiting to see if there’s a message. Wouldn’t it be better if your application could go about its business and be notified when a message arrives?

One of the highlights of the EJB 2 specification was the inclusion of the message-driven bean (MDB). MDBs are EJBs that process messages asynchronously. In other words, MDBs react to messages in a JMS destination as events and respond to those events. This is in contrast to synchronous message receivers that block until a message is available.

MDBs were a bright spot in the EJB landscape. Even many of the most rabid detractors of EJB would concede that MDBs were an elegant way of handling messages. The only blemish to be found in EJB 2 MDBs was that they had to implement javax.ejb.MessageDrivenBean. In doing so, they also had to implement a few EJB lifecycle callback methods. Put simply, EJB 2 MDBs were very un-POJO.

With the EJB 3 specification, MDBs were cleaned up to have a slightly more POJO feel to them. No longer must you implement the MessageDrivenBean interface. Instead, you implement the more generic javax.jms.MessageListener interface and annotate MDBs with @MessageDriven.

Spring 2.0 addresses the need for asynchronous consumption of messages by providing its own form of message-driven bean that is quite similar to EJB 3’s MDBs. In this section, you’ll learn how Spring supports asynchronous message consumption using message-driven POJOs (we’ll call them MDPs, for short).

Creating a message listener

For a moment, try to imagine a simpler world where the MarketingMdp doesn’t have to implement the MessageDrivenBean interface. In such a happy place, the sky would be the brightest of blues, the birds would always whistle your favorite song, and you wouldn’t have to implement the setMessageDrivenContext() method or the empty ejbRemove() method—all demands placed on an MDB developer by the EJB 2 MDB programming model. In such a world, the MarketingMdp class might look a little like listing 10.7.

Example 10.7. Simplifying the message-driven paradigm

Although the color of the sky and training birds to sing are a bit out of scope for Spring, the dream world I just described is much closer to reality with the release of Spring 2.0. In fact, the MarketingMdp class in listing 10.7 is exactly how a Spring 2.0 message-driven POJO[3] would be written to process motorist information messages in the RoadRantz marketing engine.

By itself, MarketingMdp doesn’t do much. It has an onMessage() method ready to process messages, but until we configure it in Spring, it’s just dead weight. So let’s go ahead and configure MarketingMdp as a <bean> in the Spring application context:

<bean id="rantzMdp"
    class="com.roadrantz.marketing.MarketingMdp" />

So far, this is nothing special. The most striking thing about listing 10.7 is that it looks very much like what an equivalent EJB 3 MDB might look like. The only real difference is that an EJB 3 MDB would also be annotated with @MessageDriven to indicate to the container that it is an MDB. In Spring, however, we’ll indicate that this is an MDP by wiring it into a message listener container.

Containing message listeners

A message listener container is a special bean that watches a JMS destination, waiting for a message to arrive. Once a message arrives, it retrieves the message and then passes it on to a MessageListener implementation by calling the onMessage() method. Figure 10.7 illustrates this interaction.

Figure 10.7. MessageListenerContainer listens to a queue/topic. When a message arrives, it is forwarded to a MessageListener.

Because our MarketingMdp class implements the MessageListener interface, it seems that a message listener container is in order. Table 10.2 lists the message listener containers offered by Spring.

Table 10.2. Spring’s message listener containers.

Container class (org.springframework.jms.listener.*)

What it does

SimpleMessageListenerContainer

This is the simplest message listener container. It works with a fixed number of JMS sessions and does not support transactions.

DefaultMessageListenerContainer

This message listener container builds upon SimpleMessageListenerContainer by adding support for transactions.

serversession.ServerSessionMessageListenerContainer

This is the most powerful of the message listener containers. Like DefaultMessageListenerContainer, it supports transactions. However it is unique in that it allows for dynamic management of JMS sessions.

As its name implies, SimpleMessageListenerContainer is the simplest of the message listener containers. It can be configured in Spring as follows:

<bean class="org.springframework.jms.listener.
        SimpleMessageListenerContainer">
  <property name="connectionFactory" ref="connectionFactory" />
  <property name="destination" ref="rantzDestination" />
  <property name="messageListener" ref="rantzMdp" />
</bean>

The connectionFactory and destination properties are wired with the same connectionFactory and destination properties that we wired into JmsTemplate in section 10.2. As for the messageListener property, we’ve wired it with a reference to our MDP implementation so that the onMessage() method will be invoked upon receipt of a message.

Working with transactional MDPs

SimpleMessageListenerContainer is great for basic messaging needs. But if you need messages to be received in a transaction, you’ll want to look at DefaultMessageListenerContainer instead.

DefaultMessageListenerContainer is configured similarly to SimpleMessageListenerContainer, as you can see here:

<bean class="org.springframework.jms.listener.
        DefaultMessageListenerContainer">
  <property name="connectionFactory" ref="connectionFactory" />
  <property name="destination" ref="rantzDestination" />
  <property name="messageListener" ref="rantzMdp" />
  <property name="transactionManager" ref="jmsTransactionManager" />
</bean>

The only difference here is that DefaultMessageListenerContainer’s transactionManager property is wired with a reference to a transaction manager. If you need the MDP to participate in transactions along with other transactional elements (a data access object, for example), you’ll want to wire a JtaTransactionManager into the transactionManager property. Review section 6.2.5 in chapter 6 for details on how to configure a JtaTransactionManager.

If your transactional needs are simpler, a JmsTransactionManager will do:

<bean id="jmsTransactionManager"
    class="org.springframework.jms.connection.
         JmsTransactionManager">
  <property name="connectionFactory" ref="connectionFactory" />
</bean>

It’s worth mentioning that the transactionManager property of DefaultMessageListenerContainer is optional. If you don’t inject a transaction manager into it, the MDP will not be transactional. Leaving out the transaction manager will effectively degrade DefaultMessageListenerContainer to be equivalent to SimpleMessageListenerContainer.

At this point you may be thinking that we’re trying to pull a fast one on you. We keep referring to MarketingMdp as a message-driven POJO. But there’s nothing very POJO about a class that is required to implement a MessageListener interface. It’s certainly simpler than the EJB 2 MDB, but it’s still not quite a POJO. If this discrepancy is keeping you up at night then read on as I show you how to create a MDP that is truly a POJO.

Writing pure-POJO MDPs

In MarketingMdp, the onMessage() method is really just plumbing code. It’s only there to receive and translate the message. What’s more, it’s the onMessage() method and its defining MessageListener interface that keep MarketingMdp from truly being a POJO.

The real work occurs in the processMotoristInfo() method. If there were only some way we could bypass the onMessage() method and go straight to the processMotoristInfo() method, MarketingMdp would be much simpler. And, more importantly, MarketingMdp would be a honest-to-goodness message-driven POJO.

But the MessageListener interface and its onMessage() method serve a very important purpose. If the message listener container can count on its message-Listener property being wired with an implementation of MessageListener then it knows that it only needs to call the onMessage() method when a message arrives.

Fortunately, Spring offers an alternative in MessageListenerAdapter. MessageListenerAdapter is a MessageListener that delegates to a bean and method of your choosing, as shown in figure 10.8.

Figure 10.8. A MessageListenerAdapter plays the role of MessageListener. When it receives a message, it invokes a method on a POJO.

Instead of wiring your own implementation of MessageListener into the message listener container, you can wire in a MessageListenerAdapter:

<bean class="org.springframework.jms.listener.
        SimpleMessageListenerContainer">
  <property name="connectionFactory" ref="connectionFactory" />
  <property name="destination" ref="rantzDestination" />
  <property name="messageListener" ref="purePojoMdp" />
</bean>

Here we’ve wired a reference to the purePojoMdp bean into the messageListener property. Otherwise, this message listener container is configured like any other message listener container. As for the purePojoMdp bean, it’s a MessageListenerAdapter:

<bean id="purePojoMdp"
    class="org.springframework.jms.listener.adapter.
                 MessageListenerAdapter">
  <property name="delegate" ref="rantzMdp" />
  <property name="defaultListenerMethod"
      value="processMotoristInfo" />
</bean>

As you can see, we’ve configured the MessageListenerAdapter to call the processMotoristInfo() method on the rantzMdp bean when a message arrives on the destination. By default, MessageListenerAdapter calls a handleMessage() method when a message arrives. But we want our MarketingMdp bean to handle messages through its processMotoristInfo() method, so we’ve set defaultListenerMethod to processMotoristInfo.

Because we have chosen a specific method to be invoked, there’s no need to implement MessageListener or the onMessage() method. As a result, MarketingMdp can now be simplified to look like listing 10.8.

Example 10.8. A near-POJO implementation of MarketingMdp.java

This version of MarketingMdp is somewhat simpler than before. There’s no longer an onMessage() method and MarketingMdp no longer needs to implement a MessageListener interface. When a message arrives in the destination, the processMotoristInfo() method will be called. Because the message is a MapMessage, the processMotoristInfo() method translated it into a SpammedMotorist object, which is then processed.

One thing about MarketingMdp that still feels wrong is that the processMotoristInfo() method is still called with a JMS-specific MapMessage. Although it’s a POJO, the dependency on MapMessage unnecessarily couples MarketingMdp with JMS. What’s more, MapMessage’s getString() method throws a JMSException that must be dealt with somehow. Ideally, MarketingMdp wouldn’t depend on any framework-specific types.

When MessageListenerAdapter receives a message, it considers the message type and the value of the defaultListenerMethod and tries to find an appropriate listener method signature to invoke. Table 10.3 describes how MessageListenerAdapter maps a JMS message to listener method parameters.

Table 10.3. How JMS messages are mapped to MDP message parameters.

Message type

Method parameter

TextMessage

String or TextMessage

MapMessage

java.util.Map or MapMessage

BytesMessage

byte[] or BytesMessage

ObjectMessage

java.io.Serializable or ObjectMessage

Something to notice about table 10.3 is that the listener method can be written to take either the original JMS message or a simpler type. For example, the processMotoristInfo() method could be made even simpler by using a java.util.Map:

public void processMotoristInfo(Map map) {
  SpammedMotorist motorist = new SpammedMotorist();
  motorist.setFirstName((String) map.get("firstName"));
  motorist.setLastName((String) map.get("lastName"));
  motorist.setEmail((String) map.get("email"));
  ...
}

Now MarketingMdp is truly a POJO. It no longer has any dependency on any JMS type. And since the message arrives as a simple java.util.Map, there’s no need to catch JMSException when retrieving the messages’ values. This is a lot better, but something still doesn’t feel right.

Converting MDP messages

In the original MarketingMdp class, the processMotoristInfo() method took a SpammedMotorist object as a parameter. But in the latest version, it takes a Map, which it translates into a SpammedMotorist before processing. This means that the first few lines of processMotoristInfo() still include some plumbing code to do the translation into SpammedMotorist. Wouldn’t it be great if processMotoristInfo() would be given a SpammedMotorist object, ready for processing, when a message arrives?

As you’ll recall from earlier in this chapter (see section 10.2.3), Spring message converters can be used to translate messages to and from domain-specific Java types. And from listing 10.5, we already have a message converter that converts a MapMessage into a SpammedMotorist object. All we need to do is tell MessageListenerAdapter about the message converter. As it turns out, MessageListenerAdapter has a messageConverter property for that purpose:

<bean id="purePojoMdp"
    class="org.springframework.jms.listener.adapter.
                MessageListenerAdapter">
  <property name="delegate" ref="rantzMdp" />
  <property name="defaultListenerMethod"
      value="processMotoristInfo" />
  <property name="messageConverter" ref="motoristConverter" />
</bean>

We’ve wired the messageConverter property with a reference to the motorist-Converter bean (which is configured as a MotoristMessageConverter from listing 10.5). Now we can write the ultimate version of MarketingMdp, as shown in listing 10.9.

Example 10.9. MarketingMdp, now a pure POJO (with no hint of JMS)

package com.roadrantz.marketing;

public class MarketingMdp {
  public MarketingMdp() {}

  public void processMotoristInfo (SpammedMotorist motorist) {
     ...
  }
}

Wow! MarketingMdp has come a long way from the original MessageListener version in listing 10.7. MarketingMdp started as a class purposed for processing JMS messages, but the final version has no hint of JMS. In fact, MarketingMdp doesn’t even have to be used as an MDP. Because it’s just a POJO, its processMotoristInfo() method could be called directly without a message broker involved. Keep this fact in mind, because we’re not quite done with MarketingMdp. Soon you’ll learn how we can export this same POJO as a remote service.

Now you’ve seen how Spring supports messaging through JMS abstraction. We’ve both sent and received messages without having to succumb to the complexities of the JMS API or resorting to using EJB MDBs. But there’s still one more way to use messaging in Spring that may appeal to you if you’re more comfortable with the RPC model. Before we leave the topic of messaging behind, let’s look at how to make remote procedure calls using asynchronous messaging as a transport.

Using message-based RPC

In chapter 8, we explored several of Spring’s options for making remote procedure calls on remote objects. Those were all fantastic options for communication between applications. But all of those options were synchronous. That is, the client would invoke a method on the server object and then wait for the server to respond. Moreover, if the server process weren’t available, the request would immediately end with an exception being thrown.

As you’ve seen in this chapter, there are benefits to asynchronous communication through messaging. With asynchronous messaging, the message sender doesn’t have to wait for the receiver to finish processing the message before moving on. This has a positive impact on the performance of the “client” application. Also, message delivery is guaranteed—even if the receiver isn’t available when the message is sent.

But there’s also something appealing about the RPC model. The RPC programming model makes interacting with remote services as straightforward as invoking local object methods. If there were only some way to have the simplicity of the RPC programming model with the benefits of asynchronous messaging...

Introducing Lingo

Lingo is a Spring-based remoting option that bridges the gap between RPC and asynchronous messaging. As with other Spring remoting options, Lingo provides a service exporter to export bean functionality as a Lingo service and a clientside proxy to transparently wire a reference to a remote Lingo service on the calling side.

What makes Lingo different from the other remoting options we looked at in chapter 8 is in how it communicates. In all of Spring’s other remoting options, the client communicates directly with the service via sockets. Consequently, the service must be available when the client makes a call or else the call will fail.

Lingo remoting, however, carries information over a JMS queue or topic. As such, if the service is unavailable when a call is made, the call will be held in the queue/topic until the service is available again. Also, if the client call is one-way (that is, there is no return value), the client call can immediately return without having to wait for the service to process the call.

Although Lingo is based on Spring remoting, it is not part of the Spring Framework. You can download Lingo from the Lingo homepage at http://lingo.codehaus.org/Download. Be sure to get the latest version. We’re building our examples against version 1.3.

Alternatively, if you’re using Maven 2 to build your project, you can add Lingo as a dependency in pom.xml with the following:

<dependency>
  <groupId>org.logicblaze.lingo</groupId>
  <artifactId>lingo</artifactId>
  <version>1.3</version>
  <scope>compile</scope>
</dependency>

Maven will take care of ensuring that Lingo is in the build and runtime classpath for you.

Now that you have a basic idea of what Lingo does, let’s revisit the RoadRantz marketing system. This time we’ll build the interaction between the main RoadRantz application and the marketing engine using Lingo for communication.

Exporting the service

When we last visited MarketingMdp in listing 10.9, it was a pure POJO. Sure, we were wiring it into a MessageListenerAdapter so that it could be used as a message-driven POJO. But there is nothing about the class in listing 10.9 that makes it message-driven. It’s just a POJO and we can do any POJO kind of thing we want to with it.

As a POJO, it is suitable for export as a remote service using any of Spring’s remote service exporters we discussed in chapter 8. However, instead of rehashing those conventional RPC exporters again, this time we’re going to export MarketingMdp as a Lingo service.

On the service side, Lingo provides JmsServiceExporter, a service exporter not entirely unlike those in chapter 8. Instead of exporting a service that is available for direct RPC access, however, Lingo-exported services are made available through JMS, as shown in figure 10.9.

Figure 10.9. JmsServiceExporter exports a POJO as an RPC service that receives messages from a JMS destination.

The following XML configures a JmsServiceExporter that exports the rantzMdp bean (which is a MarketingMdp) as an RPC-over-JMS service:

<bean id="server"
    class="org.logicblaze.lingo.jms.JmsServiceExporter">
  <property name="connectionFactory" ref="connectionFactory" />
  <property name="destination" ref="destination" />
  <property name="service" ref="rantzMdp" />
  <property name="serviceInterface"
      value="com.roadrantz.marketing.MarketingService" />
</bean>

The first two properties wired in JmsServiceExporter are our old friends, the connectionFactory and destination properties. Unlike conventional RPC services, which typically use TCP/IP as the transport, a Lingo-exported service is available through a JMS destination (either a topic or a queue). Therefore, instead of configuring JmsServiceExporter with a URL or a port number, we will need to configure JmsServiceExporter with a JMS connection factory and a destination so that it will know where to export the service.

The service property is wired with a reference to the rantzMdp bean, which is our MarketingMdp. Finally, the serviceInterface property is configured with the class name of an interface that defines the exported service. We’re declaring that our service will be exported with the MarketingService interface, which is defined here:

package com.roadrantz.marketing;
public interface MarketingService {
   void processMotoristInfo(SpammedMotorist motorist);
}

Because we defined the service with the MarketingService interface, this means that we’ll need to make one small tweak to the MarketingMdp class. We’ll need to change it to implement the MarketingService interface:

package com.roadrantz.marketing;

public class MarketingMdp implements MarketingService {
  public MarketingMdp () {}

  public void processMotoristInfo(SpammedMotorist motorist) {
  ...
  }
}

That’s all there is to exporting a service using Lingo. Once the application is started, the JmsServiceExporter will kick in and we’ll be ready to start using it. Now let’s look at the client side to see how the RoadRantz application will make calls to the exported marketing service.

Proxying JMS

In the RoadRantz application, we’re going to need to call the processMotoristInfo() method every time a user registers and elects to receive special offers from RoadRantz. Therefore, we’ll have to wire a reference to the Lingo-exported service into the RoadRantz application somehow.

Wiring JmsProxyFactoryBean

Lingo provides JmsProxyFactoryBean, a proxy factory bean that produces proxies to remote Lingo-exported services. Conceptually, this is no different than the proxy factory beans we looked at in chapter 8. As you can see in figure 10.10, however, the service proxied by JmsProxyFactoryBean is available through a JMS destination (either a queue or a topic) instead of through TCP/IP.

Figure 10.10. JmsProxyFactoryBean proxies a remote service that is listening on a JMS destination.

The following <bean> declaration configures a JmsProxyFactoryBean that makes the marketing service exported in section 10.4.2 available on the client side:

<bean id="marketing"
    class="org.logicblaze.lingo.jms.JmsProxyFactoryBean">
  <property name="connectionFactory" ref="connectionFactory" />
  <property name="destination" ref="destination" />
  <property name="serviceInterface"
      value="com.roadrantz.marketing.MarketingService" />
</bean>

You’ve already been introduced to the connectionFactory and destination properties—they serve the same purpose here as they have throughout this chapter. And the serviceInterface property specifies the Java interface that the proxy will implement. It is through this interface that RoadRantz will invoke the processMotoristInfo() method.

The most significant thing to notice about how JmsProxyFactoryBean is configured is the conspicuous absence of anything telling where the service is located. Unlike the proxy factory beans in chapter 8, there’s no IP address, hostname, or port number—no clue as to the service’s whereabouts. That’s because the service’s location isn’t important. You don’t need to know where it lives—only where it picks up its mail.

In fact, there’s nothing here indicating that there’s only one instance of the remote service. If we wanted to set up the marketing service for high availability, we would only have to start up two or more instances, with all of them listening to the same destination. Each instance could potentially process a request. Meanwhile, the client has no idea that there’s a pool of services waiting to respond to its request.

Making the call

With the JmsProxyFactoryBean wired, we’re ready to start making calls to the remote service. All we need to do is wire it into RantServiceImpl:

<bean id="rantService"
      class="com.roadrantz.service.RantServiceImpl">
  <property name="rantDao" ref="rantDao" />
  <property name="marketingService" ref="marketing" />
</bean>

Then we can use it in the addMotorist() method to send a SpammedMotorist object to the marketing service. Listing 10.10 shows the pertinent changes to RantServiceImpl to call the remote marketing service.

Example 10.10. Changing RantServiceImpl to send SpammedMotorists to the marketing service

As you can see, invoking a Lingo-exported service is no different from invoking an RMI service, a web service, or even a method on another bean in the same process. Nothing in listing 10.10 indicates that JMS is involved.

The only thing that is different is the Spring configuration. In this way, switching from JMS to another communication mechanism is a simple matter of changing the configuration. You could easily replace Lingo with one of the synchronous options from chapter 8. Or perhaps you could inject the marketingService property with a mock implementation of the MarketingService interface in the context of a unit test.

Summary

Asynchronous messaging presents several advantages over synchronous RPC. Indirect communication results in applications that are loosely coupled with respect to one another and thus reduces the impact of any one system going down. Additionally, because messages are forwarded to their recipients, there’s no need for a sender to wait for a response. In many circumstances, this can be a boost to application performance.

Although JMS provides a standard API for all Java applications wishing to participate in asynchronous communication, it can be a little cumbersome to use. Spring eliminates the need for JMS boilerplate code and exception-handling code and makes asynchronous messaging easier to use.

Coming up in the next chapter, we’ll watch worlds collide as we see how Spring supports the use and development of Enterprise JavaBeans.



[1] As a matter of fact, the setJmsTemplate() method is final in JmsGatewaySupport. As a result it can’t appear in any class that extends JmsGatewaySupport.

[2] Before I get emails, I’m quite aware that Old Faithful doesn’t have a cut-off valve. Old Faithful is a geyser, a natural phenomenon where incredibly hot water shoots out of the ground periodically—without being turned on or off. The valve comment was a joke.

[3] Okay, okay... I know... it’s still not a POJO because it implements the MessageListener interface. Nevertheless, many in the Spring community are satisfied to call this a message-driven POJO despite the dependence on MessageListener. If you’re still not convinced then hang on... we’ll look at pure-POJO MDPs in section 10.2.3.