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

	protected boolean addOrUpdateLink(Link lt, LinkInfo newInfo) {
		boolean linkChanged = false;

		lock.writeLock().lock();
		try {
			/*
			 * Put the new info only if new. We want a single LinkInfo
			 * to exist per Link. This will allow us to track latencies
			 * without having to conduct a deep, potentially expensive
			 * copy each time a link is updated.
			 */
			LinkInfo existingInfo = null;
			if (links.get(lt) == null) {
				links.put(lt, newInfo); /* Only put if doesn't exist or null value */
			} else {
				existingInfo = links.get(lt);
			}

			/* Update existing LinkInfo with most recent time stamp */
			if (existingInfo != null && existingInfo.getFirstSeenTime().before(newInfo.getFirstSeenTime())) {
				existingInfo.setFirstSeenTime(newInfo.getFirstSeenTime());
			}

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

			UpdateOperation updateOperation = null;
			linkChanged = false;

			if (existingInfo == null) {
				addLink(lt, newInfo);
				updateOperation = UpdateOperation.LINK_UPDATED;
				linkChanged = true;

				// Log direct links only. Multi-hop links may be numerous
				// Add all to event history
				LinkType linkType = getLinkType(lt, newInfo);
				if (linkType == ILinkDiscovery.LinkType.DIRECT_LINK) {
					log.debug("Inter-switch link detected: {}", lt);
					eventCategory.newEventNoFlush(new DirectLinkEvent(lt.getSrc(),
							lt.getSrcPort(), lt.getDst(), lt.getDstPort(), "direct-link-added::rcvd LLDP"));
				}
				notifier.postNotification("Link added: " + lt.toString());
			} else {
				linkChanged = updateLink(lt, existingInfo, newInfo);
				if (linkChanged) {
					updateOperation = UpdateOperation.LINK_UPDATED;
					LinkType linkType = getLinkType(lt, newInfo);
					if (linkType == ILinkDiscovery.LinkType.DIRECT_LINK) {
						log.debug("Inter-switch link updated: {}", lt);
						eventCategory.newEventNoFlush(new DirectLinkEvent(lt.getSrc(),
								lt.getSrcPort(), lt.getDst(), lt.getDstPort(),
								"link-port-state-updated::rcvd LLDP"));
					}
					notifier.postNotification("Link updated: " + lt.toString());
				}
			}

			if (linkChanged) {
				// find out if the link was added or removed here.
				updates.add(new LDUpdate(lt.getSrc(), lt.getSrcPort(),
						lt.getDst(), lt.getDstPort(),
						lt.getLatency(),
						getLinkType(lt, newInfo),
						updateOperation));
				/* Update link structure (FIXME shouldn't have to do this, since it should be the same object) */
				Iterator<Entry<Link, LinkInfo>> it = links.entrySet().iterator();
				while (it.hasNext()) {
					Entry<Link, LinkInfo> entry = it.next();
					if (entry.getKey().equals(lt)) {
						entry.getKey().setLatency(lt.getLatency());
						break;
					}
				}
			}
			
			// 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);
			
		} finally {
			lock.writeLock().unlock();
		}

		return linkChanged;
	}

}