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

Matthew Swift
25.33.2012 263d085885df024dca9250cc03c807912b0a7662
opendj3/opendj-ldap-sdk/src/main/java/com/forgerock/opendj/ldap/LDAPConnectionFactoryImpl.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,8 +27,6 @@
package com.forgerock.opendj.ldap;
import static org.forgerock.opendj.ldap.ErrorResultException.newErrorResult;
import java.io.IOException;
@@ -38,7 +35,13 @@
import javax.net.ssl.SSLEngine;
import org.forgerock.opendj.ldap.*;
import org.forgerock.opendj.ldap.Connection;
import org.forgerock.opendj.ldap.ConnectionFactory;
import org.forgerock.opendj.ldap.ErrorResultException;
import org.forgerock.opendj.ldap.FutureResult;
import org.forgerock.opendj.ldap.LDAPOptions;
import org.forgerock.opendj.ldap.ResultCode;
import org.forgerock.opendj.ldap.ResultHandler;
import org.forgerock.opendj.ldap.requests.Requests;
import org.forgerock.opendj.ldap.requests.StartTLSExtendedRequest;
import org.forgerock.opendj.ldap.responses.ExtendedResult;
@@ -54,306 +57,226 @@
import com.forgerock.opendj.util.FutureResultTransformer;
import com.forgerock.opendj.util.RecursiveFutureResult;
/**
 * LDAP connection factory implementation.
 */
public final class LDAPConnectionFactoryImpl implements ConnectionFactory
{
public final class LDAPConnectionFactoryImpl implements ConnectionFactory {
  @SuppressWarnings("rawtypes")
  private final class ConnectionCompletionHandler implements
      CompletionHandler<org.glassfish.grizzly.Connection>
  {
    private final FutureResultTransformer<Result, Connection> startTLSFutureResult;
    private final RecursiveFutureResult<LDAPConnection, ExtendedResult> connectionFutureResult;
    private LDAPConnection connection;
    @SuppressWarnings("rawtypes")
    private final class ConnectionCompletionHandler implements
            CompletionHandler<org.glassfish.grizzly.Connection> {
        private final FutureResultTransformer<Result, Connection> startTLSFutureResult;
        private final RecursiveFutureResult<LDAPConnection, ExtendedResult> connectionFutureResult;
        private LDAPConnection connection;
        private ConnectionCompletionHandler(final ResultHandler<? super Connection> handler) {
            this.startTLSFutureResult = new FutureResultTransformer<Result, Connection>(handler) {
    private ConnectionCompletionHandler(final ResultHandler<? super Connection> handler)
    {
      this.startTLSFutureResult = new FutureResultTransformer<Result, Connection>(
          handler)
      {
        @Override
        protected ErrorResultException transformErrorResult(
            final ErrorResultException errorResult)
        {
          // Ensure that the connection is closed.
          try
          {
            if (connection != null)
            {
              connection.close();
            }
          }
          catch (final Exception e)
          {
            // Ignore.
          }
          return errorResult;
        }
        @Override
        protected LDAPConnection transformResult(final Result result)
            throws ErrorResultException
        {
          return connection;
        }
      };
      this.connectionFutureResult = new RecursiveFutureResult<LDAPConnection, ExtendedResult>(
          startTLSFutureResult)
      {
        @Override
        protected FutureResult<? extends ExtendedResult> chainResult(
            final LDAPConnection innerResult,
            final ResultHandler<? super ExtendedResult> handler)
            throws ErrorResultException
        {
          connection = innerResult;
          if (options.getSSLContext() != null && options.useStartTLS())
          {
            // Chain StartTLS extended request.
            final StartTLSExtendedRequest startTLS = Requests
                .newStartTLSExtendedRequest(options.getSSLContext());
            startTLS.addEnabledCipherSuite(options.getEnabledCipherSuites()
                .toArray(new String[options.getEnabledCipherSuites().size()]));
            startTLS.addEnabledProtocol(options.getEnabledProtocols().toArray(
                new String[options.getEnabledProtocols().size()]));
            return connection.extendedRequestAsync(startTLS, null, handler);
          }
          else if (options.getSSLContext() != null)
          {
            // Install SSL/TLS layer.
            try
            {
              connection.startTLS(options.getSSLContext(),
                  options.getEnabledProtocols(),
                  options.getEnabledCipherSuites(),
                  new EmptyCompletionHandler<SSLEngine>()
                  {
                    @Override
                    public void completed(final SSLEngine result)
                    {
                      handler.handleResult(null);
                @Override
                protected ErrorResultException transformErrorResult(
                        final ErrorResultException errorResult) {
                    // Ensure that the connection is closed.
                    try {
                        if (connection != null) {
                            connection.close();
                        }
                    } catch (final Exception e) {
                        // Ignore.
                    }
                    return errorResult;
                }
                @Override
                protected LDAPConnection transformResult(final Result result)
                        throws ErrorResultException {
                    return connection;
                }
            };
                    @Override
                    public void failed(final Throwable throwable)
                    {
                      handler.handleErrorResult(newErrorResult(
                          ResultCode.CLIENT_SIDE_CONNECT_ERROR,
                          throwable.getMessage(), throwable));
                    }
                  });
              return null;
            }
            catch (final IOException ioe)
            {
              throw newErrorResult(ResultCode.CLIENT_SIDE_CONNECT_ERROR,
                  ioe.getMessage(), ioe);
            }
          }
          else
          {
            // Plain connection.
            handler.handleResult(null);
            return new CompletedFutureResult<ExtendedResult>(
                (ExtendedResult) null);
          }
            this.connectionFutureResult =
                    new RecursiveFutureResult<LDAPConnection, ExtendedResult>(startTLSFutureResult) {
                        @Override
                        protected FutureResult<? extends ExtendedResult> chainResult(
                                final LDAPConnection innerResult,
                                final ResultHandler<? super ExtendedResult> handler)
                                throws ErrorResultException {
                            connection = innerResult;
                            if (options.getSSLContext() != null && options.useStartTLS()) {
                                // Chain StartTLS extended request.
                                final StartTLSExtendedRequest startTLS =
                                        Requests.newStartTLSExtendedRequest(options.getSSLContext());
                                startTLS.addEnabledCipherSuite(options
                                        .getEnabledCipherSuites()
                                        .toArray(
                                                new String[options.getEnabledCipherSuites().size()]));
                                startTLS.addEnabledProtocol(options.getEnabledProtocols().toArray(
                                        new String[options.getEnabledProtocols().size()]));
                                return connection.extendedRequestAsync(startTLS, null, handler);
                            } else if (options.getSSLContext() != null) {
                                // Install SSL/TLS layer.
                                try {
                                    connection.startTLS(options.getSSLContext(), options
                                            .getEnabledProtocols(), options
                                            .getEnabledCipherSuites(),
                                            new EmptyCompletionHandler<SSLEngine>() {
                                                @Override
                                                public void completed(final SSLEngine result) {
                                                    handler.handleResult(null);
                                                }
                                                @Override
                                                public void failed(final Throwable throwable) {
                                                    handler.handleErrorResult(newErrorResult(
                                                            ResultCode.CLIENT_SIDE_CONNECT_ERROR,
                                                            throwable.getMessage(), throwable));
                                                }
                                            });
                                    return null;
                                } catch (final IOException ioe) {
                                    throw newErrorResult(ResultCode.CLIENT_SIDE_CONNECT_ERROR, ioe
                                            .getMessage(), ioe);
                                }
                            } else {
                                // Plain connection.
                                handler.handleResult(null);
                                return new CompletedFutureResult<ExtendedResult>(
                                        (ExtendedResult) null);
                            }
                        }
                    };
            startTLSFutureResult.setFutureResult(connectionFutureResult);
        }
      };
        /**
         * {@inheritDoc}
         */
        @Override
        public void cancelled() {
            // Ignore this.
        }
      startTLSFutureResult.setFutureResult(connectionFutureResult);
        /**
         * {@inheritDoc}
         */
        @Override
        public void completed(final org.glassfish.grizzly.Connection connection) {
            connectionFutureResult.handleResult(adaptConnection(connection));
        }
        /**
         * {@inheritDoc}
         */
        @Override
        public void failed(final Throwable throwable) {
            connectionFutureResult.handleErrorResult(adaptConnectionException(throwable));
        }
        /**
         * {@inheritDoc}
         */
        @Override
        public void updated(final org.glassfish.grizzly.Connection connection) {
            // Ignore this.
        }
    }
    private final SocketAddress socketAddress;
    private final TCPNIOTransport transport;
    private final FilterChain defaultFilterChain;
    private final LDAPClientFilter clientFilter;
    private final LDAPOptions options;
    /**
     * Creates a new LDAP connection factory implementation which can be used to
     * create connections to the Directory Server at the provided host and port
     * address using provided connection options.
     *
     * @param address
     *            The address of the Directory Server to connect to.
     * @param options
     *            The LDAP connection options to use when creating connections.
     */
    public LDAPConnectionFactoryImpl(final SocketAddress address, final LDAPOptions options) {
        if (options.getTCPNIOTransport() == null) {
            this.transport = DefaultTCPNIOTransport.getInstance();
        } else {
            this.transport = options.getTCPNIOTransport();
        }
        this.socketAddress = address;
        this.options = new LDAPOptions(options);
        this.clientFilter =
                new LDAPClientFilter(new LDAPReader(this.options.getDecodeOptions()), 0);
        this.defaultFilterChain =
                FilterChainBuilder.stateless().add(new TransportFilter()).add(clientFilter).build();
    }
    /**
     * {@inheritDoc}
     */
    @Override
    public void cancelled()
    {
      // Ignore this.
    public Connection getConnection() throws ErrorResultException, InterruptedException {
        return getConnectionAsync(null).get();
    }
    /**
     * {@inheritDoc}
     */
    @Override
    public void completed(final org.glassfish.grizzly.Connection connection)
    {
      connectionFutureResult.handleResult(adaptConnection(connection));
    public FutureResult<Connection> getConnectionAsync(
            final ResultHandler<? super Connection> handler) {
        final ConnectionCompletionHandler ch = new ConnectionCompletionHandler(handler);
        try {
            ch.connectionFutureResult.setFutureResult(transport.connect(socketAddress, ch));
            return ch.startTLSFutureResult;
        } catch (final IOException e) {
            final ErrorResultException result = adaptConnectionException(e);
            return new CompletedFutureResult<Connection>(result);
        }
    }
    /**
     * Returns the address of the Directory Server.
     *
     * @return The address of the Directory Server.
     */
    public SocketAddress getSocketAddress() {
        return socketAddress;
    }
    /**
     * {@inheritDoc}
     */
    @Override
    public void failed(final Throwable throwable)
    {
      connectionFutureResult
          .handleErrorResult(adaptConnectionException(throwable));
    public String toString() {
        final StringBuilder builder = new StringBuilder();
        builder.append("LDAPConnectionFactory(");
        builder.append(getSocketAddress().toString());
        builder.append(')');
        return builder.toString();
    }
    private LDAPConnection adaptConnection(final org.glassfish.grizzly.Connection<?> connection) {
        // Test shows that its much faster with non block writes but risk
        // running out of memory if the server is slow.
        connection.configureBlocking(true);
        connection.setProcessor(defaultFilterChain);
    /**
     * {@inheritDoc}
     */
    @Override
    public void updated(final org.glassfish.grizzly.Connection connection)
    {
      // Ignore this.
        final LDAPConnection ldapConnection = new LDAPConnection(connection, options);
        clientFilter.registerConnection(connection, ldapConnection);
        return ldapConnection;
    }
  }
    private ErrorResultException adaptConnectionException(Throwable t) {
        if (t instanceof ExecutionException) {
            t = t.getCause();
        }
  private final SocketAddress socketAddress;
  private final TCPNIOTransport transport;
  private final FilterChain defaultFilterChain;
  private final LDAPClientFilter clientFilter;
  private final LDAPOptions options;
  /**
   * Creates a new LDAP connection factory implementation which can be used to
   * create connections to the Directory Server at the provided host and port
   * address using provided connection options.
   *
   * @param address
   *          The address of the Directory Server to connect to.
   * @param options
   *          The LDAP connection options to use when creating connections.
   */
  public LDAPConnectionFactoryImpl(final SocketAddress address,
      final LDAPOptions options)
  {
    if (options.getTCPNIOTransport() == null)
    {
      this.transport = DefaultTCPNIOTransport.getInstance();
        return newErrorResult(ResultCode.CLIENT_SIDE_CONNECT_ERROR, t.getMessage(), t);
    }
    else
    {
      this.transport = options.getTCPNIOTransport();
    }
    this.socketAddress = address;
    this.options = new LDAPOptions(options);
    this.clientFilter = new LDAPClientFilter(new LDAPReader(
        this.options.getDecodeOptions()), 0);
    this.defaultFilterChain = FilterChainBuilder.stateless()
        .add(new TransportFilter())
        .add(clientFilter)
        .build();
  }
  /**
   * {@inheritDoc}
   */
  @Override
  public Connection getConnection() throws ErrorResultException,
      InterruptedException
  {
    return getConnectionAsync(null).get();
  }
  /**
   * {@inheritDoc}
   */
  @Override
  public FutureResult<Connection> getConnectionAsync(
      final ResultHandler<? super Connection> handler)
  {
    final ConnectionCompletionHandler ch = new ConnectionCompletionHandler(handler);
    try
    {
      ch.connectionFutureResult.setFutureResult(transport.connect(socketAddress, ch));
      return ch.startTLSFutureResult;
    }
    catch (final IOException e)
    {
      final ErrorResultException result = adaptConnectionException(e);
      return new CompletedFutureResult<Connection>(result);
    }
  }
  /**
   * Returns the address of the Directory Server.
   *
   * @return The address of the Directory Server.
   */
  public SocketAddress getSocketAddress()
  {
    return socketAddress;
  }
  /**
   * {@inheritDoc}
   */
  @Override
  public String toString()
  {
    final StringBuilder builder = new StringBuilder();
    builder.append("LDAPConnectionFactory(");
    builder.append(getSocketAddress().toString());
    builder.append(')');
    return builder.toString();
  }
  private LDAPConnection adaptConnection(
      final org.glassfish.grizzly.Connection<?> connection)
  {
    // Test shows that its much faster with non block writes but risk
    // running out of memory if the server is slow.
    connection.configureBlocking(true);
    connection.setProcessor(defaultFilterChain);
    final LDAPConnection ldapConnection = new LDAPConnection(connection,
        options);
    clientFilter.registerConnection(connection, ldapConnection);
    return ldapConnection;
  }
  private ErrorResultException adaptConnectionException(Throwable t)
  {
    if (t instanceof ExecutionException)
    {
      t = t.getCause();
    }
    return newErrorResult(ResultCode.CLIENT_SIDE_CONNECT_ERROR, t.getMessage(),
        t);
  }
}