Virgo recipe "Messaging with RabbitMQ"

Virgo Homepage

Acknowledgements

This guide is inspired by "Messaging with RabbitMQ" from the Spring Guides.

Messaging with RabbitMQ

The original guide uses Spring Boot to bootstrap the demo application. This guide shows what needs to be done to run Messaging with RabbitMQ with Virgo.

Shopping list

Note

Only Virgo Server for Apache Tomcat and Virgo Jetty Server are supported by the Virgo Tooling.

Ingredients

Preparations

Get the code

Clone the code from the Git repository: Sample Git repository:

$ cd ~/MyAwesomeProjectDirectory/
$ git clone git://git.eclipse.org/gitroot/virgo/org.eclipse.virgo.samples.git
$ cd org.eclipse.virgo.samples
$ cd recipe-messaging-rabbitmq

Once inside the source directory of this recipe - your are good to go.

Create the Custom Virgo Runtime

Create your own Virgo runtime with Gradle and Dockerizor:

$ ./gradlew :recipe-messaging-rabbitmq-runtime dockerize

The custom Virgo Runtime is packaged as tar archive and available from the runtime build directory. Extract the runtime into some directory - let’s say into VIRGO_HOME.

Caution

You’ll need a Docker daemon running in order to create your custom Virgo runtime. For more information please refer to the Dockerizor documentation.

Create Eclipse Project Metadata

Create the Eclipse IDE metadata with:

$ ./gradlew eclipse

Prepare the Virgo Tooling

Download Eclipse IDE for Java EE Developers e.g. Eclipse Mars.2 (4.5.2).

Start your Eclipse IDE and add the Virgo Tooling to your Eclipse IDE.

Optionally add the Docker Tooling to your Eclipse IDE.

Import the code into your Eclipse IDE

Now you can Import ▸ Gradle ▸ Gradle Project into your IDE. The project folder is called recipe-messaging-rabbitmq and lies directly in the "Sample Applications" folder you created previously with the Git clone command. (org.eclipse.virgo.samples).

Note

Before you can drag’n’drop the project onto your freshly created Virgo Server you might have to execute Virgo ▸ Run Generation of META-INF/MANIFEST.MF (via a right click on the project) within your Eclipse IDE.

Create new Virgo Server Runtime

Define a new server

Within the IDE New ▸ Server ▸ EclipseRT ▸ Virgo Runtime and choose the directory of the previously installed Virgo server VIRGO_HOME.

Directions

Note

In the original guide the music plays in the run method of the CommandLineRunner of the Spring Boot application.

@Configuration<XML/>

We are going to transform the @SpringBootApplication into a plain @Component with an init-method.

Spring Boot Application

@SpringBootApplication (1)
public class Application implements CommandLineRunner { (2)

  @Autowired private CustomerRepository repository; (3)

  public static void main(String[] args) {
    SpringApplication.run(Application.class, args);
  }

  public void run(String... args) throws Exception { (4)
    // messaging actions...
  }
}
  1. Will be replaced with a plain @Component.

  2. CommandLineRunner will be replaced, too.

  3. @Autowired is the common denominator.

  4. Will be replaced with a @PostConstruct method.

Virgoized Application

After the transformation the component looks something like:

@Component
public class Application {

  @Autowired private CustomerRepository repository;

  @PostConstruct
  public void init() throws Exception {
    // messaging actions...
  }
}

Message Receiver

The Receiver is unchanged:

public class Receiver {

	private CountDownLatch latch = new CountDownLatch(1);

	public void receiveMessage(String message) {
		System.out.println("Received <" + message + ">");
		latch.countDown();
	}

	public CountDownLatch getLatch() {
		return latch;
	}

}

RabbitMQ Queue

@Bean
Queue queue() {
  return new Queue("spring-boot", false);
}

vs.

<bean id="queue" class="org.springframework.amqp.core.Queue">
  <constructor-arg name="name" value="spring-boot" />
  <constructor-arg name="durable" value="false" />
</bean>

RabbitMQ Exchange

@Bean
TopicExchange exchange() {
  return new TopicExchange("spring-boot-exchange");
}

vs.

<bean id="exchange" class="org.springframework.amqp.core.TopicExchange">
  <constructor-arg name="name" value="spring-boot-exchange" />
</bean>

Binding

@Bean
Binding binding(Queue queue, TopicExchange exchange) {
  return BindingBuilder.bind(queue).to(exchange).with(queueName);
}

vs.

<bean id="binding" class="org.springframework.amqp.core.Binding">
  <constructor-arg name="destination" value="spring-boot" />
  <constructor-arg name="destinationType" ref="destination-type-queue" />
  <constructor-arg name="exchange" value="spring-boot-exchange" />
  <constructor-arg name="routingKey" value="" />
  <constructor-arg name="arguments" ref="emptyMap" />
</bean>

Connection Factory and Rabbit Template

Tip

Spring Boot automatically creates a ConnectionFactory and RabbitTemplate.

<bean id="cachingConnectionFactory" class="org.springframework.amqp.rabbit.connection.CachingConnectionFactory">
  <property name="host" value="${rabbitmq.host:localhost}" />
  <property name="port" value="${rabbitmq.port:5672}" />
</bean>
<bean id="connectionFactory" class="org.springframework.amqp.rabbit.connection.SimpleRoutingConnectionFactory">
  <property name="defaultTargetConnectionFactory" ref="cachingConnectionFactory" />
</bean>
<bean class="org.springframework.amqp.rabbit.core.RabbitTemplate">
  <constructor-arg ref="connectionFactory" />
</bean>

Message Listener

@Bean
SimpleMessageListenerContainer container(ConnectionFactory connectionFactory, MessageListenerAdapter listenerAdapter) {
  SimpleMessageListenerContainer container = new SimpleMessageListenerContainer();
  container.setConnectionFactory(connectionFactory);
  container.setQueueNames(queueName);
  container.setMessageListener(listenerAdapter);
  return container;
}

vs.

<bean class="org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer">
	<property name="connectionFactory" ref="connectionFactory" />
	<property name="queueNames" value="spring-boot" />
	<property name="messageListener" ref="messageListener" />
</bean>

OSGi Metadata

The OSGi metadata is generated from a template:

template.mf
Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: Virgo Recipe Messaging with RabbitMQ
Bundle-SymbolicName: org.eclipse.virgo.samples.recipe.messaging.rabbitmq
Bundle-Version: ${version}
Import-Template:
 javax.annotation;version="0",
 org.springframework.beans.*;version="${springframeworkVersion:[=.=.=.=, +1)}",
 org.springframework.context.*;version="${springframeworkVersion:[=.=.=.=, +1)}",
 org.springframework.stereotype.*;version="${springframeworkVersion:[=.=.=.=, +1)}",
 org.springframework.amqp.core.*;version="${springAmqpVersion:[=.=.=, +1)}",
 org.springframework.amqp.rabbit.*;version="${springAmqpRabbitMqVersion:[=.=.=.=, +1)}"

Let’s taste

log.log
System.out                           Waiting five seconds...
System.out                           Sending message...
...
o.s.a.r.c.CachingConnectionFactory   Created new connection: SimpleConnection@63fa2ace [delegate=amqp://guest@192.168.64.3:5672/]
...
System.out                           Received <Hello from RabbitMQ!>

Dockerise the Application

We use Gradle and Dockerizor to build the application. Instead of running the sample within the Eclipse IDE you can build a Docker image:

$ ./gradlew dockerize
This recipe assumes you have a local RabbitMQ up and running.

Let’s create an isolated network recipe_nw to test the complete recipe:

docker network create -d bridge --subnet 172.25.0.0/16 recipe_nw

To start a temporary RabbitMQ container inside this network issue the following command:

$ docker run --rm -it --net=recipe_nw --name rabbitmq rabbitmq:3.6.2

then run the application with the environment variable rabbitmq.host set to the container name used in the command above:

$ docker run --rm -it --net=recipe_nw --publish 8080 -e rabbitmq.host=rabbitmq virgo-recipe/messaging-rabbitmq-app