class LinkDiscoveryManager implements IOFMessageListener, IOFSwitchListener, 
IStorageSourceListener, ILinkDiscoveryService,
IFloodlightModule, IInfoProvider, IHAListener {

    protected boolean addOrUpdateLink(Link lt, LinkInfo newInfo) {

        NodePortTuple srcNpt, dstNpt;
        boolean linkChanged = false;

        lock.writeLock().lock();
        try {
            // put the new info.  if an old info exists, it will be returned.
            LinkInfo oldInfo = links.put(lt, newInfo);
            if (oldInfo != null &&
                    oldInfo.getFirstSeenTime() < newInfo.getFirstSeenTime())
                newInfo.setFirstSeenTime(oldInfo.getFirstSeenTime());

            if (log.isTraceEnabled()) {
                log.trace("addOrUpdateLink: {} {}", 
                          lt, 
                          (newInfo.getMulticastValidTime()!=null) ? "multicast" : "unicast");
            }

            UpdateOperation updateOperation = null;
            linkChanged = false;

            srcNpt = new NodePortTuple(lt.getSrc(), lt.getSrcPort());
            dstNpt = new NodePortTuple(lt.getDst(), lt.getDstPort());

            if (oldInfo == null) {
                // index it by switch source
                if (!switchLinks.containsKey(lt.getSrc()))
                    switchLinks.put(lt.getSrc(), new HashSet<Link>());
                switchLinks.get(lt.getSrc()).add(lt);

                // index it by switch dest
                if (!switchLinks.containsKey(lt.getDst()))
                    switchLinks.put(lt.getDst(), new HashSet<Link>());
                switchLinks.get(lt.getDst()).add(lt);

                // index both ends by switch:port
                if (!portLinks.containsKey(srcNpt))
                    portLinks.put(srcNpt, new HashSet<Link>());
                portLinks.get(srcNpt).add(lt);

                if (!portLinks.containsKey(dstNpt))
                    portLinks.put(dstNpt, new HashSet<Link>());
                portLinks.get(dstNpt).add(lt);

                // Add to portNOFLinks if the unicast valid time is null
                if (newInfo.getUnicastValidTime() == null)
                    addLinkToBroadcastDomain(lt);

                writeLinkToStorage(lt, newInfo);
                updateOperation = UpdateOperation.LINK_UPDATED;
                linkChanged = true;

                // Add to event history
                evHistTopoLink(lt.getSrc(),
                               lt.getDst(),
                               lt.getSrcPort(),
                               lt.getDstPort(),
                               newInfo.getSrcPortState(), newInfo.getDstPortState(),
                               getLinkType(lt, newInfo),
                               EvAction.LINK_ADDED, "LLDP Recvd");
            } else {
                // Since the link info is already there, we need to
                // update the right fields.
                if (newInfo.getUnicastValidTime() == null) {
                    // This is due to a multicast LLDP, so copy the old unicast
                    // value.
                    if (oldInfo.getUnicastValidTime() != null) {
                        newInfo.setUnicastValidTime(oldInfo.getUnicastValidTime());
                    }
                } else if (newInfo.getMulticastValidTime() == null) {
                    // This is due to a unicast LLDP, so copy the old multicast
                    // value.
                    if (oldInfo.getMulticastValidTime() != null) {
                        newInfo.setMulticastValidTime(oldInfo.getMulticastValidTime());
                    }
                }

                Long oldTime = oldInfo.getUnicastValidTime();
                Long newTime = newInfo.getUnicastValidTime();
                // the link has changed its state between openflow and non-openflow
                // if the unicastValidTimes are null or not null
                if (oldTime != null & newTime == null) {
                    // openflow -> non-openflow transition
                    // we need to add the link tuple to the portNOFLinks
                    addLinkToBroadcastDomain(lt);
                    linkChanged = true;
                } else if (oldTime == null & newTime != null) {
                    // non-openflow -> openflow transition
                    // we need to remove the link from the portNOFLinks
                    removeLinkFromBroadcastDomain(lt);
                    linkChanged = true;
                }

                // Only update the port states if they've changed
                if (newInfo.getSrcPortState().intValue() !=
                        oldInfo.getSrcPortState().intValue() ||
                        newInfo.getDstPortState().intValue() !=
                        oldInfo.getDstPortState().intValue())
                    linkChanged = true;

                // Write changes to storage. This will always write the updated
                // valid time, plus the port states if they've changed (i.e. if
                // they weren't set to null in the previous block of code.
                writeLinkToStorage(lt, newInfo);

                if (linkChanged) {
                    updateOperation = getUpdateOperation(newInfo.getSrcPortState(),
                                                         newInfo.getDstPortState());
                    if (log.isTraceEnabled()) {
                        log.trace("Updated link {}", lt);
                    }
                    // Add to event history
                    evHistTopoLink(lt.getSrc(),
                                   lt.getDst(),
                                   lt.getSrcPort(),
                                   lt.getDstPort(),
                                   newInfo.getSrcPortState(), newInfo.getDstPortState(),
                                   getLinkType(lt, newInfo),
                                   EvAction.LINK_PORT_STATE_UPDATED,
                                   "LLDP Recvd");
                }
            }

            if (linkChanged) {
                // find out if the link was added or removed here.
                updates.add(new LDUpdate(lt.getSrc(), lt.getSrcPort(),
                                         lt.getDst(), lt.getDstPort(),
                                         getLinkType(lt, newInfo),
                                         updateOperation));
            }
        } finally {
            lock.writeLock().unlock();
        }

        return linkChanged;
    }

}