Using AWS SQS as JMS provider with Spring
Recently AWS published a new client library that implements the JMS 1.1 specification and uses their Simple Queue Service (SQS) as the JMS provider (see Jeff Barr’s post here). In my post I will show you how to set up your Maven project to use the Spring Framework to use this library.
We will perform the following steps:
- Create the queue in the AWS Management Console
- Set up your AWS credentials on your machine
- Set up your Maven project
- Create the Spring configuration
- Create the Java files to produce and receive messages
This post will only show some basic use of the SQS possibilities but should be good enough to get you started. I assume you have already created your AWS Account and you are familiair with Maven and basic Spring setup.
Create the queue in the AWS Management Console
First step is to create the queue so we can use it in our program. I show you how to create the queue with the Management Console but you can also create the necessary queues programmatically.
When you go to the Management Console you can select the SQS page and click the ‘Create New Queue’ button:
Enter the name of the queue and accept the default settings for now by clicking the ‘Create Queue’ button:
Set up your AWS credentials on your machine
To be able to access your AWS stuff with the Java SDK the easiest way is to create a ‘credentials.proeprties’ file in your users home directory. Create your credentials file at ~/.aws/credentials (C:\Users\USER_NAME\.aws\credentials for Windows users) and save the following lines after replacing the capitalized values with your own.
[default] aws_access_key_id = YOUR_ACCESS_KEY_ID aws_secret_access_key = YOUR_SECRET_ACCESS_KEY
Set up your Maven project
When using Maven to setup your project you can add the following dependencies to your pom to be able to use AWS SQS with Spring 4:
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>net.pascalalma.aws</groupId> <artifactId>sqs-client</artifactId> <version>1.0-SNAPSHOT</version> <properties> <version.spring>4.1.5.RELEASE</version.spring> </properties> <dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> <version>${version.spring}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>${version.spring}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jms</artifactId> <version>${version.spring}</version> </dependency> <dependency> <groupId>com.amazonaws</groupId> <artifactId>amazon-sqs-java-messaging-lib</artifactId> <version>1.0.0</version> <type>jar</type> </dependency> <dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>1.2.17</version> </dependency> </dependencies> </project>
Create the Spring configuration
In the first example I will use a simple MessageProducer and MessageConsumer class, in which the producer will put a message on the queue and the consumer will read one message from the queue. This is model is reffered to by AWS as a ‘synchronously’ call. The Spring config looks like this:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:context="http://www.springframework.org/schema/context" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd"> <context:component-scan base-package="net.pascalalma.aws.sqs"></context:component-scan> <bean id="credentialsProviderBean" class="com.amazonaws.auth.DefaultAWSCredentialsProviderChain"/> <bean id="connectionFactoryBuilder" class="com.amazon.sqs.javamessaging.SQSConnectionFactory$Builder"> <property name="regionName" value="eu-west-1"/> <property name="numberOfMessagesToPrefetch" value="5"/> <property name="awsCredentialsProvider" ref="credentialsProviderBean"/> </bean> <bean id="connectionFactory" class="com.amazon.sqs.javamessaging.SQSConnectionFactory" factory-bean="connectionFactoryBuilder" factory-method="build"/> <bean id="jmsTemplate" class="org.springframework.jms.core.JmsTemplate"> <property name="connectionFactory" ref="connectionFactory"/> <property name="defaultDestinationName" ref="queueName"/> </bean> <bean id="queueName" class="java.lang.String"> <constructor-arg value="DefaultDemoQueue"/> </bean> </beans>
Create the Java files to produce and receive messages
The last step is to create the necessary Java files. I think they are simple enough and self-explaining so I just show the source code here. First we have the MessageProducer that puts a message on the queue when it is executed:
package net.pascalalma.aws.sqs; import org.apache.log4j.Logger; import org.springframework.jms.core.JmsTemplate; import org.springframework.jms.core.MessageCreator; import org.springframework.stereotype.Service; import javax.annotation.Resource; import javax.jms.JMSException; import javax.jms.Message; import javax.jms.Session; import javax.jms.TextMessage; @Service("myMessageProviderService") public class MyMessageProvider { final static Logger logger = Logger.getLogger(MyMessageProvider.class); @Resource(name = "jmsTemplate") private JmsTemplate jmsTemplate; public void sendMessage(final String txt) { logger.debug(String.format("Sending message with txt: %s", txt)); jmsTemplate.send(new MessageCreator() { public Message createMessage(Session session) throws JMSException { final TextMessage msg = session.createTextMessage(txt); return msg; } }); logger.debug("Message sent "); } }
Next is the MessageConsumer which in this example just reads one message from the queue when it is executed:
package net.pascalalma.aws.sqs; import org.apache.log4j.Logger; import org.springframework.jms.core.JmsTemplate; import org.springframework.stereotype.Service; import javax.annotation.Resource; import javax.jms.JMSException; import javax.jms.Message; import javax.jms.TextMessage; @Service("myMessageConsumerService") public class MyMessageConsumer { final static Logger logger = Logger.getLogger(MyMessageConsumer.class); @Resource(name = "jmsTemplate") private JmsTemplate jmsTemplate; public void readMessage() throws JMSException { logger.debug("Reading message"); Message msg = jmsTemplate.receive(); if (msg instanceof TextMessage) { TextMessage txtmsg = (TextMessage) msg; logger.info(String.format("Received text: %s", txtmsg.getText())); } logger.debug(msg.getClass()); logger.info("Done"); } }
Finally there is the Main class that reads the Spring config and runs the Producer en Consumer:
package net.pascalalma.aws.sqs; import org.apache.log4j.Logger; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import javax.jms.JMSException; public class SpringMain { final static Logger logger = Logger.getLogger(SpringMain.class); public static void main(String[] args) { //Build application context by reading spring-config.xml ApplicationContext ctx = new ClassPathXmlApplicationContext(new String[]{"application-context.xml"}); //Get an instance of ProductService class; MyMessageProvider prdSvc = (MyMessageProvider) ctx.getBean("myMessageProviderService"); MyMessageConsumer conSvc = (MyMessageConsumer) ctx.getBean("myMessageConsumerService"); //Call getProduct method of ProductService prdSvc.sendMessage("This is a test"); try { conSvc.readMessage(); } catch (JMSException e) { logger.error(e); } } }
When you run the Main class you will see the following output when everything works:
2015-03-29 10:26:39 DEBUG net.pascalalma.aws.sqs.MyMessageProvider(28) - Sending message with txt: This is a test 2015-03-29 10:26:41 DEBUG net.pascalalma.aws.sqs.MyMessageProvider(35) - Message sent 2015-03-29 10:26:41 DEBUG net.pascalalma.aws.sqs.MyMessageConsumer(24) - Reading message 2015-03-29 10:26:41 INFO net.pascalalma.aws.sqs.MyMessageConsumer(29) - Received text: This is a test 2015-03-29 10:26:41 DEBUG net.pascalalma.aws.sqs.MyMessageConsumer(32) - class com.amazon.sqs.javamessaging.message.SQSTextMessage 2015-03-29 10:26:41 INFO net.pascalalma.aws.sqs.MyMessageConsumer(33) - Done Process finished with exit code 0
Reference: | Using AWS SQS as JMS provider with Spring from our JCG partner Pascal Alma at the The Pragmatic Integrator blog. |