/**
  * Search all the remote FCEndpoints, remove those having the same RemotePortName that are in a
  * different fabric from a different device.
  *
  * @param remoteWwpn RemotePortName of new entry
  * @param fabricWwn FabricWwn of new entry
  * @param deviceId Device that found the updated connections
  * @return count of those removed
  * @throws IOException
  */
 private int removeConflictingEndpoints(String remoteWwpn, String fabricWwn, URI deviceId)
     throws IOException {
   int removedCount = 0;
   URIQueryResultList uriList = new URIQueryResultList();
   dbClient.queryByConstraint(
       AlternateIdConstraint.Factory.getFCEndpointRemotePortNameConstraint(remoteWwpn), uriList);
   for (URI uri : uriList) {
     FCEndpoint ep = dbClient.queryObject(FCEndpoint.class, uri);
     if (ep == null) {
       continue;
     }
     if (ep.getNetworkDevice().equals(deviceId)) {
       continue;
     }
     if (ep.getRemotePortName().equals(remoteWwpn) == false) {
       continue;
     }
     if (ep.getFabricWwn().equals(fabricWwn)) {
       continue;
     }
     dbClient.removeObject(ep);
     removedCount++;
   }
   return removedCount;
 }
 /**
  * Returns true if any fields of significance has been modified.
  *
  * @param existing
  * @param current
  * @return false if no updates, true if updated
  */
 private boolean checkUpdated(FCEndpoint existing, FCEndpoint current) {
   boolean updated = false;
   if (existing.getInactive() == true) {
     existing.setInactive(false);
     updated = true;
   }
   if (checkAttributeChanged(existing.getRemotePortName(), current.getRemotePortName())) {
     existing.setRemotePortName(current.getRemotePortName());
     updated = true;
   }
   if (checkAttributeChanged(existing.getRemoteNodeName(), current.getRemoteNodeName())) {
     existing.setRemoteNodeName(current.getRemoteNodeName());
     updated = true;
   }
   if (checkAttributeChanged(existing.getRemotePortAlias(), current.getRemotePortAlias())) {
     existing.setRemotePortAlias(
         current.getRemotePortAlias() == null ? "" : current.getRemotePortAlias());
     updated = true;
   }
   if (checkAttributeChanged(existing.getFabricId(), current.getFabricId())) {
     existing.setFabricId(current.getFabricId());
     updated = true;
   }
   if (checkAttributeChanged(existing.getFcid(), current.getFcid())) {
     existing.setFcid(current.getFcid());
     updated = true;
   }
   if (checkAttributeChanged(existing.getSwitchInterface(), current.getSwitchInterface())) {
     existing.setSwitchInterface(current.getSwitchInterface());
     updated = true;
   }
   if (checkAttributeChanged(existing.getSwitchName(), current.getSwitchName())) {
     existing.setSwitchName(current.getSwitchName());
     updated = true;
   }
   if (checkAttributeChanged(existing.getSwitchPortName(), current.getSwitchPortName())) {
     existing.setSwitchPortName(current.getSwitchPortName());
     updated = true;
   }
   if (checkAttributeChanged(existing.getFabricWwn(), current.getFabricWwn())) {
     existing.setFabricWwn(current.getFabricWwn());
     updated = true;
   }
   return updated;
 }
  /**
   * Reconciles the current set of a Device's endpoints with what is persisted. Updates the database
   * accordingly.
   *
   * @param dev
   * @param currentConnections
   * @throws IOException
   */
  private void reconcileFCEndpoints(NetworkSystem dev, List<FCEndpoint> currentConnections)
      throws IOException {
    // First, read all the existing connections from the device, and put them into a map
    // keyed by remote wwpn.
    URIQueryResultList uriList = new URIQueryResultList();
    dbClient.queryByConstraint(
        ContainmentConstraint.Factory.getNetworkSystemFCPortConnectionConstraint(dev.getId()),
        uriList);
    Map<String, FCEndpoint> existingEndpoints = new HashMap<String, FCEndpoint>();
    for (URI uriold : uriList) {
      FCEndpoint connection = dbClient.queryObject(FCEndpoint.class, uriold);
      if (connection != null) {
        existingEndpoints.put(connection.getRemotePortName().toUpperCase(), connection);
      }
    }
    // Now, scan the new endpoints, looking for added or updated records by
    // comparing them with the existing endpoints. Keep track of what was processed
    // so can do deletions on anything not seen in the currentConnections.
    List<FCEndpoint> updated = new ArrayList<FCEndpoint>();
    List<FCEndpoint> created = new ArrayList<FCEndpoint>();
    Set<String> processedWwpns = new HashSet<String>();
    int conflictingEndpoints = 0;
    for (FCEndpoint current : currentConnections) {
      String key = current.getRemotePortName().toUpperCase();
      processedWwpns.add(key);
      FCEndpoint existing = existingEndpoints.get(key);
      if (existing == null) {
        current.setNetworkDevice(dev.getId());
        current.setId(URIUtil.createId(FCEndpoint.class));
        created.add(current);
        conflictingEndpoints +=
            removeConflictingEndpoints(key, current.getFabricWwn(), dev.getId());
      } else {
        boolean modified = checkUpdated(existing, current);
        if (existing.getAwolCount() > 0) {
          modified = true;
          existing.setAwolCount(0);
          existing.setAwolTime(null);
        }
        if (modified) {
          updated.add(existing);
          conflictingEndpoints +=
              removeConflictingEndpoints(key, current.getFabricWwn(), dev.getId());
        }
      }
    }

    // Determine those to be deleted. Remove all the processed records from the existing set.
    // What was left were not seen this time.
    for (String key : processedWwpns) {
      existingEndpoints.remove(key);
    }
    // The remaining existingEndpoints can be processed for removal.
    // They are removed after a minimum number of samples and minimum amount of time has transpired.
    Integer removedCount = 0;
    for (FCEndpoint entry : existingEndpoints.values()) {
      int count = entry.getAwolCount();
      if (count == 0) {
        entry.setAwolTime(System.currentTimeMillis());
      }
      entry.setAwolCount(++count);
      if (count >= _minAwolSamples
          && (System.currentTimeMillis() - entry.getAwolTime()) > _minAwolTime) {
        removedCount++;
        dbClient.removeObject(entry);
      } else {
        updated.add(entry); // update counters
      }
    }
    // Persist created, modified.
    dbClient.createObject(created);
    dbClient.updateAndReindexObject(updated);
    _log.info(MessageFormat.format("{0} new connections persisted", created.size()).toString());
    _log.info(MessageFormat.format("{0} updated connections persisted", updated.size()).toString());
    _log.info(
        MessageFormat.format("{0} missing connections", existingEndpoints.values().size())
            .toString());
    _log.info(MessageFormat.format("{0} removed connections", removedCount.toString()));
    _log.info(MessageFormat.format("{0} conflicting connections (removed)", conflictingEndpoints));
  }