mirror of https://github.com/OpenIdentityPlatform/OpenDJ.git

Matthew Swift
25.33.2012 263d085885df024dca9250cc03c807912b0a7662
opendj3/opendj-ldap-sdk/src/test/java/org/forgerock/opendj/ldap/ConnectionFactoryTestCase.java
@@ -6,17 +6,16 @@
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * You can obtain a copy of the license at
 * trunk/opendj3/legal-notices/CDDLv1_0.txt
 * You can obtain a copy of the license at legal-notices/CDDLv1_0.txt
 * or http://forgerock.org/license/CDDLv1.0.html.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at
 * trunk/opendj3/legal-notices/CDDLv1_0.txt.  If applicable,
 * add the following below this CDDL HEADER, with the fields enclosed
 * by brackets "[]" replaced with your own identifying information:
 * file and include the License file at legal-notices/CDDLv1_0.txt.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information:
 *      Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
@@ -28,14 +27,13 @@
package org.forgerock.opendj.ldap;
import static org.fest.assertions.Assertions.assertThat;
import static org.mockito.Matchers.any;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import static org.testng.Assert.assertNotNull;
import static org.testng.Assert.assertTrue;
import static org.mockito.Matchers.*;
import static org.mockito.Mockito.*;
import java.net.InetSocketAddress;
import java.util.Arrays;
@@ -62,430 +60,375 @@
import com.forgerock.opendj.util.CompletedFutureResult;
import com.forgerock.opendj.util.StaticUtils;
/**
 * Tests the {@code ConnectionFactory} classes.
 */
@SuppressWarnings("javadoc")
public class ConnectionFactoryTestCase extends SdkTestCase
{
  class MyResultHandler implements ResultHandler<Connection>
  {
    // latch.
    private final CountDownLatch latch;
    // invalid flag.
    private volatile ErrorResultException error;
public class ConnectionFactoryTestCase extends SdkTestCase {
    class MyResultHandler implements ResultHandler<Connection> {
        // latch.
        private final CountDownLatch latch;
        // invalid flag.
        private volatile ErrorResultException error;
        MyResultHandler(final CountDownLatch latch) {
            this.latch = latch;
        }
        public void handleErrorResult(final ErrorResultException error) {
            // came here.
            this.error = error;
            latch.countDown();
        }
    MyResultHandler(final CountDownLatch latch)
    {
      this.latch = latch;
        public void handleResult(final Connection con) {
            //
            latch.countDown();
        }
    }
    public void handleErrorResult(final ErrorResultException error)
    {
      // came here.
      this.error = error;
      latch.countDown();
    /**
     * Ensures that the LDAP Server is running.
     *
     * @throws Exception
     *             If an unexpected problem occurs.
     */
    @BeforeClass()
    public void startServer() throws Exception {
        TestCaseUtils.startServer();
    }
    public void handleResult(final Connection con)
    {
      //
      latch.countDown();
    }
  }
  /**
   * Ensures that the LDAP Server is running.
   *
   * @throws Exception
   *           If an unexpected problem occurs.
   */
  @BeforeClass()
  public void startServer() throws Exception
  {
    TestCaseUtils.startServer();
  }
  /**
   * Disables logging before the tests.
   */
  @BeforeClass()
  public void disableLogging()
  {
    StaticUtils.DEBUG_LOG.setLevel(Level.SEVERE);
  }
  /**
   * Re-enable logging after the tests.
   */
  @AfterClass()
  public void enableLogging()
  {
    StaticUtils.DEBUG_LOG.setLevel(Level.INFO);
  }
  @DataProvider(name = "connectionFactories")
  public Object[][] getConnectionFactories() throws Exception
  {
    Object[][] factories = new Object[21][1];
    // HeartBeatConnectionFactory
    // Use custom search request.
    SearchRequest request = Requests.newSearchRequest(
        "uid=user.0,ou=people,o=test", SearchScope.BASE_OBJECT,
        "objectclass=*", "cn");
    factories[0][0] = new HeartBeatConnectionFactory(new LDAPConnectionFactory(
        "localhost", TestCaseUtils.getLdapPort()), 1000, TimeUnit.MILLISECONDS,
        request);
    // InternalConnectionFactory
    factories[1][0] = Connections.newInternalConnectionFactory(
        LDAPServer.getInstance(), null);
    // AuthenticatedConnectionFactory
    factories[2][0] = new AuthenticatedConnectionFactory(
        new LDAPConnectionFactory("localhost", TestCaseUtils.getLdapPort()),
        Requests.newSimpleBindRequest("", new char[0]));
    // AuthenticatedConnectionFactory with multi-stage SASL
    factories[3][0] = new AuthenticatedConnectionFactory(
        new LDAPConnectionFactory("localhost", TestCaseUtils.getLdapPort()),
        Requests.newCRAMMD5SASLBindRequest("id:user", "password".toCharArray()));
    // LDAPConnectionFactory with default options
    factories[4][0] = new LDAPConnectionFactory("localhost",
        TestCaseUtils.getLdapPort());
    // LDAPConnectionFactory with startTLS
    SSLContext sslContext = new SSLContextBuilder().setTrustManager(
        TrustManagers.trustAll()).getSSLContext();
    LDAPOptions options = new LDAPOptions()
        .setSSLContext(sslContext)
        .setUseStartTLS(true)
        .addEnabledCipherSuite(
            new String[] { "SSL_DH_anon_EXPORT_WITH_DES40_CBC_SHA",
                "SSL_DH_anon_EXPORT_WITH_RC4_40_MD5",
                "SSL_DH_anon_WITH_3DES_EDE_CBC_SHA",
                "SSL_DH_anon_WITH_DES_CBC_SHA", "SSL_DH_anon_WITH_RC4_128_MD5",
                "TLS_DH_anon_WITH_AES_128_CBC_SHA",
                "TLS_DH_anon_WITH_AES_256_CBC_SHA" });
    factories[5][0] = new LDAPConnectionFactory("localhost",
        TestCaseUtils.getLdapPort(), options);
    // startTLS + SASL confidentiality
    // Use IP address here so that DIGEST-MD5 host verification works if local
    // host name is not localhost (e.g. on some machines it might be
    // localhost.localdomain).
    factories[6][0] = new AuthenticatedConnectionFactory(
        new LDAPConnectionFactory(new InetSocketAddress("127.0.0.1",
            TestCaseUtils.getLdapPort()), options), Requests
            .newDigestMD5SASLBindRequest("id:user", "password".toCharArray())
            .addQOP(DigestMD5SASLBindRequest.QOP_AUTH_CONF)
            .setCipher(DigestMD5SASLBindRequest.CIPHER_LOW));
    // Connection pool and load balancing tests.
    ConnectionFactory offlineServer1 = Connections.newNamedConnectionFactory(
        new LDAPConnectionFactory("localhost", TestCaseUtils.findFreePort()),
        "offline1");
    ConnectionFactory offlineServer2 = Connections.newNamedConnectionFactory(
        new LDAPConnectionFactory("localhost", TestCaseUtils.findFreePort()),
        "offline2");
    ConnectionFactory onlineServer = Connections.newNamedConnectionFactory(
        new LDAPConnectionFactory("localhost", TestCaseUtils.getLdapPort()),
        "online");
    // Connection pools.
    factories[7][0] = Connections.newFixedConnectionPool(onlineServer, 10);
    // Round robin.
    factories[8][0] = Connections
        .newLoadBalancer(new RoundRobinLoadBalancingAlgorithm(Arrays.asList(
            onlineServer, offlineServer1)));
    factories[9][0] = factories[8][0];
    factories[10][0] = factories[8][0];
    factories[11][0] = Connections
        .newLoadBalancer(new RoundRobinLoadBalancingAlgorithm(Arrays.asList(
            offlineServer1, onlineServer)));
    factories[12][0] = Connections
        .newLoadBalancer(new RoundRobinLoadBalancingAlgorithm(Arrays.asList(
            offlineServer1, offlineServer2, onlineServer)));
    factories[13][0] = Connections
        .newLoadBalancer(new RoundRobinLoadBalancingAlgorithm(Arrays
            .<ConnectionFactory> asList(
                Connections.newFixedConnectionPool(offlineServer1, 10),
                Connections.newFixedConnectionPool(onlineServer, 10))));
    // Fail-over.
    factories[14][0] = Connections
        .newLoadBalancer(new FailoverLoadBalancingAlgorithm(Arrays.asList(
            onlineServer, offlineServer1)));
    factories[15][0] = factories[14][0];
    factories[16][0] = factories[14][0];
    factories[17][0] = Connections
        .newLoadBalancer(new FailoverLoadBalancingAlgorithm(Arrays.asList(
            offlineServer1, onlineServer)));
    factories[18][0] = Connections
        .newLoadBalancer(new FailoverLoadBalancingAlgorithm(Arrays.asList(
            offlineServer1, offlineServer2, onlineServer)));
    factories[19][0] = Connections
        .newLoadBalancer(new FailoverLoadBalancingAlgorithm(Arrays
            .<ConnectionFactory> asList(
                Connections.newFixedConnectionPool(offlineServer1, 10),
                Connections.newFixedConnectionPool(onlineServer, 10))));
    factories[20][0] = Connections.newFixedConnectionPool(onlineServer, 10);
    return factories;
  }
  /**
   * Tests the async connection in the blocking mode. This is not fully async as
   * it blocks on the future.
   *
   * @throws Exception
   */
  @Test(dataProvider = "connectionFactories")
  public void testBlockingFutureNoHandler(ConnectionFactory factory)
      throws Exception
  {
    final FutureResult<Connection> future = factory.getConnectionAsync(null);
    final Connection con = future.get();
    // quickly check if it is a valid connection.
    // Don't use a result handler.
    assertNotNull(con.readEntryAsync(DN.rootDN(), null, null).get());
    con.close();
  }
  /**
   * Tests the non-blocking fully async connection using a handler.
   *
   * @throws Exception
   */
  @Test(dataProvider = "connectionFactories")
  public void testNonBlockingFutureWithHandler(ConnectionFactory factory)
      throws Exception
  {
    // Use the handler to get the result asynchronously.
    final CountDownLatch latch = new CountDownLatch(1);
    final MyResultHandler handler = new MyResultHandler(latch);
    factory.getConnectionAsync(handler);
    // Since we don't have anything to do, we would rather
    // be notified by the latch when the other thread calls our handler.
    latch.await(); // should do a timed wait rather?
    if (handler.error != null)
    {
      throw handler.error;
    }
  }
  /**
   * Tests the synchronous connection.
   *
   * @throws Exception
   */
  @Test(dataProvider = "connectionFactories")
  public void testSynchronousConnection(ConnectionFactory factory)
      throws Exception
  {
    final Connection con = factory.getConnection();
    assertNotNull(con);
    // quickly check if it is a valid connection.
    assertTrue(con.readEntry("").getName().isRootDN());
    con.close();
  }
  /**
   * Verifies that LDAP connections take into consideration changes to the
   * default schema post creation. See OPENDJ-159.
   * <p>
   * This test is disabled because it cannot be run in parallel with rest of the
   * test suite, because it modifies the global default schema.
   *
   * @throws Exception
   *           If an unexpected error occurred.
   */
  @Test(enabled = false)
  public void testSchemaUsage() throws Exception
  {
    // Create a connection factory: this should always use the default schema,
    // even if it is updated.
    final ConnectionFactory factory = new LDAPConnectionFactory("localhost",
        TestCaseUtils.getLdapPort());
    final Schema defaultSchema = Schema.getDefaultSchema();
    final Connection connection = factory.getConnection();
    try
    {
      // Simulate a client which reads the schema from the server and then
      // sets it as the application default. We then want subsequent
      // operations to use this schema, not the original default.
      final SchemaBuilder builder = new SchemaBuilder(Schema.getCoreSchema());
      builder.addAttributeType(
          "( 0.9.2342.19200300.100.1.3 NAME 'mail' EQUALITY "
              + "caseIgnoreIA5Match SUBSTR caseIgnoreIA5SubstringsMatch "
              + "SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{256} )", false);
      final Schema testSchema = builder.toSchema().asNonStrictSchema();
      assertThat(testSchema.getWarnings()).isEmpty();
      Schema.setDefaultSchema(testSchema);
      // Read an entry containing the mail attribute.
      final SearchResultEntry e = connection
          .readEntry("uid=user.0,ou=people,o=test");
      assertThat(e.getAttribute("mail")).isNotNull();
      assertThat(
          e.getAttribute(AttributeDescription.valueOf("mail", testSchema)))
          .isNotNull();
    }
    finally
    {
      // Restore original schema.
      Schema.setDefaultSchema(defaultSchema);
      // Close connection.
      connection.close();
    }
  }
  /**
   * Tests connection pool closure.
   *
   * @throws Exception
   *           If an unexpected exception occurred.
   */
  @SuppressWarnings("unchecked")
  @Test
  public void testConnectionPoolClose() throws Exception
  {
    // We'll use a pool of 4 connections.
    final int SIZE = 4;
    // Count number of real connections which are open.
    final AtomicInteger realConnectionCount = new AtomicInteger();
    final boolean[] realConnectionIsClosed = new boolean[SIZE];
    Arrays.fill(realConnectionIsClosed, true);
    // Mock underlying connection factory which always succeeds.
    final ConnectionFactory mockFactory = mock(ConnectionFactory.class);
    when(mockFactory.getConnectionAsync(any(ResultHandler.class))).thenAnswer(
        new Answer<FutureResult<Connection>>()
        {
          public FutureResult<Connection> answer(InvocationOnMock invocation)
              throws Throwable
          {
            // Update state.
            final int connectionID = realConnectionCount.getAndIncrement();
            realConnectionIsClosed[connectionID] = false;
            // Mock connection decrements counter on close.
            Connection mockConnection = mock(Connection.class);
            doAnswer(new Answer<Void>()
            {
              public Void answer(InvocationOnMock invocation) throws Throwable
              {
                realConnectionCount.decrementAndGet();
                realConnectionIsClosed[connectionID] = true;
                return null;
              }
            }).when(mockConnection).close();
            when(mockConnection.isValid()).thenReturn(true);
            when(mockConnection.toString()).thenReturn(
                "Mock connection " + connectionID);
            // Execute handler and return future.
            ResultHandler<? super Connection> handler = (ResultHandler<? super Connection>) invocation
                .getArguments()[0];
            if (handler != null)
            {
              handler.handleResult(mockConnection);
            }
            return new CompletedFutureResult<Connection>(mockConnection);
          }
        });
    ConnectionPool pool = Connections.newFixedConnectionPool(mockFactory, SIZE);
    Connection[] pooledConnections = new Connection[SIZE];
    for (int i = 0; i < SIZE; i++)
    {
      pooledConnections[i] = pool.getConnection();
    /**
     * Disables logging before the tests.
     */
    @BeforeClass()
    public void disableLogging() {
        StaticUtils.DEBUG_LOG.setLevel(Level.SEVERE);
    }
    // Pool is fully utilized.
    assertThat(realConnectionCount.get()).isEqualTo(SIZE);
    assertThat(pooledConnections[0].isClosed()).isFalse();
    assertThat(pooledConnections[1].isClosed()).isFalse();
    assertThat(pooledConnections[2].isClosed()).isFalse();
    assertThat(pooledConnections[3].isClosed()).isFalse();
    assertThat(realConnectionIsClosed[0]).isFalse();
    assertThat(realConnectionIsClosed[1]).isFalse();
    assertThat(realConnectionIsClosed[2]).isFalse();
    assertThat(realConnectionIsClosed[3]).isFalse();
    /**
     * Re-enable logging after the tests.
     */
    @AfterClass()
    public void enableLogging() {
        StaticUtils.DEBUG_LOG.setLevel(Level.INFO);
    }
    // Release two connections.
    pooledConnections[0].close();
    pooledConnections[1].close();
    assertThat(realConnectionCount.get()).isEqualTo(4);
    assertThat(pooledConnections[0].isClosed()).isTrue();
    assertThat(pooledConnections[1].isClosed()).isTrue();
    assertThat(pooledConnections[2].isClosed()).isFalse();
    assertThat(pooledConnections[3].isClosed()).isFalse();
    assertThat(realConnectionIsClosed[0]).isFalse();
    assertThat(realConnectionIsClosed[1]).isFalse();
    assertThat(realConnectionIsClosed[2]).isFalse();
    assertThat(realConnectionIsClosed[3]).isFalse();
    @DataProvider(name = "connectionFactories")
    public Object[][] getConnectionFactories() throws Exception {
        Object[][] factories = new Object[21][1];
    // Close the pool closing the two connections immediately.
    pool.close();
    assertThat(realConnectionCount.get()).isEqualTo(2);
    assertThat(pooledConnections[0].isClosed()).isTrue();
    assertThat(pooledConnections[1].isClosed()).isTrue();
    assertThat(pooledConnections[2].isClosed()).isFalse();
    assertThat(pooledConnections[3].isClosed()).isFalse();
    assertThat(realConnectionIsClosed[0]).isTrue();
    assertThat(realConnectionIsClosed[1]).isTrue();
    assertThat(realConnectionIsClosed[2]).isFalse();
    assertThat(realConnectionIsClosed[3]).isFalse();
        // HeartBeatConnectionFactory
        // Use custom search request.
        SearchRequest request =
                Requests.newSearchRequest("uid=user.0,ou=people,o=test", SearchScope.BASE_OBJECT,
                        "objectclass=*", "cn");
    // Release two remaining connections and check that they get closed.
    pooledConnections[2].close();
    pooledConnections[3].close();
    assertThat(realConnectionCount.get()).isEqualTo(0);
    assertThat(pooledConnections[0].isClosed()).isTrue();
    assertThat(pooledConnections[1].isClosed()).isTrue();
    assertThat(pooledConnections[2].isClosed()).isTrue();
    assertThat(pooledConnections[3].isClosed()).isTrue();
    assertThat(realConnectionIsClosed[0]).isTrue();
    assertThat(realConnectionIsClosed[1]).isTrue();
    assertThat(realConnectionIsClosed[2]).isTrue();
    assertThat(realConnectionIsClosed[3]).isTrue();
  }
        factories[0][0] =
                new HeartBeatConnectionFactory(new LDAPConnectionFactory("localhost", TestCaseUtils
                        .getLdapPort()), 1000, TimeUnit.MILLISECONDS, request);
        // InternalConnectionFactory
        factories[1][0] = Connections.newInternalConnectionFactory(LDAPServer.getInstance(), null);
        // AuthenticatedConnectionFactory
        factories[2][0] =
                new AuthenticatedConnectionFactory(new LDAPConnectionFactory("localhost",
                        TestCaseUtils.getLdapPort()), Requests
                        .newSimpleBindRequest("", new char[0]));
        // AuthenticatedConnectionFactory with multi-stage SASL
        factories[3][0] =
                new AuthenticatedConnectionFactory(new LDAPConnectionFactory("localhost",
                        TestCaseUtils.getLdapPort()), Requests.newCRAMMD5SASLBindRequest("id:user",
                            "password".toCharArray()));
        // LDAPConnectionFactory with default options
        factories[4][0] = new LDAPConnectionFactory("localhost", TestCaseUtils.getLdapPort());
        // LDAPConnectionFactory with startTLS
        SSLContext sslContext =
                new SSLContextBuilder().setTrustManager(TrustManagers.trustAll()).getSSLContext();
        LDAPOptions options =
                new LDAPOptions().setSSLContext(sslContext).setUseStartTLS(true)
                        .addEnabledCipherSuite(
                                new String[] { "SSL_DH_anon_EXPORT_WITH_DES40_CBC_SHA",
                                    "SSL_DH_anon_EXPORT_WITH_RC4_40_MD5",
                                    "SSL_DH_anon_WITH_3DES_EDE_CBC_SHA",
                                    "SSL_DH_anon_WITH_DES_CBC_SHA", "SSL_DH_anon_WITH_RC4_128_MD5",
                                    "TLS_DH_anon_WITH_AES_128_CBC_SHA",
                                    "TLS_DH_anon_WITH_AES_256_CBC_SHA" });
        factories[5][0] =
                new LDAPConnectionFactory("localhost", TestCaseUtils.getLdapPort(), options);
        // startTLS + SASL confidentiality
        // Use IP address here so that DIGEST-MD5 host verification works if
        // local
        // host name is not localhost (e.g. on some machines it might be
        // localhost.localdomain).
        factories[6][0] =
                new AuthenticatedConnectionFactory(new LDAPConnectionFactory(new InetSocketAddress(
                        "127.0.0.1", TestCaseUtils.getLdapPort()), options), Requests
                        .newDigestMD5SASLBindRequest("id:user", "password".toCharArray()).addQOP(
                                DigestMD5SASLBindRequest.QOP_AUTH_CONF).setCipher(
                                DigestMD5SASLBindRequest.CIPHER_LOW));
        // Connection pool and load balancing tests.
        ConnectionFactory offlineServer1 =
                Connections.newNamedConnectionFactory(new LDAPConnectionFactory("localhost",
                        TestCaseUtils.findFreePort()), "offline1");
        ConnectionFactory offlineServer2 =
                Connections.newNamedConnectionFactory(new LDAPConnectionFactory("localhost",
                        TestCaseUtils.findFreePort()), "offline2");
        ConnectionFactory onlineServer =
                Connections.newNamedConnectionFactory(new LDAPConnectionFactory("localhost",
                        TestCaseUtils.getLdapPort()), "online");
        // Connection pools.
        factories[7][0] = Connections.newFixedConnectionPool(onlineServer, 10);
        // Round robin.
        factories[8][0] =
                Connections.newLoadBalancer(new RoundRobinLoadBalancingAlgorithm(Arrays.asList(
                        onlineServer, offlineServer1)));
        factories[9][0] = factories[8][0];
        factories[10][0] = factories[8][0];
        factories[11][0] =
                Connections.newLoadBalancer(new RoundRobinLoadBalancingAlgorithm(Arrays.asList(
                        offlineServer1, onlineServer)));
        factories[12][0] =
                Connections.newLoadBalancer(new RoundRobinLoadBalancingAlgorithm(Arrays.asList(
                        offlineServer1, offlineServer2, onlineServer)));
        factories[13][0] =
                Connections.newLoadBalancer(new RoundRobinLoadBalancingAlgorithm(Arrays
                        .<ConnectionFactory> asList(Connections.newFixedConnectionPool(
                                offlineServer1, 10), Connections.newFixedConnectionPool(
                                onlineServer, 10))));
        // Fail-over.
        factories[14][0] =
                Connections.newLoadBalancer(new FailoverLoadBalancingAlgorithm(Arrays.asList(
                        onlineServer, offlineServer1)));
        factories[15][0] = factories[14][0];
        factories[16][0] = factories[14][0];
        factories[17][0] =
                Connections.newLoadBalancer(new FailoverLoadBalancingAlgorithm(Arrays.asList(
                        offlineServer1, onlineServer)));
        factories[18][0] =
                Connections.newLoadBalancer(new FailoverLoadBalancingAlgorithm(Arrays.asList(
                        offlineServer1, offlineServer2, onlineServer)));
        factories[19][0] =
                Connections.newLoadBalancer(new FailoverLoadBalancingAlgorithm(Arrays
                        .<ConnectionFactory> asList(Connections.newFixedConnectionPool(
                                offlineServer1, 10), Connections.newFixedConnectionPool(
                                onlineServer, 10))));
        factories[20][0] = Connections.newFixedConnectionPool(onlineServer, 10);
        return factories;
    }
    /**
     * Tests the async connection in the blocking mode. This is not fully async
     * as it blocks on the future.
     *
     * @throws Exception
     */
    @Test(dataProvider = "connectionFactories")
    public void testBlockingFutureNoHandler(ConnectionFactory factory) throws Exception {
        final FutureResult<Connection> future = factory.getConnectionAsync(null);
        final Connection con = future.get();
        // quickly check if it is a valid connection.
        // Don't use a result handler.
        assertNotNull(con.readEntryAsync(DN.rootDN(), null, null).get());
        con.close();
    }
    /**
     * Tests the non-blocking fully async connection using a handler.
     *
     * @throws Exception
     */
    @Test(dataProvider = "connectionFactories")
    public void testNonBlockingFutureWithHandler(ConnectionFactory factory) throws Exception {
        // Use the handler to get the result asynchronously.
        final CountDownLatch latch = new CountDownLatch(1);
        final MyResultHandler handler = new MyResultHandler(latch);
        factory.getConnectionAsync(handler);
        // Since we don't have anything to do, we would rather
        // be notified by the latch when the other thread calls our handler.
        latch.await(); // should do a timed wait rather?
        if (handler.error != null) {
            throw handler.error;
        }
    }
    /**
     * Tests the synchronous connection.
     *
     * @throws Exception
     */
    @Test(dataProvider = "connectionFactories")
    public void testSynchronousConnection(ConnectionFactory factory) throws Exception {
        final Connection con = factory.getConnection();
        assertNotNull(con);
        // quickly check if it is a valid connection.
        assertTrue(con.readEntry("").getName().isRootDN());
        con.close();
    }
    /**
     * Verifies that LDAP connections take into consideration changes to the
     * default schema post creation. See OPENDJ-159.
     * <p>
     * This test is disabled because it cannot be run in parallel with rest of
     * the test suite, because it modifies the global default schema.
     *
     * @throws Exception
     *             If an unexpected error occurred.
     */
    @Test(enabled = false)
    public void testSchemaUsage() throws Exception {
        // Create a connection factory: this should always use the default
        // schema,
        // even if it is updated.
        final ConnectionFactory factory =
                new LDAPConnectionFactory("localhost", TestCaseUtils.getLdapPort());
        final Schema defaultSchema = Schema.getDefaultSchema();
        final Connection connection = factory.getConnection();
        try {
            // Simulate a client which reads the schema from the server and then
            // sets it as the application default. We then want subsequent
            // operations to use this schema, not the original default.
            final SchemaBuilder builder = new SchemaBuilder(Schema.getCoreSchema());
            builder.addAttributeType("( 0.9.2342.19200300.100.1.3 NAME 'mail' EQUALITY "
                    + "caseIgnoreIA5Match SUBSTR caseIgnoreIA5SubstringsMatch "
                    + "SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{256} )", false);
            final Schema testSchema = builder.toSchema().asNonStrictSchema();
            assertThat(testSchema.getWarnings()).isEmpty();
            Schema.setDefaultSchema(testSchema);
            // Read an entry containing the mail attribute.
            final SearchResultEntry e = connection.readEntry("uid=user.0,ou=people,o=test");
            assertThat(e.getAttribute("mail")).isNotNull();
            assertThat(e.getAttribute(AttributeDescription.valueOf("mail", testSchema)))
                    .isNotNull();
        } finally {
            // Restore original schema.
            Schema.setDefaultSchema(defaultSchema);
            // Close connection.
            connection.close();
        }
    }
    /**
     * Tests connection pool closure.
     *
     * @throws Exception
     *             If an unexpected exception occurred.
     */
    @SuppressWarnings("unchecked")
    @Test
    public void testConnectionPoolClose() throws Exception {
        // We'll use a pool of 4 connections.
        final int size = 4;
        // Count number of real connections which are open.
        final AtomicInteger realConnectionCount = new AtomicInteger();
        final boolean[] realConnectionIsClosed = new boolean[size];
        Arrays.fill(realConnectionIsClosed, true);
        // Mock underlying connection factory which always succeeds.
        final ConnectionFactory mockFactory = mock(ConnectionFactory.class);
        when(mockFactory.getConnectionAsync(any(ResultHandler.class))).thenAnswer(
                new Answer<FutureResult<Connection>>() {
                    public FutureResult<Connection> answer(InvocationOnMock invocation)
                            throws Throwable {
                        // Update state.
                        final int connectionID = realConnectionCount.getAndIncrement();
                        realConnectionIsClosed[connectionID] = false;
                        // Mock connection decrements counter on close.
                        Connection mockConnection = mock(Connection.class);
                        doAnswer(new Answer<Void>() {
                            public Void answer(InvocationOnMock invocation) throws Throwable {
                                realConnectionCount.decrementAndGet();
                                realConnectionIsClosed[connectionID] = true;
                                return null;
                            }
                        }).when(mockConnection).close();
                        when(mockConnection.isValid()).thenReturn(true);
                        when(mockConnection.toString()).thenReturn(
                                "Mock connection " + connectionID);
                        // Execute handler and return future.
                        ResultHandler<? super Connection> handler =
                                (ResultHandler<? super Connection>) invocation.getArguments()[0];
                        if (handler != null) {
                            handler.handleResult(mockConnection);
                        }
                        return new CompletedFutureResult<Connection>(mockConnection);
                    }
                });
        ConnectionPool pool = Connections.newFixedConnectionPool(mockFactory, size);
        Connection[] pooledConnections = new Connection[size];
        for (int i = 0; i < size; i++) {
            pooledConnections[i] = pool.getConnection();
        }
        // Pool is fully utilized.
        assertThat(realConnectionCount.get()).isEqualTo(size);
        assertThat(pooledConnections[0].isClosed()).isFalse();
        assertThat(pooledConnections[1].isClosed()).isFalse();
        assertThat(pooledConnections[2].isClosed()).isFalse();
        assertThat(pooledConnections[3].isClosed()).isFalse();
        assertThat(realConnectionIsClosed[0]).isFalse();
        assertThat(realConnectionIsClosed[1]).isFalse();
        assertThat(realConnectionIsClosed[2]).isFalse();
        assertThat(realConnectionIsClosed[3]).isFalse();
        // Release two connections.
        pooledConnections[0].close();
        pooledConnections[1].close();
        assertThat(realConnectionCount.get()).isEqualTo(4);
        assertThat(pooledConnections[0].isClosed()).isTrue();
        assertThat(pooledConnections[1].isClosed()).isTrue();
        assertThat(pooledConnections[2].isClosed()).isFalse();
        assertThat(pooledConnections[3].isClosed()).isFalse();
        assertThat(realConnectionIsClosed[0]).isFalse();
        assertThat(realConnectionIsClosed[1]).isFalse();
        assertThat(realConnectionIsClosed[2]).isFalse();
        assertThat(realConnectionIsClosed[3]).isFalse();
        // Close the pool closing the two connections immediately.
        pool.close();
        assertThat(realConnectionCount.get()).isEqualTo(2);
        assertThat(pooledConnections[0].isClosed()).isTrue();
        assertThat(pooledConnections[1].isClosed()).isTrue();
        assertThat(pooledConnections[2].isClosed()).isFalse();
        assertThat(pooledConnections[3].isClosed()).isFalse();
        assertThat(realConnectionIsClosed[0]).isTrue();
        assertThat(realConnectionIsClosed[1]).isTrue();
        assertThat(realConnectionIsClosed[2]).isFalse();
        assertThat(realConnectionIsClosed[3]).isFalse();
        // Release two remaining connections and check that they get closed.
        pooledConnections[2].close();
        pooledConnections[3].close();
        assertThat(realConnectionCount.get()).isEqualTo(0);
        assertThat(pooledConnections[0].isClosed()).isTrue();
        assertThat(pooledConnections[1].isClosed()).isTrue();
        assertThat(pooledConnections[2].isClosed()).isTrue();
        assertThat(pooledConnections[3].isClosed()).isTrue();
        assertThat(realConnectionIsClosed[0]).isTrue();
        assertThat(realConnectionIsClosed[1]).isTrue();
        assertThat(realConnectionIsClosed[2]).isTrue();
        assertThat(realConnectionIsClosed[3]).isTrue();
    }
}