Wednesday, November 26, 2014

JMS - Publish/Subscribe messaging example using ActiveMQ & Maven


In a publish/subscribe (pub/sub) product or application, clients address messages to a topic, which functions somewhat like a bulletin board. Subscribers can receive information, in the form of messages, from publishers. Topics retain messages only as long as it takes to distribute them to current subscribers. The following post introduces the basic concepts of JMS point-to-point messaging and illustrates them with a code sample using ActiveMQ and Maven.

Publish/Subscribe Messaging

Pub/sub messaging has the following characteristics:
  • Each message can have multiple consumers.
  • Publishers and subscribers have a timing dependency. A client that subscribes to a topic can consume only messages published after the client has created a subscription, and the subscriber must continue to be active in order for it to consume messages.
The JMS API relaxes this timing dependency mentioned in the second bullet to some extent by allowing subscribers to create durable subscriptions, which receive messages sent while the subscribers are not active. Durable subscriptions provide the flexibility and reliability of queues but still allow clients to send messages to many recipients.

ActiveMQ Example

Let's illustrate the above characteristics by creating a message producer that sends a message containing a first and last name to a topic. In turn a message consumer will read the message and transform it into a greeting. The code is very similar to the JMS Hello World example but contains a few key differences explained below.

Tools used:
  • ActiveMQ 5.10
  • Maven 3

The code is built and run using Maven. Specified below is the Maven POM file which contains the needed dependencies for Logback, JUnit and ActiveMQ.
<?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>info.source4code</groupId>
<artifactId>jms-activemq-publish-subscribe</artifactId>
<version>1.0</version>
<packaging>jar</packaging>

<name>JMS - Publish/Subscribe messaging using ActiveMQ</name>
<url>http://www.source4code.info/2014/11/jms-publish-subscribe-messaging-example-activemq-maven.html</url>

<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<java.version>1.6</java.version>

<logback.version>1.1.2</logback.version>
<slf4j.version>1.7.7</slf4j.version>
<junit.version>4.12-beta-2</junit.version>
<activemq.version>5.10.0</activemq.version>

<maven-compiler-plugin.version>3.1</maven-compiler-plugin.version>
</properties>

<dependencies>
<!-- Logging -->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>${logback.version}</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>${slf4j.version}</version>
</dependency>
<!-- JUnit -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>${junit.version}</version>
<scope>test</scope>
</dependency>
<!-- ActiveMQ -->
<dependency>
<groupId>org.apache.activemq</groupId>
<artifactId>activemq-all</artifactId>
<version>${activemq.version}</version>
</dependency>
</dependencies>

<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>${maven-compiler-plugin.version}</version>
<configuration>
<source>${java.version}</source>
<target>${java.version}</target>
</configuration>
</plugin>
</plugins>
</build>
</project>

Nondurable Subscription

The Publisher class contains a constructor which creates a message producer and needed connection and session objects. The sendName() operation takes as input a first and last name which are set on a TextMessage which in turn is sent to the topic set on the message producer.
package info.source4code.jms.activemq.pubsub;

import javax.jms.Connection;
import javax.jms.ConnectionFactory;
import javax.jms.JMSException;
import javax.jms.MessageProducer;
import javax.jms.Session;
import javax.jms.TextMessage;
import javax.jms.Topic;

import org.apache.activemq.ActiveMQConnection;
import org.apache.activemq.ActiveMQConnectionFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class Publisher {

private static final Logger LOGGER = LoggerFactory
.getLogger(Publisher.class);

private String clientId;
private Connection connection;
private Session session;
private MessageProducer messageProducer;

public void create(String clientId, String topicName) throws JMSException {
this.clientId = clientId;

// create a Connection Factory
ConnectionFactory connectionFactory = new ActiveMQConnectionFactory(
ActiveMQConnection.DEFAULT_BROKER_URL);

// create a Connection
connection = connectionFactory.createConnection();
connection.setClientID(clientId);

// create a Session
session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);

// create the Topic to which messages will be sent
Topic topic = session.createTopic(topicName);

// create a MessageProducer for sending messages
messageProducer = session.createProducer(topic);
}

public void closeConnection() throws JMSException {
connection.close();
}

public void sendName(String firstName, String lastName) throws JMSException {
String text = firstName + " " + lastName;

// create a JMS TextMessage
TextMessage textMessage = session.createTextMessage(text);

// send the message to the topic destination
messageProducer.send(textMessage);

LOGGER.debug(clientId + ": sent message with text='{}'", text);
}
}

The Subscriber class contains a constructor which creates a message consumer and needed connection and session objects. The getGreeting() operation reads a message from the topic and creates a greeting which is returned. A timeout parameter is passed to assure that the method does not wait indefinitely for a message to arrive.
package info.source4code.jms.activemq.pubsub;

import javax.jms.Connection;
import javax.jms.ConnectionFactory;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageConsumer;
import javax.jms.Session;
import javax.jms.TextMessage;
import javax.jms.Topic;

import org.apache.activemq.ActiveMQConnection;
import org.apache.activemq.ActiveMQConnectionFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class Subscriber {

private static final Logger LOGGER = LoggerFactory
.getLogger(Subscriber.class);

private static final String NO_GREETING = "no greeting";

private String clientId;
private Connection connection;
private Session session;
private MessageConsumer messageConsumer;

public void create(String clientId, String topicName) throws JMSException {
this.clientId = clientId;

// create a Connection Factory
ConnectionFactory connectionFactory = new ActiveMQConnectionFactory(
ActiveMQConnection.DEFAULT_BROKER_URL);

// create a Connection
connection = connectionFactory.createConnection();
connection.setClientID(clientId);

// create a Session
session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);

// create the Topic from which messages will be received
Topic topic = session.createTopic(topicName);

// create a MessageConsumer for receiving messages
messageConsumer = session.createConsumer(topic);

// start the connection in order to receive messages
connection.start();
}

public void closeConnection() throws JMSException {
connection.close();
}

public String getGreeting(int timeout) throws JMSException {

String greeting = NO_GREETING;

// read a message from the topic destination
Message message = messageConsumer.receive(timeout);

// check if a message was received
if (message != null) {
// cast the message to the correct type
TextMessage textMessage = (TextMessage) message;

// retrieve the message content
String text = textMessage.getText();
LOGGER.debug(clientId + ": received message with text='{}'", text);

// create greeting
greeting = "Hello " + text + "!";
} else {
LOGGER.debug(clientId + ": no message received");
}

LOGGER.info("greeting={}", greeting);
return greeting;
}
}

The below JUnit test class will be used to illustrate the Pub/Sub messaging characteristics mentioned at the beginning of this post. The testGreeting() test case verifies the correct working of the getGreeting() method of the Subscriber class.

The testMultipleConsumers() test case will verify that the same message can be read by multiple consumers. In order to test this, two Subscriber instances are created on the same 'multipleconsumers.t' topic.

Finally the testNonDurableSubscriber() test case will illustrate the timing dependency between publisher and subscriber. First a message is sent to a topic on which only one subscriber listens. Then a second subscriber is added to the same topic and a second message is sent. The result is that the second subscriber only receives the second message and not the first one whereas the first subscriber has received both messages.
package info.source4code.jms.activemq.pubsub;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;

import javax.jms.JMSException;

import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;

public class SubscriberTest {

private static Publisher publisherPublishSubscribe,
publisherMultipleConsumers, publisherNonDurableSubscriber;
private static Subscriber subscriberPublishSubscribe,
subscriber1MultipleConsumers, subscriber2MultipleConsumers,
subscriber1NonDurableSubscriber, subscriber2NonDurableSubscriber;

@BeforeClass
public static void setUpBeforeClass() throws Exception {
publisherPublishSubscribe = new Publisher();
publisherPublishSubscribe.create("publisher-publishsubscribe",
"publishsubscribe.t");

publisherMultipleConsumers = new Publisher();
publisherMultipleConsumers.create("publisher-multipleconsumers",
"multipleconsumers.t");

publisherNonDurableSubscriber = new Publisher();
publisherNonDurableSubscriber.create("publisher-nondurablesubscriber",
"nondurablesubscriber.t");

subscriberPublishSubscribe = new Subscriber();
subscriberPublishSubscribe.create("subscriber-publishsubscribe",
"publishsubscribe.t");

subscriber1MultipleConsumers = new Subscriber();
subscriber1MultipleConsumers.create("subscriber1-multipleconsumers",
"multipleconsumers.t");

subscriber2MultipleConsumers = new Subscriber();
subscriber2MultipleConsumers.create("subscriber2-multipleconsumers",
"multipleconsumers.t");

subscriber1NonDurableSubscriber = new Subscriber();
subscriber1NonDurableSubscriber.create(
"subscriber1-nondurablesubscriber", "nondurablesubscriber.t");

subscriber2NonDurableSubscriber = new Subscriber();
subscriber2NonDurableSubscriber.create(
"subscriber2-nondurablesubscriber", "nondurablesubscriber.t");
}

@AfterClass
public static void tearDownAfterClass() throws Exception {
publisherPublishSubscribe.closeConnection();
publisherMultipleConsumers.closeConnection();
publisherNonDurableSubscriber.closeConnection();

subscriberPublishSubscribe.closeConnection();
subscriber1MultipleConsumers.closeConnection();
subscriber2MultipleConsumers.closeConnection();
subscriber1NonDurableSubscriber.closeConnection();
subscriber2NonDurableSubscriber.closeConnection();
}

@Test
public void testGetGreeting() {
try {
publisherPublishSubscribe.sendName("Peregrin", "Took");

String greeting1 = subscriberPublishSubscribe.getGreeting(1000);
assertEquals("Hello Peregrin Took!", greeting1);

String greeting2 = subscriberPublishSubscribe.getGreeting(1000);
assertEquals("no greeting", greeting2);

} catch (JMSException e) {
fail("a JMS Exception occurred");
}
}

@Test
public void testMultipleConsumers() {
try {
publisherMultipleConsumers.sendName("Gandalf", "the Grey");

String greeting1 = subscriber1MultipleConsumers.getGreeting(1000);
assertEquals("Hello Gandalf the Grey!", greeting1);

String greeting2 = subscriber2MultipleConsumers.getGreeting(1000);
assertEquals("Hello Gandalf the Grey!", greeting2);

} catch (JMSException e) {
fail("a JMS Exception occurred");
}
}

@Test
public void testNonDurableSubscriber() {
try {
// nondurable subscriptions, will not receive messages sent while
// the subscribers are not active
subscriber2NonDurableSubscriber.closeConnection();

publisherNonDurableSubscriber.sendName("Bilbo", "Baggins");

// recreate a connection for the nondurable subscription
subscriber2NonDurableSubscriber.create(
"subscriber2-nondurablesubscriber",
"nondurablesubscriber.t");

publisherNonDurableSubscriber.sendName("Frodo", "Baggins");

String greeting1 = subscriber1NonDurableSubscriber
.getGreeting(1000);
assertEquals("Hello Bilbo Baggins!", greeting1);
String greeting2 = subscriber1NonDurableSubscriber
.getGreeting(1000);
assertEquals("Hello Frodo Baggins!", greeting2);

String greeting3 = subscriber2NonDurableSubscriber
.getGreeting(1000);
assertEquals("Hello Frodo Baggins!", greeting3);
String greeting4 = subscriber2NonDurableSubscriber
.getGreeting(1000);
assertEquals("no greeting", greeting4);

} catch (JMSException e) {
fail("a JMS Exception occurred");
}
}
}

Make sure a default ActiveMQ message broker is up and running, open a command prompt and execute following Maven command:
mvn -Dtest=SubscriberTest test

This will trigger Maven to run the above test cases which should result in the following log statements.
07:24:00.299 DEBUG [main][Publisher]
publisher-multipleconsumers: sent message with text='Gandalf the Grey'
07:24:00.303 DEBUG [main][Subscriber]
subscriber1-multipleconsumers: received message with text='Gandalf the Grey'
07:24:00.303 INFO [main][Subscriber]
greeting=Hello Gandalf the Grey!
07:24:00.304 DEBUG [main][Subscriber]
subscriber2-multipleconsumers: received message with text='Gandalf the Grey'
07:24:00.304 INFO [main][Subscriber]
greeting=Hello Gandalf the Grey!
07:24:00.306 DEBUG [main][Publisher]
publisher-publishsubscribe: sent message with text='Peregrin Took'
07:24:00.306 DEBUG [main][Subscriber]
subscriber-publishsubscribe: received message with text='Peregrin Took'
07:24:00.307 INFO [main][Subscriber]
greeting=Hello Peregrin Took!
07:24:01.307 DEBUG [main][Subscriber]
subscriber-publishsubscribe: no message received
07:24:01.307 INFO [main][Subscriber]
greeting=no greeting
07:24:01.320 DEBUG [main][Publisher]
publisher-nondurablesubscriber: sent message with text='Bilbo Baggins'
07:24:01.337 DEBUG [main][Publisher]
publisher-nondurablesubscriber: sent message with text='Frodo Baggins'
07:24:01.338 DEBUG [main][Subscriber]
subscriber1-nondurablesubscriber: received message with text='Bilbo Baggins'
07:24:01.338 INFO [main][Subscriber]
greeting=Hello Bilbo Baggins!
07:24:01.338 DEBUG [main][Subscriber]
subscriber1-nondurablesubscriber: received message with text='Frodo Baggins'
07:24:01.338 INFO [main][Subscriber]
greeting=Hello Frodo Baggins!
07:24:01.339 DEBUG [main][Subscriber]
subscriber2-nondurablesubscriber: received message with text='Frodo Baggins'
07:24:01.339 INFO [main][Subscriber]
greeting=Hello Frodo Baggins!
07:24:02.339 DEBUG [main][Subscriber]
subscriber2-nondurablesubscriber: no message received
07:24:02.339 INFO [main][Subscriber]
greeting=no greeting

Durable Subscription

As mentioned in the beginning of this post it is also possible to create a durable subscription which allows to receive messages sent while the subscribers are not active. The JMS specification dictates that the identification of a specific durable subscription is done by a combination of the client ID, the durable subscription name and the topic name.

As a result the below DurableSubscriber has three main differences with the previous Subscriber class:
  • A clientId is mandatory on the connection in order to allow a JMS provider to uniquely identify a durable subscriber.
  • A durable subscriber is created using Session.CreateDurableSubscriber.
  • A subscriptionName is needed when creating the durable subscriber.
Note that creating a MessageConsumer provides the same features as creating a TopicSubscriber. The TopicSubscriber is provided to support existing code.
    package info.source4code.jms.activemq.pubsub;

    import javax.jms.Connection;
    import javax.jms.ConnectionFactory;
    import javax.jms.JMSException;
    import javax.jms.Message;
    import javax.jms.MessageConsumer;
    import javax.jms.Session;
    import javax.jms.TextMessage;
    import javax.jms.Topic;

    import org.apache.activemq.ActiveMQConnection;
    import org.apache.activemq.ActiveMQConnectionFactory;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;

    public class DurableSubscriber {

    private static final Logger LOGGER = LoggerFactory
    .getLogger(DurableSubscriber.class);

    private static final String NO_GREETING = "no greeting";

    private String clientId;
    private Connection connection;
    private Session session;
    private MessageConsumer messageConsumer;

    private String subscriptionName;

    public void create(String clientId, String topicName,
    String subscriptionName) throws JMSException {
    this.clientId = clientId;
    this.subscriptionName = subscriptionName;

    // create a Connection Factory
    ConnectionFactory connectionFactory = new ActiveMQConnectionFactory(
    ActiveMQConnection.DEFAULT_BROKER_URL);

    // create a Connection
    connection = connectionFactory.createConnection();
    connection.setClientID(clientId);

    // create a Session
    session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);

    // create the Topic from which messages will be received
    Topic topic = session.createTopic(topicName);

    // create a MessageConsumer for receiving messages
    messageConsumer = session.createDurableSubscriber(topic,
    subscriptionName);

    // start the connection in order to receive messages
    connection.start();
    }

    public void removeDurableSubscriber() throws JMSException {
    messageConsumer.close();
    session.unsubscribe(subscriptionName);
    }

    public void closeConnection() throws JMSException {
    connection.close();
    }

    public String getGreeting(int timeout) throws JMSException {

    String greeting = NO_GREETING;

    // read a message from the topic destination
    Message message = messageConsumer.receive(timeout);

    // check if a message was received
    if (message != null) {
    // cast the message to the correct type
    TextMessage textMessage = (TextMessage) message;

    // retrieve the message content
    String text = textMessage.getText();
    LOGGER.debug(clientId + ": received message with text='{}'", text);

    // create greeting
    greeting = "Hello " + text + "!";
    } else {
    LOGGER.debug(clientId + ": no message received");
    }

    LOGGER.info("greeting={}", greeting);
    return greeting;
    }
    }

    The below JUnit test class will be used to illustrate the durable subscriber messaging characteristics. It contains a testDurableSubscriber() test case that will first remove one of the two durable subscribers that are listening on the 'durablesubscriber.t' topic by closing it's connection to the broker. Then a first message is sent to this topic on which only one subscribers is still actively listening. The second subscriber is recreated using the same client ID and subscription name and a second message is sent. The expected result is that both subscribers receive the two messages.
    Note that in the tearDownAfterClass() method the durable subscriptions are removed in order to avoid an error when rerunning the test case.
    package info.source4code.jms.activemq.pubsub;

    import static org.junit.Assert.assertEquals;
    import static org.junit.Assert.fail;

    import javax.jms.JMSException;

    import org.junit.AfterClass;
    import org.junit.BeforeClass;
    import org.junit.Test;

    public class DurableSubscriberTest {

    private static Publisher publisherPublishSubscribe,
    publisherDurableSubscriber;
    private static DurableSubscriber subscriberPublishSubscribe,

    subscriber1DurableSubscriber, subscriber2DurableSubscriber;

    @BeforeClass
    public static void setUpBeforeClass() throws Exception {
    publisherPublishSubscribe = new Publisher();
    publisherPublishSubscribe.create("publisher-publishsubscribe",
    "publishsubscribe.t");

    publisherDurableSubscriber = new Publisher();
    publisherDurableSubscriber.create("publisher-durablesubscriber",
    "durablesubscriber.t");

    subscriberPublishSubscribe = new DurableSubscriber();
    subscriberPublishSubscribe.create("subscriber-publishsubscribe",
    "publishsubscribe.t", "publishsubscribe");

    subscriber1DurableSubscriber = new DurableSubscriber();
    subscriber1DurableSubscriber.create("subscriber1-durablesubscriber",
    "durablesubscriber.t", "durablesubscriber1");

    subscriber2DurableSubscriber = new DurableSubscriber();
    subscriber2DurableSubscriber.create("subscriber2-durablesubscriber",
    "durablesubscriber.t", "durablesubscriber2");
    }

    @AfterClass
    public static void tearDownAfterClass() throws Exception {
    publisherPublishSubscribe.closeConnection();
    publisherDurableSubscriber.closeConnection();

    // remove the durable subscriptions
    subscriberPublishSubscribe.removeDurableSubscriber();
    subscriber1DurableSubscriber.removeDurableSubscriber();
    subscriber2DurableSubscriber.removeDurableSubscriber();

    subscriberPublishSubscribe.closeConnection();
    subscriber1DurableSubscriber.closeConnection();
    subscriber2DurableSubscriber.closeConnection();
    }

    @Test
    public void testGetGreeting() {
    try {
    publisherPublishSubscribe.sendName("Peregrin", "Took");

    String greeting1 = subscriberPublishSubscribe.getGreeting(1000);
    assertEquals("Hello Peregrin Took!", greeting1);

    String greeting2 = subscriberPublishSubscribe.getGreeting(1000);
    assertEquals("no greeting", greeting2);

    } catch (JMSException e) {
    fail("a JMS Exception occurred");
    }
    }

    @Test
    public void testDurableSubscriber() {
    try {
    // durable subscriptions, receive messages sent while the
    // subscribers are not active
    subscriber2DurableSubscriber.closeConnection();

    publisherDurableSubscriber.sendName("Bilbo", "Baggins");

    // recreate a connection for the durable subscription
    subscriber2DurableSubscriber.create(
    "subscriber2-durablesubscriber", "durablesubscriber.t",
    "durablesubscriber2");

    publisherDurableSubscriber.sendName("Frodo", "Baggins");

    String greeting1 = subscriber1DurableSubscriber.getGreeting(1000);
    assertEquals("Hello Bilbo Baggins!", greeting1);
    String greeting2 = subscriber2DurableSubscriber.getGreeting(1000);
    assertEquals("Hello Bilbo Baggins!", greeting2);

    String greeting3 = subscriber1DurableSubscriber.getGreeting(1000);
    assertEquals("Hello Frodo Baggins!", greeting3);
    String greeting4 = subscriber2DurableSubscriber.getGreeting(1000);
    assertEquals("Hello Frodo Baggins!", greeting4);

    } catch (JMSException e) {
    fail("a JMS Exception occurred");
    }
    }
    }

    Make sure a default ActiveMQ message broker is up and running, open a command prompt and execute following Maven command:
    mvn -Dtest=DurableSubscriberTest test

    This will trigger Maven to run the above test cases which should result in the following log statements.
    18:58:54.591 DEBUG [main][Publisher]
    publisher-durablesubscriber: sent message with text='Bilbo Baggins'
    18:58:54.632 DEBUG [main][Publisher]
    publisher-durablesubscriber: sent message with text='Frodo Baggins'
    18:58:54.633 DEBUG [main][DurableSubscriber]
    subscriber1-durablesubscriber: received message with text='Bilbo Baggins'
    18:58:54.634 INFO [main][DurableSubscriber]
    greeting=Hello Bilbo Baggins!
    18:58:54.635 DEBUG [main][DurableSubscriber]
    subscriber2-durablesubscriber: received message with text='Bilbo Baggins'
    18:58:54.635 INFO [main][DurableSubscriber]
    greeting=Hello Bilbo Baggins!
    18:58:54.636 DEBUG [main][DurableSubscriber]
    subscriber1-durablesubscriber: received message with text='Frodo Baggins'
    18:58:54.636 INFO [main][DurableSubscriber]
    greeting=Hello Frodo Baggins!
    18:58:54.636 DEBUG [main][DurableSubscriber]
    subscriber2-durablesubscriber: received message with text='Frodo Baggins'
    18:58:54.637 INFO [main][DurableSubscriber]
    greeting=Hello Frodo Baggins!
    18:58:54.669 DEBUG [main][Publisher]
    publisher-publishsubscribe: sent message with text='Peregrin Took'
    18:58:54.670 DEBUG [main][DurableSubscriber]
    subscriber-publishsubscribe: received message with text='Peregrin Took'
    18:58:54.670 INFO [main][DurableSubscriber]
    greeting=Hello Peregrin Took!
    18:58:55.670 DEBUG [main][DurableSubscriber]
    subscriber-publishsubscribe: no message received
    18:58:55.670 INFO [main][DurableSubscriber]
    greeting=no greeting


    github icon
    If you would like to run the above code sample you can download the full source code and their corresponding JUnit test cases here.

    This concludes the JMS publish/subscribe example using ActiveMQ. If you found this post helpful or have any questions or remarks, please leave a comment.

    Tuesday, November 18, 2014

    JMS - Install RabbitMQ on Windows

    RabbitMQ is an open source message broker software that implements the Advanced Message Queuing Protocol (AMQP). The RabbitMQ server is written in the Erlang programming language and client libraries to interface with the broker are available for all major programming languages. Following tutorial shows how to install RabbitMQ and perform a start/stop of the installed instance on Windows.

    Install Erlang

    Erlang is a general-purpose concurrent, garbage-collected programming language and runtime system. It was designed by Ericsson to support distributed, fault-tolerant applications. It was originally a proprietary language within Ericsson, but was released as open source in 1998. OTP (Open Telecom Platform) is the open source distribution of Erlang.

    First thing to do is to download the OTP binaries. Go the the Erlang download page and click on the Windows binary link for your system (32-bit or 64-bit). At the time of writing the latest stable release was 'otp_win64_17.3.exe'. Note that there are also pre-built packages for platforms such as: Raspbian, Ubuntu, Fedora, OS X, and more.

    Double click to run the downloaded '.exe' file and click Next keeping the default settings on the first installer step.

    erlang installer welcome

    Optionally change the default destination folder and click Next and then Install. In the example below the install location was change to 'D:\source4code\tools\erl6.2'. From now on we will refer to this directory as: [erlang_install_dir].

    erlang installer installation path

    If Microsoft Visual C++ is not already setup on your system, a second installer window will pop-up. Click the 'I have read and accept the license terms' check-box and click Install.

    microsoft visual c++ installer

    Click Finish when the Microsoft Visual C++ setup is complete and then click Close to finish the OTP installation.

    microsoft visual c++ installer completed

    In order for Erlang applications to be able to run we need to setup an 'ERLANG_HOME' environment variable that will point to the Erlang installation directory. When using Windows the above parameters can be configured on the Environment Variables panel. Click on the Windows Start button and enter "env" without quotes as shown below.

    edit environment variables for your account

    Environment variables can be set at account level or at system level. For this example click on Edit environment variables for your account and following panel should appear.

    environment variables panel

    Click on the New button and enter "ERLANG_HOME" as variable name and the [erlang_install_dir] as variable value. In this tutorial the installation directory is "D:\source4code\tools\erl6.2". Click OK to to save.

    erlang_home user variable

    Install RabbitMQ

    RabbitMQ can be downloaded from the RabbitMQ download page. There are a number of different download packages available, for this tutorial we will be installing the manual install package on Windows.At the time of writing the latest stable release was 'rabbitmq-server-windows-3.4.1.zip'.

    Extract the binaries archive downloaded in the previous step. The extracted root directory should contain a number of files and subdirectories as shown below. From now on we will refer to this directory as: [rabbitmq_install_dir].

    In order to start RabbitMQ, open a command prompt by clicking on the Windows Start button and typing "cmd" followed by pressing ENTER. A new command prompt window should open. Navigate to the [rabbitmq_install_dir]/sbin and enter following command:
    rabbitmq-server

    rabbitmq start command

    In order to stop RabbitMQ, open another command prompt at the [rabbitmq_install_dir]/sbin and enter following command:
    rabbitmqctl stop

    rabbitmq stop command

    Setup RabbitMQ

    The 'rabbitmq-management' plugin provides a browser-based UI for management and monitoring of the RabbitMQ server . In order to enable the UI, make sure RabbitMQ is running and open a new command prompt at [rabbitmq_install_dir]/sbin in which you enter following:
    rabbitmq-plugins enable rabbitmq_management

    rabbitmq enable web console

    Open the RabbitMQ web console in a browser using: http://localhost:15672 and following page should be displayed:

    rabbitmq web console login

    Enter following default credentials: Username="guest" and Password="guest" and click on Login. The overview page will be displayed that shows some basic information on the RabbitMQ server:

    rabbitmq web console


    This concludes setting up and configuring RabbitMQ. If you found this post helpful or have any questions or remarks, please leave a comment.

    Wednesday, November 12, 2014

    EasyMock - unit testing FacesContext using PowerMock, JUnit and Maven

    easymock logo
    JSF defines the FacesContext abstract base class for representing all of the contextual information associated with processing an incoming request, and creating the corresponding response. When writing unit test cases for a JSF application there might be a need to mock some of FacesContext static methods. The following post will illustrate how to do this using PowerMock, which is a framework that allows you to extend mock libraries like EasyMock with extra capabilities. In this case the capability to mock the static methods of FacesContext.

    Tools used:
    • JUnit 4.11
    • EasyMock 3.2
    • PowerMock 1.5
    • Maven 3

    The code sample is built and run using Maven. Specified below is the Maven POM file which contains the needed dependencies for JUnit, EasyMock and PowerMock. In addition the PowerMock support module for JUnit ('powermock-module-junit4') and the PowerMock API for EasyMock ('powermock-api-easymock') dependencies need to be added as specified here.

    As the FacesContext class is used in this code sample, dependencies to the EL (Expression Language) API and JSF specification API are also included.

    Note that the version of JUnit is not the latest as there seems to be a bug where PowerMock doesn't recognize the correct JUnit version when using JUnit 4.12.

    <?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>info.source4code</groupId>
    <artifactId>easymock-powermock-facescontext</artifactId>
    <version>1.0</version>
    <packaging>jar</packaging>

    <name>EasyMock - Mocking FacesContext using PowerMock</name>
    <url>http://www.source4code.info/2014/11/easymock-mocking-facescontext-using.html</url>

    <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <java.version>1.6</java.version>

    <junit.version>4.11</junit.version>
    <easymock.version>3.2</easymock.version>
    <powermock.version>1.5.6</powermock.version>

    <el.version>2.2.1-b04</el.version>
    <jsf.version>2.2.8-02</jsf.version>

    <maven-compiler-plugin.version>3.1</maven-compiler-plugin.version>
    </properties>

    <dependencies>
    <!-- JUnit -->
    <dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>${junit.version}</version>
    <scope>test</scope>
    </dependency>
    <!-- EasyMock -->
    <dependency>
    <groupId>org.easymock</groupId>
    <artifactId>easymock</artifactId>
    <version>${easymock.version}</version>
    <scope>test</scope>
    </dependency>
    <!-- PowerMock -->
    <dependency>
    <groupId>org.powermock</groupId>
    <artifactId>powermock-module-junit4</artifactId>
    <version>${powermock.version}</version>
    <scope>test</scope>
    </dependency>
    <dependency>
    <groupId>org.powermock</groupId>
    <artifactId>powermock-api-easymock</artifactId>
    <version>${powermock.version}</version>
    <scope>test</scope>
    </dependency>
    <!-- EL (Unified Expression Language) -->
    <dependency>
    <groupId>javax.el</groupId>
    <artifactId>el-api</artifactId>
    <version>${el.version}</version>
    <scope>test</scope>
    </dependency>
    <!-- JSF -->
    <dependency>
    <groupId>com.sun.faces</groupId>
    <artifactId>jsf-api</artifactId>
    <version>${jsf.version}</version>
    </dependency>
    </dependencies>

    <build>
    <plugins>
    <plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-compiler-plugin</artifactId>
    <version>${maven-compiler-plugin.version}</version>
    <configuration>
    <source>${java.version}</source>
    <target>${java.version}</target>
    </configuration>
    </plugin>
    </plugins>
    </build>
    </project>

    The SomeBean class below contains two methods that make use of FacesContext. The first addMessage() method will create a new FacesMessage and add it to the FacesContext. The second logout() method will invalidate the current session.

    package info.source4code.mockito;

    import javax.faces.application.FacesMessage;
    import javax.faces.application.FacesMessage.Severity;
    import javax.faces.bean.ManagedBean;
    import javax.faces.context.FacesContext;

    @ManagedBean
    @SessionScoped
    public class SomeBean {

    public void addMessage(Severity severity, String summary, String detail) {
    FacesContext.getCurrentInstance().addMessage(null,
    new FacesMessage(severity, summary, detail));
    }

    public String logout() {
    FacesContext.getCurrentInstance().getExternalContext()
    .invalidateSession();

    return "logout?faces-redirect=true";
    }
    }

    Next is the SomeBeanTest JUnit test class. The class is annotated using two annotations. The first @RunWith annotation tells JUnit to run the test using PowerMockRunner. The second @PrepareForTest annotation tells PowerMock to prepare to mock the FacesContext class. If there are multiple classes to be prepared for mocking, they can be specified using a comma separated list.

    In the setup() method a number of objects are specified that are similar for the two test cases. The mockStatic() method is called in order to tell PowerMock to mock all static methods of the given FacesContext class. In addition the FacesContext and ExternalContext mock objects are created.

    Next are the two test cases which follow the basic EasyMock testing steps:
    StepAction
    1Call expect(mock.[method call]).andReturn([result]) for each expected call
    2Call mock.[method call], then EasyMock.expectLastCall() for each expected void call
    3Call replay(mock) to switch from “record” mode to “playback” mode
    4Call the test method
    5Call verify(mock) to assure that all expected calls happened

    In addition to this, the first addMessage() test case uses the Capture capability of EasyMock in order to test whether a FacesMessage with the correct values was added to the FacesContext. The second testLogout() test case checks if the correct redirect was returned.

    package info.source4code.easymock;

    import static org.easymock.EasyMock.capture;
    import static org.easymock.EasyMock.createMock;
    import static org.easymock.EasyMock.expect;
    import static org.easymock.EasyMock.expectLastCall;
    import static org.easymock.EasyMock.replay;
    import static org.easymock.EasyMock.verify;
    import static org.junit.Assert.assertEquals;
    import static org.junit.Assert.assertNull;

    import javax.faces.application.FacesMessage;
    import javax.faces.context.ExternalContext;
    import javax.faces.context.FacesContext;

    import org.easymock.Capture;
    import org.junit.Before;
    import org.junit.Test;
    import org.junit.runner.RunWith;
    import org.powermock.api.easymock.PowerMock;
    import org.powermock.core.classloader.annotations.PrepareForTest;
    import org.powermock.modules.junit4.PowerMockRunner;

    @RunWith(PowerMockRunner.class)
    @PrepareForTest({ FacesContext.class })
    public class SomeBeanTest {

    private SomeBean someBean;

    private FacesContext facesContext;
    private ExternalContext externalContext;

    @Before
    public void setUp() throws Exception {
    someBean = new SomeBean();

    // mock all static methods of FacesContext using PowerMockito
    PowerMock.mockStatic(FacesContext.class);

    facesContext = createMock(FacesContext.class);
    externalContext = createMock(ExternalContext.class);
    }

    @Test
    public void testAddMessage() {
    // create Capture instances for the clientId and FacesMessage parameters
    // that will be added to the FacesContext
    Capture<String> clientIdCapture = new Capture<String>();
    Capture<FacesMessage> facesMessageCapture = new Capture<FacesMessage>();

    expect(FacesContext.getCurrentInstance()).andReturn(facesContext)
    .once();
    // expect the call to the addMessage() method and capture the arguments
    facesContext.addMessage(capture(clientIdCapture),
    capture(facesMessageCapture));
    expectLastCall().once();

    // replay the class (not the instance)
    PowerMock.replay(FacesContext.class);
    replay(facesContext);

    someBean.addMessage(FacesMessage.SEVERITY_ERROR, "error",
    "something went wrong");

    // verify the class (not the instance)
    PowerMock.verify(FacesContext.class);
    verify(facesContext);

    // check the value of the clientId that was passed
    assertNull(clientIdCapture.getValue());

    // retrieve the captured FacesMessage
    FacesMessage captured = facesMessageCapture.getValue();
    // check if the captured FacesMessage contains the expected values
    assertEquals(FacesMessage.SEVERITY_ERROR, captured.getSeverity());
    assertEquals("error", captured.getSummary());
    assertEquals("something went wrong", captured.getDetail());
    }

    @Test
    public void testLogout() {
    expect(FacesContext.getCurrentInstance()).andReturn(facesContext)
    .once();
    expect(facesContext.getExternalContext()).andReturn(externalContext)
    .once();
    // expect the call to the invalidateSession() method
    externalContext.invalidateSession();
    expectLastCall().once();

    // replay the class (not the instance)
    PowerMock.replay(FacesContext.class);
    replay(facesContext);
    replay(externalContext);

    assertEquals("logout?faces-redirect=true", someBean.logout());

    // verify the class (not the instance)
    PowerMock.verify(FacesContext.class);
    verify(facesContext);
    verify(externalContext);
    }
    }

    In order to run the above test cases, open a command prompt and execute following Maven command:

    mvn test


    github icon
    If you would like to run the above code sample you can download the full source code and their corresponding JUnit test cases here.

    This concludes the mocking FacesContext using EasyMock and PowerMock example. If you found this post helpful or have any questions or remarks, please leave a comment.

    Monday, November 10, 2014

    Mockito - unit testing FacesContext using PowerMock, JUnit and Maven

    mockito logo
    JSF defines the FacesContext abstract base class for representing all of the contextual information associated with processing an incoming request, and creating the corresponding response. When writing unit test cases for a JSF application there might be a need to mock some of the FacesContext static methods. The following post will illustrate how to do this using PowerMock, a framework that allows you to extend mock libraries like Mockito with extra capabilities. In this case the capability to mock the static methods of FacesContext.

    Tools used:
    • JUnit 4.11
    • Mockito 1.10
    • PowerMock 1.5
    • Maven 3

    The code sample is built and run using Maven. Specified below is the Maven POM file which contains the needed dependencies for JUnit, Mockito and PowerMock. In addition the PowerMock support module for JUnit ('powermock-module-junit4') and the PowerMock API for Mockito ('powermock-api-mockito') dependencies need to be added as specified here.

    As the FacesContext class is used in this code sample, dependencies to the EL (Expression Language) API and JSF specification API are also included.

    Note that the version of JUnit is not the latest as there seems to be a bug where PowerMock doesn't recognize the correct JUnit version when using JUnit 4.12.
    <?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>info.source4code</groupId>
    <artifactId>mockito-powermock-facescontext</artifactId>
    <version>1.0</version>
    <packaging>jar</packaging>

    <name>Mockito - Mocking FacesContext using PowerMock</name>
    <url>http://www.source4code.info/2014/11/mockito-mocking-facescontext-using.html</url>

    <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <java.version>1.6</java.version>

    <junit.version>4.11</junit.version>
    <mockito.version>1.10.8</mockito.version>
    <powermock.version>1.5.6</powermock.version>

    <el.version>2.2.1-b04</el.version>
    <jsf.version>2.2.8-02</jsf.version>

    <maven-compiler-plugin.version>3.1</maven-compiler-plugin.version>
    </properties>

    <dependencies>
    <!-- JUnit -->
    <dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>${junit.version}</version>
    <scope>test</scope>
    </dependency>
    <!-- Mockito -->
    <dependency>
    <groupId>org.mockito</groupId>
    <artifactId>mockito-core</artifactId>
    <version>${mockito.version}</version>
    <scope>test</scope>
    </dependency>
    <!-- PowerMock -->
    <dependency>
    <groupId>org.powermock</groupId>
    <artifactId>powermock-module-junit4</artifactId>
    <version>${powermock.version}</version>
    <scope>test</scope>
    </dependency>
    <dependency>
    <groupId>org.powermock</groupId>
    <artifactId>powermock-api-mockito</artifactId>
    <version>${powermock.version}</version>
    <scope>test</scope>
    </dependency>
    <!-- EL (Unified Expression Language) -->
    <dependency>
    <groupId>javax.el</groupId>
    <artifactId>el-api</artifactId>
    <version>${el.version}</version>
    <scope>test</scope>
    </dependency>
    <!-- JSF -->
    <dependency>
    <groupId>com.sun.faces</groupId>
    <artifactId>jsf-api</artifactId>
    <version>${jsf.version}</version>
    </dependency>
    </dependencies>

    <build>
    <plugins>
    <plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-compiler-plugin</artifactId>
    <version>${maven-compiler-plugin.version}</version>
    <configuration>
    <source>${java.version}</source>
    <target>${java.version}</target>
    </configuration>
    </plugin>
    </plugins>
    </build>
    </project>

    The SomeBean class below contains two methods that make use of FacesContext. The first addMessage() method will create a new FacesMessage and add it to the FacesContext. The second logout() method will invalidate the current session.
    package info.source4code.mockito;

    import javax.faces.application.FacesMessage;
    import javax.faces.application.FacesMessage.Severity;
    import javax.faces.bean.ManagedBean;
    import javax.faces.context.FacesContext;

    @ManagedBean
    @SessionScoped
    public class SomeBean {

    public void addMessage(Severity severity, String summary, String detail) {
    FacesContext.getCurrentInstance().addMessage(null,
    new FacesMessage(severity, summary, detail));
    }

    public String logout() {
    FacesContext.getCurrentInstance().getExternalContext()
    .invalidateSession();

    return "logout?faces-redirect=true";
    }
    }

    The SomeBeanTest JUnit test class is used to test the above. The class is annotated using two annotations. The first @RunWith annotation tells JUnit to run the test using PowerMockRunner. The second @PrepareForTest annotation tells PowerMock to prepare to mock the FacesContext class. If there are multiple classes to be prepared for mocking, they can be specified using a comma separated list.

    Mockito provides the @Mock annotation which is a shorthand for mocks creation. In the below test class it is used to create the FacesContext and ExternalContext mocks. Note that the previous @RunWith(PowerMockRunner.class) annotation will take care of initializing fields annotated with Mockito annotations

    In the setup() method a number of objects are specified that are similar for the two test cases. The mockStatic() method is called in order to tell PowerMock to mock all static methods of the given FacesContext class. We then use the when() method to specify what instance to return in case the getCurrentInstance() method is called on FacesContext. The same is done for the getExternalContext() method.
    Note that because of the 'org.mockito.Mockito.when' import there is no Mockito class name in front of the static when() method.
    The first addMessage() test case uses the ArgumentCaptor capability of Mockito in order to test whether a FacesMessage with the correct values was added to the FacesContext. The second testLogout() test case checks if the correct redirect was returned.
    package info.source4code.mockito;

    import static org.junit.Assert.assertEquals;
    import static org.junit.Assert.assertNull;
    import static org.mockito.Mockito.verify;
    import static org.mockito.Mockito.when;

    import javax.faces.application.FacesMessage;
    import javax.faces.context.ExternalContext;
    import javax.faces.context.FacesContext;

    import org.junit.Before;
    import org.junit.Test;
    import org.junit.runner.RunWith;
    import org.mockito.ArgumentCaptor;
    import org.mockito.Mock;
    import org.powermock.api.mockito.PowerMockito;
    import org.powermock.core.classloader.annotations.PrepareForTest;
    import org.powermock.modules.junit4.PowerMockRunner;

    @RunWith(PowerMockRunner.class)
    @PrepareForTest({ FacesContext.class })
    public class SomeBeanTest {

    private SomeBean someBean;

    @Mock
    private FacesContext facesContext;
    @Mock
    private ExternalContext externalContext;

    @Before
    public void setUp() throws Exception {
    someBean = new SomeBean();

    // mock all static methods of FacesContext using PowerMockito
    PowerMockito.mockStatic(FacesContext.class);

    when(FacesContext.getCurrentInstance()).thenReturn(facesContext);
    when(facesContext.getExternalContext()).thenReturn(externalContext);
    }

    @Test
    public void testAddMessage() {
    // create Captor instances for the clientId and FacesMessage parameters
    // that will be added to the FacesContext
    ArgumentCaptor<String> clientIdCaptor = ArgumentCaptor
    .forClass(String.class);
    ArgumentCaptor<FacesMessage> facesMessageCaptor = ArgumentCaptor
    .forClass(FacesMessage.class);

    // run the addMessage() method to be tested
    someBean.addMessage(FacesMessage.SEVERITY_ERROR, "error",
    "something went wrong");

    // verify if the call to addMessage() was made and capture the arguments
    verify(facesContext).addMessage(clientIdCaptor.capture(),
    facesMessageCaptor.capture());

    // check the value of the clientId that was passed
    assertNull(clientIdCaptor.getValue());

    // retrieve the captured FacesMessage
    FacesMessage captured = facesMessageCaptor.getValue();
    // check if the captured FacesMessage contains the expected values
    assertEquals(FacesMessage.SEVERITY_ERROR, captured.getSeverity());
    assertEquals("error", captured.getSummary());
    assertEquals("something went wrong", captured.getDetail());
    }

    @Test
    public void testLogout() {
    assertEquals("logout?faces-redirect=true", someBean.logout());
    }
    }

    In order to run the above test cases, open a command prompt and execute following Maven command:
    mvn test


    github icon
    If you would like to run the above code sample you can download the full source code and their corresponding JUnit test cases here.

    This concludes the mocking FacesContext using Mockito and PowerMock example. If you found this post helpful or have any questions or remarks, please leave a comment.