/**
   * 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);
  }