Thursday, 19 April 2012

JMS with Spring configured in Java

Handling JMS using standard JMS API is a tedious task - you need to create a lot of boilerplate code.
Spring provides an abstraction layer that eases this pain.
In the following example I'm using ActiveMQ (which needs to be started before running any of the code below) but Spring works with other major JMS implementations as well.

In order to send a JMS message we can use JMS template. Here's our sender that's using it:

public class JmsSender {
private JmsTemplate jmsTemplate;
public void sendText(final String text) {
jmsTemplate.send(new MessageCreator() {
public Message createMessage(Session session) throws JMSException {
return session.createTextMessage(text);
}
});
}
public void setJmsTemplate(JmsTemplate jmsTemplate) {
this.jmsTemplate = jmsTemplate;
}
}
view raw JmsSender.java hosted with ❤ by GitHub

Spring beans needs to be configured in a context. Let's define Spring configuration in Java code:

@Configuration
public class JmsSenderSpringConfig {
@Bean
public JmsTemplate jmsTemplate() {
JmsTemplate jmsTemplate = new JmsTemplate();
jmsTemplate.setDefaultDestination(new ActiveMQQueue("jms.queue"));
jmsTemplate.setConnectionFactory(connectionFactory());
return jmsTemplate;
}
@Bean
public ActiveMQConnectionFactory connectionFactory() {
ActiveMQConnectionFactory activeMQConnectionFactory = new ActiveMQConnectionFactory();
activeMQConnectionFactory.setBrokerURL("tcp://localhost:61616");
return activeMQConnectionFactory;
}
@Bean
public JmsSender jmsSender() {
JmsSender jmsSender = new JmsSender();
jmsSender.setJmsTemplate(jmsTemplate());
return jmsSender;
}
}

JMS template must have a connection factory with a broker url and a destination (in our case it's a queue).
The XML configuration equivalent would look like:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
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.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<bean id="connectionFactory" class="org.apache.activemq.ActiveMQConnectionFactory">
<property name="brokerURL" value="tcp://localhost:61616"/>
</bean>
<bean id="jmsTemplate" class="org.springframework.jms.core.JmsTemplate">
<property name="connectionFactory" ref="connectionFactory"/>
<property name="defaultDestinationName" value="jms.queue"/>
</bean>
<bean id="jmsSender" class="pl.mjedynak.jms.sender.JmsSender">
<property name="jmsTemplate" ref="jmsTemplate"/>
</bean>
</beans>

Java configuration provides not only compile-time checking but in my opinion it is more straightforward and easier to control.
Unfortunately we can't escape XML completely, we need to turn on component scanning so that Spring will read configuration from classes in our package:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<context:component-scan base-package="pl.mjedynak"/>
</beans>

The example code that would run the sender:

public class JmsSenderRunner {
public static void main(String[] args) {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("sender-context.xml");
JmsSender jmsSender = (JmsSender) applicationContext.getBean("jmsSender");
jmsSender.sendText("hellooooo " + new Date());
}
}
Apart from the sender we can also create the receiver. One of the solutions is to make him asynchronous by implementing MessageListener interface.

public class JmsReceiver implements MessageListener {
@Override
public void onMessage(Message message) {
System.out.println("Received: " + message);
}
}

The Spring config looks as follows:

@Configuration
public class JmsReceiverSpringConfig {
@Bean
public JmsReceiver jmsReceiver() {
return new JmsReceiver();
}
@Bean
public ActiveMQConnectionFactory connectionFactory() {
ActiveMQConnectionFactory activeMQConnectionFactory = new ActiveMQConnectionFactory();
activeMQConnectionFactory.setBrokerURL("tcp://localhost:61616");
return activeMQConnectionFactory;
}
@Bean
public DefaultMessageListenerContainer messageListenerContainer() {
DefaultMessageListenerContainer messageListenerContainer = new DefaultMessageListenerContainer();
messageListenerContainer.setConnectionFactory(connectionFactory());
messageListenerContainer.setDestinationName("jms.queue");
messageListenerContainer.setMessageListener(jmsReceiver());
return messageListenerContainer;
}
}

Our receiver is set in the message listener container with the connection factory and the destination.
Receiver is missing the same XML configuration as sender, and the running code is analogical.

Here's the list of maven dependencies:
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>3.1.1.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jms</artifactId>
<version>3.1.1.RELEASE</version>
</dependency>
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>2.2</version>
</dependency>
<dependency>
<groupId>org.apache.activemq</groupId>
<artifactId>activemq-core</artifactId>
<version>5.5.1</version>
</dependency>
<dependency>
<groupId>org.apache.xbean</groupId>
<artifactId>xbean-spring</artifactId>
<version>3.5</version>
</dependency>
view raw deps.xml hosted with ❤ by GitHub

Monday, 2 April 2012

Testing asynchronous calls with awaitility

Testing asynchronous systems is not an easy task. Let's take a simple example:

public class FileCreator {
private ExecutorService executor = Executors.newCachedThreadPool();
public void createFile(final String fileName) {
executor.submit(new Callable<Path>() {
@Override
public Path call() throws Exception {
// simulate delay
Thread.sleep(500);
return Files.createFile(Paths.get(fileName));
}
});
}
}

We have a class that creates file based on a given file name. The interesting thing is, that it does it asynchronously using a thread pool and returns immediately to the caller.

Let's try to create a test for it. Our first attempt could look like that:

public class FileCreatorTestWithSleeping {
private FileCreator fileCreator = new FileCreator();
private String fileName = "file.txt";
@Before
public void setUp() throws IOException {
Files.deleteIfExists(Paths.get(fileName));
}
@Test
public void shouldCreateFile() throws InterruptedException {
// when
fileCreator.createFile(fileName);
Thread.sleep(1000);
// then
verifyThatFileExists(fileName);
}
private void verifyThatFileExists(String fileName) {
Path path = Paths.get(fileName);
assertThat(Files.exists(path), is(true));
}
}

The horrible thing about it is that Thread.sleep() invocation. Test should be fast, making them wait unnecessary is very poor solution. And what if the test sometimes fails because of overloaded hardware? Are we going to sleep even more?

To eliminate unneeded waiting, we may come up with a concept of validator:

public class FileCreatorTestWithCustomValidator {
private FileCreator fileCreator = new FileCreator();
private String fileName = "file.txt";
@Before
public void setUp() throws IOException {
Files.deleteIfExists(Paths.get(fileName));
}
@Test
public void shouldCreateFile() throws InterruptedException {
// when
fileCreator.createFile(fileName);
// then
boolean fileExists = checkThatFileExists(fileName);
assertThat(fileExists, is(true));
}
private boolean checkThatFileExists(String fileName)
throws InterruptedException {
for (int i = 0; i < 100; i++) {
Path path = Paths.get(fileName);
try {
assertThat(path, exists());
return true;
} catch (AssertionError e) {
// ignore exception
}
Thread.sleep(100);
}
throw new AssertionError("Timeout exceeded");
}
}

We no longer need to sleep for a long time, but the code has been significantly polluted. Of course we can refactor our validator, make it more reusable but why reinvent the wheel? There is a nice and small library - awaitility - that will do the same for us.

public class FileCreatorTestWithAwaitility {
private FileCreator fileCreator = new FileCreator();
private String fileName = "file.txt";
@Before
public void setUp() throws IOException {
Files.deleteIfExists(Paths.get(fileName));
}
@Test
public void shouldCreateFile() throws Exception {
// when
fileCreator.createFile(fileName);
// then
await().until(fileIsCreated(fileName));
}
private Callable<Boolean> fileIsCreated(final String fileName) {
return new Callable<Boolean>() {
@Override
public Boolean call() throws Exception {
Path path = Paths.get(fileName);
return Files.exists(path);
}
};
}
}

In a very expressive way, we achieve the same result. Timeout, polling delay and polling interval are of course configurable.