/**
   * Get the connection parameters for the IT Resource GROUPER_RECONCILIATION.
   * Then returns a connection based on those parameters.
   * @return database connection
   * @throws Exception
   */
  private Connection getGrouperConnection() throws Exception {
    Map<String, String> parameters = new HashMap<String, String>();

    if (!DEBUG) {
      tcITResourceInstanceOperationsIntf moITResourceUtility = (tcITResourceInstanceOperationsIntf) 
      super.getUtility("Thor.API.Operations.tcITResourceInstanceOperationsIntf");

      Map<String, String> resourceMap = new HashMap<String, String>();
      resourceMap.put("IT Resources.Name", "GROUPER_RECONCILIATION"); // We use the Grouper Recon's connection
      tcResultSet moResultSet = moITResourceUtility.findITResourceInstances(resourceMap);
      long resourceKey = moResultSet.getLongValue("IT Resources.Key");

      moResultSet = null;
      moResultSet = moITResourceUtility.getITResourceInstanceParameters(resourceKey);
      for (int i = 0; i < moResultSet.getRowCount(); i++) {
        moResultSet.goToRow(i);
        String name = moResultSet.getStringValue("IT Resources Type Parameter.Name");
        String value = moResultSet
        .getStringValue("IT Resources Type Parameter Value.Value");
        parameters.put(name, value);
      }
    } else {
      parameters.put("username","SUPPRESSED");
      parameters.put("password", SUPPRESSED");
      parameters.put("connectionProperties", "oracle.net.encryption_client=required,oracle.net.encryption_types_client=(RC4_256),oracle.net.crypto_checksum_client=required,oracle.net.crypto_checksum_types_client=(MD5)");
      parameters.put("url", "jdbc:oracle:thin:@SUPPRESSED:SUPPRESSED:SUPPRESSED");
      parameters.put("driver", "oracle.jdbc.driver.OracleDriver");
    }

    Class.forName((String) parameters.get("driver"));
    userName = (String) parameters.get("username");
    Properties props = new Properties();
    props.put("user", parameters.get("username"));
    props.put("password", parameters.get("password"));
    if (parameters.get("connectionProperties") != null && !parameters.get("connectionProperties").equals("")) {
      String[] additionalPropsArray = ((String) parameters.get("connectionProperties")).split(",");
      for (int i = 0; i < additionalPropsArray.length; i++) {
        String[] keyValue = additionalPropsArray[i].split("=");
        props.setProperty(keyValue[0], keyValue[1]);
      }
    }

    Connection conn = DriverManager.getConnection((String) parameters.get("url"), props);
    return conn;   
  }
  /**
   * Method to retrieve connection information from the OIM IT resource and use it to establish
   * connection to the comms directories, then mine the comms directories for mailUserStatus
   * attributes for all the users in the directory and return the results in a HashMap.
   *
   * <p>This HashMap is then later used to perform the comparison against OIM.
   *
   * <p>We need to retrieve duDukeID (for purposes of comparing against OIM, where that's the user's
   * Users.User ID value), and mailUserStatus (for purposes of setting the state of the user for the
   * comparison).
   *
   * <p>Return HashMap contains one hash for each user, indexed by duDukeID, with a Boolean value
   * that's FALSE if the user has no mailbox (the mailUserStatus value doesn't exist or is
   * "removed") and TRUE otherwise.
   */
  private HashMap<String, Boolean> getLDAPData() {

    AttributeData attributeData = AttributeData.getInstance();
    Attributes attributes = null;
    SearchResult ldapResult = null;
    LdapContext context = null;
    NamingEnumeration results = null;
    NamingEnumeration<SearchResult> iresults = null;
    Boolean hasmailbox = false;
    HashMap returnValue = new HashMap();
    tcITResourceInstanceOperationsIntf moITResourceUtility = null;
    tcResultSet moResultSet = null;
    long resourceKey;
    SearchResult resval = null;
    Attributes ra = null;
    String uniqueID = null;
    String status = null;

    // Get handle for retrieving configuration from OIM
    // For the IT Resource
    try {
      moITResourceUtility =
          (tcITResourceInstanceOperationsIntf)
              super.getUtility("Thor.API.Operations.tcITResourceInstanceOperationsIntf");
    } catch (Exception e) {
      throw new RuntimeException(
          connectorName
              + " Failed to retrieve LDAP configuration from OIM - check OIM IT Resource "
              + e.getMessage(),
          e);
    }
    // Get the parameters for the LDAP connection from OIM
    Map resourceMap = new HashMap();
    resourceMap.put("IT Resources.Name", "COMMS_RECONCILIATION");
    try {
      moResultSet = moITResourceUtility.findITResourceInstances(resourceMap);
      resourceKey = moResultSet.getLongValue("IT Resources.Key");
    } catch (Exception e) {
      throw new RuntimeException(
          connectorName
              + " Unable to get IT Resource from factory - check OIM IT Resource"
              + e.getMessage(),
          e);
    }
    moResultSet = null;
    try {
      moResultSet = moITResourceUtility.getITResourceInstanceParameters(resourceKey);
      for (int i = 0; i < moResultSet.getRowCount(); i++) {
        moResultSet.goToRow(i);
        String name = moResultSet.getStringValue("IT Resources Type Parameter.Name");
        String value = moResultSet.getStringValue("IT Resources Type Parameter Value.Value");
        parameters.put(name, value);
      }
    } catch (Exception e) {
      throw new RuntimeException(
          connectorName
              + "Unable to get attributes from OIM IT resource - check IT Resource in OIM "
              + e.getMessage(),
          e);
    }
    // Start by getting a connection to the LDAP
    Hashtable<String, String> environment = new Hashtable<String, String>();
    environment.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
    environment.put(Context.PROVIDER_URL, (String) parameters.get("ldapURL"));
    environment.put(Context.SECURITY_AUTHENTICATION, "simple");
    environment.put(Context.SECURITY_PRINCIPAL, (String) parameters.get("ldapDN"));
    environment.put(Context.SECURITY_CREDENTIALS, (String) parameters.get("ldapPW"));
    environment.put(Context.SECURITY_PROTOCOL, "ssl");

    try {
      context = new InitialLdapContext(environment, null);

      // If connection fails, retry one time before failing

      if (context == null) {
        // Retry connection one more time after 20 seconds
        Thread.sleep(20000);
        context = new InitialLdapContext(environment, null);
        // and fail if the context is still null -- return null
        // hash and log an error to generate email notification
        if (context == null) {
          logger.error(
              connectorName
                  + " Failed to make LDAP connection to commsdirs during comms reconciliation - reconciliation cannot continue");
          return (null);
        }
      }
    } catch (javax.naming.NamingException e) {
      // in this case, we excepted on something in LDAP
      logger.error(
          connectorName
              + " Caught exception from JNDI during LDAP connection -- returning NULL "
              + e.getMessage());
      e.printStackTrace();
      return (null);
    } catch (java.lang.InterruptedException e) {
      logger.warn(
          connectorName
              + " Interrupt caught before timeout for retry of LDAP connection -- returning NULL");
      return (null);
    }

    // At this point, we should have a connection in the context

    // List of attributes to be retrieved from LDAP
    String[] attrs = {"duDukeID", "mailUserStatus"};

    // SearchControls to build the query
    SearchControls cons =
        new SearchControls(SearchControls.SUBTREE_SCOPE, 0, 0, attrs, false, false);

    // And execute the query in a try to catch throwables
    try {
      iresults = context.newInstance(null).search("o=Comms,dc=duke,dc=edu", "(duDukeID=*)", cons);
    } catch (NamingException e) {
      throw new RuntimeException(
          connectorName + " Failed LDAP search on bad connection: " + e.getMessage(), e);
    }

    while (iresults.hasMoreElements()) {
      try {
        resval = iresults.next();
      } catch (javax.naming.NamingException e) {
        // Somehow, we had more elements, but when we went to get the next one, it wasn't there
        // Assume this is a sign that the LDAP response was corrupt
        logger.error(connectorName + " Corrupt LDAP search results -- throwing exception ");
        throw new RuntimeException(
            connectorName
                + " Failed to find next result in LDAP search result set, even though hasMore was true "
                + e.getMessage(),
            e);
      }
      ra = resval.getAttributes();
      try {
        uniqueID = (String) ra.get("duDukeID").get();
      } catch (javax.naming.NamingException e) {
        // this one is fatal
        logger.error(
            connectorName + " DukeID was missing from user found by Unique ID -- very strange ");
        throw new RuntimeException(
            connectorName + " User found by unique ID does not have a unique ID " + e.getMessage(),
            e);
      }
      try {
        if (ra.get("mailUserStatus") != null) {
          status = (String) ra.get("mailUserStatus").get();
          if (status != null && !status.equals("") && !status.equals("removed")) {
            hasmailbox = true;
          } else {
            hasmailbox = false;
          }
        } else {
          hasmailbox = false;
        }
      } catch (Exception e) {
        // In this case, we somehow failed to figure out whether the mailbox exists or not
        // Since existence has less permanent effect on the world than non,
        // we treat this as an existent case but log the anomaly
        logger.warn(
            connectorName
                + " Exception reading mailUserStatus for user "
                + uniqueID
                + " so returning with hasmailbox = true for failsafe");
        hasmailbox = true;
      }
      returnValue.put(uniqueID, hasmailbox);
    }

    return (returnValue);
  }