/*
* Copyright (C) 2006-2016 DLR, Germany
*
* All rights reserved
*
* http://www.rcenvironment.de/
*/
package de.rcenvironment.core.communication.testutils;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import de.rcenvironment.core.communication.channel.MessageChannelTrafficListener;
import de.rcenvironment.core.communication.common.InstanceNodeSessionId;
import de.rcenvironment.core.communication.model.NetworkRequest;
import de.rcenvironment.core.communication.model.NetworkResponse;
import de.rcenvironment.core.utils.common.StringUtils;
/**
* A {@link MessageChannelTrafficListener} intended for keeping track of all message traffic in virtual
* network tests. Can be used to wait until no message has been sent for a certain time using
* {@link #waitForNetworkSilence(int, int).
*
* @author Robert Mischke
*/
public class TestNetworkTrafficListener implements MessageChannelTrafficListener {
private long lastTrafficTimestamp = 0;
private long requestCount;
private long lsaMessages = 0;
private long routedMessages = 0;
private long largestObservedHopCount = 0;
private long unsuccessfulResponses = 0;
private Semaphore trafficOccured = new Semaphore(0);
@Override
public void onRequestSentIntoChannel(NetworkRequest request) {
onTraffic(true);
}
@Override
public void onRequestReceivedFromChannel(NetworkRequest request, InstanceNodeSessionId sourceId) {
onTraffic(false);
}
@Override
public void onResponseSentIntoChannel(NetworkResponse response, NetworkRequest request, InstanceNodeSessionId sourceId) {
// note: strictly speaking, the traffic has not happened yet, but is about to
// TODO restore statistics?
// if (MessageMetaData.createLsaMessage().matches(request.accessRawMetaData())) {
// lsaMessages++;
// }
//
// if (MessageMetaData.createRoutedMessage().matches(request.accessRawMetaData())) {
// routedMessages++;
// }
//
// if (MessageMetaData.wrap(request.accessRawMetaData()).getHopCount() >
// largestObservedHopCount) {
// largestObservedHopCount =
// MessageMetaData.wrap(request.accessRawMetaData()).getHopCount();
// }
//
// if (!response.isSuccess()) {
// unsuccessfulResponses++;
// }
onTraffic(false);
}
private synchronized void onTraffic(boolean isRequest) {
lastTrafficTimestamp = System.currentTimeMillis();
if (isRequest) {
requestCount++;
trafficOccured.release();
}
}
public synchronized long getRequestCount() {
return requestCount;
}
public synchronized long getLastTrafficTimestamp() {
return lastTrafficTimestamp;
}
/**
* Clears the flag that indicates that traffic has occured.
*/
public synchronized void clearCustomTrafficFlag() {
trafficOccured.drainPermits();
}
/**
* Waits until traffic has occured, or the timeout has expired.
*
* @param maxWait the timeout
* @throws TimeoutException on timeout
* @throws InterruptedException on interruption
*/
public void waitForCustomTrafficFlag(int maxWait) throws TimeoutException, InterruptedException {
if (!trafficOccured.tryAcquire(maxWait, TimeUnit.MILLISECONDS)) {
throw new TimeoutException("Maximum wait time for custom traffic flag (" + maxWait + " msec) exceeded");
}
}
/**
* Waits until no network traffic has been reported for (at least) the given timespan.
*
* @param minSilenceTime the minimum no-traffic time
* @param maxWait the maximum time to wait for the no-traffic condition
* @throws TimeoutException on timeout
* @throws InterruptedException on interruption
*/
public void waitForNetworkSilence(int minSilenceTime, int maxWait) throws TimeoutException, InterruptedException {
int pollInterval = minSilenceTime / 2;
int totalWait = 0;
while (true) {
if (totalWait > maxWait) {
throw new TimeoutException("Maximum wait time for network silence (" + maxWait + " msec) exceeded");
}
// note: synchronized through getter
if (System.currentTimeMillis() - getLastTrafficTimestamp() >= minSilenceTime) {
return;
}
Thread.sleep(pollInterval);
totalWait += pollInterval;
}
}
/**
* @return the number of received LSA messages (?)
*
* TODO verify description
*/
public long getLsaMessages() {
return lsaMessages;
}
/**
* @return Returns the largest observed hop count.
*/
public long getLargestObservedHopCount() {
return largestObservedHopCount;
}
/**
* @return the number of unsuccessful requests
*
* TODO verify semantics and current behavior
*/
public long getUnsuccessfulResponses() {
return unsuccessfulResponses;
}
/**
* @return the number of routed messages
*
* TODO verify semantics and current behavior
*/
public long getRoutedMessages() {
return routedMessages;
}
/**
* Formats traffic statistics to a string.
*
* @param topologySize The number of instances.
* @return A string representation for debugging/displaying.
*/
public String getFormattedTrafficReport(int topologySize) {
if (topologySize <= 0) {
throw new IllegalArgumentException("Argument must be >=1");
}
return StringUtils.format("Total requests sent: %d\n"
+ "Average requests sent per node: %d\n"
+ "Total LSA messages sent: %d\n"
+ "Average LSA messages sent per node: %d\n"
+ "Total routed messages sent: %d\n"
+ "Largest observed hop count: %d (%d)\n"
+ "Unsuccessful responses received: %d\n",
getRequestCount(),
getRequestCount() / topologySize,
getLsaMessages(),
getLsaMessages() / topologySize,
getRoutedMessages(),
getLargestObservedHopCount(),
topologySize,
getUnsuccessfulResponses());
}
}