/*
* 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;
}
}