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

Matthew Swift
27.33.2014 0589dbf6c4e36eb5575982d24ab5a0fb16d134e4
Fix OPENDJ-1197: API is lacking functionality to specify TCP connect timeout

* added support for setting connect timeout in LDAPOptions
* added support for other common socket options: TCP nodelay, keepalive, linger, and reuseaddr
1 files added
10 files modified
1156 ■■■■■ changed files
opendj-core/clirr-ignored-api-changes.xml 13 ●●●●● patch | view | raw | blame | history
opendj-core/src/main/java/com/forgerock/opendj/util/AsynchronousFutureResult.java 88 ●●●● patch | view | raw | blame | history
opendj-core/src/main/java/org/forgerock/opendj/ldap/CommonLDAPOptions.java 333 ●●●●● patch | view | raw | blame | history
opendj-core/src/main/java/org/forgerock/opendj/ldap/LDAPListenerOptions.java 126 ●●●●● patch | view | raw | blame | history
opendj-core/src/main/java/org/forgerock/opendj/ldap/LDAPOptions.java 328 ●●●●● patch | view | raw | blame | history
opendj-grizzly/src/main/java/org/forgerock/opendj/grizzly/GrizzlyLDAPConnectionFactory.java 78 ●●●●● patch | view | raw | blame | history
opendj-grizzly/src/main/java/org/forgerock/opendj/grizzly/GrizzlyLDAPListener.java 12 ●●●●● patch | view | raw | blame | history
opendj-grizzly/src/main/java/org/forgerock/opendj/grizzly/GrizzlyUtils.java 49 ●●●● patch | view | raw | blame | history
opendj-grizzly/src/main/java/org/forgerock/opendj/grizzly/LDAPServerFilter.java 11 ●●●● patch | view | raw | blame | history
opendj-grizzly/src/main/resources/com/forgerock/opendj/grizzly/grizzly.properties 4 ●●● patch | view | raw | blame | history
opendj-grizzly/src/test/java/org/forgerock/opendj/grizzly/GrizzlyLDAPConnectionFactoryTestCase.java 114 ●●●●● patch | view | raw | blame | history
opendj-core/clirr-ignored-api-changes.xml
@@ -165,4 +165,17 @@
    <differenceType>8001</differenceType>
    <justification>Renamed SchemaValidationPolicy.Policy to SchemaValidationPolicy.Action</justification>
  </difference>
  <difference>
    <className>org/forgerock/opendj/ldap/LDAPListenerOptions</className>
    <differenceType>7002</differenceType>
    <method>org.forgerock.opendj.ldap.LDAPListenerOptions setDecodeOptions(org.forgerock.opendj.ldap.DecodeOptions)</method>
    <justification>OPENDJ-1197: Method return type has changed due to reification</justification>
  </difference>
  <difference>
    <className>org/forgerock/opendj/ldap/LDAPOptions</className>
    <differenceType>7002</differenceType>
    <method>org.forgerock.opendj.ldap.LDAPOptions setDecodeOptions(org.forgerock.opendj.ldap.DecodeOptions)</method>
    <justification>OPENDJ-1197: Method return type has changed due to reification</justification>
  </difference>
</differences>
opendj-core/src/main/java/com/forgerock/opendj/util/AsynchronousFutureResult.java
@@ -22,7 +22,7 @@
 *
 *
 *      Copyright 2009-2010 Sun Microsystems, Inc.
 *      Portions copyright 2013 ForgeRock AS.
 *      Portions copyright 2013-2014 ForgeRock AS.
 */
package com.forgerock.opendj.util;
@@ -162,34 +162,36 @@
            return getState() > 1;
        }
        void innerSetErrorResult(final ErrorResultException errorResult) {
            if (setStatePending()) {
                this.errorResult = errorResult;
                try {
                    // Invoke error result completion handler.
                    if (handler != null) {
                        handler.handleErrorResult(errorResult);
                    }
                } finally {
                    releaseShared(FAIL); // Publishes errorResult.
                }
        boolean innerSetErrorResult(final ErrorResultException errorResult) {
            if (!setStatePending()) {
                return false;
            }
            this.errorResult = errorResult;
            try {
                // Invoke error result completion handler.
                if (handler != null) {
                    handler.handleErrorResult(errorResult);
                }
            } finally {
                releaseShared(FAIL); // Publishes errorResult.
            }
            return true;
        }
        void innerSetResult(final M result) {
            if (setStatePending()) {
                this.result = result;
                try {
                    // Invoke result completion handler.
                    if (handler != null) {
                        handler.handleResult(result);
                    }
                } finally {
                    releaseShared(SUCCESS); // Publishes result.
                }
        boolean innerSetResult(final M result) {
            if (!setStatePending()) {
                return false;
            }
            this.result = result;
            try {
                // Invoke result completion handler.
                if (handler != null) {
                    handler.handleResult(result);
                }
            } finally {
                releaseShared(SUCCESS); // Publishes result.
            }
            return true;
        }
        private M get0() throws ErrorResultException {
@@ -316,6 +318,42 @@
    }
    /**
     * Attempts to set the error result associated with this future. If (i.e.
     * {@code isDone() == true}) then the error result will be ignored and
     * {@code false} will be returned, otherwise the result handler will be
     * invoked if one was provided and, on returning {@code true}, any threads
     * waiting on {@link #get} will be released and the provided error result
     * will be thrown.
     *
     * @param errorResult
     *            The error result.
     * @return {@code false} if this future has already been completed, either
     *         due to normal termination, an exception, or cancellation (i.e.
     *         {@code isDone() == true}).
     */
    public final boolean tryHandleErrorResult(final ErrorResultException errorResult) {
        return sync.innerSetErrorResult(errorResult);
    }
    /**
     * Attempts to set the result associated with this future. If (i.e.
     * {@code isDone() == true}) then the result will be ignored and
     * {@code false} will be returned, otherwise the result handler will be
     * invoked if one was provided and, on returning {@code true}, any threads
     * waiting on {@link #get} will be released and the provided result will be
     * returned.
     *
     * @param result
     *            The result.
     * @return {@code false} if this future has already been completed, either
     *         due to normal termination, an exception, or cancellation (i.e.
     *         {@code isDone() == true}).
     */
    public final boolean tryHandleResult(final M result) {
        return sync.innerSetResult(result);
    }
    /**
     * {@inheritDoc}
     */
    @Override
opendj-core/src/main/java/org/forgerock/opendj/ldap/CommonLDAPOptions.java
New file
@@ -0,0 +1,333 @@
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License, Version 1.0 only
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * 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 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
 *
 *      Copyright 2014 ForgeRock AS.
 */
package org.forgerock.opendj.ldap;
import org.forgerock.util.Reject;
/**
 * Common options for LDAP clients and listeners.
 */
abstract class CommonLDAPOptions<T extends CommonLDAPOptions<T>> {
    // Default values for options taken from Java properties.
    private static final boolean DEFAULT_TCP_NO_DELAY;
    private static final boolean DEFAULT_REUSE_ADDRESS;
    private static final boolean DEFAULT_KEEPALIVE;
    private static final int DEFAULT_LINGER;
    static {
        DEFAULT_LINGER = getIntProperty("org.forgerock.opendj.io.linger", -1);
        DEFAULT_TCP_NO_DELAY = getBooleanProperty("org.forgerock.opendj.io.tcpNoDelay", true);
        DEFAULT_REUSE_ADDRESS = getBooleanProperty("org.forgerock.opendj.io.reuseAddress", true);
        DEFAULT_KEEPALIVE = getBooleanProperty("org.forgerock.opendj.io.keepAlive", true);
    }
    static boolean getBooleanProperty(final String name, final boolean defaultValue) {
        final String value = System.getProperty(name);
        return value != null ? Boolean.parseBoolean(value) : defaultValue;
    }
    static int getIntProperty(final String name, final int defaultValue) {
        final String value = System.getProperty(name);
        try {
            return value != null ? Integer.parseInt(value) : defaultValue;
        } catch (final NumberFormatException e) {
            return defaultValue;
        }
    }
    private DecodeOptions decodeOptions;
    private ClassLoader providerClassLoader;
    private String transportProvider;
    private boolean tcpNoDelay = DEFAULT_TCP_NO_DELAY;
    private boolean keepAlive = DEFAULT_KEEPALIVE;
    private boolean reuseAddress = DEFAULT_REUSE_ADDRESS;
    private int linger = DEFAULT_LINGER;
    CommonLDAPOptions() {
        this.decodeOptions = new DecodeOptions();
    }
    CommonLDAPOptions(final CommonLDAPOptions<?> options) {
        this.decodeOptions = new DecodeOptions(options.decodeOptions);
        this.providerClassLoader = options.providerClassLoader;
        this.transportProvider = options.transportProvider;
        this.linger = options.linger;
        this.keepAlive = options.keepAlive;
        this.reuseAddress = options.reuseAddress;
        this.tcpNoDelay = options.tcpNoDelay;
    }
    /**
     * Returns the decoding options which will be used to control how requests
     * and responses are decoded.
     *
     * @return The decoding options which will be used to control how requests
     *         and responses are decoded (never {@code null}).
     */
    public DecodeOptions getDecodeOptions() {
        return decodeOptions;
    }
    /**
     * Returns the value of the {@link java.net.StandardSocketOptions#SO_LINGER
     * SO_LINGER} socket option for new connections.
     * <p>
     * The default setting is {@code -1} (disabled) and may be configured using
     * the {@code org.forgerock.opendj.io.linger} property.
     *
     * @return The value of the {@link java.net.StandardSocketOptions#SO_LINGER
     *         SO_LINGER} socket option for new connections, or -1 if linger
     *         should be disabled.
     */
    public int getLinger() {
        return linger;
    }
    /**
     * Returns the class loader which will be used to load the
     * {@code TransportProvider}.
     * <p>
     * By default this method will return {@code null} indicating that the
     * default class loader will be used.
     * <p>
     * The transport provider is loaded using {@code java.util.ServiceLoader},
     * the JDK service-provider loading facility. The provider must be
     * accessible from the same class loader that was initially queried to
     * locate the configuration file; note that this is not necessarily the
     * class loader from which the file was actually loaded. This method allows
     * to provide a class loader to be used for loading the provider.
     *
     * @return The class loader which will be used when loading the transport
     *         provider, or {@code null} if the default class loader should be
     *         used.
     */
    public ClassLoader getProviderClassLoader() {
        return providerClassLoader;
    }
    /**
     * Returns the name of the provider used for transport.
     * <p>
     * Transport providers implement {@code TransportProvider} interface.
     * <p>
     * The name should correspond to the name of an existing provider, as
     * returned by {@code TransportProvider#getName()} method.
     *
     * @return The name of transport provider. The name is {@code null} if no
     *         specific provider has been selected. In that case, the first
     *         provider found will be used.
     */
    public String getTransportProvider() {
        return transportProvider;
    }
    /**
     * Returns the value of the
     * {@link java.net.StandardSocketOptions#SO_KEEPALIVE SO_KEEPALIVE} socket
     * option for new connections.
     * <p>
     * The default setting is {@code true} and may be configured using the
     * {@code org.forgerock.opendj.io.keepAlive} property.
     *
     * @return The value of the
     *         {@link java.net.StandardSocketOptions#SO_KEEPALIVE SO_KEEPALIVE}
     *         socket option for new connections.
     */
    public boolean isKeepAlive() {
        return keepAlive;
    }
    /**
     * Returns the value of the
     * {@link java.net.StandardSocketOptions#SO_REUSEADDR SO_REUSEADDR} socket
     * option for new connections.
     * <p>
     * The default setting is {@code true} and may be configured using the
     * {@code org.forgerock.opendj.io.reuseAddress} property.
     *
     * @return The value of the
     *         {@link java.net.StandardSocketOptions#SO_REUSEADDR SO_REUSEADDR}
     *         socket option for new connections.
     */
    public boolean isReuseAddress() {
        return reuseAddress;
    }
    /**
     * Returns the value of the
     * {@link java.net.StandardSocketOptions#TCP_NODELAY TCP_NODELAY} socket
     * option for new connections.
     * <p>
     * The default setting is {@code true} and may be configured using the
     * {@code org.forgerock.opendj.io.tcpNoDelay} property.
     *
     * @return The value of the
     *         {@link java.net.StandardSocketOptions#TCP_NODELAY TCP_NODELAY}
     *         socket option for new connections.
     */
    public boolean isTCPNoDelay() {
        return tcpNoDelay;
    }
    /**
     * Sets the decoding options which will be used to control how requests and
     * responses are decoded.
     *
     * @param decodeOptions
     *            The decoding options which will be used to control how
     *            requests and responses are decoded (never {@code null}).
     * @return A reference to this set of options.
     * @throws NullPointerException
     *             If {@code decodeOptions} was {@code null}.
     */
    public T setDecodeOptions(final DecodeOptions decodeOptions) {
        Reject.ifNull(decodeOptions);
        this.decodeOptions = decodeOptions;
        return getThis();
    }
    /**
     * Specifies the value of the
     * {@link java.net.StandardSocketOptions#SO_KEEPALIVE SO_KEEPALIVE} socket
     * option for new connections.
     * <p>
     * The default setting is {@code true} and may be configured using the
     * {@code org.forgerock.opendj.io.keepAlive} property.
     *
     * @param keepAlive
     *            The value of the
     *            {@link java.net.StandardSocketOptions#SO_KEEPALIVE
     *            SO_KEEPALIVE} socket option for new connections.
     * @return A reference to this set of options.
     */
    public T setKeepAlive(final boolean keepAlive) {
        this.keepAlive = keepAlive;
        return getThis();
    }
    /**
     * Specifies the value of the
     * {@link java.net.StandardSocketOptions#SO_LINGER SO_LINGER} socket option
     * for new connections.
     * <p>
     * The default setting is {@code -1} (disabled) and may be configured using
     * the {@code org.forgerock.opendj.io.linger} property.
     *
     * @param linger
     *            The value of the
     *            {@link java.net.StandardSocketOptions#SO_LINGER SO_LINGER}
     *            socket option for new connections, or -1 if linger should be
     *            disabled.
     * @return A reference to this set of options.
     */
    public T setLinger(final int linger) {
        this.linger = linger;
        return getThis();
    }
    /**
     * Sets the class loader which will be used to load the
     * {@code TransportProvider}.
     * <p>
     * The default class loader will be used if no class loader is set using
     * this method.
     * <p>
     * The transport provider is loaded using {@code java.util.ServiceLoader},
     * the JDK service-provider loading facility. The provider must be
     * accessible from the same class loader that was initially queried to
     * locate the configuration file; note that this is not necessarily the
     * class loader from which the file was actually loaded. This method allows
     * to provide a class loader to be used for loading the provider.
     *
     * @param classLoader
     *            The class loader which will be used when loading the transport
     *            provider, or {@code null} if the default class loader should
     *            be used.
     * @return A reference to this set of options.
     */
    public T setProviderClassLoader(final ClassLoader classLoader) {
        this.providerClassLoader = classLoader;
        return getThis();
    }
    /**
     * Specifies the value of the
     * {@link java.net.StandardSocketOptions#SO_REUSEADDR SO_REUSEADDR} socket
     * option for new connections.
     * <p>
     * The default setting is {@code true} and may be configured using the
     * {@code org.forgerock.opendj.io.reuseAddress} property.
     *
     * @param reuseAddress
     *            The value of the
     *            {@link java.net.StandardSocketOptions#SO_REUSEADDR
     *            SO_REUSEADDR} socket option for new connections.
     * @return A reference to this set of options.
     */
    public T setReuseAddress(final boolean reuseAddress) {
        this.reuseAddress = reuseAddress;
        return getThis();
    }
    /**
     * Specifies the value of the
     * {@link java.net.StandardSocketOptions#TCP_NODELAY TCP_NODELAY} socket
     * option for new connections.
     * <p>
     * The default setting is {@code true} and may be configured using the
     * {@code org.forgerock.opendj.io.tcpNoDelay} property.
     *
     * @param tcpNoDelay
     *            The value of the
     *            {@link java.net.StandardSocketOptions#TCP_NODELAY TCP_NODELAY}
     *            socket option for new connections.
     * @return A reference to this set of options.
     */
    public T setTCPNoDelay(final boolean tcpNoDelay) {
        this.tcpNoDelay = tcpNoDelay;
        return getThis();
    }
    /**
     * Sets the name of the provider to use for transport.
     * <p>
     * Transport providers implement {@code TransportProvider} interface.
     * <p>
     * The name should correspond to the name of an existing provider, as
     * returned by {@code TransportProvider#getName()} method.
     *
     * @param providerName
     *            The name of transport provider, or {@code null} if no specific
     *            provider is preferred. In that case, the first provider found
     *            will be used.
     * @return A reference to this set of options.
     */
    public T setTransportProvider(final String providerName) {
        this.transportProvider = providerName;
        return getThis();
    }
    abstract T getThis();
}
opendj-core/src/main/java/org/forgerock/opendj/ldap/LDAPListenerOptions.java
@@ -22,30 +22,24 @@
 *
 *
 *      Copyright 2010 Sun Microsystems, Inc.
 *      Portions copyright 2012-2013 ForgeRock AS.
 *      Portions copyright 2012-2014 ForgeRock AS.
 */
package org.forgerock.opendj.ldap;
import org.forgerock.util.Reject;
/**
 * Common options for LDAP listeners.
 */
public final class LDAPListenerOptions {
public final class LDAPListenerOptions extends CommonLDAPOptions<LDAPListenerOptions> {
    private int backlog;
    private DecodeOptions decodeOptions;
    private int maxRequestSize;
    private ClassLoader providerClassLoader;
    private String transportProvider;
    /**
     * Creates a new set of listener options with default settings. SSL will not
     * be enabled, and a default set of decode options will be used.
     */
    public LDAPListenerOptions() {
        this.decodeOptions = new DecodeOptions();
        super();
    }
    /**
@@ -56,11 +50,9 @@
     *            The set of listener options to be copied.
     */
    public LDAPListenerOptions(final LDAPListenerOptions options) {
        super(options);
        this.backlog = options.backlog;
        this.maxRequestSize = options.maxRequestSize;
        this.decodeOptions = new DecodeOptions(options.decodeOptions);
        this.providerClassLoader = options.providerClassLoader;
        this.transportProvider = options.transportProvider;
    }
    /**
@@ -76,17 +68,6 @@
    }
    /**
     * Returns the decoding options which will be used to control how requests
     * and responses are decoded.
     *
     * @return The decoding options which will be used to control how requests
     *         and responses are decoded (never {@code null}).
     */
    public DecodeOptions getDecodeOptions() {
        return decodeOptions;
    }
    /**
     * Returns the maximum request size in bytes for incoming LDAP requests. If
     * an incoming request exceeds the limit then the connection will be aborted
     * by the listener. If the limit is less than {@code 1} then a default value
@@ -114,23 +95,6 @@
    }
    /**
     * Sets the decoding options which will be used to control how requests and
     * responses are decoded.
     *
     * @param decodeOptions
     *            The decoding options which will be used to control how
     *            requests and responses are decoded (never {@code null}).
     * @return A reference to this LDAP listener options.
     * @throws NullPointerException
     *             If {@code decodeOptions} was {@code null}.
     */
    public LDAPListenerOptions setDecodeOptions(final DecodeOptions decodeOptions) {
        Reject.ifNull(decodeOptions);
        this.decodeOptions = decodeOptions;
        return this;
    }
    /**
     * Sets the maximum request size in bytes for incoming LDAP requests. If an
     * incoming request exceeds the limit then the connection will be aborted by
     * the listener. If the limit is less than {@code 1} then a default value of
@@ -145,86 +109,8 @@
        return this;
    }
    /**
     * Gets the class loader which will be used to load the
     * {@code TransportProvider}.
     * <p>
     * By default this method will return {@code null} indicating that the
     * default class loader will be used.
     * <p>
     * The transport provider is loaded using {@code java.util.ServiceLoader},
     * the JDK service-provider loading facility. The provider must be
     * accessible from the same class loader that was initially queried to
     * locate the configuration file; note that this is not necessarily the
     * class loader from which the file was actually loaded. This method allows
     * to provide a class loader to be used for loading the provider.
     *
     * @return The class loader which will be used to load the transport
     *         provider, or {@code null} if the default class loader should be
     *         used.
     */
    public final ClassLoader getProviderClassLoader() {
        return providerClassLoader;
    }
    /**
     * Sets the class loader which will be used to load the
     * {@code TransportProvider}.
     * <p>
     * The default class loader will be used if no class loader is set using
     * this method.
     * <p>
     * The transport provider is loaded using {@code java.util.ServiceLoader},
     * the JDK service-provider loading facility. The provider must be
     * accessible from the same class loader that was initially queried to
     * locate the configuration file; note that this is not necessarily the
     * class loader from which the file was actually loaded. This method allows
     * to provide a class loader to be used for loading the provider.
     *
     * @param classLoader
     *            The class loader which will be used load the transport
     *            provider, or {@code null} if the default class loader should
     *            be used.
     * @return A reference to this LDAP listener options.
     */
    public final LDAPListenerOptions setProviderClassLoader(ClassLoader classLoader) {
        this.providerClassLoader = classLoader;
    @Override
    LDAPListenerOptions getThis() {
        return this;
    }
    /**
     * Returns the name of the provider used for transport.
     * <p>
     * Transport providers implement {@code TransportProvider} interface.
     * <p>
     * The name should correspond to the name of an existing provider, as
     * returned by {@code TransportProvider#getName()} method.
     *
     * @return The name of transport provider. The name is {@code null} if no
     *         specific provider has been selected. In that case, the first
     *         provider found will be used.
     */
    public String getTransportProvider() {
        return transportProvider;
    }
    /**
     * Sets the name of the provider to use for transport.
     * <p>
     * Transport providers implement {@code TransportProvider} interface.
     * <p>
     * The name should correspond to the name of an existing provider, as
     * returned by {@code TransportProvider#getName()} method.
     *
     * @param providerName
     *            The name of transport provider, or {@code null} if no specific
     *            provider is preferred. In that case, the first provider found
     *            will be used.
     * @return A reference to this LDAP listener options.
     */
    public LDAPListenerOptions setTransportProvider(String providerName) {
        this.transportProvider = providerName;
        return this;
    }
}
opendj-core/src/main/java/org/forgerock/opendj/ldap/LDAPOptions.java
@@ -54,22 +54,28 @@
 * // Connection uses StartTLS...
 * </pre>
 */
public final class LDAPOptions {
public final class LDAPOptions extends CommonLDAPOptions<LDAPOptions> {
    // Default values for options taken from Java properties.
    private static final long DEFAULT_TIMEOUT;
    private static final long DEFAULT_CONNECT_TIMEOUT;
    static {
        DEFAULT_TIMEOUT = getIntProperty("org.forgerock.opendj.io.timeout", 0);
        DEFAULT_CONNECT_TIMEOUT = getIntProperty("org.forgerock.opendj.io.connectTimeout", 5000);
    }
    private SSLContext sslContext;
    private boolean useStartTLS;
    private long timeoutInMillis;
    private DecodeOptions decodeOptions;
    private List<String> enabledCipherSuites = new LinkedList<String>();
    private List<String> enabledProtocols = new LinkedList<String>();
    private ClassLoader providerClassLoader;
    private String transportProvider;
    private long timeoutInMillis = DEFAULT_TIMEOUT;
    private long connectTimeoutInMillis = DEFAULT_CONNECT_TIMEOUT;
    private final List<String> enabledCipherSuites = new LinkedList<String>();
    private final List<String> enabledProtocols = new LinkedList<String>();
    /**
     * Creates a new set of connection options with default settings. SSL will
     * not be enabled, and a default set of decode options will be used.
     */
    public LDAPOptions() {
        this.decodeOptions = new DecodeOptions();
        super();
    }
    /**
@@ -80,25 +86,93 @@
     *            The set of connection options to be copied.
     */
    public LDAPOptions(final LDAPOptions options) {
        super(options);
        this.sslContext = options.sslContext;
        this.timeoutInMillis = options.timeoutInMillis;
        this.useStartTLS = options.useStartTLS;
        this.decodeOptions = new DecodeOptions(options.decodeOptions);
        this.enabledCipherSuites.addAll(options.getEnabledCipherSuites());
        this.enabledProtocols.addAll(options.getEnabledProtocols());
        this.providerClassLoader = options.providerClassLoader;
        this.transportProvider = options.transportProvider;
        this.connectTimeoutInMillis = options.connectTimeoutInMillis;
    }
    /**
     * Returns the decoding options which will be used to control how requests
     * and responses are decoded.
     * Adds the cipher suites enabled for secure connections with the Directory
     * Server.
     * <p>
     * The suites must be supported by the SSLContext specified in
     * {@link #setSSLContext(SSLContext)}. Following a successful call to this
     * method, only the suites listed in the protocols parameter are enabled for
     * use.
     *
     * @return The decoding options which will be used to control how requests
     *         and responses are decoded (never {@code null}).
     * @param suites
     *            Names of all the suites to enable.
     * @return A reference to this set of options.
     */
    public final DecodeOptions getDecodeOptions() {
        return decodeOptions;
    public LDAPOptions addEnabledCipherSuite(final String... suites) {
        for (final String suite : suites) {
            enabledCipherSuites.add(Reject.checkNotNull(suite));
        }
        return this;
    }
    /**
     * Adds the protocol versions enabled for secure connections with the
     * Directory Server.
     * <p>
     * The protocols must be supported by the SSLContext specified in
     * {@link #setSSLContext(SSLContext)}. Following a successful call to this
     * method, only the protocols listed in the protocols parameter are enabled
     * for use.
     *
     * @param protocols
     *            Names of all the protocols to enable.
     * @return A reference to this set of options.
     */
    public LDAPOptions addEnabledProtocol(final String... protocols) {
        for (final String protocol : protocols) {
            enabledProtocols.add(Reject.checkNotNull(protocol));
        }
        return this;
    }
    /**
     * Returns the connect timeout in the specified unit. If a connection is not
     * established within the timeout period, then a
     * {@link TimeoutResultException} error result will be returned. A timeout
     * setting of 0 causes the OS connect timeout to be used.
     * <p>
     * The default operation timeout is 10 seconds and may be configured using
     * the {@code org.forgerock.opendj.io.connectTimeout} property.
     *
     * @param unit
     *            The time unit.
     * @return The connect timeout, which may be 0 if there is no connect
     *         timeout.
     */
    public long getConnectTimeout(final TimeUnit unit) {
        return unit.convert(connectTimeoutInMillis, TimeUnit.MILLISECONDS);
    }
    /**
     * Returns the names of the protocol versions which are currently enabled
     * for secure connections with the Directory Server.
     *
     * @return An array of protocols or empty set if the default protocols are
     *         to be used.
     */
    public List<String> getEnabledCipherSuites() {
        return enabledCipherSuites;
    }
    /**
     * Returns the names of the protocol versions which are currently enabled
     * for secure connections with the Directory Server.
     *
     * @return An array of protocols or empty set if the default protocols are
     *         to be used.
     */
    public List<String> getEnabledProtocols() {
        return enabledProtocols;
    }
    /**
@@ -114,35 +188,47 @@
     *         connections with the Directory Server, which may be {@code null}
     *         indicating that connections will not be secured.
     */
    public final SSLContext getSSLContext() {
    public SSLContext getSSLContext() {
        return sslContext;
    }
    /**
     * Returns the operation timeout in the specified unit.
     * Returns the operation timeout in the specified unit. If a response is not
     * received from the Directory Server within the timeout period, then the
     * operation will be abandoned and a {@link TimeoutResultException} error
     * result returned. A timeout setting of 0 disables operation timeout
     * limits.
     * <p>
     * The default operation timeout is 0 (no timeout) and may be configured
     * using the {@code org.forgerock.opendj.io.timeout} property.
     *
     * @param unit
     *            The time unit of use.
     * @return The operation timeout.
     *            The time unit.
     * @return The operation timeout, which may be 0 if there is no operation
     *         timeout.
     */
    public final long getTimeout(final TimeUnit unit) {
    public long getTimeout(final TimeUnit unit) {
        return unit.convert(timeoutInMillis, TimeUnit.MILLISECONDS);
    }
    /**
     * Sets the decoding options which will be used to control how requests and
     * responses are decoded.
     * Sets the connect timeout. If a connection is not established within the
     * timeout period, then a {@link TimeoutResultException} error result will
     * be returned. A timeout setting of 0 causes the OS connect timeout to be
     * used.
     * <p>
     * The default operation timeout is 10 seconds and may be configured using
     * the {@code org.forgerock.opendj.io.connectTimeout} property.
     *
     * @param decodeOptions
     *            The decoding options which will be used to control how
     *            requests and responses are decoded (never {@code null}).
     * @return A reference to this LDAP connection options.
     * @throws NullPointerException
     *             If {@code decodeOptions} was {@code null}.
     * @param timeout
     *            The connect timeout, which may be 0 if there is no connect
     *            timeout.
     * @param unit
     *            The time unit.
     * @return A reference to this set of options.
     */
    public final LDAPOptions setDecodeOptions(final DecodeOptions decodeOptions) {
        Reject.ifNull(decodeOptions);
        this.decodeOptions = decodeOptions;
    public LDAPOptions setConnectTimeout(final long timeout, final TimeUnit unit) {
        this.connectTimeoutInMillis = unit.toMillis(timeout);
        return this;
    }
@@ -159,26 +245,30 @@
     *            The SSL context which will be used when initiating secure
     *            connections with the Directory Server, which may be
     *            {@code null} indicating that connections will not be secured.
     * @return A reference to this LDAP connection options.
     * @return A reference to this set of options.
     */
    public final LDAPOptions setSSLContext(final SSLContext sslContext) {
    public LDAPOptions setSSLContext(final SSLContext sslContext) {
        this.sslContext = sslContext;
        return this;
    }
    /**
     * Sets the operation timeout. If the response is not received from the
     * Directory Server in the timeout period, the operation will be abandoned
     * and an error result returned. A timeout setting of 0 disables timeout
     * limits.
     * Sets the operation timeout. If a response is not received from the
     * Directory Server within the timeout period, then the operation will be
     * abandoned and a {@link TimeoutResultException} error result returned. A
     * timeout setting of 0 disables operation timeout limits.
     * <p>
     * The default operation timeout is 0 (no timeout) and may be configured
     * using the {@code org.forgerock.opendj.io.timeout} property.
     *
     * @param timeout
     *            The operation timeout to use.
     *            The operation timeout, which may be 0 if there is no operation
     *            timeout.
     * @param unit
     *            the time unit of the time argument.
     * @return A reference to this LDAP connection options.
     *            The time unit.
     * @return A reference to this set of options.
     */
    public final LDAPOptions setTimeout(final long timeout, final TimeUnit unit) {
    public LDAPOptions setTimeout(final long timeout, final TimeUnit unit) {
        this.timeoutInMillis = unit.toMillis(timeout);
        return this;
    }
@@ -193,9 +283,9 @@
     *            {@code true} if StartTLS should be used for securing
     *            connections when an SSL context is specified, otherwise
     *            {@code false} indicating that SSL should be used.
     * @return A reference to this LDAP connection options.
     * @return A reference to this set of options.
     */
    public final LDAPOptions setUseStartTLS(final boolean useStartTLS) {
    public LDAPOptions setUseStartTLS(final boolean useStartTLS) {
        this.useStartTLS = useStartTLS;
        return this;
    }
@@ -210,154 +300,12 @@
     *         when an SSL context is specified, otherwise {@code false}
     *         indicating that SSL should be used.
     */
    public final boolean useStartTLS() {
    public boolean useStartTLS() {
        return useStartTLS;
    }
    /**
     * Adds the protocol versions enabled for secure connections with the
     * Directory Server.
     * <p>
     * The protocols must be supported by the SSLContext specified in
     * {@link #setSSLContext(SSLContext)}. Following a successful call to this
     * method, only the protocols listed in the protocols parameter are enabled
     * for use.
     *
     * @param protocols
     *            Names of all the protocols to enable.
     * @return A reference to this LDAP connection options.
     */
    public final LDAPOptions addEnabledProtocol(String... protocols) {
        for (final String protocol : protocols) {
            enabledProtocols.add(Reject.checkNotNull(protocol));
        }
    @Override
    LDAPOptions getThis() {
        return this;
    }
    /**
     * Adds the cipher suites enabled for secure connections with the Directory
     * Server.
     * <p>
     * The suites must be supported by the SSLContext specified in
     * {@link #setSSLContext(SSLContext)}. Following a successful call to this
     * method, only the suites listed in the protocols parameter are enabled for
     * use.
     *
     * @param suites
     *            Names of all the suites to enable.
     * @return A reference to this LDAP connection options.
     */
    public final LDAPOptions addEnabledCipherSuite(String... suites) {
        for (final String suite : suites) {
            enabledCipherSuites.add(Reject.checkNotNull(suite));
        }
        return this;
    }
    /**
     * Returns the names of the protocol versions which are currently enabled
     * for secure connections with the Directory Server.
     *
     * @return An array of protocols or empty set if the default protocols are
     *         to be used.
     */
    public final List<String> getEnabledProtocols() {
        return enabledProtocols;
    }
    /**
     * Returns the names of the protocol versions which are currently enabled
     * for secure connections with the Directory Server.
     *
     * @return An array of protocols or empty set if the default protocols are
     *         to be used.
     */
    public final List<String> getEnabledCipherSuites() {
        return enabledCipherSuites;
    }
    /**
     * Gets the class loader which will be used to load the
     * {@code TransportProvider}.
     * <p>
     * By default this method will return {@code null} indicating that the
     * default class loader will be used.
     * <p>
     * The transport provider is loaded using {@code java.util.ServiceLoader},
     * the JDK service-provider loading facility. The provider must be
     * accessible from the same class loader that was initially queried to
     * locate the configuration file; note that this is not necessarily the
     * class loader from which the file was actually loaded. This method allows
     * to provide a class loader to be used for loading the provider.
     *
     * @return The class loader which will be used when loading the transport
     *         provider, or {@code null} if the default class loader should be
     *         used.
     */
    public final ClassLoader getProviderClassLoader() {
        return providerClassLoader;
    }
    /**
     * Sets the class loader which will be used to load the
     * {@code TransportProvider}.
     * <p>
     * The default class loader will be used if no class loader is set using
     * this method.
     * <p>
     * The transport provider is loaded using {@code java.util.ServiceLoader},
     * the JDK service-provider loading facility. The provider must be
     * accessible from the same class loader that was initially queried to
     * locate the configuration file; note that this is not necessarily the
     * class loader from which the file was actually loaded. This method allows
     * to provide a class loader to be used for loading the provider.
     *
     * @param classLoader
     *            The class loader which will be used when loading the transport
     *            provider, or {@code null} if the default class loader should
     *            be used.
     * @return A reference to this LDAP connection options.
     */
    public final LDAPOptions setProviderClassLoader(ClassLoader classLoader) {
        this.providerClassLoader = classLoader;
        return this;
    }
    /**
     * Returns the name of the provider used for transport.
     * <p>
     * Transport providers implement {@code TransportProvider}
     * interface.
     * <p>
     * The name should correspond to the name of an existing provider, as
     * returned by {@code TransportProvider#getName()} method.
     *
     * @return The name of transport provider. The name is {@code null} if no
     *         specific provider has been selected. In that case, the first
     *         provider found will be used.
     */
    public String getTransportProvider() {
        return transportProvider;
    }
    /**
     * Sets the name of the provider to use for transport.
     * <p>
     * Transport providers implement {@code TransportProvider}
     * interface.
     * <p>
     * The name should correspond to the name of an existing provider, as
     * returned by {@code TransportProvider#getName()} method.
     *
     * @param providerName
     *            The name of transport provider, or {@code null} if no specific
     *            provider is preferred. In that case, the first provider found
     *            will be used.
     * @return A reference to this LDAP connection options.
     */
    public LDAPOptions setTransportProvider(String providerName) {
        this.transportProvider = providerName;
        return this;
    }
}
opendj-grizzly/src/main/java/org/forgerock/opendj/grizzly/GrizzlyLDAPConnectionFactory.java
@@ -27,7 +27,10 @@
package org.forgerock.opendj.grizzly;
import static com.forgerock.opendj.grizzly.GrizzlyMessages.LDAP_CONNECTION_CONNECT_TIMEOUT;
import static org.forgerock.opendj.grizzly.DefaultTCPNIOTransport.DEFAULT_TRANSPORT;
import static org.forgerock.opendj.grizzly.GrizzlyUtils.buildFilterChain;
import static org.forgerock.opendj.grizzly.GrizzlyUtils.configureConnection;
import static org.forgerock.opendj.ldap.ErrorResultException.newErrorResult;
import static org.forgerock.opendj.ldap.TimeoutChecker.TIMEOUT_CHECKER;
@@ -40,6 +43,7 @@
import javax.net.ssl.SSLEngine;
import org.forgerock.i18n.slf4j.LocalizedLogger;
import org.forgerock.opendj.ldap.Connection;
import org.forgerock.opendj.ldap.ErrorResultException;
import org.forgerock.opendj.ldap.FutureResult;
@@ -47,6 +51,7 @@
import org.forgerock.opendj.ldap.ResultCode;
import org.forgerock.opendj.ldap.ResultHandler;
import org.forgerock.opendj.ldap.TimeoutChecker;
import org.forgerock.opendj.ldap.TimeoutEventListener;
import org.forgerock.opendj.ldap.requests.Requests;
import org.forgerock.opendj.ldap.requests.StartTLSExtendedRequest;
import org.forgerock.opendj.ldap.responses.ExtendedResult;
@@ -65,19 +70,24 @@
 * LDAP connection factory implementation using Grizzly for transport.
 */
public final class GrizzlyLDAPConnectionFactory implements LDAPConnectionFactoryImpl {
    private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass();
    /**
     * Adapts a Grizzly connection completion handler to an LDAP connection
     * asynchronous future result.
     */
    @SuppressWarnings("rawtypes")
    private final class CompletionHandlerAdapter implements
            CompletionHandler<org.glassfish.grizzly.Connection> {
            CompletionHandler<org.glassfish.grizzly.Connection>, TimeoutEventListener {
        private final AsynchronousFutureResult<Connection, ResultHandler<? super Connection>> future;
        private final long timeoutEndTime;
        private CompletionHandlerAdapter(
                final AsynchronousFutureResult<Connection, ResultHandler<? super Connection>> future) {
            this.future = future;
            final long timeoutMS = getTimeout();
            this.timeoutEndTime = timeoutMS > 0 ? System.currentTimeMillis() + timeoutMS : 0;
            timeoutChecker.get().addListener(this);
        }
        @Override
@@ -98,10 +108,10 @@
            // Start TLS or install SSL layer asynchronously.
            // Give up immediately if the future has been cancelled.
            if (future.isCancelled()) {
            // Give up immediately if the future has been cancelled or timed out.
            if (future.isDone()) {
                timeoutChecker.get().removeListener(this);
                connection.close();
                releaseTransportAndTimeoutChecker();
                return;
            }
@@ -150,6 +160,7 @@
        @Override
        public void failed(final Throwable throwable) {
            // Adapt and forward.
            timeoutChecker.get().removeListener(this);
            future.handleErrorResult(adaptConnectionException(throwable));
            releaseTransportAndTimeoutChecker();
        }
@@ -159,17 +170,14 @@
            // Ignore this.
        }
        private GrizzlyLDAPConnection 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);
        private GrizzlyLDAPConnection adaptConnection(
                final org.glassfish.grizzly.Connection<?> connection) {
            configureConnection(connection, options.isTCPNoDelay(), options.isKeepAlive(), options
                    .isReuseAddress(), options.getLinger(), logger);
            final GrizzlyLDAPConnection ldapConnection =
                    new GrizzlyLDAPConnection(connection, GrizzlyLDAPConnectionFactory.this);
            if (options.getTimeout(TimeUnit.MILLISECONDS) > 0) {
                timeoutChecker.get().addListener(ldapConnection);
            }
            timeoutChecker.get().addListener(ldapConnection);
            clientFilter.registerConnection(connection, ldapConnection);
            return ldapConnection;
        }
@@ -188,20 +196,36 @@
        private void onFailure(final GrizzlyLDAPConnection connection, final Throwable t) {
            // Abort connection attempt due to error.
            connection.close();
            timeoutChecker.get().removeListener(this);
            future.handleErrorResult(adaptConnectionException(t));
            releaseTransportAndTimeoutChecker();
            connection.close();
        }
        private void onSuccess(final GrizzlyLDAPConnection connection) {
            future.handleResult(connection);
            // Close the connection if the future was cancelled.
            if (future.isCancelled()) {
            timeoutChecker.get().removeListener(this);
            if (!future.tryHandleResult(connection)) {
                // The connection has been either cancelled or it has timed out.
                connection.close();
                releaseTransportAndTimeoutChecker();
            }
        }
        @Override
        public long handleTimeout(final long currentTime) {
            if (timeoutEndTime == 0) {
                return 0;
            } else if (timeoutEndTime > currentTime) {
                return timeoutEndTime - currentTime;
            } else {
                future.handleErrorResult(newErrorResult(ResultCode.CLIENT_SIDE_TIMEOUT,
                        LDAP_CONNECTION_CONNECT_TIMEOUT.get(socketAddress, getTimeout()).toString()));
                return 0;
            }
        }
        @Override
        public long getTimeout() {
            return options.getConnectTimeout(TimeUnit.MILLISECONDS);
        }
    }
    private final LDAPClientFilter clientFilter;
@@ -226,9 +250,9 @@
            .acquire();
    /**
     * Creates a new LDAP connection factory based on Grizzly which can be used to
     * create connections to the Directory Server at the provided host and port
     * address using provided connection options.
     * Creates a new LDAP connection factory based on Grizzly 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.
@@ -260,8 +284,7 @@
        this.options = new LDAPOptions(options);
        this.clientFilter = new LDAPClientFilter(this.options.getDecodeOptions(), 0);
        this.defaultFilterChain =
                GrizzlyUtils.buildFilterChain(this.transport.get().getProcessor(), clientFilter);
                buildFilterChain(this.transport.get().getProcessor(), clientFilter);
    }
    @Override
@@ -289,8 +312,7 @@
                        .build();
        final AsynchronousFutureResult<Connection, ResultHandler<? super Connection>> future =
                new AsynchronousFutureResult<Connection, ResultHandler<? super Connection>>(handler);
        final CompletionHandlerAdapter cha = new CompletionHandlerAdapter(future);
        connectorHandler.connect(socketAddress, cha);
        connectorHandler.connect(socketAddress, new CompletionHandlerAdapter(future));
        return future;
    }
opendj-grizzly/src/main/java/org/forgerock/opendj/grizzly/GrizzlyLDAPListener.java
@@ -35,7 +35,6 @@
import org.forgerock.i18n.LocalizableMessage;
import org.forgerock.i18n.slf4j.LocalizedLogger;
import org.forgerock.opendj.ldap.Connections;
import org.forgerock.opendj.ldap.DecodeOptions;
import org.forgerock.opendj.ldap.LDAPClientContext;
import org.forgerock.opendj.ldap.LDAPListenerOptions;
import org.forgerock.opendj.ldap.ServerConnectionFactory;
@@ -51,13 +50,13 @@
 * LDAP listener implementation using Grizzly for transport.
 */
public final class GrizzlyLDAPListener implements LDAPListenerImpl {
    private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass();
    private final ReferenceCountedObject<TCPNIOTransport>.Reference transport;
    private final ServerConnectionFactory<LDAPClientContext, Integer> connectionFactory;
    private final TCPNIOServerConnection serverConnection;
    private final AtomicBoolean isClosed = new AtomicBoolean();
    private final InetSocketAddress socketAddress;
    private final LDAPListenerOptions options;
    /**
     * Creates a new LDAP listener implementation which will listen for LDAP
@@ -104,9 +103,10 @@
            final LDAPListenerOptions options, TCPNIOTransport transport) throws IOException {
        this.transport = DEFAULT_TRANSPORT.acquireIfNull(transport);
        this.connectionFactory = factory;
        final DecodeOptions decodeOptions = new DecodeOptions(options.getDecodeOptions());
        this.options = new LDAPListenerOptions(options);
        final LDAPServerFilter serverFilter =
                new LDAPServerFilter(this, decodeOptions, options.getMaxRequestSize());
                new LDAPServerFilter(this, this.options.getDecodeOptions(), this.options
                        .getMaxRequestSize());
        final FilterChain ldapChain =
                GrizzlyUtils.buildFilterChain(this.transport.get().getProcessor(), serverFilter);
        final TCPNIOBindingHandler bindingHandler =
@@ -155,4 +155,8 @@
    ServerConnectionFactory<LDAPClientContext, Integer> getConnectionFactory() {
        return connectionFactory;
    }
    LDAPListenerOptions getLDAPListenerOptions() {
        return options;
    }
}
opendj-grizzly/src/main/java/org/forgerock/opendj/grizzly/GrizzlyUtils.java
@@ -25,10 +25,17 @@
 */
package org.forgerock.opendj.grizzly;
import java.io.IOException;
import java.net.SocketOption;
import java.net.StandardSocketOptions;
import java.nio.channels.SocketChannel;
import org.forgerock.i18n.slf4j.LocalizedLogger;
import org.forgerock.opendj.io.LDAP;
import org.forgerock.opendj.io.LDAPReader;
import org.forgerock.opendj.io.LDAPWriter;
import org.forgerock.opendj.ldap.DecodeOptions;
import org.glassfish.grizzly.Connection;
import org.glassfish.grizzly.Processor;
import org.glassfish.grizzly.ThreadCache;
import org.glassfish.grizzly.filterchain.Filter;
@@ -36,6 +43,7 @@
import org.glassfish.grizzly.filterchain.FilterChainBuilder;
import org.glassfish.grizzly.filterchain.TransportFilter;
import org.glassfish.grizzly.memory.MemoryManager;
import org.glassfish.grizzly.nio.transport.TCPNIOConnection;
import org.glassfish.grizzly.ssl.SSLFilter;
/**
@@ -65,7 +73,7 @@
     *         is a {@code FilterChain}, and having the provided filter as the
     *         last filter
     */
    public static FilterChain buildFilterChain(Processor<?> processor, Filter filter) {
    static FilterChain buildFilterChain(Processor<?> processor, Filter filter) {
        if (processor instanceof FilterChain) {
            return FilterChainBuilder.stateless().addAll((FilterChain) processor).add(filter).build();
        } else {
@@ -89,7 +97,7 @@
     *            connection to update with the new filter chain containing the
     *            provided filter
     */
    public static void addFilterToConnection(final Filter filter, org.glassfish.grizzly.Connection<?> connection) {
    static void addFilterToConnection(final Filter filter, Connection<?> connection) {
        final FilterChain currentChain = (FilterChain) connection.getProcessor();
        final FilterChain newChain = addFilterToChain(filter, currentChain);
        connection.setProcessor(newChain);
@@ -111,7 +119,7 @@
     *            initial filter chain
     * @return a new filter chain which includes the provided filter
     */
    public static FilterChain addFilterToChain(final Filter filter, final FilterChain chain) {
    static FilterChain addFilterToChain(final Filter filter, final FilterChain chain) {
        // By default, before LDAP filter which is the last one
        int indexToAddFilter = chain.size() - 1;
        if (filter instanceof SSLFilter) {
@@ -139,8 +147,8 @@
     *            The memory manager to use for buffering.
     * @return a LDAP reader
     */
    public static LDAPReader<ASN1BufferReader> createReader(DecodeOptions decodeOptions, int maxASN1ElementSize,
            MemoryManager<?> memoryManager) {
    static LDAPReader<ASN1BufferReader> createReader(DecodeOptions decodeOptions,
            int maxASN1ElementSize, MemoryManager<?> memoryManager) {
        ASN1BufferReader asn1Reader = new ASN1BufferReader(maxASN1ElementSize, memoryManager);
        return LDAP.getReader(asn1Reader, decodeOptions);
    }
@@ -155,7 +163,7 @@
     * @return a LDAP writer
     */
    @SuppressWarnings("unchecked")
    public static LDAPWriter<ASN1BufferWriter> getWriter() {
    static LDAPWriter<ASN1BufferWriter> getWriter() {
        LDAPWriter<ASN1BufferWriter> writer = ThreadCache.takeFromCache(WRITER_INDEX);
        if (writer == null) {
            writer = LDAP.getWriter(new ASN1BufferWriter());
@@ -172,11 +180,38 @@
     *
     * @param writer LDAP writer to recycle
     */
    public static void recycleWriter(LDAPWriter<ASN1BufferWriter> writer) {
    static void recycleWriter(LDAPWriter<ASN1BufferWriter> writer) {
        writer.getASN1Writer().recycle();
        ThreadCache.putToCache(WRITER_INDEX, writer);
    }
    static void configureConnection(final Connection<?> connection, final boolean tcpNoDelay,
            final boolean keepAlive, final boolean reuseAddress, final int linger,
            final LocalizedLogger logger) {
        /*
         * Test shows that its much faster with non block writes but risk
         * running out of memory if the server is slow.
         */
        connection.configureBlocking(true);
        // Configure socket options.
        final SocketChannel channel = (SocketChannel) ((TCPNIOConnection) connection).getChannel();
        setSocketOption(channel, StandardSocketOptions.TCP_NODELAY, tcpNoDelay, logger);
        setSocketOption(channel, StandardSocketOptions.SO_KEEPALIVE, keepAlive, logger);
        setSocketOption(channel, StandardSocketOptions.SO_REUSEADDR, reuseAddress, logger);
        setSocketOption(channel, StandardSocketOptions.SO_LINGER, linger, logger);
    }
    private static <T> void setSocketOption(final SocketChannel channel,
            final SocketOption<T> option, final T value, final LocalizedLogger logger) {
        try {
            channel.setOption(option, value);
        } catch (final IOException e) {
            logger.traceException(e, "Unable to set " + option.name()
                    + " to %d on client connection", value);
        }
    }
    // Prevent instantiation.
    private GrizzlyUtils() {
        // No implementation required.
opendj-grizzly/src/main/java/org/forgerock/opendj/grizzly/LDAPServerFilter.java
@@ -27,6 +27,8 @@
package org.forgerock.opendj.grizzly;
import static org.forgerock.opendj.grizzly.GrizzlyUtils.configureConnection;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.security.GeneralSecurityException;
@@ -36,6 +38,7 @@
import javax.net.ssl.SSLEngine;
import javax.net.ssl.SSLSession;
import org.forgerock.i18n.slf4j.LocalizedLogger;
import org.forgerock.opendj.io.AbstractLDAPMessageHandler;
import org.forgerock.opendj.io.LDAP;
import org.forgerock.opendj.io.LDAPReader;
@@ -46,6 +49,7 @@
import org.forgerock.opendj.ldap.ErrorResultException;
import org.forgerock.opendj.ldap.IntermediateResponseHandler;
import org.forgerock.opendj.ldap.LDAPClientContext;
import org.forgerock.opendj.ldap.LDAPListenerOptions;
import org.forgerock.opendj.ldap.ResultCode;
import org.forgerock.opendj.ldap.ResultHandler;
import org.forgerock.opendj.ldap.SSLContextBuilder;
@@ -82,7 +86,6 @@
import org.glassfish.grizzly.ssl.SSLEngineConfigurator;
import org.glassfish.grizzly.ssl.SSLFilter;
import org.glassfish.grizzly.ssl.SSLUtils;
import org.forgerock.util.Reject;
/**
@@ -639,6 +642,8 @@
    private static final Attribute<ServerRequestHandler> REQUEST_HANDLER_ATTR =
            Grizzly.DEFAULT_ATTRIBUTE_BUILDER.createAttribute("ServerRequestHandler");
    private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass();
    /**
     * A dummy SSL client engine configurator as SSLFilter only needs server
     * config. This prevents Grizzly from needlessly using JVM defaults which
@@ -829,7 +834,9 @@
    @Override
    public NextAction handleAccept(final FilterChainContext ctx) throws IOException {
        final Connection<?> connection = ctx.getConnection();
        connection.configureBlocking(true);
        LDAPListenerOptions options = listener.getLDAPListenerOptions();
        configureConnection(connection, options.isTCPNoDelay(), options.isKeepAlive(), options
                .isReuseAddress(), options.getLinger(), logger);
        try {
            final ClientContextImpl clientContext = new ClientContextImpl(connection);
            final ServerConnection<Integer> serverConn =
opendj-grizzly/src/main/resources/com/forgerock/opendj/grizzly/grizzly.properties
@@ -21,10 +21,12 @@
# CDDL HEADER END
#
#
#      Copyright 2013 ForgeRock AS.
#      Copyright 2013-2014 ForgeRock AS.
#
LDAP_CONNECTION_REQUEST_TIMEOUT=The request has failed because no response \
 was received from the server within the %d ms timeout
LDAP_CONNECTION_CONNECT_TIMEOUT=The connection attempt to server %s has failed \
 because the connection timeout period of %d ms was exceeded
LDAP_CONNECTION_BIND_OR_START_TLS_REQUEST_TIMEOUT=The bind or StartTLS request \
 has failed because no response was received from the server within the %d ms \
 timeout. The LDAP connection is now in an invalid state and can no longer be used
opendj-grizzly/src/test/java/org/forgerock/opendj/grizzly/GrizzlyLDAPConnectionFactoryTestCase.java
@@ -21,17 +21,23 @@
 * CDDL HEADER END
 *
 *
 *     Copyright 2013 ForgeRock AS.
 *     Copyright 2013-2014 ForgeRock AS.
 */
package org.forgerock.opendj.grizzly;
import static org.fest.assertions.Assertions.assertThat;
import static org.fest.assertions.Fail.fail;
import static org.forgerock.opendj.ldap.TestCaseUtils.findFreeSocketAddress;
import static org.mockito.Mockito.mock;
import static org.forgerock.opendj.ldap.requests.Requests.newSimpleBindRequest;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Matchers.same;
import static org.mockito.Mockito.*;
import java.io.IOException;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicReference;
import org.forgerock.opendj.ldap.Connection;
@@ -55,19 +61,6 @@
import org.forgerock.opendj.ldap.ServerConnection;
import org.forgerock.opendj.ldap.ServerConnectionFactory;
import org.forgerock.opendj.ldap.TimeoutResultException;
import org.testng.annotations.Test;
import static org.fest.assertions.Fail.fail;
import static org.forgerock.opendj.ldap.requests.Requests.newSimpleBindRequest;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Matchers.same;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyZeroInteractions;
import java.util.concurrent.TimeoutException;
import org.forgerock.opendj.ldap.requests.AbandonRequest;
import org.forgerock.opendj.ldap.requests.BindRequest;
import org.forgerock.opendj.ldap.requests.SearchRequest;
@@ -78,13 +71,18 @@
import org.mockito.stubbing.Answer;
import org.mockito.stubbing.Stubber;
import org.testng.annotations.AfterClass;
import org.testng.annotations.Test;
/**
 * Tests the {@link LDAPConnectionFactory} class.
 */
@SuppressWarnings({ "javadoc", "unchecked" })
public class GrizzlyLDAPConnectionFactoryTestCase extends SdkTestCase {
    // Manual testing has gone up to 10000 iterations.
    /*
     * The number of test iterations for unit tests which attempt to expose
     * potential race conditions. Manual testing has gone up to 10000
     * iterations.
     */
    private static final int ITERATIONS = 100;
    // Test timeout for tests which need to wait for network events.
@@ -117,6 +115,30 @@
        server.close();
    }
    @Test(description = "OPENDJ-1197")
    public void testClientSideConnectTimeout() throws Exception {
        // Use an non-local unreachable network address.
        final ConnectionFactory factory =
                new LDAPConnectionFactory("10.20.30.40", 1389, new LDAPOptions().setConnectTimeout(
                        1, TimeUnit.MILLISECONDS));
        try {
            for (int i = 0; i < ITERATIONS; i++) {
                final ResultHandler<Connection> handler = mock(ResultHandler.class);
                final FutureResult<Connection> future = factory.getConnectionAsync(handler);
                // Wait for the connect to timeout.
                try {
                    future.get(TEST_TIMEOUT, TimeUnit.SECONDS);
                    fail("The connect request succeeded unexpectedly");
                } catch (TimeoutResultException e) {
                    verifyResultCodeIsClientSideTimeout(e);
                    verify(handler).handleErrorResult(same(e));
                }
            }
        } finally {
            factory.close();
        }
    }
    /**
     * Unit test for OPENDJ-1247: a locally timed out bind request will leave a
     * connection in an invalid state since a bind (or startTLS) is in progress
@@ -130,41 +152,39 @@
        registerBindEvent();
        registerCloseEvent();
        for (int i = 0; i < ITERATIONS; i++) {
            final Connection connection = factory.getConnection();
        final Connection connection = factory.getConnection();
        try {
            waitForConnect();
            final MockConnectionEventListener listener = new MockConnectionEventListener();
            connection.addConnectionEventListener(listener);
            final ResultHandler<BindResult> handler = mock(ResultHandler.class);
            final FutureResult<BindResult> future =
                    connection.bindAsync(newSimpleBindRequest(), null, handler);
            waitForBind();
            // Wait for the request to timeout.
            try {
                waitForConnect();
                final MockConnectionEventListener listener = new MockConnectionEventListener();
                connection.addConnectionEventListener(listener);
                future.get(TEST_TIMEOUT, TimeUnit.SECONDS);
                fail("The bind request succeeded unexpectedly");
            } catch (TimeoutResultException e) {
                verifyResultCodeIsClientSideTimeout(e);
                verify(handler).handleErrorResult(same(e));
                final ResultHandler<BindResult> handler = mock(ResultHandler.class);
                final FutureResult<BindResult> future =
                        connection.bindAsync(newSimpleBindRequest(), null, handler);
                waitForBind();
                // Wait for the request to timeout.
                try {
                    future.get(TEST_TIMEOUT, TimeUnit.SECONDS);
                    fail("The bind request succeeded unexpectedly");
                } catch (TimeoutResultException e) {
                    verifyResultCodeIsClientSideTimeout(e);
                    verify(handler).handleErrorResult(same(e));
                    /*
                     * The connection should no longer be valid, the event
                     * listener should have been notified, but no abandon should
                     * have been sent.
                     */
                    listener.awaitError(TEST_TIMEOUT, TimeUnit.SECONDS);
                    assertThat(connection.isValid()).isFalse();
                    verifyResultCodeIsClientSideTimeout(listener.getError());
                    connection.close();
                    waitForClose();
                    verifyNoAbandonSent();
                }
            } finally {
                /*
                 * The connection should no longer be valid, the event listener
                 * should have been notified, but no abandon should have been
                 * sent.
                 */
                listener.awaitError(TEST_TIMEOUT, TimeUnit.SECONDS);
                assertThat(connection.isValid()).isFalse();
                verifyResultCodeIsClientSideTimeout(listener.getError());
                connection.close();
                waitForClose();
                verifyNoAbandonSent();
            }
        } finally {
            connection.close();
        }
    }