Implement rerouting feature in PathCalcRuntimeModule

Change-Id: Ie37ebd1fa6910e999d457481d7082adb0d1d9a3a
diff --git a/src/main/java/net/onrc/onos/intent/Intent.java b/src/main/java/net/onrc/onos/intent/Intent.java
index 8e20bb7..002d6f6 100644
--- a/src/main/java/net/onrc/onos/intent/Intent.java
+++ b/src/main/java/net/onrc/onos/intent/Intent.java
@@ -50,4 +50,9 @@
 	public int hashCode() {
 		return id.hashCode();
 	}
+	
+	@Override
+	public String toString() {
+		return id.toString() + ", " + state.toString();
+	}
 }
diff --git a/src/main/java/net/onrc/onos/intent/IntentMap.java b/src/main/java/net/onrc/onos/intent/IntentMap.java
index 57ce5e0..9969433 100644
--- a/src/main/java/net/onrc/onos/intent/IntentMap.java
+++ b/src/main/java/net/onrc/onos/intent/IntentMap.java
@@ -49,18 +49,32 @@
 	}
 
 	private HashSet<ChangedListener> listeners = new HashSet<>();
-	protected HashMap<String, Intent> intents = new HashMap<>();
+	private HashMap<String, Intent> intents = new HashMap<>();
+	
+	protected void putIntent(Intent intent) {
+		if (intents.containsKey(intent.getId()))
+			removeIntent(intent.getId());
+		intents.put(intent.getId(), intent);
+	}
+	
+	protected void removeIntent(String intentId) {
+		intents.remove(intentId);		
+	}
+	
+	public Intent getIntent(String intentId) {
+		return intents.get(intentId);
+	}
 
 	public void executeOperations(IntentOperationList operations) {
 		LinkedList<ChangedEvent> events = new LinkedList<>();
 		for (IntentOperation operation: operations) {
 			switch (operation.operator) {
 			case ADD:
-				intents.put(operation.intent.getId(), operation.intent);
+				putIntent(operation.intent);
 				events.add(new ChangedEvent(ChangedEventType.ADDED, operation.intent));
 				break;
 			case REMOVE:
-				Intent intent = intents.get(operation.intent.getId());
+				Intent intent = getIntent(operation.intent.getId());
 				if (intent == null) {
 					// TODO throw exception
 				}
@@ -78,14 +92,16 @@
 	}
 
 	public void purge() {
-		Iterator<Entry<String, Intent>> i = intents.entrySet().iterator();
-		while (i.hasNext()) {
-			Entry<String, Intent> entry = i.next();
+		LinkedList<String> removeIds = new LinkedList<>();
+		for (Entry<String, Intent> entry: intents.entrySet()) {
 			Intent intent = entry.getValue();
 			if (intent.getState() == IntentState.DEL_ACK
 					|| intent.getState() == IntentState.INST_NACK) {
-				i.remove();
-			}
+				removeIds.add(intent.getId());
+			}			
+		}
+		for (String intentId: removeIds) {
+			removeIntent(intentId);
 		}
 	}
 
@@ -93,10 +109,6 @@
 		return intents.values();
 	}
 
-	public Intent getIntent(String key) {
-		return intents.get(key);
-	}
-
 	public void addChangeListener(ChangedListener listener) {
 		listeners.add(listener);
 	}
diff --git a/src/main/java/net/onrc/onos/intent/IntentOperation.java b/src/main/java/net/onrc/onos/intent/IntentOperation.java
index 8ac45c9..c769811 100644
--- a/src/main/java/net/onrc/onos/intent/IntentOperation.java
+++ b/src/main/java/net/onrc/onos/intent/IntentOperation.java
@@ -26,4 +26,9 @@
 
 	public Operator operator;
 	public Intent intent;
+	
+	@Override
+	public String toString() {
+		return operator.toString() + ", (" + intent.toString() + ")";
+	}
 }
diff --git a/src/main/java/net/onrc/onos/intent/PathIntent.java b/src/main/java/net/onrc/onos/intent/PathIntent.java
index 4ef4cf0..a5454fa 100644
--- a/src/main/java/net/onrc/onos/intent/PathIntent.java
+++ b/src/main/java/net/onrc/onos/intent/PathIntent.java
@@ -1,16 +1,19 @@
 package net.onrc.onos.intent;
 
+import java.util.LinkedList;
+import java.util.List;
+
 import net.onrc.onos.ofcontroller.networkgraph.Link;
+import net.onrc.onos.ofcontroller.networkgraph.LinkEvent;
 import net.onrc.onos.ofcontroller.networkgraph.NetworkGraph;
 import net.onrc.onos.ofcontroller.networkgraph.Path;
 import net.onrc.onos.ofcontroller.networkgraph.Port;
-import net.onrc.onos.ofcontroller.networkgraph.Switch;
 
 /**
  * @author Toshio Koide (t-koide@onlab.us)
  */
 public class PathIntent extends Intent {
-	protected long pathData[];
+	protected List<LinkEvent> path;
 	protected double bandwidth;
 	protected Intent parentIntent;
 
@@ -31,13 +34,13 @@
 	 */
 	public PathIntent(String id, Path path, double bandwidth, Intent parentIntent) {
 		super(id);
-		pathData = new long[path.size() * 4];
-		for (int i=0; i<path.size(); i++) {
-			Link link = path.get(i);
-			this.pathData[i*4] = link.getSourceSwitch().getDpid();
-			this.pathData[i*4+1] = link.getSourcePort().getNumber();
-			this.pathData[i*4+2] = link.getDestinationSwitch().getDpid();
-			this.pathData[i*4+3] = link.getDestinationPort().getNumber();
+		this.path = new LinkedList<LinkEvent>();
+		for (Link link: path) {
+			this.path.add(new LinkEvent(
+					link.getSourceSwitch().getDpid(),
+					link.getSourcePort().getNumber(),
+					link.getDestinationSwitch().getDpid(),
+					link.getDestinationPort().getNumber()));
 		}
 		this.bandwidth = bandwidth;
 		this.parentIntent = parentIntent;
@@ -47,8 +50,8 @@
 		return bandwidth;
 	}
 
-	public long[] getPathData() {
-		return pathData;
+	public List<LinkEvent> getPathByLinkEvent() {
+		return path;
 	}
 
 	/**
@@ -57,19 +60,13 @@
 	 * @return path object. If there is no path in the specified graph, returns null.
 	 */
 	public Path getPath(NetworkGraph graph) {
-		Path path = new Path();
-		Switch srcSwitch;
-		Port srcPort;
-		Link link;
-		for (int i=0; i<pathData.length; i+=4) {
-			if ((srcSwitch = graph.getSwitch(pathData[i])) == null) return null;
-			if ((srcPort = srcSwitch.getPort(pathData[i+1])) == null) return null;
-			if ((link = srcPort.getOutgoingLink()) == null) return null;
-			if (link.getDestinationSwitch().getDpid() != pathData[i+2]) return null;
-			if (link.getDestinationPort().getNumber() != pathData[i+3]) return null;
-			path.add(link);
+		Path pathObj = new Path();
+		for (LinkEvent linkEvent: path) {
+			Link link = linkEvent.getLink(graph);
+			if (link == null) return null;
+			pathObj.add(link);
 		}
-		return path;
+		return pathObj;
 	}
 
 	public Intent getParentIntent() {
diff --git a/src/main/java/net/onrc/onos/intent/PathIntentMap.java b/src/main/java/net/onrc/onos/intent/PathIntentMap.java
index 02444ff..b955922 100644
--- a/src/main/java/net/onrc/onos/intent/PathIntentMap.java
+++ b/src/main/java/net/onrc/onos/intent/PathIntentMap.java
@@ -6,43 +6,44 @@
 import java.util.HashSet;
 
 import net.onrc.onos.ofcontroller.networkgraph.Link;
-import net.onrc.onos.ofcontroller.networkgraph.NetworkGraph;
+import net.onrc.onos.ofcontroller.networkgraph.LinkEvent;
 
 /**
  * @author Toshio Koide (t-koide@onlab.us)
  */
 public class PathIntentMap extends IntentMap {
-	protected HashMap<Link, HashSet<PathIntent>> linkToIntents = new HashMap<Link, HashSet<PathIntent>>();
-	protected NetworkGraph graph;
+	protected HashMap<LinkEvent, HashSet<PathIntent>> linkToIntents = new HashMap<>();
 
-	public PathIntentMap(NetworkGraph graph) {
-		this.graph = graph;
-	}
-
-	public void addIntent(PathIntent intent) {
-		if (intents.containsKey(intent.getId()))
-			removeIntent((PathIntent)intents.get(intent.getId()));
-		intents.put(intent.getId(), intent);
-		for (Link link: intent.getPath(graph)) {
-			HashSet<PathIntent> value = linkToIntents.get(link);
-			if (value == null) {
+	@Override
+	protected void putIntent(Intent intent) {
+		super.putIntent(intent);
+		for (LinkEvent linkEvent: ((PathIntent) intent).getPathByLinkEvent()) {
+			HashSet<PathIntent> value = linkToIntents.get(linkEvent);
+			if (value == null)
 				value = new HashSet<PathIntent>();
-				linkToIntents.put(link, value);
-			}
-			value.add(intent);
+			value.add((PathIntent) intent);
+			linkToIntents.put(linkEvent, value);
 		}
 	}
 
-	public void removeIntent(PathIntent intent) {
-		intents.remove(intent);
-		for (Link link: intent.getPath(graph)) {
-			HashSet<PathIntent> value = linkToIntents.get(link);
+	@Override
+	protected void removeIntent(String intentId) {
+		PathIntent intent = (PathIntent) getIntent(intentId);
+		for (LinkEvent linkEvent: intent.getPathByLinkEvent()) {
+			HashSet<PathIntent> value = linkToIntents.get(linkEvent);
 			value.remove(intent);
 		}
+		super.removeIntent(intentId);
 	}
 
-	public Collection<PathIntent> getIntentByLink(Link link) {
-		return Collections.unmodifiableCollection(linkToIntents.get(link));
+	public Collection<PathIntent> getIntentsByLink(LinkEvent linkEvent) {
+		Collection<PathIntent> intents = linkToIntents.get(linkEvent);
+		if (intents == null) {
+			return null;
+		}
+		else {
+			return Collections.unmodifiableCollection(intents);
+		}
 	}
 
 	/**
@@ -51,9 +52,11 @@
 	 * @return
 	 */
 	public Double getAvailableBandwidth(Link link) {
+		if (link == null) return null;
 		Double bandwidth = link.getCapacity();
-		if (!bandwidth.isInfinite() && linkToIntents.containsKey(link)) {
-			for (PathIntent intent: getIntentByLink(link)) {
+		LinkEvent linkEvent = new LinkEvent(link);
+		if (!bandwidth.isInfinite() && linkToIntents.containsKey(linkEvent)) {
+			for (PathIntent intent: getIntentsByLink(linkEvent)) {
 				Double intentBandwidth = intent.getBandwidth();
 				if (intentBandwidth == null || intentBandwidth.isInfinite() || intentBandwidth.isNaN())
 					continue;
diff --git a/src/main/java/net/onrc/onos/intent/ShortestPathIntent.java b/src/main/java/net/onrc/onos/intent/ShortestPathIntent.java
index 4b324df..4198b18 100644
--- a/src/main/java/net/onrc/onos/intent/ShortestPathIntent.java
+++ b/src/main/java/net/onrc/onos/intent/ShortestPathIntent.java
@@ -58,7 +58,8 @@
 
 	@Override
 	public String toString() {
-		return String.format("srcDpid:%s, srcPort:%d, srcMac:%s, dstDpid:%s, dstPort:%d, dstMac:%s",
+		return String.format("id:%s, state:%s, srcDpid:%s, srcPort:%d, srcMac:%s, dstDpid:%s, dstPort:%d, dstMac:%s",
+				getId(), state,
 				new Dpid(srcSwitchDpid), srcPortNumber, MACAddress.valueOf(srcMacAddress),
 				new Dpid(dstSwitchDpid), dstPortNumber, MACAddress.valueOf(dstMacAddress));
 	}
diff --git a/src/main/java/net/onrc/onos/intent/runtime/PathCalcRuntime.java b/src/main/java/net/onrc/onos/intent/runtime/PathCalcRuntime.java
index 32fe746..4408f3f 100644
--- a/src/main/java/net/onrc/onos/intent/runtime/PathCalcRuntime.java
+++ b/src/main/java/net/onrc/onos/intent/runtime/PathCalcRuntime.java
@@ -5,11 +5,13 @@
 import net.floodlightcontroller.core.module.IFloodlightService;
 import net.onrc.onos.intent.ConstrainedBFSTree;
 import net.onrc.onos.intent.ConstrainedShortestPathIntent;
+import net.onrc.onos.intent.Intent;
 import net.onrc.onos.intent.IntentOperation;
 import net.onrc.onos.intent.IntentOperationList;
 import net.onrc.onos.intent.PathIntent;
 import net.onrc.onos.intent.PathIntentMap;
 import net.onrc.onos.intent.ShortestPathIntent;
+import net.onrc.onos.intent.IntentOperation.Operator;
 import net.onrc.onos.ofcontroller.networkgraph.NetworkGraph;
 import net.onrc.onos.ofcontroller.networkgraph.Path;
 import net.onrc.onos.ofcontroller.networkgraph.Switch;
@@ -74,7 +76,7 @@
 				pathIntentOpList.add(new IntentOperation(IntentOperation.Operator.ADD, pathIntent));
 				break;
 			case REMOVE:
-				pathIntentOpList.add(intentOp);
+				pathIntentOpList.add(Operator.REMOVE, new Intent("pi" + intentOp.intent.getId()));
 				break;
 			}
 		}
diff --git a/src/main/java/net/onrc/onos/intent/runtime/PathCalcRuntimeModule.java b/src/main/java/net/onrc/onos/intent/runtime/PathCalcRuntimeModule.java
index 26afe51..d72a13a 100644
--- a/src/main/java/net/onrc/onos/intent/runtime/PathCalcRuntimeModule.java
+++ b/src/main/java/net/onrc/onos/intent/runtime/PathCalcRuntimeModule.java
@@ -11,22 +11,43 @@
 import net.floodlightcontroller.core.module.IFloodlightService;
 import net.onrc.onos.datagrid.IDatagridService;
 import net.onrc.onos.datagrid.IEventChannel;
+import net.onrc.onos.intent.Intent;
 import net.onrc.onos.intent.IntentMap;
+import net.onrc.onos.intent.IntentOperation.Operator;
 import net.onrc.onos.intent.IntentOperationList;
+import net.onrc.onos.intent.PathIntent;
 import net.onrc.onos.intent.PathIntentMap;
+import net.onrc.onos.ofcontroller.networkgraph.DeviceEvent;
+import net.onrc.onos.ofcontroller.networkgraph.INetworkGraphListener;
 import net.onrc.onos.ofcontroller.networkgraph.INetworkGraphService;
-import net.onrc.onos.ofcontroller.networkgraph.NetworkGraph;
+import net.onrc.onos.ofcontroller.networkgraph.LinkEvent;
+import net.onrc.onos.ofcontroller.networkgraph.PortEvent;
+import net.onrc.onos.ofcontroller.networkgraph.SwitchEvent;
 
-public class PathCalcRuntimeModule implements IFloodlightModule, IPathCalcRuntimeService {
+/**
+ * @author Toshio Koide (t-koide@onlab.us)
+ */
+public class PathCalcRuntimeModule implements IFloodlightModule, IPathCalcRuntimeService, INetworkGraphListener {
 	private PathCalcRuntime runtime;
 	private IDatagridService datagridService;
 	private INetworkGraphService networkGraphService;
 	private IntentMap highLevelIntents;
 	private PathIntentMap pathIntents;
 
-	private IEventChannel<byte[], IntentOperationList> eventChannel;
+	private IEventChannel<String, IntentOperationList> eventChannel;
 	private static final String EVENT_CHANNEL_NAME = "onos.pathintent";
 
+	private void reroutePaths(LinkEvent linkEvent) {
+		Collection<PathIntent> oldPaths = pathIntents.getIntentsByLink(linkEvent);
+		if (oldPaths == null) return;
+		IntentOperationList reroutingOperation = new IntentOperationList();
+		for (PathIntent pathIntent: oldPaths) {
+			// TODO use Operator.UPDATE instead of REMOVE and ADD in order to optimize
+			reroutingOperation.add(Operator.REMOVE, new Intent(pathIntent.getParentIntent().getId()));
+			reroutingOperation.add(Operator.ADD, pathIntent.getParentIntent());
+		}
+		executeIntentOperations(reroutingOperation);
+	}
 
 	@Override
 	public Collection<Class<? extends IFloodlightService>> getModuleServices() {
@@ -60,23 +81,22 @@
 	public void startUp(FloodlightModuleContext context) {
 		highLevelIntents = new IntentMap();
 		runtime = new PathCalcRuntime(networkGraphService.getNetworkGraph());
-		pathIntents = new PathIntentMap(networkGraphService.getNetworkGraph());
+		pathIntents = new PathIntentMap();
 		eventChannel = datagridService.createChannel(
 				EVENT_CHANNEL_NAME,
-				byte[].class,
+				String.class,
 				IntentOperationList.class);
-	}
-
-	protected void publishPathIntentOperationList(IntentOperationList list) {
-		eventChannel.addEntry(new byte[1], list); // TODO make key bytes		
+		networkGraphService.registerNetworkGraphListener(this);
 	}
 
 	@Override
 	public IntentOperationList executeIntentOperations(IntentOperationList list) {
 		highLevelIntents.executeOperations(list);
 		IntentOperationList pathIntentOperations = runtime.calcPathIntents(list, pathIntents);
+		String key = "..."; // TODO generate key
+		System.out.println(pathIntentOperations);
 		pathIntents.executeOperations(pathIntentOperations);
-		publishPathIntentOperationList(pathIntentOperations);
+		eventChannel.addEntry(key, pathIntentOperations);
 		return pathIntentOperations;
 	}
 
@@ -95,4 +115,44 @@
 		highLevelIntents.purge();
 		pathIntents.purge();
 	}
+
+	@Override
+	public void putSwitchEvent(SwitchEvent switchEvent) {
+		// do nothing
+	}
+
+	@Override
+	public void removeSwitchEvent(SwitchEvent switchEvent) {
+		// do nothing
+	}
+
+	@Override
+	public void putPortEvent(PortEvent portEvent) {
+		// do nothing
+	}
+
+	@Override
+	public void removePortEvent(PortEvent portEvent) {
+		// do nothing
+	}
+
+	@Override
+	public void putLinkEvent(LinkEvent linkEvent) {
+		// do nothing
+	}
+
+	@Override
+	public void removeLinkEvent(LinkEvent linkEvent) {
+		reroutePaths(linkEvent);
+	}
+
+	@Override
+	public void putDeviceEvent(DeviceEvent deviceEvent) {
+		// do nothing
+	}
+
+	@Override
+	public void removeDeviceEvent(DeviceEvent deviceEvent) {
+		// do nothing
+	}
 }
diff --git a/src/main/java/net/onrc/onos/intent/runtime/PlanCalcRuntime.java b/src/main/java/net/onrc/onos/intent/runtime/PlanCalcRuntime.java
index 4582021..19a6b53 100644
--- a/src/main/java/net/onrc/onos/intent/runtime/PlanCalcRuntime.java
+++ b/src/main/java/net/onrc/onos/intent/runtime/PlanCalcRuntime.java
@@ -37,7 +37,7 @@
 		this.graph = graph;
 		this.flowEntries = new HashSet<>();
 		this.plan = new ArrayList<>();
-		this.intents = new PathIntentMap(this.graph);
+		this.intents = new PathIntentMap();
 	}
 	
 	public void addIntents(IntentOperationList intentOpList) {
diff --git a/src/main/java/net/onrc/onos/ofcontroller/networkgraph/Link.java b/src/main/java/net/onrc/onos/ofcontroller/networkgraph/Link.java
index ef74f73..4fd53af 100644
--- a/src/main/java/net/onrc/onos/ofcontroller/networkgraph/Link.java
+++ b/src/main/java/net/onrc/onos/ofcontroller/networkgraph/Link.java
@@ -12,7 +12,7 @@
 	public Port getDestinationPort();
 	public Switch getSourceSwitch();
 	public Switch getDestinationSwitch();
-		
+
 	public long getLastSeenTime();
 
 	public int getCost();
@@ -21,8 +21,12 @@
 	// Not sure if we want to expose these northbound
 	// Toshi: I think these are unnecessary because we can get them
 	// Toshi: like "this.getSourcePort().getSwitch()" etc.
+	@Deprecated
 	public Long getSourceSwitchDpid();
+	@Deprecated
 	public Long getSourcePortNumber();
+	@Deprecated
 	public Long getDestinationSwitchDpid();
+	@Deprecated
 	public Long getDestinationPortNumber();
 }
diff --git a/src/main/java/net/onrc/onos/ofcontroller/networkgraph/LinkEvent.java b/src/main/java/net/onrc/onos/ofcontroller/networkgraph/LinkEvent.java
index c8cf71e..83af786 100644
--- a/src/main/java/net/onrc/onos/ofcontroller/networkgraph/LinkEvent.java
+++ b/src/main/java/net/onrc/onos/ofcontroller/networkgraph/LinkEvent.java
@@ -24,18 +24,23 @@
 
     public LinkEvent(Long src_dpid, Long src_port_no, Long dst_dpid,
 	    Long dst_port_no) {
-
 	src = new SwitchPort(src_dpid, src_port_no);
 	dst = new SwitchPort(dst_dpid, dst_port_no);
+    }
 
+    public LinkEvent(Link link) {
+	src = new SwitchPort(link.getSourceSwitch().getDpid(),
+		link.getSourcePort().getNumber());
+	dst = new SwitchPort(link.getDestinationSwitch().getDpid(),
+		link.getDestinationPort().getNumber());
     }
 
     public SwitchPort getSrc() {
-        return src;
+	return src;
     }
 
     public SwitchPort getDst() {
-        return dst;
+	return dst;
     }
 
     @Override
@@ -54,6 +59,47 @@
 
     public byte[] getID() {
 	return getLinkID(src.getDpid(), src.getNumber(),
-			 dst.getDpid(), dst.getNumber());
+		dst.getDpid(), dst.getNumber());
     }
-}
+
+    public Link getLink(NetworkGraph graph) {
+	Port srcPort = graph.getPort(getSrc().getDpid(), getSrc().getNumber());
+	if (srcPort == null) return null;
+	Link link = srcPort.getOutgoingLink();
+	if (link == null) return null;
+	if (link.getDestinationSwitch().getDpid() != getDst().getDpid()) return null;
+	if (link.getDestinationPort().getNumber() != getDst().getNumber()) return null;
+	return link;
+    }
+
+    @Override
+    public int hashCode() {
+	final int prime = 31;
+	int result = 1;
+	result = prime * result + ((dst == null) ? 0 : dst.hashCode());
+	result = prime * result + ((src == null) ? 0 : src.hashCode());
+	return result;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+	if (this == obj)
+	    return true;
+	if (obj == null)
+	    return false;
+	if (getClass() != obj.getClass())
+	    return false;
+	LinkEvent other = (LinkEvent) obj;
+	if (dst == null) {
+	    if (other.dst != null)
+		return false;
+	} else if (!dst.equals(other.dst))
+	    return false;
+	if (src == null) {
+	    if (other.src != null)
+		return false;
+	} else if (!src.equals(other.src))
+	    return false;
+	return true;
+    }
+}
\ No newline at end of file
diff --git a/src/main/java/net/onrc/onos/ofcontroller/networkgraph/LinkImpl.java b/src/main/java/net/onrc/onos/ofcontroller/networkgraph/LinkImpl.java
index c8954a9..801e780 100644
--- a/src/main/java/net/onrc/onos/ofcontroller/networkgraph/LinkImpl.java
+++ b/src/main/java/net/onrc/onos/ofcontroller/networkgraph/LinkImpl.java
@@ -76,39 +76,43 @@
 	}
 
 	@Override
+	public Double getCapacity() {
+		return capacity;
+	}
+
+	public void setCapacity(Double capacity) {
+		this.capacity = capacity;
+	}
+
+	@Deprecated
+	@Override
 	public Long getSourceSwitchDpid() {
 		return srcPort.getSwitch().getDpid();
 	}
 
+	@Deprecated
 	@Override
 	public Long getSourcePortNumber() {
 		return srcPort.getNumber();
 	}
 
+	@Deprecated
 	@Override
 	public Long getDestinationSwitchDpid() {
 		return dstPort.getSwitch().getDpid();
 	}
 
+	@Deprecated
 	@Override
 	public Long getDestinationPortNumber() {
 		return dstPort.getNumber();
 	}
 
 	@Override
-	public Double getCapacity() {
-		return capacity;
-	}
-
-	@Override
 	public String toString() {
 		return String.format("%s --(cap:%f Mbps)--> %s",
 				getSourcePort().toString(),
 				getCapacity(),
 				getDestinationPort().toString());
 	}
-
-	public void setCapacity(Double capacity) {
-		this.capacity = capacity;
-	}
 }
diff --git a/src/main/java/net/onrc/onos/ofcontroller/networkgraph/PortEvent.java b/src/main/java/net/onrc/onos/ofcontroller/networkgraph/PortEvent.java
index b42bf1e..8172f29 100644
--- a/src/main/java/net/onrc/onos/ofcontroller/networkgraph/PortEvent.java
+++ b/src/main/java/net/onrc/onos/ofcontroller/networkgraph/PortEvent.java
@@ -10,8 +10,8 @@
  */
 public class PortEvent {
     public static class SwitchPort {
-        public final Long dpid;
-        public final Long number;
+	public final Long dpid;
+	public final Long number;
 
 	/**
 	 * Default constructor.
@@ -39,6 +39,37 @@
             return "(" + Long.toHexString(dpid) + "@" + number + ")";
         }
 
+        @Override
+        public int hashCode() {
+            final int prime = 31;
+            int result = 1;
+            result = prime * result + ((dpid == null) ? 0 : dpid.hashCode());
+            result = prime * result
+        	    + ((number == null) ? 0 : number.hashCode());
+            return result;
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            if (this == obj)
+        	return true;
+            if (obj == null)
+        	return false;
+            if (getClass() != obj.getClass())
+        	return false;
+            SwitchPort other = (SwitchPort) obj;
+            if (dpid == null) {
+        	if (other.dpid != null)
+        	    return false;
+            } else if (!dpid.equals(other.dpid))
+        	return false;
+            if (number == null) {
+        	if (other.number != null)
+        	    return false;
+            } else if (!number.equals(other.number))
+        	return false;
+            return true;
+        }
     }
 
     private final SwitchPort id;