/*
* Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.amqp.rabbit.core;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.fail;
import static org.mockito.Matchers.any;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.atomic.AtomicReference;
import org.junit.Test;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
import org.springframework.amqp.core.AmqpAdmin;
import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.Binding.DestinationType;
import org.springframework.amqp.core.DirectExchange;
import org.springframework.amqp.core.Exchange;
import org.springframework.amqp.core.Queue;
import org.springframework.amqp.rabbit.connection.CachingConnectionFactory;
import org.springframework.amqp.rabbit.connection.CachingConnectionFactory.CacheMode;
import org.springframework.amqp.rabbit.connection.Connection;
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
import org.springframework.amqp.rabbit.connection.ConnectionListener;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.support.GenericApplicationContext;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.impl.AMQImpl;
/**
* @author Gary Russell
* @author Artem Bilan
* @since 1.2
*
*/
public class RabbitAdminDeclarationTests {
@Test
public void testUnconditional() throws Exception {
ConnectionFactory cf = mock(ConnectionFactory.class);
Connection conn = mock(Connection.class);
Channel channel = mock(Channel.class);
when(cf.createConnection()).thenReturn(conn);
when(conn.createChannel(false)).thenReturn(channel);
when(channel.queueDeclare("foo", true, false, false, null)).thenReturn(new AMQImpl.Queue.DeclareOk("foo", 0, 0));
final AtomicReference<ConnectionListener> listener = new AtomicReference<ConnectionListener>();
doAnswer(new Answer<Object>() {
@Override
public Object answer(InvocationOnMock invocation) throws Throwable {
listener.set((ConnectionListener) invocation.getArguments()[0]);
return null;
}
}).when(cf).addConnectionListener(any(ConnectionListener.class));
RabbitAdmin admin = new RabbitAdmin(cf);
GenericApplicationContext context = new GenericApplicationContext();
Queue queue = new Queue("foo");
context.getBeanFactory().registerSingleton("foo", queue);
DirectExchange exchange = new DirectExchange("bar");
context.getBeanFactory().registerSingleton("bar", exchange);
Binding binding = new Binding("foo", DestinationType.QUEUE, "bar", "foo", null);
context.getBeanFactory().registerSingleton("baz", binding);
context.refresh();
admin.setApplicationContext(context);
admin.afterPropertiesSet();
assertNotNull(listener.get());
listener.get().onCreate(conn);
verify(channel).queueDeclare("foo", true, false, false, null);
verify(channel).exchangeDeclare("bar", "direct", true, false, new HashMap<String, Object>());
verify(channel).queueBind("foo", "bar", "foo", null);
}
@Test
public void testNoDeclareWithCachedConnections() throws Exception {
com.rabbitmq.client.ConnectionFactory mockConnectionFactory = mock(com.rabbitmq.client.ConnectionFactory.class);
final List<com.rabbitmq.client.Connection> mockConnections = new ArrayList<com.rabbitmq.client.Connection>();
final List<Channel> mockChannels = new ArrayList<Channel>();
doAnswer(new Answer<com.rabbitmq.client.Connection>() {
private int connectionNumber;
@Override
public com.rabbitmq.client.Connection answer(InvocationOnMock invocation) throws Throwable {
com.rabbitmq.client.Connection connection = mock(com.rabbitmq.client.Connection.class);
doAnswer(new Answer<Channel>() {
private int channelNumber;
@Override
public Channel answer(InvocationOnMock invocation) throws Throwable {
Channel channel = mock(Channel.class);
when(channel.isOpen()).thenReturn(true);
int channelNumnber = ++this.channelNumber;
when(channel.toString()).thenReturn("mockChannel" + channelNumnber);
mockChannels.add(channel);
return channel;
}
}).when(connection).createChannel();
int connectionNumber = ++this.connectionNumber;
when(connection.toString()).thenReturn("mockConnection" + connectionNumber);
when(connection.isOpen()).thenReturn(true);
mockConnections.add(connection);
return connection;
}
}).when(mockConnectionFactory).newConnection((ExecutorService) null);
CachingConnectionFactory ccf = new CachingConnectionFactory(mockConnectionFactory);
ccf.setCacheMode(CacheMode.CONNECTION);
ccf.afterPropertiesSet();
RabbitAdmin admin = new RabbitAdmin(ccf);
GenericApplicationContext context = new GenericApplicationContext();
Queue queue = new Queue("foo");
context.getBeanFactory().registerSingleton("foo", queue);
context.refresh();
admin.setApplicationContext(context);
admin.afterPropertiesSet();
ccf.createConnection().close();
ccf.destroy();
assertEquals("Admin should not have created a channel", 0, mockChannels.size());
}
@Test
public void testUnconditionalWithExplicitFactory() throws Exception {
ConnectionFactory cf = mock(ConnectionFactory.class);
Connection conn = mock(Connection.class);
Channel channel = mock(Channel.class);
when(cf.createConnection()).thenReturn(conn);
when(conn.createChannel(false)).thenReturn(channel);
when(channel.queueDeclare("foo", true, false, false, null)).thenReturn(new AMQImpl.Queue.DeclareOk("foo", 0, 0));
final AtomicReference<ConnectionListener> listener = new AtomicReference<ConnectionListener>();
doAnswer(new Answer<Object>() {
@Override
public Object answer(InvocationOnMock invocation) throws Throwable {
listener.set((ConnectionListener) invocation.getArguments()[0]);
return null;
}
}).when(cf).addConnectionListener(any(ConnectionListener.class));
RabbitAdmin admin = new RabbitAdmin(cf);
GenericApplicationContext context = new GenericApplicationContext();
Queue queue = new Queue("foo");
queue.setAdminsThatShouldDeclare(admin);
context.getBeanFactory().registerSingleton("foo", queue);
DirectExchange exchange = new DirectExchange("bar");
exchange.setAdminsThatShouldDeclare(admin);
context.getBeanFactory().registerSingleton("bar", exchange);
Binding binding = new Binding("foo", DestinationType.QUEUE, "bar", "foo", null);
binding.setAdminsThatShouldDeclare(admin);
context.getBeanFactory().registerSingleton("baz", binding);
context.refresh();
admin.setApplicationContext(context);
admin.afterPropertiesSet();
assertNotNull(listener.get());
listener.get().onCreate(conn);
verify(channel).queueDeclare("foo", true, false, false, null);
verify(channel).exchangeDeclare("bar", "direct", true, false, new HashMap<String, Object>());
verify(channel).queueBind("foo", "bar", "foo", null);
}
@Test
public void testSkipBecauseDifferentFactory() throws Exception {
ConnectionFactory cf = mock(ConnectionFactory.class);
Connection conn = mock(Connection.class);
Channel channel = mock(Channel.class);
when(cf.createConnection()).thenReturn(conn);
when(conn.createChannel(false)).thenReturn(channel);
when(channel.queueDeclare("foo", true, false, false, null)).thenReturn(new AMQImpl.Queue.DeclareOk("foo", 0, 0));
final AtomicReference<ConnectionListener> listener = new AtomicReference<ConnectionListener>();
doAnswer(new Answer<Object>() {
@Override
public Object answer(InvocationOnMock invocation) throws Throwable {
listener.set((ConnectionListener) invocation.getArguments()[0]);
return null;
}
}).when(cf).addConnectionListener(any(ConnectionListener.class));
RabbitAdmin admin = new RabbitAdmin(cf);
RabbitAdmin other = new RabbitAdmin(cf);
GenericApplicationContext context = new GenericApplicationContext();
Queue queue = new Queue("foo");
queue.setAdminsThatShouldDeclare(other);
context.getBeanFactory().registerSingleton("foo", queue);
DirectExchange exchange = new DirectExchange("bar");
exchange.setAdminsThatShouldDeclare(other);
context.getBeanFactory().registerSingleton("bar", exchange);
Binding binding = new Binding("foo", DestinationType.QUEUE, "bar", "foo", null);
binding.setAdminsThatShouldDeclare(other);
context.getBeanFactory().registerSingleton("baz", binding);
context.refresh();
admin.setApplicationContext(context);
admin.afterPropertiesSet();
assertNotNull(listener.get());
listener.get().onCreate(conn);
verify(channel, times(0)).queueDeclare("foo", true, false, false, null);
verify(channel, times(0)).exchangeDeclare("bar", "direct", true, false, new HashMap<String, Object>());
verify(channel, times(0)).queueBind("foo", "bar", "foo", null);
}
@Test
public void testSkipBecauseShouldntDeclare() throws Exception {
ConnectionFactory cf = mock(ConnectionFactory.class);
Connection conn = mock(Connection.class);
Channel channel = mock(Channel.class);
when(cf.createConnection()).thenReturn(conn);
when(conn.createChannel(false)).thenReturn(channel);
when(channel.queueDeclare("foo", true, false, false, null)).thenReturn(new AMQImpl.Queue.DeclareOk("foo", 0, 0));
final AtomicReference<ConnectionListener> listener = new AtomicReference<ConnectionListener>();
doAnswer(new Answer<Object>() {
@Override
public Object answer(InvocationOnMock invocation) throws Throwable {
listener.set((ConnectionListener) invocation.getArguments()[0]);
return null;
}
}).when(cf).addConnectionListener(any(ConnectionListener.class));
RabbitAdmin admin = new RabbitAdmin(cf);
GenericApplicationContext context = new GenericApplicationContext();
Queue queue = new Queue("foo");
queue.setShouldDeclare(false);
context.getBeanFactory().registerSingleton("foo", queue);
DirectExchange exchange = new DirectExchange("bar");
exchange.setShouldDeclare(false);
context.getBeanFactory().registerSingleton("bar", exchange);
Binding binding = new Binding("foo", DestinationType.QUEUE, "bar", "foo", null);
binding.setShouldDeclare(false);
context.getBeanFactory().registerSingleton("baz", binding);
context.refresh();
admin.setApplicationContext(context);
admin.afterPropertiesSet();
assertNotNull(listener.get());
listener.get().onCreate(conn);
verify(channel, times(0)).queueDeclare("foo", true, false, false, null);
verify(channel, times(0)).exchangeDeclare("bar", "direct", true, false, new HashMap<String, Object>());
verify(channel, times(0)).queueBind("foo", "bar", "foo", null);
}
@Test
public void testJavaConfig() throws Exception {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Config.class);
Config.listener1.onCreate(Config.conn1);
verify(Config.channel1).queueDeclare("foo", true, false, false, null);
verify(Config.channel1).exchangeDeclare("bar", "direct", true, false, new HashMap<String, Object>());
verify(Config.channel1).queueBind("foo", "bar", "foo", null);
Config.listener2.onCreate(Config.conn2);
verify(Config.channel2, times(0)).queueDeclare("foo", true, false, false, null);
verify(Config.channel2, times(0)).exchangeDeclare("bar", "direct", true, false, new HashMap<String, Object>());
verify(Config.channel2, times(0)).queueBind("foo", "bar", "foo", null);
context.destroy();
}
@Test
public void testAddRemove() {
Queue queue = new Queue("foo");
ConnectionFactory cf = mock(ConnectionFactory.class);
RabbitAdmin admin1 = new RabbitAdmin(cf);
RabbitAdmin admin2 = new RabbitAdmin(cf);
queue.setAdminsThatShouldDeclare(admin1, admin2);
assertEquals(2, queue.getDeclaringAdmins().size());
queue.setAdminsThatShouldDeclare(admin1);
assertEquals(1, queue.getDeclaringAdmins().size());
queue.setAdminsThatShouldDeclare(new Object[] {null});
assertEquals(0, queue.getDeclaringAdmins().size());
queue.setAdminsThatShouldDeclare(admin1, admin2);
assertEquals(2, queue.getDeclaringAdmins().size());
queue.setAdminsThatShouldDeclare();
assertEquals(0, queue.getDeclaringAdmins().size());
queue.setAdminsThatShouldDeclare(admin1, admin2);
assertEquals(2, queue.getDeclaringAdmins().size());
queue.setAdminsThatShouldDeclare((AmqpAdmin) null);
assertEquals(0, queue.getDeclaringAdmins().size());
queue.setAdminsThatShouldDeclare(admin1, admin2);
assertEquals(2, queue.getDeclaringAdmins().size());
queue.setAdminsThatShouldDeclare((Object[]) null);
assertEquals(0, queue.getDeclaringAdmins().size());
try {
queue.setAdminsThatShouldDeclare(null, admin1);
fail("Expected Exception");
}
catch (IllegalArgumentException e) {}
}
@Configuration
public static class Config {
private static Connection conn1 = mock(Connection.class);
private static Connection conn2 = mock(Connection.class);
private static Channel channel1 = mock(Channel.class);
private static Channel channel2 = mock(Channel.class);
private static ConnectionListener listener1;
private static ConnectionListener listener2;
@Bean
public ConnectionFactory cf1() throws IOException {
ConnectionFactory connectionFactory = mock(ConnectionFactory.class);
when(connectionFactory.createConnection()).thenReturn(conn1);
when(conn1.createChannel(false)).thenReturn(channel1);
when(channel1.queueDeclare("foo", true, false, false, null)).thenReturn(new AMQImpl.Queue.DeclareOk("foo", 0, 0));
doAnswer(new Answer<Object>() {
@Override
public Object answer(InvocationOnMock invocation) throws Throwable {
listener1 = (ConnectionListener) invocation.getArguments()[0];
return null;
}
}).when(connectionFactory).addConnectionListener(any(ConnectionListener.class));
return connectionFactory;
}
@Bean
public ConnectionFactory cf2() throws IOException {
ConnectionFactory connectionFactory = mock(ConnectionFactory.class);
when(connectionFactory.createConnection()).thenReturn(conn2);
when(conn2.createChannel(false)).thenReturn(channel2);
when(channel2.queueDeclare("foo", true, false, false, null)).thenReturn(new AMQImpl.Queue.DeclareOk("foo", 0, 0));
doAnswer(new Answer<Object>() {
@Override
public Object answer(InvocationOnMock invocation) throws Throwable {
listener2 = (ConnectionListener) invocation.getArguments()[0];
return null;
}
}).when(connectionFactory).addConnectionListener(any(ConnectionListener.class));
return connectionFactory;
}
@Bean
public RabbitAdmin admin1() throws IOException {
RabbitAdmin rabbitAdmin = new RabbitAdmin(cf1());
rabbitAdmin.afterPropertiesSet();
return rabbitAdmin;
}
@Bean
public RabbitAdmin admin2() throws IOException {
RabbitAdmin rabbitAdmin = new RabbitAdmin(cf2());
rabbitAdmin.afterPropertiesSet();
return rabbitAdmin;
}
@Bean
public Queue queue() throws IOException {
Queue queue = new Queue("foo");
queue.setAdminsThatShouldDeclare(admin1());
return queue;
}
@Bean
public Exchange exchange() throws IOException {
DirectExchange exchange = new DirectExchange("bar");
exchange.setAdminsThatShouldDeclare(admin1());
return exchange;
}
@Bean
public Binding binding() throws IOException {
Binding binding = new Binding("foo", DestinationType.QUEUE, exchange().getName(), "foo", null);
binding.setAdminsThatShouldDeclare(admin1());
return binding;
}
}
}