private static List<KerberosIdentityDescriptor> processIdentities(List<KerberosIdentityDescriptor> previousStackIdentities,
                                                                    List<KerberosIdentityDescriptor> newStackIdentities,
                                                                    List<KerberosIdentityDescriptor> userIdentities) {

    if ((userIdentities == null) || userIdentities.isEmpty() || ((previousStackIdentities == null) && (newStackIdentities == null))) {
      return userIdentities;
    }

    // Create maps to make processing easier....
    Map<String, KerberosIdentityDescriptor> previousStackIdentityMap = toMap(previousStackIdentities);
    Map<String, KerberosIdentityDescriptor> newStackIdentityMap = toMap(newStackIdentities);
    Map<String, KerberosIdentityDescriptor> userStackIdentityMap = toMap(userIdentities);

    Map<String, KerberosIdentityDescriptor> updatedIdentities = new TreeMap<>();

    if (previousStackIdentityMap == null) {
      previousStackIdentityMap = Collections.emptyMap();
    }

    if (newStackIdentityMap == null) {
      newStackIdentityMap = Collections.emptyMap();
    }

    // Find identities to modify or remove
    for (Map.Entry<String, KerberosIdentityDescriptor> entry : userStackIdentityMap.entrySet()) {
      String name = entry.getKey();
      KerberosIdentityDescriptor userValue = entry.getValue();

      if (userValue != null) {
        if (newStackIdentityMap.containsKey(name)) {
          // Modify the new stack identity value by changing on the principal value and/or keytab
          // file value since they are the only fields in this structure that should be changed
          // by a user. However, the new stack identity may have been converted to a pure reference
          // where the user changes will then be ignored.
          KerberosIdentityDescriptor newValue = newStackIdentityMap.get(name);
          KerberosIdentityDescriptor previousValue = previousStackIdentityMap.get(name);

          updatedIdentities.put(name, processIdentity(previousValue, newValue, userValue));

        } else if (previousStackIdentityMap.containsKey(name)) {
          LOG.debug("Removing identity named {} from user-specified Kerberos Descriptor", name);
          // Nothing to do here, just don't add it to the updated identity map...
        } else {
          LOG.debug("Leaving identity named {} in user-specified Kerberos Descriptor unchanged since it was user-defined.", name);
          updatedIdentities.put(name, userValue);
        }
      }
    }

    // Note: there is no need to add identity definitions that do not exist since they will get
    // added dynamically when merged with the stack default value.

    return new ArrayList<>(updatedIdentities.values());
  }
Processes the identity-level Kerberos descriptors to add, remove, or update data in the user-specified
Kerberos descriptor.

@param previousStackIdentities a map of {@link KerberosIdentityDescriptor}s from the previous stack version's Kerberos descriptor
@param newStackIdentities      a map of {@link KerberosIdentityDescriptor}s from the new stack version's Kerberos descriptor
@param userIdentities          a map of {@link KerberosIdentityDescriptor}s from the user-supplied Kerberos descriptor
@return a list of updated {@link KerberosIdentityDescriptor}s
ambari-server/src/main/java/org/apache/ambari/server/state/kerberos/KerberosDescriptorUpdateHelper.java