/**
* Copyright 2014-2017 Linagora, Université Joseph Fourier, Floralis
*
* The present code is developed in the scope of the joint LINAGORA -
* Université Joseph Fourier - Floralis research program and is designated
* as a "Result" pursuant to the terms and conditions of the LINAGORA
* - Université Joseph Fourier - Floralis research program. Each copyright
* holder of Results enumerated here above fully & independently holds complete
* ownership of the complete Intellectual Property rights applicable to the whole
* of said Results, and may freely exploit it in any manner which does not infringe
* the moral rights of the other copyright holders.
*
* 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 net.roboconf.messaging.rabbitmq.internal;
import static net.roboconf.messaging.rabbitmq.RabbitMqConstants.RABBITMQ_SERVER_IP;
import static net.roboconf.messaging.rabbitmq.RabbitMqConstants.RABBITMQ_SERVER_PASSWORD;
import static net.roboconf.messaging.rabbitmq.RabbitMqConstants.RABBITMQ_SERVER_USERNAME;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import org.junit.After;
import org.junit.Assert;
import org.junit.Assume;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import net.roboconf.core.model.beans.Application;
import net.roboconf.core.model.beans.ApplicationTemplate;
import net.roboconf.core.model.beans.Instance;
import net.roboconf.core.model.beans.Instance.InstanceStatus;
import net.roboconf.messaging.api.business.IClient;
import net.roboconf.messaging.api.business.ListenerCommand;
import net.roboconf.messaging.api.extensions.MessagingContext.RecipientKind;
import net.roboconf.messaging.api.internal.client.AbstractMessagingTest;
import net.roboconf.messaging.api.messages.Message;
import net.roboconf.messaging.api.messages.from_agent_to_dm.MsgNotifHeartbeat;
import net.roboconf.messaging.api.messages.from_agent_to_dm.MsgNotifMachineDown;
import net.roboconf.messaging.api.messages.from_dm_to_agent.MsgCmdChangeInstanceState;
import net.roboconf.messaging.api.messages.from_dm_to_agent.MsgCmdRemoveInstance;
import net.roboconf.messaging.api.messages.from_dm_to_agent.MsgCmdSetScopedInstance;
import net.roboconf.messaging.api.reconfigurables.ReconfigurableClientAgent;
import net.roboconf.messaging.api.reconfigurables.ReconfigurableClientDm;
import net.roboconf.messaging.rabbitmq.RabbitMqConstants;
import net.roboconf.messaging.rabbitmq.internal.utils.RabbitMqTestUtils;
import net.roboconf.messaging.rabbitmq.internal.utils.RabbitMqUtils;
/**
* @author Vincent Zurczak - Linagora
*/
public class RabbitMqTest extends AbstractMessagingTest {
private static boolean rabbitMqIsRunning = false;
@BeforeClass
public static void checkRabbitMqIsRunning() throws Exception {
rabbitMqIsRunning = RabbitMqTestUtils.checkRabbitMqIsRunning();
}
@Override
protected long getDelay() {
return 700;
}
@Before
public void registerRabbitMqFactory() {
final RabbitMqClientFactory factory = new RabbitMqClientFactory();
factory.configuration = getMessagingConfiguration();
this.registry.addMessagingClientFactory(factory);
}
@After
public void cleanRabbitMq() throws Exception {
if( rabbitMqIsRunning ) {
RabbitMqClient client = new RabbitMqClient(
null,
getMessagingConfiguration(),
RecipientKind.DM );
client.openConnection();
for( String domain : Arrays.asList( null, "domain0", "domain1", "domain2", "domain" )) {
client.setOwnerProperties( RecipientKind.DM, domain, null, null );
client.deleteMessagingServerArtifacts( new Application( "app", null ));
client.deleteMessagingServerArtifacts( new Application( "app1", null ));
client.deleteMessagingServerArtifacts( new Application( "app2", null ));
client.channel.exchangeDelete( RabbitMqUtils.buildExchangeNameForTheDm( domain ));
client.channel.exchangeDelete( RabbitMqUtils.buildExchangeNameForInterApp( domain ));
}
client.closeConnection();
}
}
/**
* @return the messaging configuration to use during the tests
*/
protected Map<String,String> getMessagingConfiguration() {
Map<String,String> configuration = new HashMap<> ();
configuration.put( RABBITMQ_SERVER_IP, getMessagingIp());
configuration.put( RABBITMQ_SERVER_USERNAME, getMessagingUsername());
configuration.put( RABBITMQ_SERVER_PASSWORD, getMessagingPassword());
return configuration;
}
@Override
@Test
public void testExchangesBetweenTheDmAndOneAgent() throws Exception {
Assume.assumeTrue( rabbitMqIsRunning );
super.testExchangesBetweenTheDmAndOneAgent();
}
@Override
@Test
public void testExchangesBetweenTheDmAndThreeAgents() throws Exception {
Assume.assumeTrue( rabbitMqIsRunning );
super.testExchangesBetweenTheDmAndThreeAgents();
}
@Override
@Test
public void testExportsBetweenAgents() throws Exception {
Assume.assumeTrue( rabbitMqIsRunning );
super.testExportsBetweenAgents();
}
@Override
@Test
public void testExportsRequestsBetweenAgents() throws Exception {
Assume.assumeTrue( rabbitMqIsRunning );
super.testExportsRequestsBetweenAgents();
}
@Override
@Test
public void testExportsBetweenSiblingAgents() throws Exception {
Assume.assumeTrue( rabbitMqIsRunning );
super.testExportsBetweenSiblingAgents();
}
@Override
@Test
public void testPropagateAgentTermination() throws Exception {
Assume.assumeTrue( rabbitMqIsRunning );
super.testPropagateAgentTermination();
}
@Override
@Test
public void testDmDebug() throws Exception {
Assume.assumeTrue( rabbitMqIsRunning );
super.testDmDebug();
}
@Override
@Test
public void testExternalExports_withTwoApplications() throws Exception {
Assume.assumeTrue( rabbitMqIsRunning );
super.testExternalExports_withTwoApplications();
}
@Test
@Override
public void testExternalExports_twoApplicationsAndTheDm_verifyAgentTerminationPropagation()
throws Exception {
Assume.assumeTrue( rabbitMqIsRunning );
super.testExternalExports_twoApplicationsAndTheDm_verifyAgentTerminationPropagation();
}
@Test
@Override
public void test_applicationRegeneration() throws Exception {
Assume.assumeTrue( rabbitMqIsRunning );
super.test_applicationRegeneration();
}
/*
* This test is specific to RabbiMQ, as it is one of the rare
* implementations to actually use "domains".
*/
@Test
public void testExchangesBetweenDmAndOneAgent_with_twoDomains() throws Exception {
Assume.assumeTrue( rabbitMqIsRunning );
// Initialize everything
Application app = new Application( "app", new ApplicationTemplate());
Instance rootInstance = new Instance( "root" );
final int finalSize = 3;
final ReconfigurableClientDm[] dmClients = new ReconfigurableClientDm[ finalSize ];
final ReconfigurableClientAgent[] agentClients = new ReconfigurableClientAgent[ finalSize ];
final Map<IClient,List<Message>> map = new LinkedHashMap<> ();
for( int i=0; i<finalSize; i++ ) {
List<Message> dmMessages = new ArrayList<> ();
dmClients[ i ] = new ReconfigurableClientDm();
dmClients[ i ].setRegistry( this.registry );
dmClients[ i ].setDomain( "domain" + i );
dmClients[ i ].associateMessageProcessor( createDmProcessor( dmMessages ));
dmClients[ i ].switchMessagingType( getMessagingType());
map.put( dmClients[ i ], dmMessages );
this.clients.add( dmClients[ i ]);
List<Message> agentMessages = new ArrayList<> ();
agentClients[ i ] = new ReconfigurableClientAgent();
agentClients[ i ].setRegistry( this.registry );
agentClients[ i ].setDomain( "domain" + i );
agentClients[ i ].associateMessageProcessor( createAgentProcessor( agentMessages ));
agentClients[ i ].setApplicationName( app.getName());
agentClients[ i ].setScopedInstancePath( "/" + rootInstance.getName());
agentClients[ i ].setExternalMapping( app.getExternalExports());
agentClients[ i ].switchMessagingType( getMessagingType());
map.put( agentClients[ i ], agentMessages );
this.clients.add( agentClients[ i ]);
}
// No message yet
Thread.sleep( getDelay());
for( Map.Entry<IClient,List<Message>> entry : map.entrySet())
Assert.assertEquals( entry.getKey().getDomain(), 0, entry.getValue().size());
// Domain 0: the agent is already listening to the DM.
List<Message> agentMessages0 = map.get( agentClients[ 0 ]);
List<Message> dmMessages0 = map.get( dmClients[ 0 ]);
dmClients[ 0 ].sendMessageToAgent( app, rootInstance, new MsgCmdSetScopedInstance( rootInstance ));
Thread.sleep( getDelay());
Assert.assertEquals( 1, agentMessages0.size());
Assert.assertEquals( MsgCmdSetScopedInstance.class, agentMessages0.get( 0 ).getClass());
agentClients[ 0 ].listenToTheDm( ListenerCommand.START );
dmClients[ 0 ].sendMessageToAgent( app, rootInstance, new MsgCmdRemoveInstance( rootInstance ));
Thread.sleep( getDelay());
Assert.assertEquals( 2, agentMessages0.size());
Assert.assertEquals( MsgCmdSetScopedInstance.class, agentMessages0.get( 0 ).getClass());
Assert.assertEquals( MsgCmdRemoveInstance.class, agentMessages0.get( 1 ).getClass());
// Other domains were not notified
for( int i=1; i<finalSize; i++ ) {
Assert.assertEquals( 0, map.get( dmClients[ i ]).size());
Assert.assertEquals( 0, map.get( agentClients[ i ]).size());
}
// The agent sends a message to the DM
Assert.assertEquals( 0, dmMessages0.size());
agentClients[ 0 ].sendMessageToTheDm( new MsgNotifHeartbeat( app.getName(), rootInstance, "192.168.1.45" ));
Thread.sleep( getDelay());
Assert.assertEquals( 0, dmMessages0.size());
dmClients[ 0 ].listenToAgentMessages( app, ListenerCommand.START );
agentClients[ 0 ].sendMessageToTheDm( new MsgNotifMachineDown( app.getName(), rootInstance ));
Thread.sleep( getDelay());
Assert.assertEquals( 1, dmMessages0.size());
Assert.assertEquals( MsgNotifMachineDown.class, dmMessages0.get( 0 ).getClass());
// The DM sends another message
dmClients[ 0 ].sendMessageToAgent( app, rootInstance, new MsgCmdChangeInstanceState( rootInstance, InstanceStatus.DEPLOYED_STARTED ));
Thread.sleep( getDelay());
Assert.assertEquals( 3, agentMessages0.size());
Assert.assertEquals( MsgCmdSetScopedInstance.class, agentMessages0.get( 0 ).getClass());
Assert.assertEquals( MsgCmdRemoveInstance.class, agentMessages0.get( 1 ).getClass());
Assert.assertEquals( MsgCmdChangeInstanceState.class, agentMessages0.get( 2 ).getClass());
// Verify other domains
for( int i=1; i<finalSize; i++ ) {
Assert.assertEquals( 0, map.get( dmClients[ i ]).size());
Assert.assertEquals( 0, map.get( agentClients[ i ]).size());
}
// Let's deploy an agent from another domain
List<Message> agentMessages2 = map.get( agentClients[ 2 ]);
dmClients[ 2 ].sendMessageToAgent( app, rootInstance, new MsgCmdChangeInstanceState( rootInstance, InstanceStatus.DEPLOYED_STARTED ));
Thread.sleep( getDelay());
Assert.assertEquals( 1, agentMessages2.size());
Assert.assertEquals( MsgCmdChangeInstanceState.class, agentMessages2.get( 0 ).getClass());
// Verify other domains
Assert.assertEquals( 1, map.get( dmClients[ 0 ]).size());
Assert.assertEquals( 3, map.get( agentClients[ 0 ]).size());
Assert.assertEquals( 0, map.get( dmClients[ 1 ]).size());
Assert.assertEquals( 0, map.get( agentClients[ 1 ]).size());
Assert.assertEquals( 0, map.get( dmClients[ 2 ]).size());
Assert.assertEquals( 1, map.get( agentClients[ 2 ]).size());
// The agent stops listening the DM
agentClients[ 0 ].listenToTheDm( ListenerCommand.STOP );
Thread.sleep( getDelay());
// The agent is not listening to the DM anymore.
// With RabbitMQ, the next invocation will result in a NO_ROUTE error in the channel.
dmClients[ 0 ].sendMessageToAgent( app, rootInstance, new MsgCmdChangeInstanceState( rootInstance, InstanceStatus.DEPLOYED_STARTED ));
Thread.sleep( getDelay());
Assert.assertEquals( 3, agentMessages0.size());
Thread.sleep( getDelay());
Assert.assertEquals( 3, agentMessages0.size());
Thread.sleep( getDelay());
Assert.assertEquals( 3, agentMessages0.size());
// Verify all the domains
Assert.assertEquals( 1, map.get( dmClients[ 0 ]).size());
Assert.assertEquals( 3, map.get( agentClients[ 0 ]).size());
Assert.assertEquals( 0, map.get( dmClients[ 1 ]).size());
Assert.assertEquals( 0, map.get( agentClients[ 1 ]).size());
Assert.assertEquals( 0, map.get( dmClients[ 2 ]).size());
Assert.assertEquals( 1, map.get( agentClients[ 2 ]).size());
// The DM stops listening the agent
dmClients[ 0 ].listenToAgentMessages( app, ListenerCommand.STOP );
agentClients[ 0 ].sendMessageToTheDm( new MsgNotifHeartbeat( app.getName(), rootInstance, "192.168.1.47" ));
Thread.sleep( getDelay());
Assert.assertEquals( 1, dmMessages0.size());
Thread.sleep( getDelay());
Assert.assertEquals( 1, dmMessages0.size());
Thread.sleep( getDelay());
Assert.assertEquals( 1, dmMessages0.size());
// Verify other domains
Assert.assertEquals( 1, map.get( dmClients[ 0 ]).size());
Assert.assertEquals( 3, map.get( agentClients[ 0 ]).size());
Assert.assertEquals( 0, map.get( dmClients[ 1 ]).size());
Assert.assertEquals( 0, map.get( agentClients[ 1 ]).size());
Assert.assertEquals( 0, map.get( dmClients[ 2 ]).size());
Assert.assertEquals( 1, map.get( agentClients[ 2 ]).size());
}
private String getMessagingIp() {
return "localhost";
}
private String getMessagingUsername() {
return "guest";
}
private String getMessagingPassword() {
return "guest";
}
@Override
protected String getMessagingType() {
return RabbitMqConstants.FACTORY_RABBITMQ;
}
}