Support multiple Links from a Port

- Support multiple Links from a Port (ONOS-1574,ONOS-1391)

Change-Id: I0b53a950d0ad1e37bbedb726fee2d35df4ff0d60
diff --git a/src/main/java/net/onrc/onos/core/topology/Port.java b/src/main/java/net/onrc/onos/core/topology/Port.java
index 0ca5cce..8613537 100644
--- a/src/main/java/net/onrc/onos/core/topology/Port.java
+++ b/src/main/java/net/onrc/onos/core/topology/Port.java
@@ -1,5 +1,7 @@
 package net.onrc.onos.core.topology;
 
+import java.util.Collection;
+
 import net.onrc.onos.core.topology.web.serializers.PortSerializer;
 import net.onrc.onos.core.util.Dpid;
 import net.onrc.onos.core.util.PortNumber;
@@ -66,6 +68,9 @@
 
     /**
      * Gets the outgoing link from this port.
+     * <p/>
+     * FIXME As a temporary workaround, it will look for type "packet" and
+     * returns it if found, else return whichever link is found first.
      *
      * @return {@link Link} if there exist a outgoing link from this,
      *         {@code null} otherwise.
@@ -73,13 +78,48 @@
     public Link getOutgoingLink();
 
     /**
+     * Gets the outgoing link from this port.
+     *
+     * @param type type of the link
+     * @return {@link Link} if there exist a outgoing link from this,
+     *         {@code null} otherwise.
+     */
+    public Link getOutgoingLink(String type);
+
+    /**
+     * Gets all the outgoing links from this port.
+     *
+     * @return Collection of {@link Link}s
+     */
+    public Collection<Link> getOutgoingLinks();
+
+    /**
      * Gets the incoming link to this port.
+     * <p/>
+     * FIXME As a temporary workaround, it will look for type "packet" and
+     * returns it if found, else return whichever link is found first.
      *
      * @return {@link Link} if there exist a incoming link to this, {@code null}
      *         otherwise.
      */
     public Link getIncomingLink();
 
+    /**
+     * Gets the incoming link to this port.
+     *
+     * @param type type of the link
+     * @return {@link Link} if there exist a incoming link to this, {@code null}
+     *         otherwise.
+     */
+    public Link getIncomingLink(String type);
+
+    /**
+     * Gets all the incoming links to this port.
+     *
+     * @return Collection of {@link Link}s
+     */
+    public Collection<Link> getIncomingLinks();
+
     // XXX Iterable or Collection?
     /**
      * Gets all the devices attached to this port.
diff --git a/src/main/java/net/onrc/onos/core/topology/PortImpl.java b/src/main/java/net/onrc/onos/core/topology/PortImpl.java
index 52dbe47..f022438 100644
--- a/src/main/java/net/onrc/onos/core/topology/PortImpl.java
+++ b/src/main/java/net/onrc/onos/core/topology/PortImpl.java
@@ -1,5 +1,6 @@
 package net.onrc.onos.core.topology;
 
+import java.util.Collection;
 import java.util.Map;
 
 import org.apache.commons.lang.Validate;
@@ -139,6 +140,26 @@
     }
 
     @Override
+    public Link getOutgoingLink(String type) {
+        topology.acquireReadLock();
+        try {
+            return topology.getOutgoingLink(asSwitchPort(), type);
+        } finally {
+            topology.releaseReadLock();
+        }
+    }
+
+    @Override
+    public Collection<Link> getOutgoingLinks() {
+        topology.acquireReadLock();
+        try {
+            return topology.getOutgoingLinks(asSwitchPort());
+        } finally {
+            topology.releaseReadLock();
+        }
+    }
+
+    @Override
     public Link getIncomingLink() {
         topology.acquireReadLock();
         try {
@@ -149,6 +170,26 @@
     }
 
     @Override
+    public Link getIncomingLink(String type) {
+        topology.acquireReadLock();
+        try {
+            return topology.getIncomingLink(asSwitchPort(), type);
+        } finally {
+            topology.releaseReadLock();
+        }
+    }
+
+    @Override
+    public Collection<Link> getIncomingLinks() {
+        topology.acquireReadLock();
+        try {
+            return topology.getIncomingLinks(asSwitchPort());
+        } finally {
+            topology.releaseReadLock();
+        }
+    }
+
+    @Override
     public Iterable<Device> getDevices() {
         topology.acquireReadLock();
         try {
diff --git a/src/main/java/net/onrc/onos/core/topology/Topology.java b/src/main/java/net/onrc/onos/core/topology/Topology.java
index 089d0e1..607d22c 100644
--- a/src/main/java/net/onrc/onos/core/topology/Topology.java
+++ b/src/main/java/net/onrc/onos/core/topology/Topology.java
@@ -60,6 +60,9 @@
 
     /**
      * Gets the outgoing link from a switch port.
+     * <p/>
+     * FIXME As a temporary workaround, it will look for type "packet" and
+     * returns it if found, else return whichever link is found first.
      *
      * @param dpid   the switch DPID.
      * @param number the switch port number.
@@ -70,13 +73,46 @@
     /**
      * Gets the outgoing link from a switch port.
      *
+     * @param dpid   the switch DPID.
+     * @param number the switch port number.
+     * @param type   type of the link
+     * @return the outgoing link if found, otherwise null.
+     */
+    public Link getOutgoingLink(Dpid dpid, PortNumber number, String type);
+
+    /**
+     * Gets the outgoing link from a switch port.
+     * <p/>
+     * FIXME As a temporary workaround, it will look for type "packet" and
+     * returns it if found, else return whichever link is found first.
+     *
      * @param port port identifier
-     * @return the switch port if found, otherwise null.
+     * @return the outgoing link if found, otherwise null.
      */
     public Link getOutgoingLink(SwitchPort port);
 
     /**
+     * Gets the outgoing link from a switch port.
+     *
+     * @param port port identifier
+     * @param type type of the link
+     * @return the outgoing link if found, otherwise null.
+     */
+    public Link getOutgoingLink(SwitchPort port, String type);
+
+    /**
+     * Gets all the outgoing link from a switch port.
+     *
+     * @param port port identifier
+     * @return outgoing links
+     */
+    public Collection<Link> getOutgoingLinks(SwitchPort port);
+
+    /**
      * Gets the incoming link to a switch port.
+     * <p/>
+     * FIXME As a temporary workaround, it will look for type "packet" and
+     * returns it if found, else return whichever link is found first.
      *
      * @param dpid   the switch DPID.
      * @param number the switch port number.
@@ -86,13 +122,47 @@
 
     /**
      * Gets the incoming link to a switch port.
+     * <p/>
+     * FIXME As a temporary workaround, it will look for type "packet" and
+     * returns it if found, else return whichever link is found first.
+     *
+     * @param dpid   the switch DPID.
+     * @param number the switch port number.
+     * @param type type of the link
+     * @return the incoming link if found, otherwise null.
+     */
+    public Link getIncomingLink(Dpid dpid, PortNumber number, String type);
+
+
+    /**
+     * Gets the incoming link to a switch port.
+     * <p/>
+     * FIXME As a temporary workaround, it will look for type "packet" and
+     * returns it if found, else return whichever link is found first.
      *
      * @param port port identifier
-     * @return the switch port if found, otherwise null.
+     * @return the incoming link if found, otherwise null.
      */
     public Link getIncomingLink(SwitchPort port);
 
     /**
+     * Gets the incoming link to a switch port.
+     *
+     * @param port port identifier
+     * @param type type of the link
+     * @return the incoming link if found, otherwise null.
+     */
+    public Link getIncomingLink(SwitchPort port, String type);
+
+    /**
+     * Gets all the incoming link from a switch port.
+     *
+     * @param port port identifier
+     * @return incoming links
+     */
+    public Collection<Link> getIncomingLinks(SwitchPort port);
+
+    /**
      * Gets the outgoing link from a switch and a port to another switch and
      * a port.
      *
@@ -106,9 +176,23 @@
                         Dpid dstDpid, PortNumber dstNumber);
 
     /**
+     * Gets the outgoing link from a switch and a port to another switch and
+     * a port.
+     *
+     * @param srcDpid   the source switch DPID.
+     * @param srcNumber the source switch port number.
+     * @param dstDpid   the destination switch DPID.
+     * @param dstNumber the destination switch port number.
+     * @param type      type of the link
+     * @return the outgoing link if found, otherwise null.
+     */
+    public Link getLink(Dpid srcDpid, PortNumber srcNumber,
+                        Dpid dstDpid, PortNumber dstNumber,
+                        String type);
+
+    /**
      * Gets all links in the network.
      * <p/>
-     * TODO: Not clear if this method is needed. Remove if not used.
      *
      * @return all links in the network.
      */
diff --git a/src/main/java/net/onrc/onos/core/topology/TopologyImpl.java b/src/main/java/net/onrc/onos/core/topology/TopologyImpl.java
index 5ce3729..e6e6763 100644
--- a/src/main/java/net/onrc/onos/core/topology/TopologyImpl.java
+++ b/src/main/java/net/onrc/onos/core/topology/TopologyImpl.java
@@ -1,13 +1,19 @@
 package net.onrc.onos.core.topology;
 
+import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ConcurrentMap;
 import java.util.concurrent.locks.Lock;
 import java.util.concurrent.locks.ReadWriteLock;
 import java.util.concurrent.locks.ReentrantReadWriteLock;
 
+import javax.annotation.concurrent.GuardedBy;
+
 import net.floodlightcontroller.util.MACAddress;
 import net.onrc.onos.core.util.Dpid;
 import net.onrc.onos.core.util.PortNumber;
@@ -33,8 +39,9 @@
     private final Multimap<SwitchPort, Device> devices;
     private final ConcurrentMap<MACAddress, Device> mac2Device;
 
-    private final ConcurrentMap<SwitchPort, Link> outgoingLinks;
-    private final ConcurrentMap<SwitchPort, Link> incomingLinks;
+    // SwitchPort -> (type -> Link)
+    private final ConcurrentMap<SwitchPort, ConcurrentMap<String, Link>> outgoingLinks;
+    private final ConcurrentMap<SwitchPort, ConcurrentMap<String, Link>> incomingLinks;
 
     private ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
     private Lock readLock = readWriteLock.readLock();
@@ -128,29 +135,108 @@
 
     @Override
     public Link getOutgoingLink(Dpid dpid, PortNumber number) {
-        return outgoingLinks.get(new SwitchPort(dpid, number));
+        return getOutgoingLink(new SwitchPort(dpid, number));
     }
 
     @Override
     public Link getOutgoingLink(SwitchPort port) {
-        return outgoingLinks.get(port);
+        Map<String, Link> links = outgoingLinks.get(port);
+        return getPacketLinkIfExists(links);
+    }
+
+    // TODO remove when we no longer need packet fall back behavior
+    /**
+     * Gets the "packet" link if such exists, if not return whatever found.
+     *
+     * @param links Collection of links to search from
+     * @return Link instance found or null if no link exists
+     */
+    private Link getPacketLinkIfExists(Map<String, Link> links) {
+
+        if (links == null) {
+            return null;
+        }
+
+        Link link = links.get(TopologyElement.TYPE_PACKET);
+        if (link != null) {
+            // return packet link
+            return link;
+        } else {
+            // return whatever found
+            Iterator<Link> it = links.values().iterator();
+            if (it.hasNext()) {
+                return it.next();
+            }
+        }
+        return null;
+    }
+
+    @Override
+    public Link getOutgoingLink(Dpid dpid, PortNumber number, String type) {
+        return getOutgoingLink(new SwitchPort(dpid, number), type);
+    }
+
+    @Override
+    public Link getOutgoingLink(SwitchPort port, String type) {
+        Map<String, Link> links = outgoingLinks.get(port);
+        return links.get(type);
+    }
+
+    @Override
+    public Collection<Link> getOutgoingLinks(SwitchPort port) {
+        return Collections.unmodifiableCollection(outgoingLinks.get(port).values());
     }
 
     @Override
     public Link getIncomingLink(Dpid dpid, PortNumber number) {
-        return incomingLinks.get(new SwitchPort(dpid, number));
+        return getIncomingLink(new SwitchPort(dpid, number));
     }
 
     @Override
     public Link getIncomingLink(SwitchPort port) {
-        return incomingLinks.get(port);
+        Map<String, Link> links = incomingLinks.get(port);
+        return getPacketLinkIfExists(links);
+    }
+
+    @Override
+    public Link getIncomingLink(Dpid dpid, PortNumber number, String type) {
+        return getIncomingLink(new SwitchPort(dpid, number), type);
+    }
+
+    @Override
+    public Link getIncomingLink(SwitchPort port, String type) {
+        Map<String, Link> links = incomingLinks.get(port);
+        return links.get(type);
+    }
+
+    @Override
+    public Collection<Link> getIncomingLinks(SwitchPort port) {
+        return Collections.unmodifiableCollection(incomingLinks.get(port).values());
     }
 
     @Override
     public Link getLink(Dpid srcDpid, PortNumber srcNumber,
                         Dpid dstDpid, PortNumber dstNumber) {
 
-        Link link = getOutgoingLink(srcDpid, srcNumber);
+        final SwitchPort dstSwitchPort = new SwitchPort(dstDpid, dstNumber);
+        Collection<Link> links = getOutgoingLinks(new SwitchPort(srcDpid, srcNumber));
+        for (Link link : links) {
+            if (link == null) {
+                continue;
+            }
+            if (link.getDstPort().asSwitchPort().equals(dstSwitchPort)) {
+                return link;
+            }
+        }
+        return null;
+    }
+
+    @Override
+    public Link getLink(Dpid srcDpid, PortNumber srcNumber,
+                        Dpid dstDpid, PortNumber dstNumber,
+                        String type) {
+
+        Link link = getOutgoingLink(srcDpid, srcNumber, type);
         if (link == null) {
             return null;
         }
@@ -165,17 +251,53 @@
 
     @Override
     public Iterable<Link> getLinks() {
-        return Collections.unmodifiableCollection(outgoingLinks.values());
+        List<Link> links = new ArrayList<>();
+
+        for (Map<String, Link> portLinks : outgoingLinks.values()) {
+            links.addAll(portLinks.values());
+        }
+        return links;
     }
 
+    @GuardedBy("topology.writeLock")
     protected void putLink(Link link) {
-        outgoingLinks.put(link.getSrcPort().asSwitchPort(), link);
-        incomingLinks.put(link.getDstPort().asSwitchPort(), link);
+        putLinkMap(outgoingLinks, link.getSrcPort().asSwitchPort(), link);
+        putLinkMap(incomingLinks, link.getDstPort().asSwitchPort(), link);
     }
 
+    /**
+     * Helper method to update outgoingLinks, incomingLinks.
+     *
+     * @param linkMap outgoingLinks or incomingLinks
+     * @param port Map key
+     * @param link Link to add
+     */
+    @GuardedBy("topology.writeLock")
+    private void putLinkMap(ConcurrentMap<SwitchPort, ConcurrentMap<String, Link>> linkMap,
+                            SwitchPort port, Link link) {
+        ConcurrentMap<String, Link> portLinks = new ConcurrentHashMap<String, Link>(3);
+        portLinks.put(link.getType(), link);
+        Map<String, Link> existing = linkMap.putIfAbsent(
+                    port,
+                    portLinks);
+        if (existing != null) {
+            // no conditional update here
+            existing.put(link.getType(), link);
+        }
+    }
+
+    @GuardedBy("topology.writeLock")
     protected void removeLink(Link link) {
-        outgoingLinks.remove(link.getSrcPort().asSwitchPort(), link);
-        incomingLinks.remove(link.getDstPort().asSwitchPort(), link);
+        ConcurrentMap<String, Link> portLinks = outgoingLinks.get(link.getSrcPort().asSwitchPort());
+        if (portLinks != null) {
+            // no conditional update here
+            portLinks.remove(link.getType());
+        }
+        portLinks = incomingLinks.get(link.getDstPort().asSwitchPort());
+        if (portLinks != null) {
+            // no conditional update here
+            portLinks.remove(link.getType());
+        }
     }
 
     @Override