/*
* Copyright (C) 2006-2016 DLR, Germany
*
* All rights reserved
*
* http://www.rcenvironment.de/
*/
package de.rcenvironment.core.communication.routing.internal;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import org.apache.commons.logging.LogFactory;
import de.rcenvironment.core.communication.common.InstanceNodeSessionId;
import de.rcenvironment.core.communication.model.internal.NetworkGraphImpl;
import de.rcenvironment.core.communication.spi.NetworkTopologyChangeListener;
import de.rcenvironment.core.toolkitbridge.transitional.ConcurrencyUtils;
import de.rcenvironment.toolkit.modules.concurrency.api.AsyncCallback;
import de.rcenvironment.toolkit.modules.concurrency.api.AsyncCallbackExceptionPolicy;
import de.rcenvironment.toolkit.modules.concurrency.api.AsyncOrderedCallbackManager;
import de.rcenvironment.toolkit.modules.concurrency.api.TaskDescription;
/**
* Keeps track of the last known topology information, and generates change events on relevant updates.
*
* @author Robert Mischke
*/
class NetworkTopologyChangeTracker {
private Set<InstanceNodeSessionId> lastReachableNodes; // invariant: must always contain an *immutable* set
private final AsyncOrderedCallbackManager<NetworkTopologyChangeListener> callbackManager;
private NetworkGraphImpl cachedReachableNetworkGraph;
NetworkTopologyChangeTracker() {
this.lastReachableNodes = Collections.unmodifiableSet(new HashSet<InstanceNodeSessionId>()); // see invariant
this.callbackManager =
ConcurrencyUtils.getFactory().createAsyncOrderedCallbackManager(AsyncCallbackExceptionPolicy.LOG_AND_CANCEL_LISTENER);
}
public synchronized boolean updateReachableNetwork(final NetworkGraphImpl reachableNetworkGraph) {
Set<InstanceNodeSessionId> addedNodes;
Set<InstanceNodeSessionId> removedNodes;
Set<? extends InstanceNodeSessionId> reachableNodeIds = reachableNetworkGraph.getNodeIds();
// create difference sets
addedNodes = new HashSet<InstanceNodeSessionId>(reachableNodeIds);
addedNodes.removeAll(lastReachableNodes);
removedNodes = new HashSet<InstanceNodeSessionId>(lastReachableNodes);
removedNodes.removeAll(reachableNodeIds);
callbackManager.enqueueCallback(new AsyncCallback<NetworkTopologyChangeListener>() {
@Override
@TaskDescription("Communication Layer: Topology change callback (1p)")
public void performCallback(NetworkTopologyChangeListener listener) {
listener.onReachableNetworkChanged(reachableNetworkGraph);
}
});
cachedReachableNetworkGraph = reachableNetworkGraph;
if (!addedNodes.isEmpty() || !removedNodes.isEmpty()) {
// create thread-safe copies of sets
final Set<InstanceNodeSessionId> newReachableNodesCopy = Collections.unmodifiableSet(reachableNodeIds);
final Set<InstanceNodeSessionId> addedNodesCopy = Collections.unmodifiableSet(addedNodes);
final Set<InstanceNodeSessionId> removedNodesCopy = Collections.unmodifiableSet(removedNodes);
callbackManager.enqueueCallback(new AsyncCallback<NetworkTopologyChangeListener>() {
@Override
@TaskDescription("Communication Layer: Topology change callback (3p)")
public void performCallback(NetworkTopologyChangeListener listener) {
listener.onReachableNodesChanged(newReachableNodesCopy, addedNodesCopy, removedNodesCopy);
}
});
lastReachableNodes = newReachableNodesCopy; // see invariant
sendLegacyListenerNotification();
return true;
} else {
sendLegacyListenerNotification();
return false;
}
}
public synchronized Set<InstanceNodeSessionId> getCurrentReachableNodes() {
return lastReachableNodes; // immutable set; see invariant
}
/**
* Adds a new {@link NetworkTopologyChangeListener}.
*
* @param listener the listener
*/
public synchronized void addListener(NetworkTopologyChangeListener listener) {
// make copies in synchronized block
final Set<InstanceNodeSessionId> lastReachableNodesCopy = lastReachableNodes;
final NetworkGraphImpl networkGraphCopy = cachedReachableNetworkGraph;
callbackManager.addListenerAndEnqueueCallback(listener, new AsyncCallback<NetworkTopologyChangeListener>() {
@Override
public void performCallback(NetworkTopologyChangeListener listener) {
// send specific callback to bring the listener up to date
listener.onReachableNodesChanged(lastReachableNodesCopy, lastReachableNodesCopy, new HashSet<InstanceNodeSessionId>());
if (networkGraphCopy != null) {
listener.onReachableNetworkChanged(networkGraphCopy);
} else {
// this case is harmless; it would be nice to change it for consistency, though, if possible - misc_ro
LogFactory.getLog(getClass()).debug(
"Topology listener " + listener.getClass().getName()
+ " registered before an initial network graph was set; skipping initial callback");
}
}
});
}
/**
* Removes a {@link NetworkTopologyChangeListener}.
*
* @param listener the listener
*/
public synchronized void removeListener(NetworkTopologyChangeListener listener) {
callbackManager.removeListener(listener);
}
private void sendLegacyListenerNotification() {
// forward low-level event for backward compatibility
// TODO remove when not used anymore - misc_ro
callbackManager.enqueueCallback(new AsyncCallback<NetworkTopologyChangeListener>() {
@Override
@TaskDescription("Communication Layer: Topology change callback (0p)")
public void performCallback(NetworkTopologyChangeListener listener) {
listener.onNetworkTopologyChanged();
}
});
}
}