/* * Copyright (C) 2006-2016 DLR, Germany * * All rights reserved * * http://www.rcenvironment.de/ */ package de.rcenvironment.core.communication.transport.virtual.testutils; import java.util.concurrent.TimeoutException; import de.rcenvironment.core.communication.model.NetworkContactPoint; import de.rcenvironment.core.communication.testutils.VirtualInstance; import de.rcenvironment.core.communication.testutils.VirtualInstanceGroup; /** * Utility wrapper to set up and handle test topologies. (Note: This is a redesigned successor of the {@link VirtualInstanceTestUtils} * class.) * * TODO move to de.rcenvironment.core.communication.testutils * * @author Robert Mischke */ public class VirtualTopology { private VirtualInstance[] instances; private VirtualInstanceGroup allInstancesGroup; private VirtualInstanceTestUtils testUtils = new VirtualInstanceTestUtils(null, null); private int lastIndex; private boolean connectBothDirectionsByDefaultFlag; public VirtualTopology(VirtualInstance... instances) { this.instances = instances; allInstancesGroup = new VirtualInstanceGroup(instances); lastIndex = instances.length - 1; } public int getInstanceCount() { return instances.length; } public VirtualInstance[] getInstances() { return instances; } /** * Returns the instance with the given zero-based index. * * @param i the instance index * @return the instance */ public VirtualInstance getInstance(int i) { return instances[i]; } public VirtualInstance getFirstInstance() { return instances[0]; } public VirtualInstance getLastInstance() { return instances[lastIndex]; } public VirtualInstance getRandomInstance() { return testUtils.getRandomInstance(instances); } /** * Returns a random instance, but not one of the given ones. * * @param not the instances to exclude from the candidates * @return the randomly-chosen instance */ public VirtualInstance getRandomInstanceExcept(VirtualInstance... not) { return testUtils.getRandomInstance(instances, not); } public VirtualInstanceGroup getAsGroup() { return allInstancesGroup; } /** * Connects the instances given by their zero-based indices. Depending on the flag set by * {@link #setConnectBothDirectionsByDefaultFlag(boolean)}, the connection is initiated in one or both directions. The default behavior * is to only connect the from->to direction. * * @param from the connecting instance * @param to the connected-to instance */ public void connect(int from, int to) { connect(from, to, connectBothDirectionsByDefaultFlag); } /** * Connects the instances given by their zero-based indices. Depending on the boolean parameter, the connection is initiated in one or * both directions. * * @param from the connecting instance * @param to the connected-to instance * @param bothDirections true if the connection should be initiated in both directions; if false, it is only initiated in the from->to * direction */ public void connect(int from, int to, boolean bothDirections) { NetworkContactPoint targetSCP = instances[to].getDefaultContactPoint(); instances[from].connectAsync(targetSCP); if (bothDirections) { connect(to, from, false); // explicit "false" is required here; do not remove } } /** * Blocks until the second node is part of the first node's reachable network topology, or until the timeout is reached. * * Note that for this explicit method, there is no special handling in case of bidirectional connections (see * {@link #setConnectBothDirectionsByDefaultFlag(boolean)}). * * @param from the instance that should "see" the second instance * @param to the instance that should be "seen" by the first instance * @param timeout the maximum time to wait * @throws InterruptedException on interruption * @throws TimeoutException on timeout */ public void waitUntilReachable(int from, int to, int timeout) throws InterruptedException, TimeoutException { getInstance(from).waitUntilContainsInReachableNodes(getInstance(to).getInstanceNodeSessionId(), timeout); } /** * Connects the instances given by their zero-based indices, and waits until the second node is part of the first node's reachable * network topology, or until the timeout is reached. If the flag set by {@link #setConnectBothDirectionsByDefaultFlag(boolean)} is * true, the connection is initiated in both directions, and both directions are waited for. * * @param from the connecting instance * @param to the connected-to instance * @param timeoutMsec the maximum time to wait * @throws TimeoutException if the wait time is exceeded * @throws InterruptedException on thread interruption */ public void connectAndWait(int from, int to, int timeoutMsec) throws TimeoutException, InterruptedException { connect(from, to); waitUntilReachable(from, to, timeoutMsec); if (connectBothDirectionsByDefaultFlag) { waitUntilReachable(to, from, timeoutMsec); } } /** * Connects all instances to a sequentially-connected "chain". * * @param bothDirections see {@link #connect(int, int, boolean)} */ public void connectToChain(boolean bothDirections) { connectToChain(0, lastIndex, bothDirections); } /** * Connects a range of instances to a sequentially-connected "chain". * * @param from the start of the range * @param to the end of the range * @param bothDirections see {@link #connect(int, int, boolean)} */ public void connectToChain(int from, int to, boolean bothDirections) { if (to < from) { throw new IllegalArgumentException(); } for (int i = 0; i < to; i++) { connect(i, i + 1, bothDirections); } } /** * Connects all instances to a closed ring/loop. * * @param bothDirections see {@link #connect(int, int, boolean)} */ public void connectToRing(boolean bothDirections) { connectToRing(0, lastIndex, bothDirections); } /** * Connects a range of instances to a closed ring/loop. * * @param from the start of the range * @param to the end of the range * @param bothDirections see {@link #connect(int, int, boolean)} */ public void connectToRing(int from, int to, boolean bothDirections) { // note: "from <= to" check is performed in connectToChain() connectToChain(from, to, bothDirections); // close loop connect(to, from, bothDirections); } /** * Determines whether all instances consider themselves "converged". * * TODO add definition of convergence * * @return true if all instances consider themselves "converged" */ public boolean allInstancesConverged() { return testUtils.allInstancesHaveSameRawNetworkGraph(instances); } public void setConnectBothDirectionsByDefaultFlag(boolean connectBothDirectionsByDefaultFlag) { this.connectBothDirectionsByDefaultFlag = connectBothDirectionsByDefaultFlag; } }