/* * Copyright (C) 2006-2016 DLR, Germany * * All rights reserved * * http://www.rcenvironment.de/ */ package de.rcenvironment.core.communication.transport.virtual.testutils; import java.util.Random; import java.util.concurrent.Callable; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import de.rcenvironment.core.communication.common.NetworkGraph; import de.rcenvironment.core.communication.model.NetworkContactPoint; import de.rcenvironment.core.communication.routing.internal.NetworkFormatter; import de.rcenvironment.core.communication.testutils.NetworkContactPointGenerator; import de.rcenvironment.core.communication.testutils.VirtualInstance; import de.rcenvironment.core.communication.testutils.VirtualInstanceGroup; import de.rcenvironment.core.communication.testutils.VirtualInstanceState; import de.rcenvironment.core.communication.transport.spi.NetworkTransportProvider; import de.rcenvironment.core.toolkitbridge.transitional.ConcurrencyUtils; import de.rcenvironment.core.utils.common.StringUtils; import de.rcenvironment.core.utils.incubator.DebugSettings; import de.rcenvironment.toolkit.modules.concurrency.api.CallablesGroup; import de.rcenvironment.toolkit.modules.concurrency.api.TaskDescription; /** * Utilities for {@link VirtualInstance} tests. Mostly superseded by the new {@link VirtualTopology} class. * * TODO move to de.rcenvironment.core.communication.testutils * * @author Phillip Kroll * @author Robert Mischke */ public class VirtualInstanceTestUtils { private static final int UPPER_UNIQUE_TOKEN_LIMIT = (int) 10e+6; private final Random randomGenerator = new Random(); private NetworkTransportProvider transportProvider; private NetworkContactPointGenerator contactPointGenerator; private final boolean verboseLogging = DebugSettings.getVerboseLoggingEnabled(getClass()); private final Log log = LogFactory.getLog(getClass()); public VirtualInstanceTestUtils(NetworkTransportProvider transportProvider, NetworkContactPointGenerator contactPointGenerator) { super(); this.transportProvider = transportProvider; this.contactPointGenerator = contactPointGenerator; } /** * Create a set of initialized and started virtual instances. * * @param size the number of instances to generate * @return the generated instances * @throws InterruptedException on interruption */ @Deprecated public VirtualInstance[] spawnDefaultInstances(int size) throws InterruptedException { // legacy default behaviour return spawnDefaultInstances(size, false, true); } /** * Create a set of initialized and optionally started virtual instances. * * @param size the number of instances to generate * @param useDuplexTransport true if duplex connections should be allowed * @param startInstances true if the instances should be started automatically * @return the generated instances * @throws InterruptedException on interruption */ public VirtualInstance[] spawnDefaultInstances(int size, boolean useDuplexTransport, boolean startInstances) throws InterruptedException { if (size < 2) { throw new IllegalArgumentException("Illegal number of instances: " + size); } VirtualInstance[] instances = new VirtualInstance[size]; CallablesGroup<VirtualInstance> callablesGroup = ConcurrencyUtils.getFactory().createCallablesGroup(VirtualInstance.class); for (int i = 1; i <= size; i++) { final int i2 = i; callablesGroup.add(new Callable<VirtualInstance>() { @Override @TaskDescription("Default VirtualInstance creation") public VirtualInstance call() throws Exception { if (verboseLogging) { log.debug("Creating instance " + i2); } VirtualInstance instance = new VirtualInstance("Instance " + i2); if (verboseLogging) { log.debug("Finished creating instance " + i2); } return instance; } }); } instances = callablesGroup.executeParallel(null).toArray(instances); VirtualInstanceGroup group = new VirtualInstanceGroup(instances); // add transport provider to every instance group.registerNetworkTransportProvider(transportProvider); // a server contact point for everyone for (VirtualInstance vi : instances) { vi.addServerConfigurationEntry(contactPointGenerator.createContactPoint()); } if (startInstances) { startAll(instances); } return instances; } /** * Sets the target state of all instances to "started" and waits until the state changes have finished. * * @param instances the instances to start * @throws InterruptedException on interruption */ public void startAll(VirtualInstance[] instances) throws InterruptedException { VirtualInstanceGroup group = new VirtualInstanceGroup(instances); group.setTargetState(VirtualInstanceState.STARTED); group.waitForStateChangesToFinish(); } /** * Log description of an instance. * * @param instances the pool of instances * @param index the index within the pool of instances * @return the log description with display name and id */ public String getFormattedName(VirtualInstance[] instances, int index) { return getFormattedName(instances[index]); } /** * Log description of an instance. * * @param instance the instance * @return the log description with display name and id */ @Deprecated public String getFormattedName(VirtualInstance instance) { return StringUtils.format("%s (%s)", instance.getInstanceNodeSessionId(), instance.getConfigurationService().getInitialNodeInformation().getLogDescription()); } /** * @return a string with a random component */ public String generateUniqueMessageToken() { return StringUtils.format("Unique message token: %s", randomGenerator.nextInt(UPPER_UNIQUE_TOKEN_LIMIT)); } /** * Returns a random instance from a set of instances. * * @param instances the set to choose from * @return the randomly chosen instance */ public VirtualInstance getRandomInstance(VirtualInstance[] instances) { return instances[randomGenerator.nextInt(instances.length)]; } /** * Returns a random instance from a set of instances, with the option to specify exclusions. * * @param instances the set to choose from * @param not the instances to exclude as candidates * @return the randomly chosen instance */ public VirtualInstance getRandomInstance(VirtualInstance[] instances, VirtualInstance... not) { int i = 0; int rand = randomGenerator.nextInt(instances.length); boolean run = true; while (run) { if (i++ > instances.length * 2) { break; } rand = randomGenerator.nextInt(instances.length); run = false; for (VirtualInstance vi : not) { run |= vi.equals(instances[rand]); } } return instances[rand]; } /** * Tests whether all instances consider themselves "converged". * * TODO @krol_ph: definition of convergence * * TODO refer to central glossary? * * @param instances the instances to check * @return true if all instances are "converged" */ public boolean allInstancesHaveSameRawNetworkGraph(VirtualInstance[] instances) { int differences = 0; VirtualInstance instance0 = instances[0]; NetworkGraph networkGraph0 = instance0.getRawNetworkGraph(); String compact0 = networkGraph0.getCompactRepresentation(); for (int i = 1; i < instances.length; i++) { VirtualInstance instanceN = instances[i]; NetworkGraph networkGraphN = instanceN.getRawNetworkGraph(); String compactN = networkGraphN.getCompactRepresentation(); if (!compactN.equals(compact0)) { differences++; if (differences == 1) { // only log first log.warn(StringUtils.format( "At least two instances do not share a common view of the network topology; first difference:\n" + "Instance 0 (%s):\n%s\n%s\n%s\nInstance %d (%s):\n %s\n%s\n%s", instance0.getInstanceNodeSessionId(), compact0, NetworkFormatter.networkGraphToGraphviz(networkGraph0, true), instance0.getFormattedLSAKnowledge(), i, instanceN.getInstanceNodeSessionId(), compactN, NetworkFormatter.networkGraphToGraphviz(networkGraphN, true), instanceN.getFormattedLSAKnowledge())); } } } if (differences == 0) { return true; } else { log.warn("Total number of differences (from instance 0): " + differences + " out of " + instances.length); return false; } } // /** // * Sets the TTL for multiple instances. // * // * @param instances the instances to use // * @param value the new TTL to set // */ // public void batchSetTimeToLive(VirtualInstance[] instances, int value) { // for (VirtualInstance vi : instances) { // vi.getRoutingService().getProtocolManager().setTimeToLive(value); // } // } /** * @param instances the instances to connect */ public void connectToChainTopology(VirtualInstance[] instances) { for (int i = 0; i < instances.length - 1; i++) { instances[i].connectAsync(instances[i + 1].getConfigurationService().getServerContactPoints().get(0)); } } /** * @param instances the instances to connect */ public void connectToRingTopology(VirtualInstance[] instances) { connectToChainTopology(instances); instances[instances.length - 1].connectAsync(instances[0].getConfigurationService().getServerContactPoints().get(0)); } /** * @param instances the instances to connect a subset of * @param min the start index of the range * @param max the end index of the range */ public void connectToDoubleChainTopology(VirtualInstance[] instances, int min, int max) { for (int i = min; i <= max - 1; i++) { instances[i].connectAsync(instances[i + 1].getConfigurationService().getServerContactPoints().get(0)); instances[i + 1].connectAsync(instances[i].getConfigurationService().getServerContactPoints().get(0)); } } /** * @param instances the instances to connect */ public void connectToDoubleChainTopology(VirtualInstance[] instances) { connectToDoubleChainTopology(instances, 0, instances.length - 1); } /** * @param instances the instances to connect */ public void connectToDoubleRingTopology(VirtualInstance[] instances) { connectToDoubleRingTopology(instances, 0, instances.length - 1); } /** * @param instances the instances to connect a subset of * @param min the start index of the range * @param max the end index of the range */ public void connectToDoubleRingTopology(VirtualInstance[] instances, int min, int max) { connectToDoubleChainTopology(instances, min, max); instances[max].connectAsync(instances[min].getConfigurationService().getServerContactPoints().get(0)); instances[min].connectAsync(instances[max].getConfigurationService().getServerContactPoints().get(0)); } /** * @param instances the instances to connect */ public void connectToDoubleStarTopology(VirtualInstance[] instances) { for (int i = 0; i < instances.length - 1; i++) { instances[i].connectAsync(instances[instances.length - 1].getConfigurationService().getServerContactPoints().get(0)); instances[instances.length - 1].connectAsync(instances[i].getConfigurationService().getServerContactPoints().get(0)); } } /** * @param instances the instances to connect */ public void connectToInwardStarTopology(VirtualInstance[] instances) { NetworkContactPoint hubServerNCP = instances[0].getConfigurationService().getServerContactPoints().get(0); for (int i = 1; i < instances.length; i++) { instances[i].connectAsync(hubServerNCP); } } /** * Concatenate two random instances from two topologies. * * @param instances1 the first topology * @param instances2 the second topology */ public void randomlyConcatenateTopologies(VirtualInstance[] instances1, VirtualInstance[] instances2) { int index1 = randomGenerator.nextInt(instances1.length); int index2 = randomGenerator.nextInt(instances2.length); instances1[index1].connectAsync(instances2[index2].getConfigurationService().getServerContactPoints().get(0)); instances2[index2].connectAsync(instances1[index1].getConfigurationService().getServerContactPoints().get(0)); } /** * One-way concatenate two instances within a topology. * * @param instances the topology * @param first the source node * @param second the target node */ public void concatenateInstances(VirtualInstance[] instances, int first, int second) { instances[first].connectAsync(instances[second].getConfigurationService().getServerContactPoints().get(0)); } /** * Two-way concatenate two instances within a topology. * * @param instances the topology * @param first the source node * @param second the target node */ public void doubleConcatenateInstances(VirtualInstance[] instances, int first, int second) { instances[first].connectAsync(instances[second].getConfigurationService().getServerContactPoints().get(0)); instances[second].connectAsync(instances[first].getConfigurationService().getServerContactPoints().get(0)); } }