From 37718ae75138d7699fdc1da4e6cdbdd34b5dc95c Mon Sep 17 00:00:00 2001
From: Matthew Swift <matthew.swift@forgerock.com>
Date: Thu, 15 Sep 2011 11:00:33 +0000
Subject: [PATCH] Issue OPENDJ-262: Implement pass through authentication (PTA)

---
 opends/tests/unit-tests-testng/src/server/org/opends/server/extensions/LDAPPassThroughAuthenticationPolicyTestCase.java |   17 
 opends/src/server/org/opends/server/extensions/LDAPPassThroughAuthenticationPolicyFactory.java                          | 1268 ++++++++++++++++++++++++++++-----------------------------
 2 files changed, 629 insertions(+), 656 deletions(-)

diff --git a/opends/src/server/org/opends/server/extensions/LDAPPassThroughAuthenticationPolicyFactory.java b/opends/src/server/org/opends/server/extensions/LDAPPassThroughAuthenticationPolicyFactory.java
index dbd18eb..6283310 100644
--- a/opends/src/server/org/opends/server/extensions/LDAPPassThroughAuthenticationPolicyFactory.java
+++ b/opends/src/server/org/opends/server/extensions/LDAPPassThroughAuthenticationPolicyFactory.java
@@ -76,6 +76,83 @@
   // TODO: handle password policy response controls? AD?
   // TODO: periodically ping offline servers in order to detect when they come
   // back.
+  // TODO: provide alternative cfg for search password.
+
+  /**
+   * A factory which returns pre-authenticated connections for searches.
+   * <p>
+   * Package private for testing.
+   */
+  static final class AuthenticatedConnectionFactory implements
+      ConnectionFactory
+  {
+
+    private final ConnectionFactory factory;
+    private final DN username;
+    private final String password;
+
+
+
+    /**
+     * Creates a new authenticated connection factory which will bind on
+     * connect.
+     *
+     * @param factory
+     *          The underlying connection factory whose connections are to be
+     *          authenticated.
+     * @param username
+     *          The username taken from the configuration.
+     * @param password
+     *          The password taken from the configuration.
+     */
+    AuthenticatedConnectionFactory(final ConnectionFactory factory,
+        final DN username, final String password)
+    {
+      this.factory = factory;
+      this.username = username;
+      this.password = password;
+    }
+
+
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void close()
+    {
+      factory.close();
+    }
+
+
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public Connection getConnection() throws DirectoryException
+    {
+      final Connection connection = factory.getConnection();
+      if (username != null && !username.isNullDN() && password != null
+          && password.length() > 0)
+      {
+        try
+        {
+          connection.simpleBind(ByteString.valueOf(username.toString()),
+              ByteString.valueOf(password));
+        }
+        catch (final DirectoryException e)
+        {
+          connection.close();
+          throw e;
+        }
+      }
+      return connection;
+    }
+
+  }
+
+
 
   /**
    * An LDAP connection which will be used in order to search for or
@@ -137,9 +214,19 @@
    * a connection, perform a single operation (search or bind), and then close
    * it.
    */
-  static interface ConnectionFactory
+  static interface ConnectionFactory extends Closeable
   {
     /**
+     * {@inheritDoc}
+     * <p>
+     * Must never throw an exception.
+     */
+    @Override
+    void close();
+
+
+
+    /**
      * Returns a connection which can be used in order to search for or
      * authenticate users.
      *
@@ -154,26 +241,275 @@
 
 
   /**
-   * An interface for obtaining a connection factory for LDAP connections to a
-   * named LDAP server.
+   * PTA connection pool.
+   * <p>
+   * Package private for testing.
    */
-  static interface LDAPConnectionFactoryProvider
+  static final class ConnectionPool implements ConnectionFactory, Closeable
   {
+
     /**
-     * Returns a connection factory which can be used for obtaining connections
-     * to the specified LDAP server.
-     *
-     * @param host
-     *          The LDAP server host name.
-     * @param port
-     *          The LDAP server port.
-     * @param options
-     *          The LDAP connection options.
-     * @return A connection factory which can be used for obtaining connections
-     *         to the specified LDAP server.
+     * Pooled connection's intercept close and release connection back to the
+     * pool.
      */
-    ConnectionFactory getLDAPConnectionFactory(String host, int port,
-        LDAPPassThroughAuthenticationPolicyCfg options);
+    private final class PooledConnection implements Connection
+    {
+      private final Connection connection;
+      private boolean connectionIsClosed = false;
+
+
+
+      private PooledConnection(final Connection connection)
+      {
+        this.connection = connection;
+      }
+
+
+
+      /**
+       * {@inheritDoc}
+       */
+      @Override
+      public void close()
+      {
+        if (!connectionIsClosed)
+        {
+          connectionIsClosed = true;
+
+          // Guarded by PolicyImpl
+          if (poolIsClosed)
+          {
+            connection.close();
+          }
+          else
+          {
+            connectionPool.offer(connection);
+          }
+          availableConnections.release();
+        }
+      }
+
+
+
+      /**
+       * {@inheritDoc}
+       */
+      @Override
+      public ByteString search(final DN baseDN, final SearchScope scope,
+          final SearchFilter filter) throws DirectoryException
+      {
+        try
+        {
+          return connection.search(baseDN, scope, filter);
+        }
+        catch (final DirectoryException e)
+        {
+          // Don't put the connection back in the pool if it has failed.
+          closeConnectionOnFatalError(e);
+          throw e;
+        }
+      }
+
+
+
+      /**
+       * {@inheritDoc}
+       */
+      @Override
+      public void simpleBind(final ByteString username,
+          final ByteString password) throws DirectoryException
+      {
+        try
+        {
+          connection.simpleBind(username, password);
+        }
+        catch (final DirectoryException e)
+        {
+          // Don't put the connection back in the pool if it has failed.
+          closeConnectionOnFatalError(e);
+          throw e;
+        }
+      }
+
+
+
+      private void closeConnectionOnFatalError(final DirectoryException e)
+      {
+        if (isFatalResultCode(e.getResultCode()))
+        {
+          if (!connectionIsClosed)
+          {
+            connectionIsClosed = true;
+            connection.close();
+            availableConnections.release();
+          }
+        }
+      }
+
+    }
+
+
+
+    // Guarded by PolicyImpl.lock.
+    private boolean poolIsClosed = false;
+
+    private final ConnectionFactory factory;
+    private final int poolSize = Runtime.getRuntime().availableProcessors() * 2;
+    private final Semaphore availableConnections = new Semaphore(poolSize);
+    private final Queue<Connection> connectionPool =
+      new ConcurrentLinkedQueue<Connection>();
+
+
+
+    /**
+     * Creates a new connection pool for the provided factory.
+     *
+     * @param factory
+     *          The underlying connection factory whose connections are to be
+     *          pooled.
+     */
+    ConnectionPool(final ConnectionFactory factory)
+    {
+      this.factory = factory;
+    }
+
+
+
+    /**
+     * Release all connections: do we want to block?
+     */
+    @Override
+    public void close()
+    {
+      // No need for synchronization as this can only be called with the
+      // policy's exclusive lock.
+      poolIsClosed = true;
+
+      Connection connection;
+      while ((connection = connectionPool.poll()) != null)
+      {
+        connection.close();
+      }
+
+      factory.close();
+
+      // Since we have the exclusive lock, there should be no more connections
+      // in use.
+      if (availableConnections.availablePermits() != poolSize)
+      {
+        throw new IllegalStateException(
+            "Pool has remaining connections open after close");
+      }
+    }
+
+
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public Connection getConnection() throws DirectoryException
+    {
+      // This should only be called with the policy's shared lock.
+      if (poolIsClosed)
+      {
+        throw new IllegalStateException("pool is closed");
+      }
+
+      availableConnections.acquireUninterruptibly();
+
+      // There is either a pooled connection or we are allowed to create
+      // one.
+      Connection connection = connectionPool.poll();
+      if (connection == null)
+      {
+        try
+        {
+          connection = factory.getConnection();
+        }
+        catch (final DirectoryException e)
+        {
+          availableConnections.release();
+          throw e;
+        }
+      }
+
+      return new PooledConnection(connection);
+    }
+  }
+
+
+
+  /**
+   * A simplistic two-way fail-over connection factory implementation.
+   * <p>
+   * Package private for testing.
+   */
+  static final class FailoverConnectionFactory implements ConnectionFactory,
+      Closeable
+  {
+    private final ConnectionFactory primary;
+    private final ConnectionFactory secondary;
+
+
+
+    /**
+     * Creates a new fail-over connection factory which will always try the
+     * primary connection factory first, before trying the second.
+     *
+     * @param primary
+     *          The primary connection factory.
+     * @param secondary
+     *          The secondary connection factory.
+     */
+    FailoverConnectionFactory(final ConnectionFactory primary,
+        final ConnectionFactory secondary)
+    {
+      this.primary = primary;
+      this.secondary = secondary;
+    }
+
+
+
+    /**
+     * Close underlying load-balancers.
+     */
+    @Override
+    public void close()
+    {
+      primary.close();
+      if (secondary != null)
+      {
+        secondary.close();
+      }
+    }
+
+
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public Connection getConnection() throws DirectoryException
+    {
+      if (secondary == null)
+      {
+        // No fail-over so just use the primary.
+        return primary.getConnection();
+      }
+      else
+      {
+        try
+        {
+          return primary.getConnection();
+        }
+        catch (final DirectoryException e)
+        {
+          return secondary.getConnection();
+        }
+      }
+    }
+
   }
 
 
@@ -337,14 +673,14 @@
               throw new DirectoryException(
                   ResultCode.CLIENT_SIDE_MORE_RESULTS_TO_RETURN,
                   ERR_LDAP_PTA_CONNECTION_SEARCH_SIZE_LIMIT.get(host, port,
-                      String.valueOf(options.dn()), String.valueOf(baseDN),
+                      String.valueOf(cfg.dn()), String.valueOf(baseDN),
                       String.valueOf(filter)));
 
             default:
               // The search failed for some reason.
               throw new DirectoryException(resultCode,
                   ERR_LDAP_PTA_CONNECTION_SEARCH_FAILED.get(host, port,
-                      String.valueOf(options.dn()), String.valueOf(baseDN),
+                      String.valueOf(cfg.dn()), String.valueOf(baseDN),
                       String.valueOf(filter), resultCode.getIntValue(),
                       resultCode.getResultCodeName(),
                       searchResult.getErrorMessage()));
@@ -366,7 +702,7 @@
           throw new DirectoryException(
               ResultCode.CLIENT_SIDE_MORE_RESULTS_TO_RETURN,
               ERR_LDAP_PTA_CONNECTION_SEARCH_SIZE_LIMIT.get(host, port,
-                  String.valueOf(options.dn()), String.valueOf(baseDN),
+                  String.valueOf(cfg.dn()), String.valueOf(baseDN),
                   String.valueOf(filter)));
         }
 
@@ -376,7 +712,7 @@
           throw new DirectoryException(
               ResultCode.CLIENT_SIDE_NO_RESULTS_RETURNED,
               ERR_LDAP_PTA_CONNECTION_SEARCH_NO_MATCHES.get(host, port,
-                  String.valueOf(options.dn()), String.valueOf(baseDN),
+                  String.valueOf(cfg.dn()), String.valueOf(baseDN),
                   String.valueOf(filter)));
         }
 
@@ -418,7 +754,7 @@
             // The bind failed for some reason.
             throw new DirectoryException(resultCode,
                 ERR_LDAP_PTA_CONNECTION_BIND_FAILED.get(host, port,
-                    String.valueOf(options.dn()), String.valueOf(username),
+                    String.valueOf(cfg.dn()), String.valueOf(username),
                     resultCode.getIntValue(), resultCode.getResultCodeName(),
                     bindResponse.getErrorMessage()));
           }
@@ -436,23 +772,6 @@
        * {@inheritDoc}
        */
       @Override
-      public String toString()
-      {
-        final StringBuilder builder = new StringBuilder();
-        builder.append("LDAPConnection(");
-        builder.append(String.valueOf(ldapSocket.getLocalSocketAddress()));
-        builder.append(", ");
-        builder.append(String.valueOf(ldapSocket.getRemoteSocketAddress()));
-        builder.append(')');
-        return builder.toString();
-      }
-
-
-
-      /**
-       * {@inheritDoc}
-       */
-      @Override
       protected void finalize()
       {
         close();
@@ -474,7 +793,7 @@
           {
             throw new DirectoryException(ResultCode.valueOf(extendedResponse
                 .getResultCode()), ERR_LDAP_PTA_CONNECTION_DISCONNECTING.get(
-                host, port, String.valueOf(options.dn()),
+                host, port, String.valueOf(cfg.dn()),
                 extendedResponse.getErrorMessage()));
           }
         }
@@ -482,7 +801,7 @@
         // Unexpected response type.
         throw new DirectoryException(ResultCode.CLIENT_SIDE_DECODING_ERROR,
             ERR_LDAP_PTA_CONNECTION_WRONG_RESPONSE.get(host, port,
-                String.valueOf(options.dn()),
+                String.valueOf(cfg.dn()),
                 String.valueOf(responseMessage.getProtocolOp())));
       }
 
@@ -503,45 +822,45 @@
           {
             throw new DirectoryException(ResultCode.CLIENT_SIDE_TIMEOUT,
                 ERR_LDAP_PTA_CONNECTION_TIMEOUT.get(host, port,
-                    String.valueOf(options.dn())), e);
+                    String.valueOf(cfg.dn())), e);
           }
           else if (e.getCause() instanceof IOException)
           {
             throw new DirectoryException(ResultCode.CLIENT_SIDE_SERVER_DOWN,
                 ERR_LDAP_PTA_CONNECTION_OTHER_ERROR.get(host, port,
-                    String.valueOf(options.dn()), e.getMessage()), e);
+                    String.valueOf(cfg.dn()), e.getMessage()), e);
           }
           else
           {
             throw new DirectoryException(ResultCode.CLIENT_SIDE_DECODING_ERROR,
                 ERR_LDAP_PTA_CONNECTION_DECODE_ERROR.get(host, port,
-                    String.valueOf(options.dn()), e.getMessage()), e);
+                    String.valueOf(cfg.dn()), e.getMessage()), e);
           }
         }
         catch (final LDAPException e)
         {
           throw new DirectoryException(ResultCode.CLIENT_SIDE_DECODING_ERROR,
               ERR_LDAP_PTA_CONNECTION_DECODE_ERROR.get(host, port,
-                  String.valueOf(options.dn()), e.getMessage()), e);
+                  String.valueOf(cfg.dn()), e.getMessage()), e);
         }
         catch (final SocketTimeoutException e)
         {
           throw new DirectoryException(ResultCode.CLIENT_SIDE_TIMEOUT,
               ERR_LDAP_PTA_CONNECTION_TIMEOUT.get(host, port,
-                  String.valueOf(options.dn())), e);
+                  String.valueOf(cfg.dn())), e);
         }
         catch (final IOException e)
         {
           throw new DirectoryException(ResultCode.CLIENT_SIDE_SERVER_DOWN,
               ERR_LDAP_PTA_CONNECTION_OTHER_ERROR.get(host, port,
-                  String.valueOf(options.dn()), e.getMessage()), e);
+                  String.valueOf(cfg.dn()), e.getMessage()), e);
         }
 
         if (responseMessage == null)
         {
           throw new DirectoryException(ResultCode.CLIENT_SIDE_SERVER_DOWN,
               ERR_LDAP_PTA_CONNECTION_CLOSED.get(host, port,
-                  String.valueOf(options.dn())));
+                  String.valueOf(cfg.dn())));
         }
         return responseMessage;
       }
@@ -562,7 +881,7 @@
         {
           throw new DirectoryException(ResultCode.CLIENT_SIDE_SERVER_DOWN,
               ERR_LDAP_PTA_CONNECTION_OTHER_ERROR.get(host, port,
-                  String.valueOf(options.dn()), e.getMessage()), e);
+                  String.valueOf(cfg.dn()), e.getMessage()), e);
         }
       }
     }
@@ -571,7 +890,7 @@
 
     private final String host;
     private final int port;
-    private final LDAPPassThroughAuthenticationPolicyCfg options;
+    private final LDAPPassThroughAuthenticationPolicyCfg cfg;
     private final int timeoutMS;
 
 
@@ -584,19 +903,19 @@
      *          The server host name.
      * @param port
      *          The server port.
-     * @param options
-     *          The options (SSL).
+     * @param cfg
+     *          The configuration (for SSL).
      */
     LDAPConnectionFactory(final String host, final int port,
-        final LDAPPassThroughAuthenticationPolicyCfg options)
+        final LDAPPassThroughAuthenticationPolicyCfg cfg)
     {
       this.host = host;
       this.port = port;
-      this.options = options;
+      this.cfg = cfg;
 
       // Normalize the timeoutMS to an integer (admin framework ensures that the
       // value is non-negative).
-      this.timeoutMS = (int) Math.min(options.getConnectionTimeout(),
+      this.timeoutMS = (int) Math.min(cfg.getConnectionTimeout(),
           Integer.MAX_VALUE);
     }
 
@@ -606,6 +925,17 @@
      * {@inheritDoc}
      */
     @Override
+    public void close()
+    {
+      // Nothing to do.
+    }
+
+
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
     public Connection getConnection() throws DirectoryException
     {
       try
@@ -624,24 +954,24 @@
 
         try
         {
-          // Set ldapSocket options before connecting.
-          plainSocket.setTcpNoDelay(options.isUseTCPNoDelay());
-          plainSocket.setKeepAlive(options.isUseTCPKeepAlive());
+          // Set ldapSocket cfg before connecting.
+          plainSocket.setTcpNoDelay(cfg.isUseTCPNoDelay());
+          plainSocket.setKeepAlive(cfg.isUseTCPKeepAlive());
           plainSocket.setSoTimeout(timeoutMS);
 
           // Connect the ldapSocket.
           plainSocket.connect(socketAddress, timeoutMS);
 
-          if (options.isUseSSL())
+          if (cfg.isUseSSL())
           {
             // Obtain the optional configured trust manager which will be used
             // in order to determine the trust of the remote LDAP server.
             TrustManager[] tm = null;
-            DN trustManagerDN = options.getTrustManagerProviderDN();
+            final DN trustManagerDN = cfg.getTrustManagerProviderDN();
             if (trustManagerDN != null)
             {
-              TrustManagerProvider<?> trustManagerProvider = DirectoryServer
-                  .getTrustManagerProvider(trustManagerDN);
+              final TrustManagerProvider<?> trustManagerProvider =
+                DirectoryServer.getTrustManagerProvider(trustManagerDN);
               if (trustManagerProvider != null)
               {
                 tm = trustManagerProvider.getTrustManagers();
@@ -649,25 +979,26 @@
             }
 
             // Create the SSL context and initialize it.
-            SSLContext sslContext = SSLContext.getInstance("TLS");
+            final SSLContext sslContext = SSLContext.getInstance("TLS");
             sslContext.init(null /* key managers */, tm, null /* rng */);
 
             // Create the SSL socket.
-            SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory();
-            SSLSocket sslSocket = (SSLSocket) sslSocketFactory.createSocket(
-                plainSocket, host, port, true);
+            final SSLSocketFactory sslSocketFactory = sslContext
+                .getSocketFactory();
+            final SSLSocket sslSocket = (SSLSocket) sslSocketFactory
+                .createSocket(plainSocket, host, port, true);
             ldapSocket = sslSocket;
 
             sslSocket.setUseClientMode(true);
-            if (!options.getSSLProtocol().isEmpty())
+            if (!cfg.getSSLProtocol().isEmpty())
             {
-              sslSocket.setEnabledProtocols(options.getSSLProtocol().toArray(
+              sslSocket.setEnabledProtocols(cfg.getSSLProtocol().toArray(
                   new String[0]));
             }
-            if (!options.getSSLCipherSuite().isEmpty())
+            if (!cfg.getSSLCipherSuite().isEmpty())
             {
-              sslSocket.setEnabledCipherSuites(options.getSSLCipherSuite()
-                  .toArray(new String[0]));
+              sslSocket.setEnabledCipherSuites(cfg.getSSLCipherSuite().toArray(
+                  new String[0]));
             }
 
             // Force TLS negotiation.
@@ -735,7 +1066,7 @@
         }
         throw new DirectoryException(ResultCode.CLIENT_SIDE_CONNECT_ERROR,
             ERR_LDAP_PTA_CONNECT_UNKNOWN_HOST.get(host, port,
-                String.valueOf(options.dn()), host), e);
+                String.valueOf(cfg.dn()), host), e);
       }
       catch (final ConnectException e)
       {
@@ -745,7 +1076,7 @@
         }
         throw new DirectoryException(ResultCode.CLIENT_SIDE_CONNECT_ERROR,
             ERR_LDAP_PTA_CONNECT_ERROR.get(host, port,
-                String.valueOf(options.dn()), port), e);
+                String.valueOf(cfg.dn()), port), e);
       }
       catch (final SocketTimeoutException e)
       {
@@ -755,7 +1086,7 @@
         }
         throw new DirectoryException(ResultCode.CLIENT_SIDE_TIMEOUT,
             ERR_LDAP_PTA_CONNECT_TIMEOUT.get(host, port,
-                String.valueOf(options.dn())), e);
+                String.valueOf(cfg.dn())), e);
       }
       catch (final SSLException e)
       {
@@ -765,7 +1096,7 @@
         }
         throw new DirectoryException(ResultCode.CLIENT_SIDE_CONNECT_ERROR,
             ERR_LDAP_PTA_CONNECT_SSL_ERROR.get(host, port,
-                String.valueOf(options.dn()), e.getMessage()), e);
+                String.valueOf(cfg.dn()), e.getMessage()), e);
       }
       catch (final Exception e)
       {
@@ -775,10 +1106,77 @@
         }
         throw new DirectoryException(ResultCode.CLIENT_SIDE_CONNECT_ERROR,
             ERR_LDAP_PTA_CONNECT_OTHER_ERROR.get(host, port,
-                String.valueOf(options.dn()), e.getMessage()), e);
+                String.valueOf(cfg.dn()), e.getMessage()), e);
       }
 
     }
+  }
+
+
+
+  /**
+   * An interface for obtaining a connection factory for LDAP connections to a
+   * named LDAP server.
+   */
+  static interface LDAPConnectionFactoryProvider
+  {
+    /**
+     * Returns a connection factory which can be used for obtaining connections
+     * to the specified LDAP server.
+     *
+     * @param host
+     *          The LDAP server host name.
+     * @param port
+     *          The LDAP server port.
+     * @param cfg
+     *          The LDAP connection configuration.
+     * @return A connection factory which can be used for obtaining connections
+     *         to the specified LDAP server.
+     */
+    ConnectionFactory getLDAPConnectionFactory(String host, int port,
+        LDAPPassThroughAuthenticationPolicyCfg cfg);
+  }
+
+
+
+  /**
+   * A simplistic load-balancer connection factory implementation using
+   * approximately round-robin balancing.
+   */
+  static final class LoadBalancer implements ConnectionFactory, Closeable
+  {
+    private final ConnectionFactory[] factories;
+    private final AtomicInteger nextIndex = new AtomicInteger();
+    private final int maxIndex;
+
+
+
+    /**
+     * Creates a new load-balancer which will distribute connection requests
+     * across a set of underlying connection factories.
+     *
+     * @param factories
+     *          The list of underlying connection factories.
+     */
+    LoadBalancer(final ConnectionFactory[] factories)
+    {
+      this.factories = factories;
+      this.maxIndex = factories.length;
+    }
+
+
+
+    /**
+     * Close underlying connection pools.
+     */
+    @Override
+    public void close()
+    {
+      for (final ConnectionFactory factory : factories)
+      {
+        factory.close();
+      }
+    }
 
 
 
@@ -786,16 +1184,68 @@
      * {@inheritDoc}
      */
     @Override
-    public String toString()
+    public Connection getConnection() throws DirectoryException
     {
-      final StringBuilder builder = new StringBuilder();
-      builder.append("LDAPConnectionFactory(");
-      builder.append(host);
-      builder.append(':');
-      builder.append(port);
-      builder.append(')');
-      return builder.toString();
+      final int startIndex = getStartIndex();
+      int index = startIndex;
+      for (;;)
+      {
+        final ConnectionFactory factory = factories[index];
+
+        try
+        {
+          return factory.getConnection();
+        }
+        catch (final DirectoryException e)
+        {
+          // Try the next index.
+          if (++index == maxIndex)
+          {
+            index = 0;
+          }
+
+          // If all the factories have been tried then give up and throw the
+          // exception.
+          if (index == startIndex)
+          {
+            throw e;
+          }
+        }
+      }
     }
+
+
+
+    // Determine the start index.
+    private int getStartIndex()
+    {
+      // A round robin pool of one connection factories is unlikely in
+      // practice and requires special treatment.
+      if (maxIndex == 1)
+      {
+        return 0;
+      }
+
+      // Determine the next factory to use: avoid blocking algorithm.
+      int oldNextIndex;
+      int newNextIndex;
+      do
+      {
+        oldNextIndex = nextIndex.get();
+        newNextIndex = oldNextIndex + 1;
+        if (newNextIndex == maxIndex)
+        {
+          newNextIndex = 0;
+        }
+      }
+      while (!nextIndex.compareAndSet(oldNextIndex, newNextIndex));
+
+      // There's a potential, but benign, race condition here: other threads
+      // could jump in and rotate through the list before we return the
+      // connection factory.
+      return oldNextIndex;
+    }
+
   }
 
 
@@ -808,485 +1258,6 @@
   {
 
     /**
-     * A factory which returns pre-authenticated connections for searches.
-     */
-    private final class AuthenticatedConnectionFactory implements
-        ConnectionFactory
-    {
-
-      private final ConnectionFactory factory;
-
-
-
-      private AuthenticatedConnectionFactory(final ConnectionFactory factory)
-      {
-        this.factory = factory;
-      }
-
-
-
-      /**
-       * {@inheritDoc}
-       */
-      @Override
-      public Connection getConnection() throws DirectoryException
-      {
-        final DN username = configuration.getMappedSearchBindDN();
-        final String password = configuration.getMappedSearchBindPassword();
-
-        final Connection connection = factory.getConnection();
-        if (username != null && !username.isNullDN() && password != null
-            && password.length() > 0)
-        {
-          try
-          {
-            connection.simpleBind(ByteString.valueOf(username.toString()),
-                ByteString.valueOf(password));
-          }
-          catch (final DirectoryException e)
-          {
-            connection.close();
-            throw e;
-          }
-        }
-        return connection;
-      }
-
-
-
-      /**
-       * {@inheritDoc}
-       */
-      @Override
-      public String toString()
-      {
-        final StringBuilder builder = new StringBuilder();
-        builder.append("AuthenticationConnectionFactory(");
-        builder.append(factory);
-        builder.append(')');
-        return builder.toString();
-      }
-
-    }
-
-
-
-    /**
-     * PTA connection pool.
-     */
-    private final class ConnectionPool implements ConnectionFactory, Closeable
-    {
-
-      /**
-       * Pooled connection's intercept close and release connection back to the
-       * pool.
-       */
-      private final class PooledConnection implements Connection
-      {
-        private final Connection connection;
-        private boolean connectionIsClosed = false;
-
-
-
-        private PooledConnection(final Connection connection)
-        {
-          this.connection = connection;
-        }
-
-
-
-        /**
-         * {@inheritDoc}
-         */
-        @Override
-        public void close()
-        {
-          if (!connectionIsClosed)
-          {
-            connectionIsClosed = true;
-
-            // Guarded by PolicyImpl
-            if (poolIsClosed)
-            {
-              connection.close();
-            }
-            else
-            {
-              connectionPool.offer(connection);
-            }
-            availableConnections.release();
-          }
-        }
-
-
-
-        /**
-         * {@inheritDoc}
-         */
-        @Override
-        public ByteString search(final DN baseDN, final SearchScope scope,
-            final SearchFilter filter) throws DirectoryException
-        {
-          try
-          {
-            return connection.search(baseDN, scope, filter);
-          }
-          catch (final DirectoryException e)
-          {
-            // Don't put the connection back in the pool if it has failed.
-            closeConnectionOnFatalError(e);
-            throw e;
-          }
-        }
-
-
-
-        /**
-         * {@inheritDoc}
-         */
-        @Override
-        public void simpleBind(final ByteString username,
-            final ByteString password) throws DirectoryException
-        {
-          try
-          {
-            connection.simpleBind(username, password);
-          }
-          catch (final DirectoryException e)
-          {
-            // Don't put the connection back in the pool if it has failed.
-            closeConnectionOnFatalError(e);
-            throw e;
-          }
-        }
-
-
-
-        private void closeConnectionOnFatalError(final DirectoryException e)
-        {
-          if (isFatalResultCode(e.getResultCode()))
-          {
-            if (!connectionIsClosed)
-            {
-              connectionIsClosed = true;
-              connection.close();
-              availableConnections.release();
-            }
-          }
-        }
-
-      }
-
-
-
-      // Guarded by PolicyImpl.lock.
-      private boolean poolIsClosed = false;
-
-      private final ConnectionFactory factory;
-      private final int poolSize =
-        Runtime.getRuntime().availableProcessors() * 2;
-      private final Semaphore availableConnections = new Semaphore(poolSize);
-      private final ConcurrentLinkedQueue<Connection> connectionPool =
-        new ConcurrentLinkedQueue<Connection>();
-
-
-
-      private ConnectionPool(final ConnectionFactory factory)
-      {
-        this.factory = factory;
-      }
-
-
-
-      /**
-       * Release all connections: do we want to block?
-       */
-      @Override
-      public void close()
-      {
-        // No need for synchronization as this can only be called with the
-        // policy's exclusive lock.
-        poolIsClosed = true;
-
-        Connection connection;
-        while ((connection = connectionPool.poll()) != null)
-        {
-          connection.close();
-        }
-
-        // Since we have the exclusive lock, there should be no more connections
-        // in use.
-        if (availableConnections.availablePermits() != poolSize)
-        {
-          throw new IllegalStateException(
-              "Pool has remaining connections open after close");
-        }
-      }
-
-
-
-      /**
-       * {@inheritDoc}
-       */
-      @Override
-      public Connection getConnection() throws DirectoryException
-      {
-        // This should only be called with the policy's shared lock.
-        if (poolIsClosed)
-        {
-          throw new IllegalStateException("pool is closed");
-        }
-
-        availableConnections.acquireUninterruptibly();
-
-        // There is either a pooled connection or we are allowed to create
-        // one.
-        Connection connection = connectionPool.poll();
-        if (connection == null)
-        {
-          try
-          {
-            connection = factory.getConnection();
-          }
-          catch (final DirectoryException e)
-          {
-            availableConnections.release();
-            throw e;
-          }
-        }
-
-        return new PooledConnection(connection);
-      }
-
-
-
-      /**
-       * {@inheritDoc}
-       */
-      @Override
-      public String toString()
-      {
-        final StringBuilder builder = new StringBuilder();
-        builder.append("ConnectionPool(");
-        builder.append(factory);
-        builder.append(", poolSize=");
-        builder.append(poolSize);
-        builder.append(", inPool=");
-        builder.append(connectionPool.size());
-        builder.append(", available=");
-        builder.append(availableConnections.availablePermits());
-        builder.append(')');
-        return builder.toString();
-      }
-    }
-
-
-
-    /**
-     * A simplistic two-way fail-over connection factory implementation.
-     */
-    private final class FailoverConnectionFactory implements ConnectionFactory,
-        Closeable
-    {
-      private final LoadBalancer primary;
-      private final LoadBalancer secondary;
-
-
-
-      private FailoverConnectionFactory(final LoadBalancer primary,
-          final LoadBalancer secondary)
-      {
-        this.primary = primary;
-        this.secondary = secondary;
-      }
-
-
-
-      /**
-       * Close underlying load-balancers.
-       */
-      @Override
-      public void close()
-      {
-        primary.close();
-        if (secondary != null)
-        {
-          secondary.close();
-        }
-      }
-
-
-
-      /**
-       * {@inheritDoc}
-       */
-      @Override
-      public Connection getConnection() throws DirectoryException
-      {
-        if (secondary == null)
-        {
-          // No fail-over so just use the primary.
-          return primary.getConnection();
-        }
-        else
-        {
-          try
-          {
-            return primary.getConnection();
-          }
-          catch (final DirectoryException e)
-          {
-            return secondary.getConnection();
-          }
-        }
-      }
-
-
-
-      /**
-       * {@inheritDoc}
-       */
-      @Override
-      public String toString()
-      {
-        final StringBuilder builder = new StringBuilder();
-        builder.append("FailoverConnectionFactory(");
-        builder.append(primary);
-        builder.append(", ");
-        builder.append(secondary);
-        builder.append(')');
-        return builder.toString();
-      }
-
-    }
-
-
-
-    /**
-     * A simplistic load-balancer connection factory implementation using
-     * approximately round-robin balancing.
-     */
-    private final class LoadBalancer implements ConnectionFactory, Closeable
-    {
-      private final ConnectionPool[] factories;
-      private final AtomicInteger nextIndex = new AtomicInteger();
-      private final int maxIndex;
-
-
-
-      private LoadBalancer(final ConnectionPool[] factories)
-      {
-        this.factories = factories;
-        this.maxIndex = factories.length;
-      }
-
-
-
-      /**
-       * Close underlying connection pools.
-       */
-      @Override
-      public void close()
-      {
-        for (final ConnectionPool pool : factories)
-        {
-          pool.close();
-        }
-      }
-
-
-
-      /**
-       * {@inheritDoc}
-       */
-      @Override
-      public Connection getConnection() throws DirectoryException
-      {
-        final int startIndex = getStartIndex();
-        int index = startIndex;
-        for (;;)
-        {
-          final ConnectionFactory factory = factories[index];
-
-          try
-          {
-            return factory.getConnection();
-          }
-          catch (final DirectoryException e)
-          {
-            // Try the next index.
-            if (++index == maxIndex)
-            {
-              index = 0;
-            }
-
-            // If all the factories have been tried then give up and throw the
-            // exception.
-            if (index == startIndex)
-            {
-              throw e;
-            }
-          }
-        }
-      }
-
-
-
-      /**
-       * {@inheritDoc}
-       */
-      @Override
-      public String toString()
-      {
-        final StringBuilder builder = new StringBuilder();
-        builder.append("LoadBalancer(");
-        builder.append(nextIndex);
-        for (final ConnectionFactory factory : factories)
-        {
-          builder.append(", ");
-          builder.append(factory);
-        }
-        builder.append(')');
-        return builder.toString();
-      }
-
-
-
-      // Determine the start index.
-      private int getStartIndex()
-      {
-        // A round robin pool of one connection factories is unlikely in
-        // practice and requires special treatment.
-        if (maxIndex == 1)
-        {
-          return 0;
-        }
-
-        // Determine the next factory to use: avoid blocking algorithm.
-        int oldNextIndex;
-        int newNextIndex;
-        do
-        {
-          oldNextIndex = nextIndex.get();
-          newNextIndex = oldNextIndex + 1;
-          if (newNextIndex == maxIndex)
-          {
-            newNextIndex = 0;
-          }
-        }
-        while (!nextIndex.compareAndSet(oldNextIndex, newNextIndex));
-
-        // There's a potential, but benign, race condition here: other threads
-        // could jump in and rotate through the list before we return the
-        // connection factory.
-        return oldNextIndex;
-      }
-
-    }
-
-
-
-    /**
      * LDAP PTA policy state implementation.
      */
     private final class StateImpl extends AuthenticationPolicyState
@@ -1344,7 +1315,7 @@
           // directory.
           ByteString username = null;
 
-          switch (configuration.getMappingPolicy())
+          switch (cfg.getMappingPolicy())
           {
           case UNMAPPED:
             // The bind DN is the name of the user's entry.
@@ -1352,8 +1323,7 @@
             break;
           case MAPPED_BIND:
             // The bind DN is contained in an attribute in the user's entry.
-            mapBind: for (final AttributeType at : configuration
-                .getMappedAttribute())
+            mapBind: for (final AttributeType at : cfg.getMappedAttribute())
             {
               final List<Attribute> attributes = userEntry.getAttribute(at);
               if (attributes != null && !attributes.isEmpty())
@@ -1378,10 +1348,10 @@
                * references a non-user entry.
                */
               throw new DirectoryException(ResultCode.INVALID_CREDENTIALS,
-                  ERR_LDAP_PTA_MAPPING_ATTRIBUTE_NOT_FOUND.get(String
-                      .valueOf(userEntry.getDN()), String.valueOf(configuration
-                      .dn()), mappedAttributesAsString(configuration
-                      .getMappedAttribute())));
+                  ERR_LDAP_PTA_MAPPING_ATTRIBUTE_NOT_FOUND.get(
+                      String.valueOf(userEntry.getDN()),
+                      String.valueOf(cfg.dn()),
+                      mappedAttributesAsString(cfg.getMappedAttribute())));
             }
 
             break;
@@ -1392,7 +1362,7 @@
             // Construct the search filter.
             final LinkedList<SearchFilter> filterComponents =
               new LinkedList<SearchFilter>();
-            for (final AttributeType at : configuration.getMappedAttribute())
+            for (final AttributeType at : cfg.getMappedAttribute())
             {
               final List<Attribute> attributes = userEntry.getAttribute(at);
               if (attributes != null && !attributes.isEmpty())
@@ -1417,10 +1387,10 @@
                * references a non-user entry.
                */
               throw new DirectoryException(ResultCode.INVALID_CREDENTIALS,
-                  ERR_LDAP_PTA_MAPPING_ATTRIBUTE_NOT_FOUND.get(String
-                      .valueOf(userEntry.getDN()), String.valueOf(configuration
-                      .dn()), mappedAttributesAsString(configuration
-                      .getMappedAttribute())));
+                  ERR_LDAP_PTA_MAPPING_ATTRIBUTE_NOT_FOUND.get(
+                      String.valueOf(userEntry.getDN()),
+                      String.valueOf(cfg.dn()),
+                      mappedAttributesAsString(cfg.getMappedAttribute())));
             }
 
             final SearchFilter filter;
@@ -1435,7 +1405,7 @@
 
             // Now search the configured base DNs, stopping at the first
             // success.
-            for (final DN baseDN : configuration.getMappedSearchBaseDN())
+            for (final DN baseDN : cfg.getMappedSearchBaseDN())
             {
               Connection connection = null;
               try
@@ -1457,8 +1427,8 @@
                   throw new DirectoryException(ResultCode.INVALID_CREDENTIALS,
                       ERR_LDAP_PTA_MAPPED_SEARCH_TOO_MANY_CANDIDATES.get(
                           String.valueOf(userEntry.getDN()),
-                          String.valueOf(configuration.dn()),
-                          String.valueOf(baseDN), String.valueOf(filter)));
+                          String.valueOf(cfg.dn()), String.valueOf(baseDN),
+                          String.valueOf(filter)));
                 default:
                   // We don't want to propagate this internal error to the
                   // client. We should log it and map it to a more appropriate
@@ -1466,8 +1436,7 @@
                   throw new DirectoryException(ResultCode.INVALID_CREDENTIALS,
                       ERR_LDAP_PTA_MAPPED_SEARCH_FAILED.get(
                           String.valueOf(userEntry.getDN()),
-                          String.valueOf(configuration.dn()),
-                          e.getMessageObject()), e);
+                          String.valueOf(cfg.dn()), e.getMessageObject()), e);
                 }
               }
               finally
@@ -1487,8 +1456,7 @@
               throw new DirectoryException(ResultCode.INVALID_CREDENTIALS,
                   ERR_LDAP_PTA_MAPPED_SEARCH_NO_CANDIDATES.get(
                       String.valueOf(userEntry.getDN()),
-                      String.valueOf(configuration.dn()),
-                      String.valueOf(filter)));
+                      String.valueOf(cfg.dn()), String.valueOf(filter)));
             }
 
             break;
@@ -1513,12 +1481,10 @@
               // We don't want to propagate this internal error to the
               // client. We should log it and map it to a more appropriate
               // error.
-              throw new DirectoryException(
-                  ResultCode.INVALID_CREDENTIALS,
+              throw new DirectoryException(ResultCode.INVALID_CREDENTIALS,
                   ERR_LDAP_PTA_MAPPED_BIND_FAILED.get(
                       String.valueOf(userEntry.getDN()),
-                      String.valueOf(configuration.dn()), e.getMessageObject()),
-                  e);
+                      String.valueOf(cfg.dn()), e.getMessageObject()), e);
             }
           }
           finally
@@ -1544,7 +1510,7 @@
     private final WriteLock exclusiveLock = lock.writeLock();
 
     // Current configuration.
-    private LDAPPassThroughAuthenticationPolicyCfg configuration;
+    private LDAPPassThroughAuthenticationPolicyCfg cfg;
 
     private FailoverConnectionFactory searchFactory = null;
     private FailoverConnectionFactory bindFactory = null;
@@ -1564,13 +1530,13 @@
      */
     @Override
     public ConfigChangeResult applyConfigurationChange(
-        final LDAPPassThroughAuthenticationPolicyCfg configuration)
+        final LDAPPassThroughAuthenticationPolicyCfg cfg)
     {
       exclusiveLock.lock();
       try
       {
         closeConnections();
-        initializeConfiguration(configuration);
+        initializeConfiguration(cfg);
       }
       finally
       {
@@ -1603,7 +1569,7 @@
       exclusiveLock.lock();
       try
       {
-        configuration.removeLDAPPassThroughChangeListener(this);
+        cfg.removeLDAPPassThroughChangeListener(this);
         closeConnections();
       }
       finally
@@ -1620,7 +1586,7 @@
     @Override
     public DN getDN()
     {
-      return configuration.dn();
+      return cfg.dn();
     }
 
 
@@ -1630,11 +1596,11 @@
      */
     @Override
     public boolean isConfigurationChangeAcceptable(
-        final LDAPPassThroughAuthenticationPolicyCfg configuration,
+        final LDAPPassThroughAuthenticationPolicyCfg cfg,
         final List<Message> unacceptableReasons)
     {
       return LDAPPassThroughAuthenticationPolicyFactory.this
-          .isConfigurationAcceptable(configuration, unacceptableReasons);
+          .isConfigurationAcceptable(cfg, unacceptableReasons);
     }
 
 
@@ -1666,9 +1632,9 @@
 
 
     private void initializeConfiguration(
-        final LDAPPassThroughAuthenticationPolicyCfg configuration)
+        final LDAPPassThroughAuthenticationPolicyCfg cfg)
     {
-      this.configuration = configuration;
+      this.cfg = cfg;
 
       // Use two pools per server: one for authentication (bind) and one for
       // searches. Even if the searches are performed anonymously we cannot use
@@ -1679,7 +1645,7 @@
       final LoadBalancer primarySearchLoadBalancer;
       final LoadBalancer primaryBindLoadBalancer;
 
-      Set<String> servers = configuration.getPrimaryRemoteLDAPServer();
+      Set<String> servers = cfg.getPrimaryRemoteLDAPServer();
       ConnectionPool[] searchPool = new ConnectionPool[servers.size()];
       ConnectionPool[] bindPool = new ConnectionPool[servers.size()];
       int index = 0;
@@ -1687,7 +1653,9 @@
       {
         final ConnectionFactory factory = newLDAPConnectionFactory(hostPort);
         searchPool[index] = new ConnectionPool(
-            new AuthenticatedConnectionFactory(factory));
+            new AuthenticatedConnectionFactory(factory,
+                cfg.getMappedSearchBindDN(),
+                cfg.getMappedSearchBindPassword()));
         bindPool[index++] = new ConnectionPool(factory);
       }
       primarySearchLoadBalancer = new LoadBalancer(searchPool);
@@ -1697,7 +1665,7 @@
       final LoadBalancer secondarySearchLoadBalancer;
       final LoadBalancer secondaryBindLoadBalancer;
 
-      servers = configuration.getSecondaryRemoteLDAPServer();
+      servers = cfg.getSecondaryRemoteLDAPServer();
       if (servers.isEmpty())
       {
         secondarySearchLoadBalancer = null;
@@ -1712,7 +1680,9 @@
         {
           final ConnectionFactory factory = newLDAPConnectionFactory(hostPort);
           searchPool[index] = new ConnectionPool(
-              new AuthenticatedConnectionFactory(factory));
+              new AuthenticatedConnectionFactory(factory,
+                  cfg.getMappedSearchBindDN(),
+                  cfg.getMappedSearchBindPassword()));
           bindPool[index++] = new ConnectionPool(factory);
         }
         secondarySearchLoadBalancer = new LoadBalancer(searchPool);
@@ -1733,7 +1703,7 @@
       final int colonIndex = hostPort.lastIndexOf(":");
       final String hostname = hostPort.substring(0, colonIndex);
       final int port = Integer.parseInt(hostPort.substring(colonIndex + 1));
-      return provider.getLDAPConnectionFactory(hostname, port, configuration);
+      return provider.getLDAPConnectionFactory(hostname, port, cfg);
     }
 
   }
@@ -1766,9 +1736,9 @@
 
     @Override
     public ConnectionFactory getLDAPConnectionFactory(final String host,
-        final int port, final LDAPPassThroughAuthenticationPolicyCfg options)
+        final int port, final LDAPPassThroughAuthenticationPolicyCfg cfg)
     {
-      return new LDAPConnectionFactory(host, port, options);
+      return new LDAPConnectionFactory(host, port, cfg);
     }
 
   };
@@ -1809,6 +1779,51 @@
 
 
 
+  private static boolean isServerAddressValid(
+      final LDAPPassThroughAuthenticationPolicyCfg configuration,
+      final List<Message> unacceptableReasons, final String hostPort)
+  {
+    final int colonIndex = hostPort.lastIndexOf(":");
+    final int port = Integer.parseInt(hostPort.substring(colonIndex + 1));
+    if (port < 1 || port > 65535)
+    {
+      if (unacceptableReasons != null)
+      {
+        final Message msg = ERR_LDAP_PTA_INVALID_PORT_NUMBER.get(
+            String.valueOf(configuration.dn()), hostPort);
+        unacceptableReasons.add(msg);
+      }
+      return false;
+    }
+    return true;
+  }
+
+
+
+  private static String mappedAttributesAsString(
+      final Collection<AttributeType> attributes)
+  {
+    switch (attributes.size())
+    {
+    case 0:
+      return "";
+    case 1:
+      return attributes.iterator().next().getNameOrOID();
+    default:
+      final StringBuilder builder = new StringBuilder();
+      final Iterator<AttributeType> i = attributes.iterator();
+      builder.append(i.next().getNameOrOID());
+      while (i.hasNext())
+      {
+        builder.append(", ");
+        builder.append(i.next().getNameOrOID());
+      }
+      return builder.toString();
+    }
+  }
+
+
+
   /**
    * Public default constructor used by the admin framework. This will use the
    * default LDAP connection factory provider.
@@ -1864,13 +1879,13 @@
     // capabilities).
     boolean configurationIsAcceptable = true;
 
-    for (String hostPort : configuration.getPrimaryRemoteLDAPServer())
+    for (final String hostPort : configuration.getPrimaryRemoteLDAPServer())
     {
       configurationIsAcceptable &= isServerAddressValid(configuration,
           unacceptableReasons, hostPort);
     }
 
-    for (String hostPort : configuration.getSecondaryRemoteLDAPServer())
+    for (final String hostPort : configuration.getSecondaryRemoteLDAPServer())
     {
       configurationIsAcceptable &= isServerAddressValid(configuration,
           unacceptableReasons, hostPort);
@@ -1878,49 +1893,4 @@
 
     return configurationIsAcceptable;
   }
-
-
-
-  private static boolean isServerAddressValid(
-      final LDAPPassThroughAuthenticationPolicyCfg configuration,
-      final List<Message> unacceptableReasons, String hostPort)
-  {
-    final int colonIndex = hostPort.lastIndexOf(":");
-    final int port = Integer.parseInt(hostPort.substring(colonIndex + 1));
-    if (port < 1 || port > 65535)
-    {
-      if (unacceptableReasons != null)
-      {
-        Message msg = ERR_LDAP_PTA_INVALID_PORT_NUMBER.get(
-            String.valueOf(configuration.dn()), hostPort);
-        unacceptableReasons.add(msg);
-      }
-      return false;
-    }
-    return true;
-  }
-
-
-
-  private static String mappedAttributesAsString(
-      Collection<AttributeType> attributes)
-  {
-    switch (attributes.size())
-    {
-    case 0:
-      return "";
-    case 1:
-      return attributes.iterator().next().getNameOrOID();
-    default:
-      StringBuilder builder = new StringBuilder();
-      Iterator<AttributeType> i = attributes.iterator();
-      builder.append(i.next().getNameOrOID());
-      while (i.hasNext())
-      {
-        builder.append(", ");
-        builder.append(i.next().getNameOrOID());
-      }
-      return builder.toString();
-    }
-  }
 }
diff --git a/opends/tests/unit-tests-testng/src/server/org/opends/server/extensions/LDAPPassThroughAuthenticationPolicyTestCase.java b/opends/tests/unit-tests-testng/src/server/org/opends/server/extensions/LDAPPassThroughAuthenticationPolicyTestCase.java
index d6fe354..d43921d 100644
--- a/opends/tests/unit-tests-testng/src/server/org/opends/server/extensions/LDAPPassThroughAuthenticationPolicyTestCase.java
+++ b/opends/tests/unit-tests-testng/src/server/org/opends/server/extensions/LDAPPassThroughAuthenticationPolicyTestCase.java
@@ -403,6 +403,16 @@
       }
     }
 
+
+
+    /**
+     * {@inheritDoc}
+     */
+    public void close()
+    {
+      // Nothing to do.
+    }
+
   }
 
 
@@ -1200,22 +1210,15 @@
   private final String phost2 = "phost2:22";
   private final String phost3 = "phost3:33";
   private final String shost1 = "shost1:11";
-  private final String shost2 = "shost2:22";
-
-  private final String shost3 = "shost3:33";
   private DN policyDN;
-
   private final String policyDNString = "cn=test policy,o=test";
   private DN searchBindDN;
-
   private final String searchBindDNString = "cn=search bind dn";
   private DN trustManagerDN;
-
   private final String trustManagerDNString = "cn=ignored";
   private final String adDNString = "uid=aduser,o=ad";
   private final String opendjDNString = "cn=test user,o=opendj";
   private Entry userEntry;
-
   private final String userPassword = "password";
 
 

--
Gitblit v1.10.0