Enterprise Java

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:

screenshot-at-mar-28-20-10-00

Enter the name of the queue and accept the default settings for now by clicking the ‘Create Queue’ button:

screenshot-at-mar-28-20-10-59

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

Pascal Alma

Pascal is a senior JEE Developer and Architect at 4Synergy in The Netherlands. Pascal has been designing and building J2EE applications since 2001. He is particularly interested in Open Source toolstack (Mule, Spring Framework, JBoss) and technologies like Web Services, SOA and Cloud technologies. Specialties: JEE, SOA, Mule ESB, Maven, Cloud Technology, Amazon AWS.
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

0 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
Back to top button