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(); } }