/*
* Copyright (C) 2006-2016 DLR, Germany
*
* All rights reserved
*
* http://www.rcenvironment.de/
*/
package de.rcenvironment.core.communication.model.internal;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import de.rcenvironment.core.communication.common.NetworkGraphLink;
import de.rcenvironment.core.communication.common.NetworkGraphNode;
import de.rcenvironment.core.communication.common.InstanceNodeSessionId;
import de.rcenvironment.core.communication.model.NetworkRoutingInformation;
import de.rcenvironment.core.communication.routing.internal.NetworkFormatter;
import de.rcenvironment.core.communication.routing.internal.v2.NoRouteToNodeException;
import de.rcenvironment.core.toolkitbridge.transitional.StatsCounter;
import de.rcenvironment.toolkit.utils.common.AutoCreationMap;
import edu.uci.ics.jung.algorithms.shortestpath.DijkstraShortestPath;
import edu.uci.ics.jung.graph.DirectedSparseMultigraph;
/**
* Internal implementation of {@link NetworkRoutingInformation}.
*
* @author Robert Mischke
*/
public final class NetworkRoutingInformationImpl implements NetworkRoutingInformation {
private Map<InstanceNodeSessionId, NetworkGraphLink> routingTable;
private Map<InstanceNodeSessionId, NetworkGraphLink> incomingEdgesById;
private Set<NetworkGraphLink> spanningTreeLinkSet;
private Map<InstanceNodeSessionId, List<NetworkGraphLink>> spanningTreeLinkMap;
// for unit testing
private int routingCacheMisses = 0;
private final InstanceNodeSessionId localNodeId;
private final Map<InstanceNodeSessionId, NetworkGraphLinkImpl> incomingEdgeMap;
private final DijkstraShortestPath<InstanceNodeSessionId, NetworkGraphLinkImpl> shortestPathAlgorithm;
private final Set<InstanceNodeSessionId> reachableNodes;
public NetworkRoutingInformationImpl(NetworkGraphImpl rawNetworkGraph) {
DirectedSparseMultigraph<InstanceNodeSessionId, NetworkGraphLinkImpl> rawJungGraph = rawNetworkGraph.getJungGraph();
this.localNodeId = rawNetworkGraph.getLocalNodeId();
// Note: unless edge weights are used, UnweightedShortestPath would work as well - misc_ro
this.shortestPathAlgorithm = new DijkstraShortestPath<InstanceNodeSessionId, NetworkGraphLinkImpl>(rawJungGraph);
try {
this.incomingEdgeMap = shortestPathAlgorithm.getIncomingEdgeMap(rawNetworkGraph.getLocalNodeId());
} catch (IllegalArgumentException e) {
throw new IllegalArgumentException(NetworkFormatter.networkGraphToGraphviz(rawNetworkGraph, false), e);
}
reachableNodes = Collections.unmodifiableSet(incomingEdgeMap.keySet());
}
@Override
public Set<InstanceNodeSessionId> getReachableNodes() {
return reachableNodes;
}
@Override
public synchronized NetworkGraphLink getNextLinkTowards(InstanceNodeSessionId targetNodeId) throws NoRouteToNodeException {
StatsCounter.count("Network topology/routing", "Route requests");
if (targetNodeId.equals(localNodeId)) {
throw new NoRouteToNodeException("Cannot route to Local node", localNodeId);
}
if (routingTable == null) {
StatsCounter.count("Network topology/routing", "Routing table calculations");
// initialize basic structures
routingTable = new HashMap<InstanceNodeSessionId, NetworkGraphLink>();
incomingEdgesById = new HashMap<InstanceNodeSessionId, NetworkGraphLink>();
for (NetworkGraphLinkImpl link : incomingEdgeMap.values()) {
if (link != null) {
incomingEdgesById.put(link.getTargetNodeId(), link);
}
}
}
// individual routing map entries are calculated lazily, as in large networks,
// only a few nodes will probably be contacted at once (TODO actually track/measure this) - misc_ro
return determineRoutingTableEntryFor(targetNodeId);
}
@Override
public synchronized List<? extends NetworkGraphLink> getRouteTo(InstanceNodeSessionId destination) {
if (destination.equals(localNodeId)) {
throw new IllegalArgumentException("Invalid route request to local node");
}
List<? extends NetworkGraphLink> path = shortestPathAlgorithm.getPath(localNodeId, destination);
if (path.size() != 0) {
return Collections.unmodifiableList(path);
} else {
// empty path = target unreachable
return null;
}
}
@Override
public NetworkGraphLink getNextLinkTowards(NetworkGraphNode targetNode) throws NoRouteToNodeException {
return getNextLinkTowards(targetNode.getNodeId());
}
@Override
public synchronized Set<NetworkGraphLink> getSpanningTreeLinks() {
// lazy init, as this is rarely called
if (spanningTreeLinkSet == null) {
spanningTreeLinkSet = new HashSet<NetworkGraphLink>();
for (NetworkGraphLinkImpl link : incomingEdgeMap.values()) {
if (link != null) {
spanningTreeLinkSet.add(link);
}
}
spanningTreeLinkSet = Collections.unmodifiableSet(spanningTreeLinkSet);
}
return spanningTreeLinkSet;
}
@Override
public synchronized Map<InstanceNodeSessionId, List<NetworkGraphLink>> getSpanningTreeLinkMap() {
if (spanningTreeLinkMap == null) {
// lazy init, as this is rarely called
spanningTreeLinkMap = createSpanningTree();
}
// note: not fully guarded against external modification (inner maps are mutable) - misc_ro
return spanningTreeLinkMap;
}
protected int getRoutingCacheMisses() {
return routingCacheMisses;
}
protected void resetCacheMisses() {
routingCacheMisses = 0;
}
private NetworkGraphLink determineRoutingTableEntryFor(InstanceNodeSessionId targetNodeId) throws NoRouteToNodeException {
NetworkGraphLink result = routingTable.get(targetNodeId);
// cached entry present?
if (result != null) {
return result;
}
routingCacheMisses++;
NetworkGraphLink incomingEdge = incomingEdgesById.get(targetNodeId);
// consistency check
if (incomingEdge == null) {
throw new NoRouteToNodeException("No incoming edge for " + targetNodeId, targetNodeId);
}
InstanceNodeSessionId predecessorNodeId = incomingEdge.getSourceNodeId();
if (predecessorNodeId.equals(localNodeId)) {
// source node reached; current target node is adjacent and reachable
result = incomingEdge;
} else {
// otherwise, delegate to predecessor
result = determineRoutingTableEntryFor(predecessorNodeId);
}
// add result to cache and return
routingTable.put(targetNodeId, result);
return result;
}
private Map<InstanceNodeSessionId, List<NetworkGraphLink>> createSpanningTree() {
AutoCreationMap<InstanceNodeSessionId, List<NetworkGraphLink>> spanningTreeLinks =
new AutoCreationMap<InstanceNodeSessionId, List<NetworkGraphLink>>() {
@Override
protected List<NetworkGraphLink> createNewEntry(InstanceNodeSessionId key) {
return new ArrayList<NetworkGraphLink>();
}
};
// build spanning tree from incoming edge map (map inversion)
for (NetworkGraphLinkImpl link : incomingEdgeMap.values()) {
if (link != null) {
spanningTreeLinks.get(link.getSourceNodeId()).add(link);
}
}
Map<InstanceNodeSessionId, List<NetworkGraphLink>> temp = spanningTreeLinks.getImmutableShallowCopy();
return temp;
}
}