This guide walks you through the process of publishing and subscribing to messages using a JMS broker.
You’ll build an application that uses Spring’s JmsTemplate to post a single message and subscribes to it with a POJO using MessageListenerAdapter.
-
About 15 minutes
-
ActiveMQ JMS broker (instructions below)
-
A favorite text editor or IDE
-
JDK 6 or later
-
You can also import the code from this guide as well as view the web page directly into Spring Tool Suite (STS) and work your way through it from there.
Like all Spring’s Getting Started guides, you can start from scratch and complete each step, or you can bypass basic setup steps that are already familiar to you. Either way, you end up with working code.
To start from scratch, move on to Set up the project.
To skip the basics, do the following:
-
Download and unzip the source repository for this guide, or clone it using Git:
$ git clone https://github.com/spring-guides/gs-messaging-jms.git
-
cd into gs-messaging-jms/initial.
-
Jump ahead to Install and run ActiveMQ broker.
When you’re finished, you can check your results against the code in gs-messaging-jms/complete.
First you set up a basic build script. You can use any build system you like when building apps with Spring, but the code you need to work with Gradle and {maven}[Maven] is included here. If you’re not familiar with either, refer to Building Java Projects with Gradle or Building Java Projects with Maven.
In a project directory of your choosing, create the following subdirectory structure; for example, with mkdir -p src/main/java/hello on *nix systems:
└── src └── main └── java └── hello
Below is the initial Gradle build file. But you can also use Maven. The pom.xml file is included right here. If you are using Spring Tool Suite (STS), you can import the guide directly.
buildscript {
repositories {
maven { url "http://repo.springsource.org/libs-snapshot" }
mavenLocal()
}
}
apply plugin: 'java'
apply plugin: 'eclipse'
apply plugin: 'idea'
jar {
baseName = 'gs-messaging-jms'
version = '0.1.0'
}
repositories {
mavenCentral()
maven { url "http://repo.springsource.org/libs-snapshot" }
}
dependencies {
compile("org.springframework.boot:spring-boot-starter-web:0.5.0.M2")
compile("org.springframework:spring-jms:4.0.0.M3")
compile("org.apache.activemq:activemq-client:5.8.0")
testCompile("junit:junit:4.11")
}
task wrapper(type: Wrapper) {
gradleVersion = '1.7'
}
This guide is using Spring Boot’s starter POMs.
To publish and subscribe to messages, you need to install a JMS broker. For this guide, you will use ActiveMQ.
Note
|
ActiveMQ is an AMQP broker that supports multiple protocols including JMS, the focus of this guide. |
Visit the ActiveMQ download page, get the proper version, then unpack it.
Alternatively, if you use a Mac with Homebrew:
$ brew install activemq
Or on Ubuntu Linux:
$ sudo apt-get install activemq
If you download the bundle, unpack it and cd into the bin folder. If you use a package manager like apt-get or brew, it should already be on your path.
Launch a simple broker:
$ activemq start
You see output something like this:
INFO: Using default configuration (you can configure options in one of these file: /etc/default/activemq /Users/gturnquist/.activemqrc) INFO: Invoke the following command to create a configuration file /usr/local/Cellar/activemq/5.8.0/libexec/bin/activemq setup [ /etc/default/activemq | /Users/gturnquist/.activemqrc ] INFO: Using java '/Library/Java/JavaVirtualMachines/jdk1.7.0_11.jdk/Contents/Home//bin/java' INFO: Starting - inspect logfiles specified in logging.properties and log4j.properties to get details INFO: pidfile created : '/usr/local/Cellar/activemq/5.8.0/libexec/data/activemq-retina.pid' (pid '7781')
Note
|
To shut down the broker, run activemq stop. |
Now you’re all set to run the rest of the code in this guide!
Spring provides the means to publish messages to any POJO.
package hello;
public class Receiver {
public void receiveMessage(String message) {
System.out.println("Received <" + message + ">");
}
}
This is also known as a message driven POJO. As you can see in the code above, there is no need to implement any particular interface or for the method to have any particular name.
Next, wire up a sender and a receiver.
package hello;
import javax.jms.ConnectionFactory;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.Session;
import org.apache.activemq.ActiveMQConnectionFactory;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jms.connection.CachingConnectionFactory;
import org.springframework.jms.core.JmsTemplate;
import org.springframework.jms.core.MessageCreator;
import org.springframework.jms.listener.SimpleMessageListenerContainer;
import org.springframework.jms.listener.adapter.MessageListenerAdapter;
@Configuration
public class Application {
static String mailboxDestination = "mailbox-destination";
@Bean
ConnectionFactory connectionFactory() {
return new CachingConnectionFactory(
new ActiveMQConnectionFactory("tcp://localhost:61616"));
}
@Bean
MessageListenerAdapter receiver() {
return new MessageListenerAdapter(new Receiver()) {{
setDefaultListenerMethod("receiveMessage");
}};
}
@Bean
SimpleMessageListenerContainer container(final MessageListenerAdapter messageListener,
final ConnectionFactory connectionFactory) {
return new SimpleMessageListenerContainer() {{
setMessageListener(messageListener);
setConnectionFactory(connectionFactory);
setDestinationName(mailboxDestination);
}};
}
@Bean
JmsTemplate jmsTemplate(ConnectionFactory connectionFactory) {
return new JmsTemplate(connectionFactory);
}
public static void main(String args[]) throws Throwable {
AnnotationConfigApplicationContext context =
new AnnotationConfigApplicationContext(Application.class);
MessageCreator messageCreator = new MessageCreator() {
@Override
public Message createMessage(Session session) throws JMSException {
return session.createTextMessage("ping!");
}
};
JmsTemplate jmsTemplate = context.getBean(JmsTemplate.class);
System.out.println("Sending a new mesage.");
jmsTemplate.send(mailboxDestination, messageCreator);
context.close();
}
}
The first key component is the ConnectionFactory interface. It consists of a CachingConnectionFactory wrapping an ActiveMQConnectionFactory pointed at tcp://localhost:61616, the default port of ActiveMQ.
To wrap the Receiver you coded earlier, use MessageListenerAdapter. Then use the setDefaultListenerMethod to configure which method to invoke when a message comes in. Thus you avoid implementing any JMS- or broker-specific interfaces.
The SimpleMessageListenerContainer class is an asynchronous message receiver. It uses the MessageListenerAdapter and the ConnectionFactory and is fired up when the application context starts. Another parameter is the queue name set in mailboxDestination.
Spring provides a convenient template class called JmsTemplate. JmsTemplate makes it very simple to send messages to a JMS message queue. In the main runner method, after starting things up, you create a MessageCreator and use it from jmsTemplate to send a message.
Warning
|
Spring’s JmsTemplate can receive messages directly through its receive method, but it only works synchronously, meaning it will block. That’s why Spring recommends that you use Spring’s SimpleMessageListenerContainer with a cache-based connection factory, so you can consume messages asynchronously and with maximum connection efficiency. |
Now that your Application class is ready, you simply instruct the build system to create a single, executable jar containing everything. This makes it easy to ship, version, and deploy the service as an application throughout the development lifecycle, across different environments, and so forth.
Below are the Gradle steps, but if you are using Maven, you can find the updated pom.xml right here and build it by typing mvn clean package.
Update your Gradle build.gradle file’s buildscript section, so that it looks like this:
buildscript {
repositories {
maven { url "http://repo.springsource.org/libs-snapshot" }
mavenLocal()
}
dependencies {
classpath("org.springframework.boot:spring-boot-gradle-plugin:0.5.0.M2")
}
}
Further down inside build.gradle, add the following to the list of applied plugins:
apply plugin: 'spring-boot'
You can see the final version of build.gradle right here.
The Spring Boot gradle plugin collects all the jars on the classpath and builds a single ``über-jar'', which makes it more convenient to execute and transport your service. It also searches for the public static void main() method to flag as a runnable class.
Now run the following command to produce a single executable JAR file containing all necessary dependency classes and resources:
$ ./gradlew build
If you are using Gradle, you can run the JAR by typing:
$ java -jar build/libs/gs-messaging-jms-0.1.0.jar
If you are using Maven, you can run the JAR by typing:
$ java -jar target/gs-messaging-jms-0.1.0.jar
Note
|
The procedure above will create a runnable JAR. You can also opt to build a classic WAR file instead. |
If you are using Gradle, you can run your service at the command line this way:
$ ./gradlew clean build && java -jar build/libs/gs-messaging-jms-0.1.0.jar
Note
|
If you are using Maven, you can run your service by typing mvn clean package && java -jar target/gs-messaging-jms-0.1.0.jar. |
When it runs, buried amidst all the logging, you should see these messages:
Sending a new mesage. Received <ping!>