/*
* Copyright 2002-2017 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.connection;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.BDDMockito.given;
import static org.mockito.BDDMockito.willAnswer;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
import org.junit.Test;
import org.mockito.Mockito;
import org.springframework.amqp.rabbit.listener.DirectMessageListenerContainer;
import org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer;
import com.rabbitmq.client.Channel;
/**
* @author Artem Bilan
* @author Josh Chappelle
* @author Gary Russell
* @since 1.3
*/
public class RoutingConnectionFactoryTests {
@Test
public void testAbstractRoutingConnectionFactory() {
ConnectionFactory connectionFactory1 = Mockito.mock(ConnectionFactory.class);
ConnectionFactory connectionFactory2 = Mockito.mock(ConnectionFactory.class);
Map<Object, ConnectionFactory> factories = new HashMap<Object, ConnectionFactory>(2);
factories.put(Boolean.TRUE, connectionFactory1);
factories.put(Boolean.FALSE, connectionFactory2);
ConnectionFactory defaultConnectionFactory = Mockito.mock(ConnectionFactory.class);
final AtomicBoolean lookupFlag = new AtomicBoolean(true);
final AtomicInteger count = new AtomicInteger();
AbstractRoutingConnectionFactory connectionFactory = new AbstractRoutingConnectionFactory() {
@Override
protected Object determineCurrentLookupKey() {
return count.incrementAndGet() > 3 ? null : lookupFlag.getAndSet(!lookupFlag.get());
}
};
connectionFactory.setDefaultTargetConnectionFactory(defaultConnectionFactory);
connectionFactory.setTargetConnectionFactories(factories);
for (int i = 0; i < 5; i++) {
connectionFactory.createConnection();
}
verify(connectionFactory1, times(2)).createConnection();
verify(connectionFactory2).createConnection();
verify(defaultConnectionFactory, times(2)).createConnection();
}
@Test
public void testSimpleRoutingConnectionFactory() throws InterruptedException {
ConnectionFactory connectionFactory1 = Mockito.mock(ConnectionFactory.class);
ConnectionFactory connectionFactory2 = Mockito.mock(ConnectionFactory.class);
Map<Object, ConnectionFactory> factories = new HashMap<Object, ConnectionFactory>(2);
factories.put("foo", connectionFactory1);
factories.put("bar", connectionFactory2);
final AbstractRoutingConnectionFactory connectionFactory = new SimpleRoutingConnectionFactory();
connectionFactory.setTargetConnectionFactories(factories);
ExecutorService executorService = Executors.newFixedThreadPool(3);
for (int i = 0; i < 3; i++) {
final AtomicInteger count = new AtomicInteger(i);
executorService.execute(() -> {
SimpleResourceHolder.bind(connectionFactory, count.get() % 2 == 0 ? "foo" : "bar");
connectionFactory.createConnection();
SimpleResourceHolder.unbind(connectionFactory);
});
}
executorService.shutdown();
assertTrue(executorService.awaitTermination(10, TimeUnit.SECONDS));
verify(connectionFactory1, times(2)).createConnection();
verify(connectionFactory2).createConnection();
}
@Test
public void testGetAddAndRemoveOperationsForTargetConnectionFactories() {
ConnectionFactory targetConnectionFactory = Mockito.mock(ConnectionFactory.class);
AbstractRoutingConnectionFactory routingFactory = new AbstractRoutingConnectionFactory() {
@Override
protected Object determineCurrentLookupKey() {
return null;
}
};
//Make sure map is initialized and doesn't contain lookup key "1"
assertNull(routingFactory.getTargetConnectionFactory("1"));
//Add one and make sure it's there
routingFactory.addTargetConnectionFactory("1", targetConnectionFactory);
assertEquals(targetConnectionFactory, routingFactory.getTargetConnectionFactory("1"));
assertNull(routingFactory.getTargetConnectionFactory("2"));
//Remove it and make sure it's gone
ConnectionFactory removedConnectionFactory = routingFactory.removeTargetConnectionFactory("1");
assertEquals(targetConnectionFactory, removedConnectionFactory);
assertNull(routingFactory.getTargetConnectionFactory("1"));
}
@Test
public void testAddTargetConnectionFactoryAddsExistingConnectionListenersToConnectionFactory() {
AbstractRoutingConnectionFactory routingFactory = new AbstractRoutingConnectionFactory() {
@Override
protected Object determineCurrentLookupKey() {
return null;
}
};
routingFactory.addConnectionListener(Mockito.mock(ConnectionListener.class));
routingFactory.addConnectionListener(Mockito.mock(ConnectionListener.class));
ConnectionFactory targetConnectionFactory = Mockito.mock(ConnectionFactory.class);
routingFactory.addTargetConnectionFactory("1", targetConnectionFactory);
verify(targetConnectionFactory,
times(2)).addConnectionListener(any(ConnectionListener.class));
}
@Test
public void testAbstractRoutingConnectionFactoryWithListenerContainer() {
ConnectionFactory connectionFactory1 = mock(ConnectionFactory.class);
ConnectionFactory connectionFactory2 = mock(ConnectionFactory.class);
Map<Object, ConnectionFactory> factories = new HashMap<Object, ConnectionFactory>(2);
factories.put("[baz]", connectionFactory1);
factories.put("[foo,bar]", connectionFactory2);
ConnectionFactory defaultConnectionFactory = mock(ConnectionFactory.class);
SimpleRoutingConnectionFactory connectionFactory = new SimpleRoutingConnectionFactory();
connectionFactory.setDefaultTargetConnectionFactory(defaultConnectionFactory);
connectionFactory.setTargetConnectionFactories(factories);
SimpleMessageListenerContainer container = new SimpleMessageListenerContainer(connectionFactory);
container.setQueueNames("foo, bar");
container.afterPropertiesSet();
container.start();
verify(connectionFactory1, never()).createConnection();
verify(connectionFactory2).createConnection();
verify(defaultConnectionFactory, never()).createConnection();
reset(connectionFactory1, connectionFactory2, defaultConnectionFactory);
container.setQueueNames("baz");
verify(connectionFactory1).createConnection();
verify(connectionFactory2, never()).createConnection();
verify(defaultConnectionFactory, never()).createConnection();
reset(connectionFactory1, connectionFactory2, defaultConnectionFactory);
container.setQueueNames("qux");
verify(connectionFactory1, never()).createConnection();
verify(connectionFactory2, never()).createConnection();
verify(defaultConnectionFactory).createConnection();
container.stop();
}
@Test
public void testWithSMLCAndConnectionListener() throws Exception {
ConnectionFactory connectionFactory1 = mock(ConnectionFactory.class);
Map<Object, ConnectionFactory> factories = new HashMap<Object, ConnectionFactory>(2);
factories.put("xxx[foo]", connectionFactory1);
final SimpleRoutingConnectionFactory connectionFactory = new SimpleRoutingConnectionFactory();
final Connection connection = mock(Connection.class);
Channel channel = mock(Channel.class);
given(connection.createChannel(anyBoolean())).willReturn(channel);
final AtomicReference<Object> connectionMakerKey1 = new AtomicReference<>();
final CountDownLatch latch = new CountDownLatch(1);
willAnswer(i -> {
connectionMakerKey1.set(connectionFactory.determineCurrentLookupKey());
latch.countDown();
return connection;
}).given(connectionFactory1).createConnection();
connectionFactory.setTargetConnectionFactories(factories);
final AtomicReference<Object> connectionMakerKey2 = new AtomicReference<>();
SimpleMessageListenerContainer container = new SimpleMessageListenerContainer(connectionFactory) {
@Override
protected synchronized void redeclareElementsIfNecessary() {
connectionMakerKey2.set(connectionFactory.determineCurrentLookupKey());
}
};
container.setQueueNames("foo");
container.setLookupKeyQualifier("xxx");
container.afterPropertiesSet();
container.start();
assertTrue(latch.await(10, TimeUnit.SECONDS));
container.stop();
assertThat(connectionMakerKey1.get(), equalTo("xxx[foo]"));
assertThat(connectionMakerKey2.get(), equalTo("xxx[foo]"));
}
@Test
public void testWithDMLCAndConnectionListener() throws Exception {
ConnectionFactory connectionFactory1 = mock(ConnectionFactory.class);
Map<Object, ConnectionFactory> factories = new HashMap<Object, ConnectionFactory>(2);
factories.put("xxx[foo]", connectionFactory1);
final SimpleRoutingConnectionFactory connectionFactory = new SimpleRoutingConnectionFactory();
final Connection connection = mock(Connection.class);
Channel channel = mock(Channel.class);
given(connection.createChannel(anyBoolean())).willReturn(channel);
final AtomicReference<Object> connectionMakerKey = new AtomicReference<>();
final CountDownLatch latch = new CountDownLatch(1);
willAnswer(i -> {
connectionMakerKey.set(connectionFactory.determineCurrentLookupKey());
latch.countDown();
return connection;
}).given(connectionFactory1).createConnection();
connectionFactory.setTargetConnectionFactories(factories);
final AtomicReference<Object> connectionMakerKey2 = new AtomicReference<>();
DirectMessageListenerContainer container = new DirectMessageListenerContainer(connectionFactory) {
@Override
protected synchronized void redeclareElementsIfNecessary() {
connectionMakerKey2.set(connectionFactory.determineCurrentLookupKey());
}
};
container.setQueueNames("foo");
container.setLookupKeyQualifier("xxx");
container.setShutdownTimeout(10);
container.afterPropertiesSet();
container.start();
assertTrue(latch.await(10, TimeUnit.SECONDS));
container.stop();
assertThat(connectionMakerKey.get(), equalTo("xxx[foo]"));
assertThat(connectionMakerKey2.get(), equalTo("xxx[foo]"));
}
}