/**
  * Tells whether we are using StartTLS in the provided InitialLdapContext.
  *
  * @param ctx the context to analyze.
  * @return <CODE>true</CODE> if we are using StartTLS and <CODE>false</CODE> otherwise.
  */
 public static boolean isStartTLS(InitialLdapContext ctx) {
   boolean isStartTLS = false;
   try {
     isStartTLS = "true".equalsIgnoreCase((String) ctx.getEnvironment().get(STARTTLS_PROPERTY));
   } catch (NamingException ne) {
     // This is really strange.  Seems like a bug somewhere.
     LOG.log(Level.WARNING, "Naming exception getting environment of " + ctx, ne);
   }
   return isStartTLS;
 }
 /**
  * Returns the LDAP URL used in the provided InitialLdapContext.
  *
  * @param ctx the context to analyze.
  * @return the LDAP URL used in the provided InitialLdapContext.
  */
 public static String getLdapUrl(InitialLdapContext ctx) {
   String s = null;
   try {
     s = (String) ctx.getEnvironment().get(Context.PROVIDER_URL);
   } catch (NamingException ne) {
     // This is really strange.  Seems like a bug somewhere.
     LOG.log(Level.WARNING, "Naming exception getting environment of " + ctx, ne);
   }
   return s;
 }
 /**
  * Returns the password used in the provided InitialLdapContext.
  *
  * @param ctx the context to analyze.
  * @return the password used in the provided InitialLdapContext.
  */
 public static String getBindPassword(InitialLdapContext ctx) {
   String bindPwd = null;
   try {
     bindPwd = (String) ctx.getEnvironment().get(Context.SECURITY_CREDENTIALS);
   } catch (NamingException ne) {
     // This is really strange.  Seems like a bug somewhere.
     LOG.log(Level.WARNING, "Naming exception getting environment of " + ctx, ne);
   }
   return bindPwd;
 }
  /**
   * Clones the provided InitialLdapContext and returns a connection using the same parameters.
   *
   * @param ctx the connection to be cloned.
   * @param timeout the timeout to establish the connection in milliseconds. Use {@code 0} to
   *     express no timeout.
   * @param trustManager the trust manager to be used to connect.
   * @param keyManager the key manager to be used to connect.
   * @return the new InitialLdapContext connected to the server.
   * @throws NamingException if there was an error creating the new connection.
   */
  public static InitialLdapContext cloneInitialLdapContext(
      final InitialLdapContext ctx, int timeout, TrustManager trustManager, KeyManager keyManager)
      throws NamingException {
    Hashtable<?, ?> env = ctx.getEnvironment();
    Control[] ctls = ctx.getConnectControls();
    Control[] newCtls = null;
    if (ctls != null) {
      newCtls = new Control[ctls.length];
      System.arraycopy(ctls, 0, newCtls, 0, ctls.length);
    }
    /* Contains the DirContext and the Exception if any */
    final Object[] pair = new Object[] {null, null};
    final Hashtable<?, ?> fEnv = env;
    final TrustManager fTrustManager = trustManager;
    final KeyManager fKeyManager = keyManager;
    final Control[] fNewCtls = newCtls;

    Thread t =
        new Thread(
            new Runnable() {
              @Override
              public void run() {
                try {
                  if (isSSL(ctx) || isStartTLS(ctx)) {
                    TrustedSocketFactory.setCurrentThreadTrustManager(fTrustManager, fKeyManager);
                  }
                  pair[0] = new InitialLdapContext(fEnv, fNewCtls);

                } catch (NamingException ne) {
                  pair[1] = ne;

                } catch (RuntimeException re) {
                  pair[1] = re;
                }
              }
            });
    return getInitialLdapContext(t, pair, timeout);
  }