/** * 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.api.internal.client; import java.util.ArrayList; import java.util.List; import org.junit.After; import org.junit.Assert; import net.roboconf.core.internal.tests.TestApplication; import net.roboconf.core.model.beans.Application; import net.roboconf.core.model.beans.ApplicationTemplate; import net.roboconf.core.model.beans.Component; import net.roboconf.core.model.beans.ExportedVariable; import net.roboconf.core.model.beans.Facet; import net.roboconf.core.model.beans.ImportedVariable; import net.roboconf.core.model.beans.Instance; import net.roboconf.core.model.beans.Instance.InstanceStatus; import net.roboconf.core.model.helpers.InstanceHelpers; import net.roboconf.messaging.api.AbstractMessageProcessor; import net.roboconf.messaging.api.business.IAgentClient; import net.roboconf.messaging.api.business.IDmClient; import net.roboconf.messaging.api.business.ListenerCommand; import net.roboconf.messaging.api.factory.MessagingClientFactoryRegistry; import net.roboconf.messaging.api.messages.Message; import net.roboconf.messaging.api.messages.from_agent_to_agent.MsgCmdAddImport; import net.roboconf.messaging.api.messages.from_agent_to_agent.MsgCmdRemoveImport; import net.roboconf.messaging.api.messages.from_agent_to_agent.MsgCmdRequestImport; 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.messages.from_dm_to_dm.MsgEcho; import net.roboconf.messaging.api.reconfigurables.ReconfigurableClient; import net.roboconf.messaging.api.reconfigurables.ReconfigurableClientAgent; import net.roboconf.messaging.api.reconfigurables.ReconfigurableClientDm; /** * This class defines messaging tests, independently of the implementation. * <p> * They should work with RabbitMQ or any other messaging server that is * supported by Roboconf. * </p> * <p> * So, we can consider this class as a TCK for our messaging API. * Sub-class it. If all the tests go well, you can consider your Roboconf * messaging implementation is compliant with this API. * </p> * * @author Vincent Zurczak - Linagora */ public abstract class AbstractMessagingTest { protected MessagingClientFactoryRegistry registry = new MessagingClientFactoryRegistry(); protected final List<ReconfigurableClient<?>> clients = new ArrayList<> (); @After public void releaseClients() throws Exception { for( ReconfigurableClient<?> client : this.clients ) { client.getMessageProcessor().stopProcessor(); client.getMessageProcessor().interrupt(); client.closeConnection(); } this.clients.clear(); } /** * @return the delay to wait for messages (in ms) */ abstract protected long getDelay(); /** * Tests synchronous exchanges between the DM and an agent. * @throws Exception */ public void testExchangesBetweenTheDmAndOneAgent() throws Exception { // Initialize everything Application app = new Application( "app", new ApplicationTemplate()); Instance rootInstance = new Instance( "root" ); List<Message> dmMessages = new ArrayList<>(); List<Message> agentMessages = new ArrayList<>(); ReconfigurableClientDm dmClient = new ReconfigurableClientDm(); dmClient.setRegistry( this.registry ); dmClient.associateMessageProcessor( createDmProcessor( dmMessages )); dmClient.switchMessagingType( getMessagingType()); this.clients.add( dmClient ); ReconfigurableClientAgent agentClient = new ReconfigurableClientAgent(); agentClient.setRegistry( this.registry ); agentClient.associateMessageProcessor( createAgentProcessor( agentMessages )); agentClient.setApplicationName( app.getName()); agentClient.setScopedInstancePath( "/" + rootInstance.getName()); agentClient.setExternalMapping( app.getExternalExports()); agentClient.switchMessagingType( getMessagingType()); this.clients.add( agentClient ); // No message yet Thread.sleep( getDelay()); Assert.assertEquals( 0, dmMessages.size()); Assert.assertEquals( 0, agentMessages.size()); // The agent is already listening to the DM. dmClient.sendMessageToAgent( app, rootInstance, new MsgCmdSetScopedInstance( rootInstance )); Thread.sleep( getDelay()); Assert.assertEquals( 1, agentMessages.size()); Assert.assertEquals( MsgCmdSetScopedInstance.class, agentMessages.get( 0 ).getClass()); agentClient.listenToTheDm( ListenerCommand.START ); dmClient.sendMessageToAgent( app, rootInstance, new MsgCmdRemoveInstance( rootInstance )); Thread.sleep( getDelay()); Assert.assertEquals( 2, agentMessages.size()); Assert.assertEquals( MsgCmdSetScopedInstance.class, agentMessages.get( 0 ).getClass()); Assert.assertEquals( MsgCmdRemoveInstance.class, agentMessages.get( 1 ).getClass()); // The agent sends a message to the DM Assert.assertEquals( 0, dmMessages.size()); agentClient.sendMessageToTheDm( new MsgNotifHeartbeat( app.getName(), rootInstance, "192.168.1.45" )); Thread.sleep( getDelay()); Assert.assertEquals( 0, dmMessages.size()); dmClient.listenToAgentMessages( app, ListenerCommand.START ); agentClient.sendMessageToTheDm( new MsgNotifMachineDown( app.getName(), rootInstance )); Thread.sleep( getDelay()); Assert.assertEquals( 1, dmMessages.size()); Assert.assertEquals( MsgNotifMachineDown.class, dmMessages.get( 0 ).getClass()); // The DM sends another message dmClient.sendMessageToAgent( app, rootInstance, new MsgCmdChangeInstanceState( rootInstance, InstanceStatus.DEPLOYED_STARTED )); Thread.sleep( getDelay()); Assert.assertEquals( 3, agentMessages.size()); Assert.assertEquals( MsgCmdSetScopedInstance.class, agentMessages.get( 0 ).getClass()); Assert.assertEquals( MsgCmdRemoveInstance.class, agentMessages.get( 1 ).getClass()); Assert.assertEquals( MsgCmdChangeInstanceState.class, agentMessages.get( 2 ).getClass()); // The agent stops listening the DM agentClient.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. dmClient.sendMessageToAgent( app, rootInstance, new MsgCmdChangeInstanceState( rootInstance, InstanceStatus.DEPLOYED_STARTED )); Thread.sleep( getDelay()); Assert.assertEquals( 3, agentMessages.size()); Thread.sleep( getDelay()); Assert.assertEquals( 3, agentMessages.size()); Thread.sleep( getDelay()); Assert.assertEquals( 3, agentMessages.size()); // The DM stops listening the agent dmClient.listenToAgentMessages( app, ListenerCommand.STOP ); agentClient.sendMessageToTheDm( new MsgNotifHeartbeat( app.getName(), rootInstance, "192.168.1.47" )); Thread.sleep( getDelay()); Assert.assertEquals( 1, dmMessages.size()); Thread.sleep( getDelay()); Assert.assertEquals( 1, dmMessages.size()); Thread.sleep( getDelay()); Assert.assertEquals( 1, dmMessages.size()); } /** * Makes sure messages go to the right agent. * <p> * This is about messages routing. * </p> * * @throws Exception */ public void testExchangesBetweenTheDmAndThreeAgents() throws Exception { // Initialize everything // 1 DM, 2 agents (root1 and root2) for application app1 and 1 agent (root) for app2. Application app1 = new Application( "app1", new ApplicationTemplate()); Application app2 = new Application( "app2", new ApplicationTemplate()); Instance app1_root1 = new Instance( "root1" ); Instance app1_root2 = new Instance( "root2" ); Instance app2_root = new Instance( "root" ); List<Message> dmMessages = new ArrayList<>(); ReconfigurableClientDm dmClient = new ReconfigurableClientDm(); dmClient.setRegistry( this.registry ); dmClient.associateMessageProcessor( createDmProcessor( dmMessages )); dmClient.switchMessagingType( getMessagingType()); this.clients.add( dmClient ); List<Message> agentMessages_11 = new ArrayList<>(); ReconfigurableClientAgent agentClient_11 = new ReconfigurableClientAgent(); agentClient_11.setRegistry( this.registry ); agentClient_11.associateMessageProcessor( createAgentProcessor( agentMessages_11 )); agentClient_11.setApplicationName( app1.getName()); agentClient_11.setScopedInstancePath( "/" + app1_root1.getName()); agentClient_11.setExternalMapping( app1.getExternalExports()); agentClient_11.switchMessagingType( getMessagingType()); this.clients.add( agentClient_11 ); List<Message> agentMessages_12 = new ArrayList<>(); ReconfigurableClientAgent agentClient_12 = new ReconfigurableClientAgent(); agentClient_12.setRegistry( this.registry ); agentClient_12.associateMessageProcessor( createAgentProcessor( agentMessages_12 )); agentClient_12.setApplicationName( app1.getName()); agentClient_12.setScopedInstancePath( "/" + app1_root2.getName()); agentClient_12.setExternalMapping( app1.getExternalExports()); agentClient_12.switchMessagingType( getMessagingType()); this.clients.add( agentClient_12 ); List<Message> agentMessages_2 = new ArrayList<>(); ReconfigurableClientAgent agentClient_2 = new ReconfigurableClientAgent(); agentClient_2.setRegistry( this.registry ); agentClient_2.associateMessageProcessor( createAgentProcessor( agentMessages_2 )); agentClient_2.setApplicationName( app2.getName()); agentClient_2.setScopedInstancePath( "/" + app2_root.getName()); agentClient_2.setExternalMapping( app2.getExternalExports()); agentClient_2.switchMessagingType( getMessagingType()); this.clients.add( agentClient_2 ); // Everybody starts listening... agentClient_11.listenToTheDm( ListenerCommand.START ); agentClient_12.listenToTheDm( ListenerCommand.START ); agentClient_2.listenToTheDm( ListenerCommand.START ); Thread.sleep( getDelay()); // The DM sends messages dmClient.sendMessageToAgent( app1, app1_root1, new MsgCmdSetScopedInstance( app1_root1 )); dmClient.sendMessageToAgent( app2, app2_root, new MsgCmdSetScopedInstance( app2_root )); dmClient.sendMessageToAgent( app1, app1_root2, new MsgCmdSetScopedInstance( app1_root2, null, null, null )); dmClient.sendMessageToAgent( app2, app2_root, new MsgCmdRemoveInstance( app2_root )); dmClient.sendMessageToAgent( app2, app2_root, new MsgCmdChangeInstanceState( app2_root, InstanceStatus.DEPLOYED_STOPPED )); dmClient.sendMessageToAgent( app1, app1_root2, new MsgCmdRemoveInstance( app1_root2 )); dmClient.sendMessageToAgent( app1, app1_root2, new MsgCmdRemoveInstance( app1_root2 )); dmClient.sendMessageToAgent( app1, app1_root2, new MsgCmdChangeInstanceState( app1_root2, InstanceStatus.NOT_DEPLOYED )); dmClient.sendMessageToAgent( app1, app1_root1, new MsgCmdRemoveInstance( app1_root1 )); dmClient.sendMessageToAgent( app1, app1_root1, new MsgCmdSetScopedInstance( app1_root1 )); // Check what was received Thread.sleep( getDelay()); Assert.assertEquals( 3, agentMessages_11.size()); Assert.assertEquals( MsgCmdSetScopedInstance.class, agentMessages_11.get( 0 ).getClass()); Assert.assertEquals( MsgCmdRemoveInstance.class, agentMessages_11.get( 1 ).getClass()); Assert.assertEquals( MsgCmdSetScopedInstance.class, agentMessages_11.get( 2 ).getClass()); Assert.assertEquals( 4, agentMessages_12.size()); Assert.assertEquals( MsgCmdSetScopedInstance.class, agentMessages_12.get( 0 ).getClass()); Assert.assertEquals( MsgCmdRemoveInstance.class, agentMessages_12.get( 1 ).getClass()); Assert.assertEquals( MsgCmdRemoveInstance.class, agentMessages_12.get( 2 ).getClass()); Assert.assertEquals( MsgCmdChangeInstanceState.class, agentMessages_12.get( 3 ).getClass()); Assert.assertEquals( 3, agentMessages_2.size()); Assert.assertEquals( MsgCmdSetScopedInstance.class, agentMessages_2.get( 0 ).getClass()); Assert.assertEquals( MsgCmdRemoveInstance.class, agentMessages_2.get( 1 ).getClass()); Assert.assertEquals( MsgCmdChangeInstanceState.class, agentMessages_2.get( 2 ).getClass()); } /** * Makes sure exports are exchanged correctly between agents. * @throws Exception */ public void testExportsBetweenAgents() throws Exception { // 3 agents (tomcat, mysql, apache) for application app1 and 1 agent (root) for app2. // This last one should not receive anything! Application app1 = new Application( "app1", new ApplicationTemplate()); Application app2 = new Application( "app2", new ApplicationTemplate()); Component tomcatComponent = new Component( "Tomcat" ); tomcatComponent.addExportedVariable( new ExportedVariable( "Tomcat.ip", "localhost" )); tomcatComponent.addExportedVariable( new ExportedVariable( "Tomcat.port", "8080" )); tomcatComponent.addImportedVariable( new ImportedVariable( "MySQL.port", false, false )); tomcatComponent.addImportedVariable( new ImportedVariable( "MySQL.ip", false, false )); Instance tomcat = new Instance( "tomcat" ).component( tomcatComponent ); Component mysqlComponent = new Component( "MySQL" ); mysqlComponent.addExportedVariable( new ExportedVariable( "MySQL.port", "3306" )); mysqlComponent.addExportedVariable( new ExportedVariable( "MySQL.ip", "192.168.1.15" )); Instance mysql = new Instance( "mysql" ).component( mysqlComponent ); Component apacheComponent = new Component( "Apache" ); apacheComponent.addExportedVariable( new ExportedVariable( "Apache.ip", "apache.roboconf.net" )); apacheComponent.addImportedVariable( new ImportedVariable( "Tomcat.port", false, false )); apacheComponent.addImportedVariable( new ImportedVariable( "Tomcat.ip", false, false )); Instance apache = new Instance( "apache" ).component( apacheComponent ); // This one is a good candidate to receive something when others publish something. // Except it is not in the same application. Component otherComponent = new Component( "other" ); apacheComponent.addImportedVariable( new ImportedVariable( "Tomcat.port", false, false )); apacheComponent.addImportedVariable( new ImportedVariable( "Tomcat.ip", false, false )); apacheComponent.addImportedVariable( new ImportedVariable( "Mongo.ip", false, false )); mysqlComponent.addExportedVariable( new ExportedVariable( "MySQL.port", "3306" )); mysqlComponent.addExportedVariable( new ExportedVariable( "MySQL.ip", "192.168.1.15" )); Instance other = new Instance( "other" ).component( otherComponent ); // Initialize the messaging List<Message> tomcatMessages = new ArrayList<>(); ReconfigurableClientAgent tomcatClient = new ReconfigurableClientAgent(); tomcatClient.setRegistry( this.registry ); tomcatClient.associateMessageProcessor( createAgentProcessor( tomcatMessages )); tomcatClient.setApplicationName( app1.getName()); tomcatClient.setScopedInstancePath( "/" + tomcat.getName()); tomcatClient.setExternalMapping( app1.getExternalExports()); tomcatClient.switchMessagingType( getMessagingType()); this.clients.add( tomcatClient ); List<Message> apacheMessages = new ArrayList<>(); ReconfigurableClientAgent apacheClient = new ReconfigurableClientAgent(); apacheClient.setRegistry( this.registry ); apacheClient.associateMessageProcessor( createAgentProcessor( apacheMessages )); apacheClient.setApplicationName( app1.getName()); apacheClient.setScopedInstancePath( "/" + apache.getName()); apacheClient.setExternalMapping( app1.getExternalExports()); apacheClient.switchMessagingType( getMessagingType()); this.clients.add( apacheClient ); List<Message> mySqlMessages = new ArrayList<>(); ReconfigurableClientAgent mySqlClient = new ReconfigurableClientAgent(); mySqlClient.setRegistry( this.registry ); mySqlClient.associateMessageProcessor( createAgentProcessor( mySqlMessages )); mySqlClient.setApplicationName( app1.getName()); mySqlClient.setScopedInstancePath( "/" + mysql.getName()); mySqlClient.setExternalMapping( app1.getExternalExports()); mySqlClient.switchMessagingType( getMessagingType()); this.clients.add( mySqlClient ); List<Message> otherMessages = new ArrayList<>(); ReconfigurableClientAgent otherClient = new ReconfigurableClientAgent(); otherClient.setRegistry( this.registry ); otherClient.associateMessageProcessor( createAgentProcessor( otherMessages )); otherClient.setApplicationName( app2.getName()); otherClient.setScopedInstancePath( "/" + other.getName()); otherClient.setExternalMapping( app2.getExternalExports()); otherClient.switchMessagingType( getMessagingType()); this.clients.add( otherClient ); // OK, let's start. // MySQL publishes its exports but nobody is listening. mySqlClient.publishExports( mysql ); Thread.sleep( getDelay()); Assert.assertEquals( 0, mySqlMessages.size()); Assert.assertEquals( 0, apacheMessages.size()); Assert.assertEquals( 0, tomcatMessages.size()); Assert.assertEquals( 0, otherMessages.size()); // Tomcat and Other are now listening. // Let's re-export MySQL. otherClient.listenToExportsFromOtherAgents( ListenerCommand.START, other ); tomcatClient.listenToExportsFromOtherAgents( ListenerCommand.START, tomcat ); Thread.sleep( getDelay()); mySqlClient.publishExports( mysql ); Thread.sleep( getDelay()); Assert.assertEquals( 0, mySqlMessages.size()); Assert.assertEquals( 0, apacheMessages.size()); Assert.assertEquals( 0, otherMessages.size()); Assert.assertEquals( 1, tomcatMessages.size()); Assert.assertEquals( MsgCmdAddImport.class, tomcatMessages.get( 0 ).getClass()); MsgCmdAddImport msg = (MsgCmdAddImport) tomcatMessages.get( 0 ); Assert.assertEquals( "MySQL", msg.getComponentOrFacetName()); Assert.assertEquals( "/mysql", msg.getAddedInstancePath()); Assert.assertEquals( 2, msg.getExportedVariables().size()); Assert.assertTrue( msg.getExportedVariables().containsKey( "MySQL.port" )); Assert.assertTrue( msg.getExportedVariables().containsKey( "MySQL.ip" )); // Let's publish an unknown facet. Nobody should receive it. mySqlClient.publishExports( mysql, "an-unknown-facet-or-component-name" ); Thread.sleep( getDelay()); Assert.assertEquals( 0, mySqlMessages.size()); Assert.assertEquals( 0, apacheMessages.size()); Assert.assertEquals( 0, otherMessages.size()); Assert.assertEquals( 1, tomcatMessages.size()); // Other publishes its exports. // Tomcat is not supposed to receive it. otherClient.publishExports( other ); Thread.sleep( getDelay()); Assert.assertEquals( 0, mySqlMessages.size()); Assert.assertEquals( 0, apacheMessages.size()); Assert.assertEquals( 0, otherMessages.size()); Assert.assertEquals( 1, tomcatMessages.size()); Assert.assertEquals( MsgCmdAddImport.class, tomcatMessages.get( 0 ).getClass()); // Everybody is listening... // Tomcat publishes its exports. apacheClient.listenToExportsFromOtherAgents( ListenerCommand.START, apache ); mySqlClient.listenToExportsFromOtherAgents( ListenerCommand.START, mysql ); Thread.sleep( getDelay()); tomcatClient.publishExports( tomcat ); Thread.sleep( getDelay()); Assert.assertEquals( 0, mySqlMessages.size()); Assert.assertEquals( 0, otherMessages.size()); Assert.assertEquals( 1, tomcatMessages.size()); Assert.assertEquals( 1, apacheMessages.size()); Assert.assertEquals( MsgCmdAddImport.class, apacheMessages.get( 0 ).getClass()); msg = (MsgCmdAddImport) apacheMessages.get( 0 ); Assert.assertEquals( "Tomcat", msg.getComponentOrFacetName()); Assert.assertEquals( "/tomcat", msg.getAddedInstancePath()); Assert.assertEquals( 2, msg.getExportedVariables().size()); Assert.assertTrue( msg.getExportedVariables().containsKey( "Tomcat.port" )); Assert.assertTrue( msg.getExportedVariables().containsKey( "Tomcat.ip" )); // MySQL publishes (again) its exports mySqlClient.publishExports( mysql ); Thread.sleep( getDelay()); Assert.assertEquals( 0, mySqlMessages.size()); Assert.assertEquals( 0, otherMessages.size()); Assert.assertEquals( 1, apacheMessages.size()); Assert.assertEquals( 2, tomcatMessages.size()); Assert.assertEquals( MsgCmdAddImport.class, tomcatMessages.get( 0 ).getClass()); Assert.assertEquals( MsgCmdAddImport.class, tomcatMessages.get( 1 ).getClass()); msg = (MsgCmdAddImport) tomcatMessages.get( 1 ); Assert.assertEquals( "MySQL", msg.getComponentOrFacetName()); Assert.assertEquals( "/mysql", msg.getAddedInstancePath()); Assert.assertEquals( 2, msg.getExportedVariables().size()); Assert.assertTrue( msg.getExportedVariables().containsKey( "MySQL.port" )); Assert.assertTrue( msg.getExportedVariables().containsKey( "MySQL.ip" )); // MySQL un-publishes its exports mySqlClient.unpublishExports( mysql ); Thread.sleep( getDelay()); Assert.assertEquals( 0, mySqlMessages.size()); Assert.assertEquals( 0, otherMessages.size()); Assert.assertEquals( 1, apacheMessages.size()); Assert.assertEquals( 3, tomcatMessages.size()); Assert.assertEquals( MsgCmdAddImport.class, tomcatMessages.get( 0 ).getClass()); Assert.assertEquals( MsgCmdAddImport.class, tomcatMessages.get( 1 ).getClass()); Assert.assertEquals( MsgCmdRemoveImport.class, tomcatMessages.get( 2 ).getClass()); MsgCmdRemoveImport newMsg = (MsgCmdRemoveImport) tomcatMessages.get( 2 ); Assert.assertEquals( "MySQL", newMsg.getComponentOrFacetName()); Assert.assertEquals( "/mysql", newMsg.getRemovedInstancePath()); // MySQL publishes (again) its exports // But this time, Tomcat does not listen anymore tomcatClient.listenToExportsFromOtherAgents( ListenerCommand.STOP, tomcat ); Thread.sleep( getDelay()); mySqlClient.publishExports( mysql ); Thread.sleep( getDelay()); Assert.assertEquals( 0, mySqlMessages.size()); Assert.assertEquals( 0, otherMessages.size()); Assert.assertEquals( 1, apacheMessages.size()); Assert.assertEquals( 3, tomcatMessages.size()); } /** * Makes sure exports requests are exchanged correctly between agents. * @throws Exception */ public void testExportsRequestsBetweenAgents() throws Exception { // 3 agents (tomcat, mysql, apache) for application app1 and 1 agent (root) for app2. // This last one should not receive anything! Application app1 = new Application( "app1", new ApplicationTemplate()); Application app2 = new Application( "app2", new ApplicationTemplate()); Component tomcatComponent = new Component( "Tomcat" ); tomcatComponent.addExportedVariable( new ExportedVariable( "Tomcat.ip", "localhost" )); tomcatComponent.addExportedVariable( new ExportedVariable( "Tomcat.port", "8080" )); tomcatComponent.addImportedVariable( new ImportedVariable( "MySQL.port", false, false )); tomcatComponent.addImportedVariable( new ImportedVariable( "MySQL.ip", false, false )); Instance tomcat = new Instance( "tomcat" ).component( tomcatComponent ); Component mysqlComponent = new Component( "MySQL" ); mysqlComponent.addExportedVariable( new ExportedVariable( "MySQL.port", "3306" )); mysqlComponent.addExportedVariable( new ExportedVariable( "MySQL.ip", "192.168.1.15" )); Instance mysql = new Instance( "mysql" ).component( mysqlComponent ); Component apacheComponent = new Component( "Apache" ); apacheComponent.addExportedVariable( new ExportedVariable( "Apache.ip", "apache.roboconf.net" )); apacheComponent.addImportedVariable( new ImportedVariable( "Tomcat.port", false, false )); apacheComponent.addImportedVariable( new ImportedVariable( "Tomcat.ip", false, false )); Instance apache = new Instance( "apache" ).component( apacheComponent ); // This one is a good candidate to receive something when others publish something. // Except it is not in the same application. Component otherComponent = new Component( "other" ); apacheComponent.addImportedVariable( new ImportedVariable( "Tomcat.port", false, false )); apacheComponent.addImportedVariable( new ImportedVariable( "Tomcat.ip", false, false )); apacheComponent.addImportedVariable( new ImportedVariable( "Mongo.ip", false, false )); mysqlComponent.addExportedVariable( new ExportedVariable( "MySQL.port", "3306" )); mysqlComponent.addExportedVariable( new ExportedVariable( "MySQL.ip", "192.168.1.15" )); Instance other = new Instance( "other" ).component( otherComponent ); // Initialize the messaging List<Message> tomcatMessages = new ArrayList<>(); ReconfigurableClientAgent tomcatClient = new ReconfigurableClientAgent(); tomcatClient.setRegistry( this.registry ); tomcatClient.associateMessageProcessor( createAgentProcessor( tomcatMessages )); tomcatClient.setApplicationName( app1.getName()); tomcatClient.setScopedInstancePath( "/" + tomcat.getName()); tomcatClient.setExternalMapping( app1.getExternalExports()); tomcatClient.switchMessagingType( getMessagingType()); this.clients.add( tomcatClient ); List<Message> apacheMessages = new ArrayList<>(); ReconfigurableClientAgent apacheClient = new ReconfigurableClientAgent(); apacheClient.setRegistry( this.registry ); apacheClient.associateMessageProcessor( createAgentProcessor( apacheMessages )); apacheClient.setApplicationName( app1.getName()); apacheClient.setScopedInstancePath( "/" + apache.getName()); apacheClient.setExternalMapping( app1.getExternalExports()); apacheClient.switchMessagingType( getMessagingType()); this.clients.add( apacheClient ); List<Message> mySqlMessages = new ArrayList<>(); ReconfigurableClientAgent mySqlClient = new ReconfigurableClientAgent(); mySqlClient.setRegistry( this.registry ); mySqlClient.associateMessageProcessor( createAgentProcessor( mySqlMessages )); mySqlClient.setApplicationName( app1.getName()); mySqlClient.setScopedInstancePath( "/" + mysql.getName()); mySqlClient.setExternalMapping( app1.getExternalExports()); mySqlClient.switchMessagingType( getMessagingType()); this.clients.add( mySqlClient ); List<Message> otherMessages = new ArrayList<>(); ReconfigurableClientAgent otherClient = new ReconfigurableClientAgent(); otherClient.setRegistry( this.registry ); otherClient.associateMessageProcessor( createAgentProcessor( otherMessages )); otherClient.setApplicationName( app2.getName()); otherClient.setScopedInstancePath( "/" + other.getName()); otherClient.setExternalMapping( app2.getExternalExports()); otherClient.switchMessagingType( getMessagingType()); this.clients.add( otherClient ); // OK, let's start. // Tomcat requests MySQL exports but MySQL is not listening tomcatClient.requestExportsFromOtherAgents( tomcat ); Thread.sleep( getDelay()); Assert.assertEquals( 0, mySqlMessages.size()); Assert.assertEquals( 0, apacheMessages.size()); Assert.assertEquals( 0, tomcatMessages.size()); Assert.assertEquals( 0, otherMessages.size()); // Now, Other and MySQL are listening. // Only MySQL should receive it (Other is in another application). otherClient.listenToRequestsFromOtherAgents( ListenerCommand.START, other ); mySqlClient.listenToRequestsFromOtherAgents( ListenerCommand.START, mysql ); Thread.sleep( getDelay()); tomcatClient.requestExportsFromOtherAgents( tomcat ); Thread.sleep( getDelay()); Assert.assertEquals( 0, apacheMessages.size()); Assert.assertEquals( 0, tomcatMessages.size()); Assert.assertEquals( 0, otherMessages.size()); Assert.assertEquals( 1, mySqlMessages.size()); Assert.assertEquals( MsgCmdRequestImport.class, mySqlMessages.get( 0 ).getClass()); MsgCmdRequestImport msg = (MsgCmdRequestImport) mySqlMessages.get( 0 ); Assert.assertEquals( "MySQL", msg.getComponentOrFacetName()); // Now, let's do it again but MySQL stops listening. mySqlClient.listenToRequestsFromOtherAgents( ListenerCommand.STOP, mysql ); Thread.sleep( getDelay()); tomcatClient.requestExportsFromOtherAgents( tomcat ); Thread.sleep( getDelay()); Assert.assertEquals( 0, apacheMessages.size()); Assert.assertEquals( 0, tomcatMessages.size()); Assert.assertEquals( 0, otherMessages.size()); Assert.assertEquals( 1, mySqlMessages.size()); // Other requires exports from others. otherClient.requestExportsFromOtherAgents( other ); Thread.sleep( getDelay()); Assert.assertEquals( 0, apacheMessages.size()); Assert.assertEquals( 0, tomcatMessages.size()); Assert.assertEquals( 0, otherMessages.size()); Assert.assertEquals( 1, mySqlMessages.size()); } /** * Tests exchanges between sibling agents and several variable groups. * @throws Exception */ public void testExportsBetweenSiblingAgents() throws Exception { // The model Application app = new Application( "app", null ); Facet facet = new Facet( "facet" ); facet.addExportedVariable( new ExportedVariable( "facet.data", "hello" )); Component component = new Component( "Component" ); component.associateFacet( facet ); component.addExportedVariable( new ExportedVariable( "Component.ip", "localhost" )); component.addExportedVariable( new ExportedVariable( "Component.port", "8080" )); component.addImportedVariable( new ImportedVariable( "Component.port", true, false )); component.addImportedVariable( new ImportedVariable( "Component.ip", true, false )); component.addImportedVariable( new ImportedVariable( "facet.data", true, false )); Instance instance1 = new Instance( "instance1" ).component( component ); Instance instance2 = new Instance( "instance2" ).component( component ); // Initialize the messaging List<Message> messages1 = new ArrayList<>(); ReconfigurableClientAgent client1 = new ReconfigurableClientAgent(); client1.setRegistry( this.registry ); client1.associateMessageProcessor( createAgentProcessor( messages1 )); client1.setApplicationName( app.getName()); client1.setScopedInstancePath( "/" + instance1.getName()); client1.setExternalMapping( app.getExternalExports()); client1.switchMessagingType( getMessagingType()); this.clients.add( client1 ); List<Message> messages2 = new ArrayList<>(); ReconfigurableClientAgent client2 = new ReconfigurableClientAgent(); client2.setRegistry( this.registry ); client2.associateMessageProcessor( createAgentProcessor( messages2 )); client2.setApplicationName( app.getName()); client2.setScopedInstancePath( "/" + instance2.getName()); client2.setExternalMapping( app.getExternalExports()); client2.switchMessagingType( getMessagingType()); this.clients.add( client2 ); // OK, let's start. // Instance1 is alone. client1.requestExportsFromOtherAgents( instance1 ); Thread.sleep( getDelay()); Assert.assertEquals( 0, messages1.size()); Assert.assertEquals( 0, messages2.size()); // Now, instance2 is listening. client2.listenToRequestsFromOtherAgents( ListenerCommand.START, instance2 ); Thread.sleep( getDelay()); client1.requestExportsFromOtherAgents( instance1 ); Thread.sleep( getDelay()); Assert.assertEquals( 0, messages1.size()); Assert.assertEquals( 2, messages2.size()); Assert.assertEquals( MsgCmdRequestImport.class, messages2.get( 0 ).getClass()); Assert.assertEquals( MsgCmdRequestImport.class, messages2.get( 1 ).getClass()); String facet1 = ((MsgCmdRequestImport) messages2.get( 0 )).getComponentOrFacetName(); String facet2 = ((MsgCmdRequestImport) messages2.get( 1 )).getComponentOrFacetName(); Assert.assertNotSame( facet1, facet2 ); Assert.assertTrue( facet1.equals( "Component" ) || facet1.equals( "facet" )); Assert.assertTrue( facet2.equals( "Component" ) || facet2.equals( "facet" )); // instance1 is now listening // instance2 stops listening // instance1 should receive the notification it has sent. It will be up to the agent to ignore it. client2.listenToRequestsFromOtherAgents( ListenerCommand.STOP, instance2 ); client1.listenToRequestsFromOtherAgents( ListenerCommand.START, instance1 ); Thread.sleep( getDelay()); client1.requestExportsFromOtherAgents( instance1 ); Thread.sleep( getDelay()); Assert.assertEquals( 2, messages2.size()); Assert.assertEquals( 2, messages1.size()); Assert.assertEquals( MsgCmdRequestImport.class, messages1.get( 0 ).getClass()); Assert.assertEquals( MsgCmdRequestImport.class, messages1.get( 1 ).getClass()); facet1 = ((MsgCmdRequestImport) messages1.get( 0 )).getComponentOrFacetName(); facet2 = ((MsgCmdRequestImport) messages1.get( 1 )).getComponentOrFacetName(); Assert.assertNotSame( facet1, facet2 ); Assert.assertTrue( facet1.equals( "Component" ) || facet1.equals( "facet" )); Assert.assertTrue( facet2.equals( "Component" ) || facet2.equals( "facet" )); } /** * Checks that agents termination results in messages to the right agents. * @throws Exception */ public void testPropagateAgentTermination() throws Exception { // 3 agents (tomcat, mysql, apache) for application app1 and 1 agent (root) for app2. // This last one should not receive anything! Application app1 = new Application( "app1", new ApplicationTemplate( "tpl1" )); Application app2 = new Application( "app2", new ApplicationTemplate( "tpl2" )); Component tomcatComponent = new Component( "Tomcat" ); tomcatComponent.addExportedVariable( new ExportedVariable( "Tomcat.ip", "localhost" )); tomcatComponent.addExportedVariable( new ExportedVariable( "Tomcat.port", "8080" )); tomcatComponent.addImportedVariable( new ImportedVariable( "MySQL.port", false, false )); tomcatComponent.addImportedVariable( new ImportedVariable( "MySQL.ip", false, false )); Instance tomcat = new Instance( "tomcat" ).component( tomcatComponent ); Component mysqlComponent = new Component( "MySQL" ); mysqlComponent.addExportedVariable( new ExportedVariable( "MySQL.port", "3306" )); mysqlComponent.addExportedVariable( new ExportedVariable( "MySQL.ip", "192.168.1.15" )); Instance mysql = new Instance( "mysql" ).component( mysqlComponent ); Component apacheComponent = new Component( "Apache" ); apacheComponent.addExportedVariable( new ExportedVariable( "Apache.ip", "apache.roboconf.net" )); apacheComponent.addImportedVariable( new ImportedVariable( "Tomcat.port", false, false )); apacheComponent.addImportedVariable( new ImportedVariable( "Tomcat.ip", false, false )); Instance apache = new Instance( "apache" ).component( apacheComponent ); // This one is a good candidate to receive something when others publish something. // Except it is not in the same application. List<Message> dmMessages = new ArrayList<>(); ReconfigurableClientDm dmClient = new ReconfigurableClientDm(); dmClient.setRegistry( this.registry ); dmClient.associateMessageProcessor( createDmProcessor( dmMessages )); dmClient.switchMessagingType( getMessagingType()); this.clients.add( dmClient ); Component otherComponent = new Component( "other" ); apacheComponent.addImportedVariable( new ImportedVariable( "Tomcat.port", false, false )); apacheComponent.addImportedVariable( new ImportedVariable( "Tomcat.ip", false, false )); apacheComponent.addImportedVariable( new ImportedVariable( "Mongo.ip", false, false )); mysqlComponent.addExportedVariable( new ExportedVariable( "MySQL.port", "3306" )); mysqlComponent.addExportedVariable( new ExportedVariable( "MySQL.ip", "192.168.1.15" )); Instance other = new Instance( "other" ).component( otherComponent ); // Initialize the messaging List<Message> tomcatMessages = new ArrayList<>(); ReconfigurableClientAgent tomcatClient = new ReconfigurableClientAgent(); tomcatClient.setRegistry( this.registry ); tomcatClient.associateMessageProcessor( createAgentProcessor( tomcatMessages )); tomcatClient.setApplicationName( app1.getName()); tomcatClient.setScopedInstancePath( "/" + tomcat.getName()); tomcatClient.setExternalMapping( app1.getExternalExports()); tomcatClient.switchMessagingType( getMessagingType()); tomcatClient.listenToTheDm( ListenerCommand.START ); tomcatClient.listenToExportsFromOtherAgents( ListenerCommand.START, tomcat ); this.clients.add( tomcatClient ); List<Message> apacheMessages = new ArrayList<>(); ReconfigurableClientAgent apacheClient = new ReconfigurableClientAgent(); apacheClient.setRegistry( this.registry ); apacheClient.associateMessageProcessor( createAgentProcessor( apacheMessages )); apacheClient.setApplicationName( app1.getName()); apacheClient.setScopedInstancePath( "/" + apache.getName()); apacheClient.setExternalMapping( app1.getExternalExports()); apacheClient.switchMessagingType( getMessagingType()); apacheClient.listenToTheDm( ListenerCommand.START ); apacheClient.listenToExportsFromOtherAgents( ListenerCommand.START, apache ); this.clients.add( apacheClient ); List<Message> mySqlMessages = new ArrayList<>(); ReconfigurableClientAgent mySqlClient = new ReconfigurableClientAgent(); mySqlClient.setRegistry( this.registry ); mySqlClient.associateMessageProcessor( createAgentProcessor( mySqlMessages )); mySqlClient.setApplicationName( app1.getName()); mySqlClient.setScopedInstancePath( "/" + mysql.getName()); mySqlClient.setExternalMapping( app1.getExternalExports()); mySqlClient.switchMessagingType( getMessagingType()); mySqlClient.listenToTheDm( ListenerCommand.START ); mySqlClient.listenToExportsFromOtherAgents( ListenerCommand.START, mysql ); this.clients.add( mySqlClient ); List<Message> otherMessages = new ArrayList<>(); ReconfigurableClientAgent otherClient = new ReconfigurableClientAgent(); otherClient.setRegistry( this.registry ); otherClient.associateMessageProcessor( createAgentProcessor( otherMessages )); otherClient.setApplicationName( app2.getName()); otherClient.setScopedInstancePath( "/" + other.getName()); otherClient.setExternalMapping( app2.getExternalExports()); otherClient.switchMessagingType( getMessagingType()); otherClient.listenToTheDm( ListenerCommand.START ); otherClient.listenToExportsFromOtherAgents( ListenerCommand.START, other ); this.clients.add( otherClient ); // Propagate the termination of MySQL should only notify the Tomcat agent. // Terminate the other component should notify no other instance. dmClient.propagateAgentTermination( app1, mysql ); dmClient.propagateAgentTermination( app2, other ); Thread.sleep( getDelay()); Assert.assertEquals( 0, apacheMessages.size()); Assert.assertEquals( 0, mySqlMessages.size()); Assert.assertEquals( 0, otherMessages.size()); Assert.assertEquals( 1, tomcatMessages.size()); Assert.assertEquals( MsgCmdRemoveImport.class, tomcatMessages.get( 0 ).getClass()); MsgCmdRemoveImport msg = (MsgCmdRemoveImport) tomcatMessages.get( 0 ); Assert.assertEquals( "MySQL", msg.getComponentOrFacetName()); Assert.assertEquals( InstanceHelpers.computeInstancePath( mysql ), msg.getRemovedInstancePath()); } /** * Checks the DM's debugging exchanges. * @throws Exception */ public void testDmDebug() throws Exception { List<Message> dmMessages = new ArrayList<>(); ReconfigurableClientDm dmClient = new ReconfigurableClientDm(); dmClient.setRegistry( this.registry ); dmClient.associateMessageProcessor( createDmProcessor( dmMessages )); dmClient.switchMessagingType( getMessagingType()); this.clients.add( dmClient ); dmClient.sendMessageToTheDm( new MsgEcho( "hey 1" )); Thread.sleep( getDelay()); Assert.assertEquals( 0, dmMessages.size()); dmClient.listenToTheDm( ListenerCommand.START ); dmClient.sendMessageToTheDm( new MsgEcho( "hey 2" )); dmClient.sendMessageToTheDm( new MsgEcho( "hey 3" )); Thread.sleep( getDelay()); Assert.assertEquals( 2, dmMessages.size()); Assert.assertEquals( MsgEcho.class, dmMessages.get( 0 ).getClass()); Assert.assertEquals( "hey 2", ((MsgEcho) dmMessages.get( 0 )).getContent()); Assert.assertEquals( MsgEcho.class, dmMessages.get( 1 ).getClass()); Assert.assertEquals( "hey 3", ((MsgEcho) dmMessages.get( 1 )).getContent()); dmClient.listenToTheDm( ListenerCommand.STOP ); dmClient.sendMessageToTheDm( new MsgEcho( "hey again" )); Thread.sleep( getDelay()); Assert.assertEquals( 2, dmMessages.size()); } /** * Makes sure exports requests are exchanged correctly between agents. * @throws Exception */ public void testExternalExports_withTwoApplications() throws Exception { // Create two applications TestApplication app1 = new TestApplication(); app1.getTemplate().setName( "tpl1" ); app1.setName( "app1" ); TestApplication app2 = new TestApplication(); app2.getTemplate().setName( "tpl2" ); app2.setName( "app2" ); // Add an external dependency between them: app1 depends on app2 ImportedVariable var = new ImportedVariable( "tpl2.ext-ip", false, true ); app1.getTomcat().getComponent().importedVariables.put( var.getName(), var ); app2.getTemplate().externalExports.put( "mysql.ip", "tpl2.ext-ip" ); app2.getTemplate().externalExports.put( "mysql.port", "tpl2.ext-port" ); // Prepare messaging clients - we only focus on MySQL and Tomcat List<Message> app1_mysqlMessages = new ArrayList<> (); ReconfigurableClientAgent app1_mysqlClient = new ReconfigurableClientAgent(); app1_mysqlClient.setRegistry( this.registry ); app1_mysqlClient.associateMessageProcessor( createAgentProcessor( app1_mysqlMessages )); app1_mysqlClient.setApplicationName( app1.getName()); app1_mysqlClient.setScopedInstancePath( InstanceHelpers.computeInstancePath( app1.getMySqlVm())); app1_mysqlClient.setExternalMapping( app1.getExternalExports()); app1_mysqlClient.switchMessagingType( getMessagingType()); this.clients.add( app1_mysqlClient ); List<Message> app2_mysqlMessages = new ArrayList<> (); ReconfigurableClientAgent app2_mysqlClient = new ReconfigurableClientAgent(); app2_mysqlClient.setRegistry( this.registry ); app2_mysqlClient.associateMessageProcessor( createAgentProcessor( app2_mysqlMessages )); app2_mysqlClient.setApplicationName( app2.getName()); app2_mysqlClient.setScopedInstancePath( InstanceHelpers.computeInstancePath( app2.getMySqlVm())); app2_mysqlClient.setExternalMapping( app2.getExternalExports()); app2_mysqlClient.switchMessagingType( getMessagingType()); this.clients.add( app2_mysqlClient ); List<Message> app1_tomcatMessages = new ArrayList<> (); ReconfigurableClientAgent app1_tomcatClient = new ReconfigurableClientAgent(); app1_tomcatClient.setRegistry( this.registry ); app1_tomcatClient.associateMessageProcessor( createAgentProcessor( app1_tomcatMessages )); app1_tomcatClient.setApplicationName( app1.getName()); app1_tomcatClient.setScopedInstancePath( InstanceHelpers.computeInstancePath( app1.getTomcatVm())); app1_tomcatClient.setExternalMapping( app1.getExternalExports()); app1_tomcatClient.switchMessagingType( getMessagingType()); this.clients.add( app1_tomcatClient ); List<Message> app2_tomcatMessages = new ArrayList<> (); ReconfigurableClientAgent app2_tomcatClient = new ReconfigurableClientAgent(); app2_tomcatClient.setRegistry( this.registry ); app2_tomcatClient.associateMessageProcessor( createAgentProcessor( app2_tomcatMessages )); app2_tomcatClient.setApplicationName( app2.getName()); app2_tomcatClient.setScopedInstancePath( InstanceHelpers.computeInstancePath( app2.getTomcatVm())); app2_tomcatClient.setExternalMapping( app2.getExternalExports()); app2_tomcatClient.switchMessagingType( getMessagingType()); this.clients.add( app2_tomcatClient ); // OK, let's start. // Tomcat requests MySQL exports but no MySQL is listening app1_tomcatClient.requestExportsFromOtherAgents( app1.getTomcat()); Thread.sleep( getDelay()); Assert.assertEquals( 0, app1_tomcatMessages.size()); Assert.assertEquals( 0, app2_tomcatMessages.size()); Assert.assertEquals( 0, app1_mysqlMessages.size()); Assert.assertEquals( 0, app2_mysqlMessages.size()); // Now, start the (external) MySQL, the one in app2. app2_mysqlClient.listenToRequestsFromOtherAgents( ListenerCommand.START, app2.getMySql()); app1_tomcatClient.requestExportsFromOtherAgents( app1.getTomcat()); Thread.sleep( getDelay()); Assert.assertEquals( 0, app1_tomcatMessages.size()); Assert.assertEquals( 0, app2_tomcatMessages.size()); Assert.assertEquals( 0, app1_mysqlMessages.size()); Assert.assertEquals( 1, app2_mysqlMessages.size()); Assert.assertEquals( MsgCmdRequestImport.class, app2_mysqlMessages.get( 0 ).getClass()); MsgCmdRequestImport msg1 = (MsgCmdRequestImport) app2_mysqlMessages.get( 0 ); Assert.assertEquals( "tpl2", msg1.getComponentOrFacetName()); // Let's check exports. // Tomcat is not listening... app2_mysqlClient.publishExports( app2.getMySql()); Thread.sleep( getDelay()); Assert.assertEquals( 0, app1_tomcatMessages.size()); Assert.assertEquals( 0, app2_tomcatMessages.size()); Assert.assertEquals( 0, app1_mysqlMessages.size()); Assert.assertEquals( 1, app2_mysqlMessages.size()); // Let's check exports with Tomcat listening... app1_tomcatClient.listenToExportsFromOtherAgents( ListenerCommand.START, app1.getTomcat()); app2_mysqlClient.publishExports( app2.getMySql()); Thread.sleep( getDelay()); Assert.assertEquals( 0, app2_tomcatMessages.size()); Assert.assertEquals( 0, app1_mysqlMessages.size()); Assert.assertEquals( 1, app2_mysqlMessages.size()); Assert.assertEquals( 1, app1_tomcatMessages.size()); Assert.assertEquals( MsgCmdAddImport.class, app1_tomcatMessages.get( 0 ).getClass()); MsgCmdAddImport msg2 = (MsgCmdAddImport) app1_tomcatMessages.get( 0 ); Assert.assertEquals( "tpl2", msg2.getComponentOrFacetName()); Assert.assertEquals( "app2", msg2.getApplicationOrContextName()); Assert.assertEquals( InstanceHelpers.computeInstancePath( app2.getMySql()), msg2.getAddedInstancePath()); Assert.assertEquals( 2, msg2.getExportedVariables().size()); Assert.assertEquals( "3306", msg2.getExportedVariables().get( "tpl2.ext-port" )); Assert.assertTrue( msg2.getExportedVariables().containsKey( "tpl2.ext-ip" )); // What happens if we ask to only publish an external variable? app2_mysqlClient.publishExports( app2.getMySql(), "tpl2" ); Thread.sleep( getDelay()); Assert.assertEquals( 0, app2_tomcatMessages.size()); Assert.assertEquals( 0, app1_mysqlMessages.size()); Assert.assertEquals( 1, app2_mysqlMessages.size()); Assert.assertEquals( 2, app1_tomcatMessages.size()); Assert.assertEquals( MsgCmdAddImport.class, app1_tomcatMessages.get( 1 ).getClass()); msg2 = (MsgCmdAddImport) app1_tomcatMessages.get( 1 ); Assert.assertEquals( "tpl2", msg2.getComponentOrFacetName()); Assert.assertEquals( "app2", msg2.getApplicationOrContextName()); Assert.assertEquals( InstanceHelpers.computeInstancePath( app2.getMySql()), msg2.getAddedInstancePath()); Assert.assertEquals( 2, msg2.getExportedVariables().size()); Assert.assertEquals( "3306", msg2.getExportedVariables().get( "tpl2.ext-port" )); Assert.assertTrue( msg2.getExportedVariables().containsKey( "tpl2.ext-ip" )); // Good! Now, let's check unpublish events. // Just to be sure, turn off the listening on the Tomcat side. app1_tomcatClient.listenToExportsFromOtherAgents( ListenerCommand.STOP, app1.getTomcat()); app2_mysqlClient.unpublishExports( app2.getMySql()); Thread.sleep( getDelay()); Assert.assertEquals( 0, app2_tomcatMessages.size()); Assert.assertEquals( 0, app1_mysqlMessages.size()); Assert.assertEquals( 1, app2_mysqlMessages.size()); Assert.assertEquals( 2, app1_tomcatMessages.size()); // Good! Now, let's check unpublish events. app1_tomcatClient.listenToExportsFromOtherAgents( ListenerCommand.START, app1.getTomcat()); app2_mysqlClient.unpublishExports( app2.getMySql()); Thread.sleep( getDelay()); Assert.assertEquals( 0, app2_tomcatMessages.size()); Assert.assertEquals( 0, app1_mysqlMessages.size()); Assert.assertEquals( 1, app2_mysqlMessages.size()); Assert.assertEquals( 3, app1_tomcatMessages.size()); Assert.assertEquals( MsgCmdRemoveImport.class, app1_tomcatMessages.get( 2 ).getClass()); MsgCmdRemoveImport msg3 = (MsgCmdRemoveImport) app1_tomcatMessages.get( 2 ); Assert.assertEquals( "tpl2", msg3.getComponentOrFacetName()); Assert.assertEquals( "app2", msg3.getApplicationOrContextName()); Assert.assertEquals( InstanceHelpers.computeInstancePath( app2.getMySql()), msg3.getRemovedInstancePath()); // app2 >> MySQL does not listen anymore. Requests are not propagated anymore. app2_mysqlClient.listenToRequestsFromOtherAgents( ListenerCommand.STOP, app2.getMySql()); app1_tomcatClient.requestExportsFromOtherAgents( app1.getTomcat()); Thread.sleep( getDelay()); Assert.assertEquals( 0, app2_tomcatMessages.size()); Assert.assertEquals( 0, app1_mysqlMessages.size()); Assert.assertEquals( 1, app2_mysqlMessages.size()); Assert.assertEquals( 3, app1_tomcatMessages.size()); } /** * Verifies agent termination is correctly propagated by the DM. * @throws Exception */ public void testExternalExports_twoApplicationsAndTheDm_verifyAgentTerminationPropagation() throws Exception { // Create two applications TestApplication app1 = new TestApplication(); app1.getTemplate().setName( "tpl1" ); app1.setName( "app1" ); TestApplication app2 = new TestApplication(); app2.getTemplate().setName( "tpl2" ); app2.setName( "app2" ); // Add an external dependency between them: app1 depends on app2 ImportedVariable var = new ImportedVariable( "tpl2.ext-ip", false, true ); app1.getTomcat().getComponent().importedVariables.put( var.getName(), var ); app2.getTemplate().externalExports.put( "mysql.ip", "tpl2.ext-ip" ); app2.getTemplate().externalExports.put( "mysql.port", "tpl2.ext-port" ); // Prepare messaging clients List<Message> dmMessages = new ArrayList<>(); ReconfigurableClientDm dmClient = new ReconfigurableClientDm(); dmClient.setRegistry( this.registry ); dmClient.associateMessageProcessor( createDmProcessor( dmMessages )); dmClient.switchMessagingType( getMessagingType()); this.clients.add( dmClient ); List<Message> app2_mysqlMessages = new ArrayList<> (); ReconfigurableClientAgent app2_mysqlClient = new ReconfigurableClientAgent(); app2_mysqlClient.setRegistry( this.registry ); app2_mysqlClient.associateMessageProcessor( createAgentProcessor( app2_mysqlMessages )); app2_mysqlClient.setApplicationName( app2.getName()); app2_mysqlClient.setScopedInstancePath( InstanceHelpers.computeInstancePath( app2.getMySqlVm())); app2_mysqlClient.setExternalMapping( app2.getExternalExports()); app2_mysqlClient.switchMessagingType( getMessagingType()); this.clients.add( app2_mysqlClient ); List<Message> app1_tomcatMessages = new ArrayList<> (); ReconfigurableClientAgent app1_tomcatClient = new ReconfigurableClientAgent(); app1_tomcatClient.setRegistry( this.registry ); app1_tomcatClient.associateMessageProcessor( createAgentProcessor( app1_tomcatMessages )); app1_tomcatClient.setApplicationName( app1.getName()); app1_tomcatClient.setScopedInstancePath( InstanceHelpers.computeInstancePath( app1.getTomcatVm())); app1_tomcatClient.setExternalMapping( app1.getExternalExports()); app1_tomcatClient.switchMessagingType( getMessagingType()); this.clients.add( app1_tomcatClient ); // OK, deploy and start all. // Verify everything works app1_tomcatClient.requestExportsFromOtherAgents( app1.getTomcat()); Thread.sleep( getDelay()); Assert.assertEquals( 0, app1_tomcatMessages.size()); Assert.assertEquals( 0, app2_mysqlMessages.size()); // Now, start the (external) MySQL, the one in app2. app2_mysqlClient.listenToRequestsFromOtherAgents( ListenerCommand.START, app2.getMySql()); app1_tomcatClient.requestExportsFromOtherAgents( app1.getTomcat()); Thread.sleep( getDelay()); Assert.assertEquals( 0, app1_tomcatMessages.size()); Assert.assertEquals( 1, app2_mysqlMessages.size()); Assert.assertEquals( MsgCmdRequestImport.class, app2_mysqlMessages.get( 0 ).getClass()); MsgCmdRequestImport msg1 = (MsgCmdRequestImport) app2_mysqlMessages.get( 0 ); Assert.assertEquals( "tpl2", msg1.getComponentOrFacetName()); // Let's check exports with Tomcat listening... app1_tomcatClient.listenToExportsFromOtherAgents( ListenerCommand.START, app1.getTomcat()); app2_mysqlClient.publishExports( app2.getMySql()); Thread.sleep( getDelay()); Assert.assertEquals( 1, app2_mysqlMessages.size()); Assert.assertEquals( 1, app1_tomcatMessages.size()); Assert.assertEquals( MsgCmdAddImport.class, app1_tomcatMessages.get( 0 ).getClass()); MsgCmdAddImport msg2 = (MsgCmdAddImport) app1_tomcatMessages.get( 0 ); Assert.assertEquals( "tpl2", msg2.getComponentOrFacetName()); Assert.assertEquals( "app2", msg2.getApplicationOrContextName()); Assert.assertEquals( InstanceHelpers.computeInstancePath( app2.getMySql()), msg2.getAddedInstancePath()); Assert.assertEquals( 2, msg2.getExportedVariables().size()); Assert.assertEquals( "3306", msg2.getExportedVariables().get( "tpl2.ext-port" )); Assert.assertTrue( msg2.getExportedVariables().containsKey( "tpl2.ext-ip" )); // What happens if we ask to only publish an external variable? app2_mysqlClient.publishExports( app2.getMySql(), "tpl2" ); Thread.sleep( getDelay()); Assert.assertEquals( 1, app2_mysqlMessages.size()); Assert.assertEquals( 2, app1_tomcatMessages.size()); Assert.assertEquals( MsgCmdAddImport.class, app1_tomcatMessages.get( 1 ).getClass()); msg2 = (MsgCmdAddImport) app1_tomcatMessages.get( 1 ); Assert.assertEquals( "tpl2", msg2.getComponentOrFacetName()); Assert.assertEquals( "app2", msg2.getApplicationOrContextName()); Assert.assertEquals( InstanceHelpers.computeInstancePath( app2.getMySql()), msg2.getAddedInstancePath()); Assert.assertEquals( 2, msg2.getExportedVariables().size()); Assert.assertEquals( "3306", msg2.getExportedVariables().get( "tpl2.ext-port" )); Assert.assertTrue( msg2.getExportedVariables().containsKey( "tpl2.ext-ip" )); // Now, let's simulate the MySQL instance being stopped. // We directly invoke the DM client to propagate the information. dmClient.propagateAgentTermination( app2, app2.getMySqlVm()); Thread.sleep( getDelay()); // The Tomcat should have received a notification because one of its dependencies disappeared. Assert.assertEquals( 3, app1_tomcatMessages.size()); Assert.assertEquals( MsgCmdRemoveImport.class, app1_tomcatMessages.get( 2 ).getClass()); MsgCmdRemoveImport msg3 = (MsgCmdRemoveImport) app1_tomcatMessages.get( 2 ); Assert.assertEquals( "tpl2", msg3.getComponentOrFacetName()); Assert.assertEquals( "/mysql-vm/mysql-server", msg3.getRemovedInstancePath()); Assert.assertEquals( "app2", msg3.getApplicationOrContextName()); } /** * Mimics the DM's behavior when it creates, deletes and recreates a same application. * <p> * This class prevents regressions for #749. * </p> * * @throws Exception */ public void test_applicationRegeneration() throws Exception { TestApplication app = new TestApplication(); List<Message> dmMessages = new ArrayList<>(); ReconfigurableClientDm dmClient = new ReconfigurableClientDm(); dmClient.setRegistry( this.registry ); dmClient.associateMessageProcessor( createDmProcessor( dmMessages )); dmClient.switchMessagingType( getMessagingType()); this.clients.add( dmClient ); dmClient.listenToAgentMessages( app, ListenerCommand.START ); dmClient.listenToAgentMessages( app, ListenerCommand.STOP ); dmClient.deleteMessagingServerArtifacts( app ); Thread.sleep( getDelay()); dmClient.listenToAgentMessages( app, ListenerCommand.START ); dmClient.listenToAgentMessages( app, ListenerCommand.STOP ); dmClient.deleteMessagingServerArtifacts( app ); } protected AbstractMessageProcessor<IDmClient> createDmProcessor( final List<Message> dmMessages ) { return new AbstractMessageProcessor<IDmClient>( "DM Processor - Test" ) { @Override protected void processMessage( Message message ) { dmMessages.add( message ); } }; } protected AbstractMessageProcessor<IAgentClient> createAgentProcessor( final List<Message> agentMessages ) { return new AbstractMessageProcessor<IAgentClient>( "Agent Processor - Test" ) { @Override protected void processMessage( Message message ) { agentMessages.add( message ); } }; } protected abstract String getMessagingType(); }